mirror of
https://github.com/grafana/grafana.git
synced 2025-02-10 15:45:43 -06:00
SelectOrgPage: migrate API function calls to Redux (#43133)
* SelectOrgPage: migrate API function calls to Redux * used a much better approach * writes test for getUserOrganizations action * writes test for userOrganizationsLoaded reducer * change userOrg to plural
This commit is contained in:
parent
8206802f19
commit
d03aef1951
@ -1,10 +1,10 @@
|
|||||||
import React, { FC, useState } from 'react';
|
import React, { FC } from 'react';
|
||||||
import Page from 'app/core/components/Page/Page';
|
import Page from 'app/core/components/Page/Page';
|
||||||
import { getBackendSrv, config } from '@grafana/runtime';
|
import { config } from '@grafana/runtime';
|
||||||
import { UserOrg } from 'app/types';
|
import { StoreState, UserOrg } from 'app/types';
|
||||||
import { useAsync } from 'react-use';
|
import { useEffectOnce } from 'react-use';
|
||||||
import { Button, HorizontalGroup } from '@grafana/ui';
|
import { Button, HorizontalGroup } from '@grafana/ui';
|
||||||
import { setUserOrganization } from './state/actions';
|
import { getUserOrganizations, setUserOrganization } from './state/actions';
|
||||||
import { connect, ConnectedProps } from 'react-redux';
|
import { connect, ConnectedProps } from 'react-redux';
|
||||||
|
|
||||||
const navModel = {
|
const navModel = {
|
||||||
@ -18,29 +18,31 @@ const navModel = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const getUserOrgs = async () => {
|
const mapStateToProps = (state: StoreState) => {
|
||||||
return await getBackendSrv().get('/api/user/orgs');
|
return {
|
||||||
|
userOrgs: state.organization.userOrgs,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
setUserOrganization,
|
setUserOrganization,
|
||||||
|
getUserOrganizations,
|
||||||
};
|
};
|
||||||
|
|
||||||
const connector = connect(null, mapDispatchToProps);
|
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||||
|
|
||||||
type Props = ConnectedProps<typeof connector>;
|
type Props = ConnectedProps<typeof connector>;
|
||||||
|
|
||||||
export const SelectOrgPage: FC<Props> = ({ setUserOrganization }) => {
|
export const SelectOrgPage: FC<Props> = ({ setUserOrganization, getUserOrganizations, userOrgs }) => {
|
||||||
const [orgs, setOrgs] = useState<UserOrg[]>();
|
|
||||||
|
|
||||||
const setUserOrg = async (org: UserOrg) => {
|
const setUserOrg = async (org: UserOrg) => {
|
||||||
await setUserOrganization(org.orgId);
|
await setUserOrganization(org.orgId);
|
||||||
window.location.href = config.appSubUrl + '/';
|
window.location.href = config.appSubUrl + '/';
|
||||||
};
|
};
|
||||||
|
|
||||||
useAsync(async () => {
|
useEffectOnce(() => {
|
||||||
setOrgs(await getUserOrgs());
|
getUserOrganizations();
|
||||||
}, []);
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page navModel={navModel}>
|
<Page navModel={navModel}>
|
||||||
<Page.Contents>
|
<Page.Contents>
|
||||||
@ -50,8 +52,8 @@ export const SelectOrgPage: FC<Props> = ({ setUserOrganization }) => {
|
|||||||
now. You can change this later at any time.
|
now. You can change this later at any time.
|
||||||
</p>
|
</p>
|
||||||
<HorizontalGroup wrap>
|
<HorizontalGroup wrap>
|
||||||
{orgs &&
|
{userOrgs &&
|
||||||
orgs.map((org) => (
|
userOrgs.map((org) => (
|
||||||
<Button key={org.orgId} icon="signin" onClick={() => setUserOrg(org)}>
|
<Button key={org.orgId} icon="signin" onClick={() => setUserOrg(org)}>
|
||||||
{org.name}
|
{org.name}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { updateOrganization, setUserOrganization } from './actions';
|
import { updateOrganization, setUserOrganization, getUserOrganizations } from './actions';
|
||||||
import { updateConfigurationSubtitle } from 'app/core/actions';
|
import { updateConfigurationSubtitle } from 'app/core/actions';
|
||||||
import { thunkTester } from 'test/core/thunk/thunkTester';
|
import { thunkTester } from 'test/core/thunk/thunkTester';
|
||||||
|
import { OrgRole } from 'app/types';
|
||||||
|
|
||||||
const setup = () => {
|
const setup = () => {
|
||||||
const initialState = {
|
const initialState = {
|
||||||
@ -9,6 +10,7 @@ const setup = () => {
|
|||||||
id: 1,
|
id: 1,
|
||||||
name: 'New Org Name',
|
name: 'New Org Name',
|
||||||
},
|
},
|
||||||
|
userOrg: [{ orgId: 1, name: 'New Org Name', role: OrgRole.Editor }],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -61,3 +63,22 @@ describe('setUserOrganization', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getUserOrganizations', () => {
|
||||||
|
describe('when getUserOrganizations thunk is dispatched', () => {
|
||||||
|
const getMock = jest.fn().mockResolvedValue({ orgId: 1, name: 'New Org Name', role: OrgRole.Editor });
|
||||||
|
const backendSrvMock: any = {
|
||||||
|
get: getMock,
|
||||||
|
};
|
||||||
|
|
||||||
|
it('then it should dispatch updateConfigurationSubtitle', async () => {
|
||||||
|
const { initialState } = setup();
|
||||||
|
|
||||||
|
const dispatchedActions = await thunkTester(initialState)
|
||||||
|
.givenThunk(getUserOrganizations)
|
||||||
|
.whenThunkIsDispatched({ getBackendSrv: () => backendSrvMock });
|
||||||
|
|
||||||
|
expect(dispatchedActions[0].payload).toEqual(initialState.organization.userOrg[0]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ThunkResult } from 'app/types';
|
import { ThunkResult } from 'app/types';
|
||||||
import { getBackendSrv } from '@grafana/runtime';
|
import { getBackendSrv } from '@grafana/runtime';
|
||||||
import { organizationLoaded } from './reducers';
|
import { organizationLoaded, userOrganizationsLoaded } from './reducers';
|
||||||
import { updateConfigurationSubtitle } from 'app/core/actions';
|
import { updateConfigurationSubtitle } from 'app/core/actions';
|
||||||
|
|
||||||
type OrganizationDependencies = { getBackendSrv: typeof getBackendSrv };
|
type OrganizationDependencies = { getBackendSrv: typeof getBackendSrv };
|
||||||
@ -50,3 +50,14 @@ export function createOrganization(
|
|||||||
dispatch(setUserOrganization(result.orgId));
|
dispatch(setUserOrganization(result.orgId));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getUserOrganizations(
|
||||||
|
dependencies: OrganizationDependencies = { getBackendSrv: getBackendSrv }
|
||||||
|
): ThunkResult<any> {
|
||||||
|
return async (dispatch) => {
|
||||||
|
const result = await dependencies.getBackendSrv().get('/api/user/orgs');
|
||||||
|
dispatch(userOrganizationsLoaded(result));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
import { reducerTester } from '../../../../test/core/redux/reducerTester';
|
import { reducerTester } from '../../../../test/core/redux/reducerTester';
|
||||||
import { OrganizationState } from '../../../types';
|
import { OrganizationState, OrgRole } from '../../../types';
|
||||||
import { initialState, organizationLoaded, organizationReducer, setOrganizationName } from './reducers';
|
import {
|
||||||
|
initialState,
|
||||||
|
organizationLoaded,
|
||||||
|
organizationReducer,
|
||||||
|
userOrganizationsLoaded,
|
||||||
|
setOrganizationName,
|
||||||
|
} from './reducers';
|
||||||
|
|
||||||
describe('organizationReducer', () => {
|
describe('organizationReducer', () => {
|
||||||
describe('when organizationLoaded is dispatched', () => {
|
describe('when organizationLoaded is dispatched', () => {
|
||||||
@ -10,6 +16,7 @@ describe('organizationReducer', () => {
|
|||||||
.whenActionIsDispatched(organizationLoaded({ id: 1, name: 'An org' }))
|
.whenActionIsDispatched(organizationLoaded({ id: 1, name: 'An org' }))
|
||||||
.thenStateShouldEqual({
|
.thenStateShouldEqual({
|
||||||
organization: { id: 1, name: 'An org' },
|
organization: { id: 1, name: 'An org' },
|
||||||
|
userOrgs: [],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -21,6 +28,23 @@ describe('organizationReducer', () => {
|
|||||||
.whenActionIsDispatched(setOrganizationName('New Name'))
|
.whenActionIsDispatched(setOrganizationName('New Name'))
|
||||||
.thenStateShouldEqual({
|
.thenStateShouldEqual({
|
||||||
organization: { id: 1, name: 'New Name' },
|
organization: { id: 1, name: 'New Name' },
|
||||||
|
userOrgs: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when userOrganizationsLoaded is dispatched', () => {
|
||||||
|
it('then state should be correct', () => {
|
||||||
|
reducerTester<OrganizationState>()
|
||||||
|
.givenReducer(organizationReducer, {
|
||||||
|
...initialState,
|
||||||
|
organization: { id: 1, name: 'An org' },
|
||||||
|
userOrgs: [],
|
||||||
|
})
|
||||||
|
.whenActionIsDispatched(userOrganizationsLoaded([{ orgId: 1, name: 'New org', role: OrgRole.Editor }]))
|
||||||
|
.thenStateShouldEqual({
|
||||||
|
organization: { id: 1, name: 'An org' },
|
||||||
|
userOrgs: [{ orgId: 1, name: 'New org', role: OrgRole.Editor }],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
import { Organization, OrganizationState } from 'app/types';
|
import { Organization, OrganizationState, UserOrg } from 'app/types';
|
||||||
|
|
||||||
export const initialState: OrganizationState = {
|
export const initialState: OrganizationState = {
|
||||||
organization: {} as Organization,
|
organization: {} as Organization,
|
||||||
|
userOrgs: [] as UserOrg[],
|
||||||
};
|
};
|
||||||
|
|
||||||
const organizationSlice = createSlice({
|
const organizationSlice = createSlice({
|
||||||
@ -16,10 +17,13 @@ const organizationSlice = createSlice({
|
|||||||
setOrganizationName: (state, action: PayloadAction<string>): OrganizationState => {
|
setOrganizationName: (state, action: PayloadAction<string>): OrganizationState => {
|
||||||
return { ...state, organization: { ...state.organization, name: action.payload } };
|
return { ...state, organization: { ...state.organization, name: action.payload } };
|
||||||
},
|
},
|
||||||
|
userOrganizationsLoaded: (state, action: PayloadAction<UserOrg[]>): OrganizationState => {
|
||||||
|
return { ...state, userOrgs: action.payload };
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { setOrganizationName, organizationLoaded } = organizationSlice.actions;
|
export const { setOrganizationName, organizationLoaded, userOrganizationsLoaded } = organizationSlice.actions;
|
||||||
|
|
||||||
export const organizationReducer = organizationSlice.reducer;
|
export const organizationReducer = organizationSlice.reducer;
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { UserOrg } from 'app/types';
|
||||||
|
|
||||||
export interface Organization {
|
export interface Organization {
|
||||||
name: string;
|
name: string;
|
||||||
id: number;
|
id: number;
|
||||||
@ -5,4 +7,5 @@ export interface Organization {
|
|||||||
|
|
||||||
export interface OrganizationState {
|
export interface OrganizationState {
|
||||||
organization: Organization;
|
organization: Organization;
|
||||||
|
userOrgs: UserOrg[];
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user