From 0cfcf2685e66af76895664c86f562952d63ca812 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Mon, 10 Sep 2018 16:58:17 +0200 Subject: [PATCH] actions for group sync --- .../app/features/teams/TeamGroupSync.test.tsx | 0 public/app/features/teams/TeamGroupSync.tsx | 81 ++++++++++++------- public/app/features/teams/TeamPages.tsx | 3 +- public/app/features/teams/state/actions.ts | 68 +++++++++++++++- public/app/features/teams/state/reducers.ts | 3 + public/app/features/teams/state/selectors.ts | 1 + 6 files changed, 120 insertions(+), 36 deletions(-) create mode 100644 public/app/features/teams/TeamGroupSync.test.tsx diff --git a/public/app/features/teams/TeamGroupSync.test.tsx b/public/app/features/teams/TeamGroupSync.test.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/public/app/features/teams/TeamGroupSync.tsx b/public/app/features/teams/TeamGroupSync.tsx index 6562820d717..39fdd8d413e 100644 --- a/public/app/features/teams/TeamGroupSync.tsx +++ b/public/app/features/teams/TeamGroupSync.tsx @@ -1,11 +1,16 @@ -import React from 'react'; -import { hot } from 'react-hot-loader'; +import React, { PureComponent } from 'react'; +import { connect } from 'react-redux'; import SlideDown from 'app/core/components/Animations/SlideDown'; import Tooltip from 'app/core/components/Tooltip/Tooltip'; -import { Team, TeamGroup } from '../../types'; +import { TeamGroup } from '../../types'; +import { addTeamGroup, loadTeamGroups, removeTeamGroup } from './state/actions'; +import { getTeamGroups } from './state/selectors'; -interface Props { - team: Team; +export interface Props { + groups: TeamGroup[]; + loadTeamGroups: typeof loadTeamGroups; + addTeamGroup: typeof addTeamGroup; + removeTeamGroup: typeof removeTeamGroup; } interface State { @@ -15,14 +20,39 @@ interface State { const headerTooltip = `Sync LDAP or OAuth groups with your Grafana teams.`; -export class TeamGroupSync extends React.Component { +export class TeamGroupSync extends PureComponent { constructor(props) { super(props); this.state = { isAdding: false, newGroupId: '' }; } componentDidMount() { - // this.props.team.loadGroups(); + this.fetchTeamGroups(); + } + + async fetchTeamGroups() { + await this.props.loadTeamGroups(); + } + + onToggleAdding = () => { + this.setState({ isAdding: !this.state.isAdding }); + }; + + onNewGroupIdChanged = evt => { + this.setState({ newGroupId: evt.target.value }); + }; + + onAddGroup = () => { + this.props.addTeamGroup(this.state.newGroupId); + this.setState({ isAdding: false, newGroupId: '' }); + }; + + onRemoveGroup = (group: TeamGroup) => { + this.props.removeTeamGroup(group.groupId); + }; + + isNewGroupValid() { + return this.state.newGroupId.length > 1; } renderGroup(group: TeamGroup) { @@ -38,30 +68,9 @@ export class TeamGroupSync extends React.Component { ); } - onToggleAdding = () => { - this.setState({ isAdding: !this.state.isAdding }); - }; - - onNewGroupIdChanged = evt => { - this.setState({ newGroupId: evt.target.value }); - }; - - onAddGroup = () => { - // this.props.team.addGroup(this.state.newGroupId); - this.setState({ isAdding: false, newGroupId: '' }); - }; - - onRemoveGroup = (group: TeamGroup) => { - // this.props.team.removeGroup(group.groupId); - }; - - isNewGroupValid() { - return this.state.newGroupId.length > 1; - } - render() { const { isAdding, newGroupId } = this.state; - const groups = this.props.team.groups; + const groups = this.props.groups; return (
@@ -144,4 +153,16 @@ export class TeamGroupSync extends React.Component { } } -export default hot(module)(TeamGroupSync); +function mapStateToProps(state) { + return { + groups: getTeamGroups(state.team), + }; +} + +const mapDispatchToProps = { + loadTeamGroups, + addTeamGroup, + removeTeamGroup, +}; + +export default connect(mapStateToProps, mapDispatchToProps)(TeamGroupSync); diff --git a/public/app/features/teams/TeamPages.tsx b/public/app/features/teams/TeamPages.tsx index a4ab4a06d4d..f28bde518d2 100644 --- a/public/app/features/teams/TeamPages.tsx +++ b/public/app/features/teams/TeamPages.tsx @@ -57,7 +57,6 @@ export class TeamPages extends PureComponent { } renderPage() { - const { team } = this.props; const { isSyncEnabled } = this.state; const currentPage = this.getCurrentPage(); @@ -69,7 +68,7 @@ export class TeamPages extends PureComponent { return ; case PageTypes.GroupSync: - return isSyncEnabled && ; + return isSyncEnabled && ; } return null; diff --git a/public/app/features/teams/state/actions.ts b/public/app/features/teams/state/actions.ts index 5b203d0a502..9b3ab3a8177 100644 --- a/public/app/features/teams/state/actions.ts +++ b/public/app/features/teams/state/actions.ts @@ -1,8 +1,9 @@ import { ThunkAction } from 'redux-thunk'; import { getBackendSrv } from 'app/core/services/backend_srv'; -import { NavModelItem, StoreState, Team, TeamMember } from '../../../types'; +import { NavModelItem, StoreState, Team, TeamGroup, TeamMember } from '../../../types'; import { updateNavIndex } from '../../../core/actions'; import { UpdateNavIndexAction } from '../../../core/actions/navModel'; +import config from 'app/core/config'; export enum ActionTypes { LoadTeams = 'LOAD_TEAMS', @@ -10,6 +11,7 @@ export enum ActionTypes { SetSearchQuery = 'SET_SEARCH_QUERY', SetSearchMemberQuery = 'SET_SEARCH_MEMBER_QUERY', LoadTeamMembers = 'TEAM_MEMBERS_LOADED', + LoadTeamGroups = 'TEAM_GROUPS_LOADED', } export interface LoadTeamsAction { @@ -27,6 +29,11 @@ export interface LoadTeamMembersAction { payload: TeamMember[]; } +export interface LoadTeamGroupsAction { + type: ActionTypes.LoadTeamGroups; + payload: TeamGroup[]; +} + export interface SetSearchQueryAction { type: ActionTypes.SetSearchQuery; payload: string; @@ -42,7 +49,8 @@ export type Action = | SetSearchQueryAction | LoadTeamAction | LoadTeamMembersAction - | SetSearchMemberQueryAction; + | SetSearchMemberQueryAction + | LoadTeamGroupsAction; type ThunkResult = ThunkAction; @@ -61,6 +69,11 @@ const teamMembersLoaded = (teamMembers: TeamMember[]): LoadTeamMembersAction => payload: teamMembers, }); +const teamGroupsLoaded = (teamGroups: TeamGroup[]): LoadTeamGroupsAction => ({ + type: ActionTypes.LoadTeamGroups, + payload: teamGroups, +}); + export const setSearchMemberQuery = (searchQuery: string): SetSearchMemberQueryAction => ({ type: ActionTypes.SetSearchMemberQuery, payload: searchQuery, @@ -79,7 +92,7 @@ export function loadTeams(): ThunkResult { } function buildNavModel(team: Team): NavModelItem { - return { + const navModel = { img: team.avatarUrl, id: 'team-' + team.id, subTitle: 'Manage members & settings', @@ -103,6 +116,18 @@ function buildNavModel(team: Team): NavModelItem { }, ], }; + + if (config.buildInfo.isEnterprise) { + navModel.children.push({ + active: false, + icon: 'fa fa-fw fa-refresh', + id: 'team-settings', + text: 'External group sync', + url: `org/teams/edit/${team.id}/groupsync`, + }); + } + + return navModel; } export function loadTeam(id: number): ThunkResult { @@ -117,7 +142,6 @@ export function loadTeam(id: number): ThunkResult { } export function loadTeamMembers(): ThunkResult { - console.log('loading team members'); return async (dispatch, getStore) => { const team = getStore().team.team; @@ -167,6 +191,42 @@ export function updateTeam(name: string, email: string): ThunkResult { }; } +export function loadTeamGroups(): ThunkResult { + return async (dispatch, getStore) => { + const team = getStore().team.team; + + await getBackendSrv() + .get(`/api/teams/${team.id}/groups`) + .then(response => { + dispatch(teamGroupsLoaded(response)); + }); + }; +} + +export function addTeamGroup(groupId: string): ThunkResult { + return async (dispatch, getStore) => { + const team = getStore().team.team; + + await getBackendSrv() + .post(`/api/teams/${team.id}/groups`, { groupId: groupId }) + .then(() => { + dispatch(loadTeamGroups()); + }); + }; +} + +export function removeTeamGroup(groupId: string): ThunkResult { + return async (dispatch, getStore) => { + const team = getStore().team.team; + + await getBackendSrv() + .delete(`/api/teams/${team.id}/groups/${groupId}`) + .then(() => { + dispatch(loadTeamGroups()); + }); + }; +} + export function deleteTeam(id: number): ThunkResult { return async dispatch => { await getBackendSrv() diff --git a/public/app/features/teams/state/reducers.ts b/public/app/features/teams/state/reducers.ts index f02ade60923..4af36f2e01c 100644 --- a/public/app/features/teams/state/reducers.ts +++ b/public/app/features/teams/state/reducers.ts @@ -30,6 +30,9 @@ export const teamReducer = (state = initialTeamState, action: Action): TeamState case ActionTypes.SetSearchMemberQuery: return { ...state, searchMemberQuery: action.payload }; + + case ActionTypes.LoadTeamGroups: + return { ...state, groups: action.payload }; } return state; diff --git a/public/app/features/teams/state/selectors.ts b/public/app/features/teams/state/selectors.ts index 5e22f96eaf7..416e293ec78 100644 --- a/public/app/features/teams/state/selectors.ts +++ b/public/app/features/teams/state/selectors.ts @@ -1,5 +1,6 @@ export const getSearchQuery = state => state.searchQuery; export const getSearchMemberQuery = state => state.searchMemberQuery; +export const getTeamGroups = state => state.groups; export const getTeam = (state, currentTeamId) => { if (state.team.id === parseInt(currentTeamId)) {