mirror of https://github.com/grafana/grafana
commit
89ea47e7fb
@ -0,0 +1,63 @@ |
||||
import React from 'react'; |
||||
import { shallow } from 'enzyme'; |
||||
import { Props, TeamGroupSync } from './TeamGroupSync'; |
||||
import { TeamGroup } from '../../types'; |
||||
import { getMockTeamGroups } from './__mocks__/teamMocks'; |
||||
|
||||
const setup = (propOverrides?: object) => { |
||||
const props: Props = { |
||||
groups: [] as TeamGroup[], |
||||
loadTeamGroups: jest.fn(), |
||||
addTeamGroup: jest.fn(), |
||||
removeTeamGroup: jest.fn(), |
||||
}; |
||||
|
||||
Object.assign(props, propOverrides); |
||||
|
||||
const wrapper = shallow(<TeamGroupSync {...props} />); |
||||
const instance = wrapper.instance() as TeamGroupSync; |
||||
|
||||
return { |
||||
wrapper, |
||||
instance, |
||||
}; |
||||
}; |
||||
|
||||
describe('Render', () => { |
||||
it('should render component', () => { |
||||
const { wrapper } = setup(); |
||||
|
||||
expect(wrapper).toMatchSnapshot(); |
||||
}); |
||||
|
||||
it('should render groups table', () => { |
||||
const { wrapper } = setup({ |
||||
groups: getMockTeamGroups(3), |
||||
}); |
||||
|
||||
expect(wrapper).toMatchSnapshot(); |
||||
}); |
||||
}); |
||||
|
||||
describe('Functions', () => { |
||||
it('should call add group', () => { |
||||
const { instance } = setup(); |
||||
|
||||
instance.setState({ newGroupId: 'some/group' }); |
||||
const mockEvent = { preventDefault: jest.fn() }; |
||||
|
||||
instance.onAddGroup(mockEvent); |
||||
|
||||
expect(instance.props.addTeamGroup).toHaveBeenCalledWith('some/group'); |
||||
}); |
||||
|
||||
it('should call remove group', () => { |
||||
const { instance } = setup(); |
||||
|
||||
const mockGroup: TeamGroup = { teamId: 1, groupId: 'some/group' }; |
||||
|
||||
instance.onRemoveGroup(mockGroup); |
||||
|
||||
expect(instance.props.removeTeamGroup).toHaveBeenCalledWith('some/group'); |
||||
}); |
||||
}); |
@ -0,0 +1,281 @@ |
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP |
||||
|
||||
exports[`Render should render component 1`] = ` |
||||
<div> |
||||
<div |
||||
className="page-action-bar" |
||||
> |
||||
<h3 |
||||
className="page-sub-heading" |
||||
> |
||||
External group sync |
||||
</h3> |
||||
<class_1 |
||||
className="page-sub-heading-icon" |
||||
content="Sync LDAP or OAuth groups with your Grafana teams." |
||||
placement="auto" |
||||
> |
||||
<i |
||||
className="gicon gicon-question gicon--has-hover" |
||||
/> |
||||
</class_1> |
||||
<div |
||||
className="page-action-bar__spacer" |
||||
/> |
||||
</div> |
||||
<Component |
||||
in={false} |
||||
> |
||||
<div |
||||
className="cta-form" |
||||
> |
||||
<button |
||||
className="cta-form__close btn btn-transparent" |
||||
onClick={[Function]} |
||||
> |
||||
<i |
||||
className="fa fa-close" |
||||
/> |
||||
</button> |
||||
<h5> |
||||
Add External Group |
||||
</h5> |
||||
<form |
||||
className="gf-form-inline" |
||||
onSubmit={[Function]} |
||||
> |
||||
<div |
||||
className="gf-form" |
||||
> |
||||
<input |
||||
className="gf-form-input width-30" |
||||
onChange={[Function]} |
||||
placeholder="cn=ops,ou=groups,dc=grafana,dc=org" |
||||
type="text" |
||||
value="" |
||||
/> |
||||
</div> |
||||
<div |
||||
className="gf-form" |
||||
> |
||||
<button |
||||
className="btn btn-success gf-form-btn" |
||||
disabled={true} |
||||
type="submit" |
||||
> |
||||
Add group |
||||
</button> |
||||
</div> |
||||
</form> |
||||
</div> |
||||
</Component> |
||||
<div |
||||
className="empty-list-cta" |
||||
> |
||||
<div |
||||
className="empty-list-cta__title" |
||||
> |
||||
There are no external groups to sync with |
||||
</div> |
||||
<button |
||||
className="empty-list-cta__button btn btn-xlarge btn-success" |
||||
onClick={[Function]} |
||||
> |
||||
<i |
||||
className="gicon gicon-add-team" |
||||
/> |
||||
Add Group |
||||
</button> |
||||
<div |
||||
className="empty-list-cta__pro-tip" |
||||
> |
||||
<i |
||||
className="fa fa-rocket" |
||||
/> |
||||
|
||||
Sync LDAP or OAuth groups with your Grafana teams. |
||||
<a |
||||
className="text-link empty-list-cta__pro-tip-link" |
||||
href="asd" |
||||
target="_blank" |
||||
> |
||||
Learn more |
||||
</a> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
`; |
||||
|
||||
exports[`Render should render groups table 1`] = ` |
||||
<div> |
||||
<div |
||||
className="page-action-bar" |
||||
> |
||||
<h3 |
||||
className="page-sub-heading" |
||||
> |
||||
External group sync |
||||
</h3> |
||||
<class_1 |
||||
className="page-sub-heading-icon" |
||||
content="Sync LDAP or OAuth groups with your Grafana teams." |
||||
placement="auto" |
||||
> |
||||
<i |
||||
className="gicon gicon-question gicon--has-hover" |
||||
/> |
||||
</class_1> |
||||
<div |
||||
className="page-action-bar__spacer" |
||||
/> |
||||
<button |
||||
className="btn btn-success pull-right" |
||||
onClick={[Function]} |
||||
> |
||||
<i |
||||
className="fa fa-plus" |
||||
/> |
||||
Add group |
||||
</button> |
||||
</div> |
||||
<Component |
||||
in={false} |
||||
> |
||||
<div |
||||
className="cta-form" |
||||
> |
||||
<button |
||||
className="cta-form__close btn btn-transparent" |
||||
onClick={[Function]} |
||||
> |
||||
<i |
||||
className="fa fa-close" |
||||
/> |
||||
</button> |
||||
<h5> |
||||
Add External Group |
||||
</h5> |
||||
<form |
||||
className="gf-form-inline" |
||||
onSubmit={[Function]} |
||||
> |
||||
<div |
||||
className="gf-form" |
||||
> |
||||
<input |
||||
className="gf-form-input width-30" |
||||
onChange={[Function]} |
||||
placeholder="cn=ops,ou=groups,dc=grafana,dc=org" |
||||
type="text" |
||||
value="" |
||||
/> |
||||
</div> |
||||
<div |
||||
className="gf-form" |
||||
> |
||||
<button |
||||
className="btn btn-success gf-form-btn" |
||||
disabled={true} |
||||
type="submit" |
||||
> |
||||
Add group |
||||
</button> |
||||
</div> |
||||
</form> |
||||
</div> |
||||
</Component> |
||||
<div |
||||
className="admin-list-table" |
||||
> |
||||
<table |
||||
className="filter-table filter-table--hover form-inline" |
||||
> |
||||
<thead> |
||||
<tr> |
||||
<th> |
||||
External Group ID |
||||
</th> |
||||
<th |
||||
style={ |
||||
Object { |
||||
"width": "1%", |
||||
} |
||||
} |
||||
/> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr |
||||
key="group-1" |
||||
> |
||||
<td> |
||||
group-1 |
||||
</td> |
||||
<td |
||||
style={ |
||||
Object { |
||||
"width": "1%", |
||||
} |
||||
} |
||||
> |
||||
<a |
||||
className="btn btn-danger btn-mini" |
||||
onClick={[Function]} |
||||
> |
||||
<i |
||||
className="fa fa-remove" |
||||
/> |
||||
</a> |
||||
</td> |
||||
</tr> |
||||
<tr |
||||
key="group-2" |
||||
> |
||||
<td> |
||||
group-2 |
||||
</td> |
||||
<td |
||||
style={ |
||||
Object { |
||||
"width": "1%", |
||||
} |
||||
} |
||||
> |
||||
<a |
||||
className="btn btn-danger btn-mini" |
||||
onClick={[Function]} |
||||
> |
||||
<i |
||||
className="fa fa-remove" |
||||
/> |
||||
</a> |
||||
</td> |
||||
</tr> |
||||
<tr |
||||
key="group-3" |
||||
> |
||||
<td> |
||||
group-3 |
||||
</td> |
||||
<td |
||||
style={ |
||||
Object { |
||||
"width": "1%", |
||||
} |
||||
} |
||||
> |
||||
<a |
||||
className="btn btn-danger btn-mini" |
||||
onClick={[Function]} |
||||
> |
||||
<i |
||||
className="fa fa-remove" |
||||
/> |
||||
</a> |
||||
</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
</div> |
||||
`; |
@ -1,156 +0,0 @@ |
||||
import { types, getEnv, flow } from 'mobx-state-tree'; |
||||
|
||||
export const TeamMemberModel = types.model('TeamMember', { |
||||
userId: types.identifier(types.number), |
||||
teamId: types.number, |
||||
avatarUrl: types.string, |
||||
email: types.string, |
||||
login: types.string, |
||||
}); |
||||
|
||||
type TeamMemberType = typeof TeamMemberModel.Type; |
||||
export interface TeamMember extends TeamMemberType {} |
||||
|
||||
export const TeamGroupModel = types.model('TeamGroup', { |
||||
groupId: types.identifier(types.string), |
||||
teamId: types.number, |
||||
}); |
||||
|
||||
type TeamGroupType = typeof TeamGroupModel.Type; |
||||
export interface TeamGroup extends TeamGroupType {} |
||||
|
||||
export const TeamModel = types |
||||
.model('Team', { |
||||
id: types.identifier(types.number), |
||||
name: types.string, |
||||
avatarUrl: types.string, |
||||
email: types.string, |
||||
memberCount: types.number, |
||||
search: types.optional(types.string, ''), |
||||
members: types.optional(types.map(TeamMemberModel), {}), |
||||
groups: types.optional(types.map(TeamGroupModel), {}), |
||||
}) |
||||
.views(self => ({ |
||||
get filteredMembers(this: Team) { |
||||
const members = this.members.values(); |
||||
const regex = new RegExp(self.search, 'i'); |
||||
return members.filter(member => { |
||||
return regex.test(member.login) || regex.test(member.email); |
||||
}); |
||||
}, |
||||
})) |
||||
.actions(self => ({ |
||||
setName(name: string) { |
||||
self.name = name; |
||||
}, |
||||
|
||||
setEmail(email: string) { |
||||
self.email = email; |
||||
}, |
||||
|
||||
setSearchQuery(query: string) { |
||||
self.search = query; |
||||
}, |
||||
|
||||
update: flow(function* load() { |
||||
const backendSrv = getEnv(self).backendSrv; |
||||
|
||||
yield backendSrv.put(`/api/teams/${self.id}`, { |
||||
name: self.name, |
||||
email: self.email, |
||||
}); |
||||
}), |
||||
|
||||
loadMembers: flow(function* load() { |
||||
const backendSrv = getEnv(self).backendSrv; |
||||
const rsp = yield backendSrv.get(`/api/teams/${self.id}/members`); |
||||
self.members.clear(); |
||||
|
||||
for (const member of rsp) { |
||||
self.members.set(member.userId.toString(), TeamMemberModel.create(member)); |
||||
} |
||||
}), |
||||
|
||||
removeMember: flow(function* load(member: TeamMember) { |
||||
const backendSrv = getEnv(self).backendSrv; |
||||
yield backendSrv.delete(`/api/teams/${self.id}/members/${member.userId}`); |
||||
// remove from store map
|
||||
self.members.delete(member.userId.toString()); |
||||
}), |
||||
|
||||
addMember: flow(function* load(userId: number) { |
||||
const backendSrv = getEnv(self).backendSrv; |
||||
yield backendSrv.post(`/api/teams/${self.id}/members`, { userId: userId }); |
||||
}), |
||||
|
||||
loadGroups: flow(function* load() { |
||||
const backendSrv = getEnv(self).backendSrv; |
||||
const rsp = yield backendSrv.get(`/api/teams/${self.id}/groups`); |
||||
self.groups.clear(); |
||||
|
||||
for (const group of rsp) { |
||||
self.groups.set(group.groupId, TeamGroupModel.create(group)); |
||||
} |
||||
}), |
||||
|
||||
addGroup: flow(function* load(groupId: string) { |
||||
const backendSrv = getEnv(self).backendSrv; |
||||
yield backendSrv.post(`/api/teams/${self.id}/groups`, { groupId: groupId }); |
||||
self.groups.set( |
||||
groupId, |
||||
TeamGroupModel.create({ |
||||
teamId: self.id, |
||||
groupId: groupId, |
||||
}) |
||||
); |
||||
}), |
||||
|
||||
removeGroup: flow(function* load(groupId: string) { |
||||
const backendSrv = getEnv(self).backendSrv; |
||||
yield backendSrv.delete(`/api/teams/${self.id}/groups/${groupId}`); |
||||
self.groups.delete(groupId); |
||||
}), |
||||
})); |
||||
|
||||
type TeamType = typeof TeamModel.Type; |
||||
export interface Team extends TeamType {} |
||||
|
||||
export const TeamsStore = types |
||||
.model('TeamsStore', { |
||||
map: types.map(TeamModel), |
||||
search: types.optional(types.string, ''), |
||||
}) |
||||
.views(self => ({ |
||||
get filteredTeams(this: any) { |
||||
const teams = this.map.values(); |
||||
const regex = new RegExp(self.search, 'i'); |
||||
return teams.filter(team => { |
||||
return regex.test(team.name); |
||||
}); |
||||
}, |
||||
})) |
||||
.actions(self => ({ |
||||
loadTeams: flow(function* load() { |
||||
const backendSrv = getEnv(self).backendSrv; |
||||
const rsp = yield backendSrv.get('/api/teams/search/', { perpage: 50, page: 1 }); |
||||
self.map.clear(); |
||||
|
||||
for (const team of rsp.teams) { |
||||
self.map.set(team.id.toString(), TeamModel.create(team)); |
||||
} |
||||
}), |
||||
|
||||
setSearchQuery(query: string) { |
||||
self.search = query; |
||||
}, |
||||
|
||||
loadById: flow(function* load(id: string) { |
||||
if (self.map.has(id)) { |
||||
return; |
||||
} |
||||
|
||||
const backendSrv = getEnv(self).backendSrv; |
||||
const team = yield backendSrv.get(`/api/teams/${id}`); |
||||
self.map.set(id, TeamModel.create(team)); |
||||
}), |
||||
})); |
@ -0,0 +1,35 @@ |
||||
export interface AlertRuleDTO { |
||||
id: number; |
||||
dashboardId: number; |
||||
dashboardUid: string; |
||||
dashboardSlug: string; |
||||
panelId: number; |
||||
name: string; |
||||
state: string; |
||||
newStateDate: string; |
||||
evalDate: string; |
||||
evalData?: object; |
||||
executionError: string; |
||||
url: string; |
||||
} |
||||
|
||||
export interface AlertRule { |
||||
id: number; |
||||
dashboardId: number; |
||||
panelId: number; |
||||
name: string; |
||||
state: string; |
||||
stateText: string; |
||||
stateIcon: string; |
||||
stateClass: string; |
||||
stateAge: string; |
||||
url: string; |
||||
info?: string; |
||||
executionError?: string; |
||||
evalData?: { noData: boolean }; |
||||
} |
||||
|
||||
export interface AlertRulesState { |
||||
items: AlertRule[]; |
||||
searchQuery: string; |
||||
} |
@ -0,0 +1,15 @@ |
||||
export interface LocationUpdate { |
||||
path?: string; |
||||
query?: UrlQueryMap; |
||||
routeParams?: UrlQueryMap; |
||||
} |
||||
|
||||
export interface LocationState { |
||||
url: string; |
||||
path: string; |
||||
query: UrlQueryMap; |
||||
routeParams: UrlQueryMap; |
||||
} |
||||
|
||||
export type UrlQueryValue = string | number | boolean | string[] | number[] | boolean[]; |
||||
export type UrlQueryMap = { [s: string]: UrlQueryValue }; |
@ -0,0 +1,22 @@ |
||||
export interface NavModelItem { |
||||
text: string; |
||||
url: string; |
||||
subTitle?: string; |
||||
icon?: string; |
||||
img?: string; |
||||
id: string; |
||||
active?: boolean; |
||||
hideFromTabs?: boolean; |
||||
divider?: boolean; |
||||
children?: NavModelItem[]; |
||||
breadcrumbs?: Array<{ title: string; url: string }>; |
||||
target?: string; |
||||
parentItem?: NavModelItem; |
||||
} |
||||
|
||||
export interface NavModel { |
||||
main: NavModelItem; |
||||
node: NavModelItem; |
||||
} |
||||
|
||||
export type NavIndex = { [s: string]: NavModelItem }; |
@ -0,0 +1,32 @@ |
||||
export interface Team { |
||||
id: number; |
||||
name: string; |
||||
avatarUrl: string; |
||||
email: string; |
||||
memberCount: number; |
||||
} |
||||
|
||||
export interface TeamMember { |
||||
userId: number; |
||||
teamId: number; |
||||
avatarUrl: string; |
||||
email: string; |
||||
login: string; |
||||
} |
||||
|
||||
export interface TeamGroup { |
||||
groupId: string; |
||||
teamId: number; |
||||
} |
||||
|
||||
export interface TeamsState { |
||||
teams: Team[]; |
||||
searchQuery: string; |
||||
} |
||||
|
||||
export interface TeamState { |
||||
team: Team; |
||||
members: TeamMember[]; |
||||
groups: TeamGroup[]; |
||||
searchMemberQuery: string; |
||||
} |
Loading…
Reference in new issue