diff --git a/public/app/features/org/SelectOrgPage.tsx b/public/app/features/org/SelectOrgPage.tsx index 01295c13742..1367e5d6573 100644 --- a/public/app/features/org/SelectOrgPage.tsx +++ b/public/app/features/org/SelectOrgPage.tsx @@ -1,10 +1,10 @@ -import React, { FC, useState } from 'react'; +import React, { FC } from 'react'; import Page from 'app/core/components/Page/Page'; -import { getBackendSrv, config } from '@grafana/runtime'; -import { UserOrg } from 'app/types'; -import { useAsync } from 'react-use'; +import { config } from '@grafana/runtime'; +import { StoreState, UserOrg } from 'app/types'; +import { useEffectOnce } from 'react-use'; import { Button, HorizontalGroup } from '@grafana/ui'; -import { setUserOrganization } from './state/actions'; +import { getUserOrganizations, setUserOrganization } from './state/actions'; import { connect, ConnectedProps } from 'react-redux'; const navModel = { @@ -18,29 +18,31 @@ const navModel = { }, }; -const getUserOrgs = async () => { - return await getBackendSrv().get('/api/user/orgs'); +const mapStateToProps = (state: StoreState) => { + return { + userOrgs: state.organization.userOrgs, + }; }; const mapDispatchToProps = { setUserOrganization, + getUserOrganizations, }; -const connector = connect(null, mapDispatchToProps); +const connector = connect(mapStateToProps, mapDispatchToProps); type Props = ConnectedProps; -export const SelectOrgPage: FC = ({ setUserOrganization }) => { - const [orgs, setOrgs] = useState(); - +export const SelectOrgPage: FC = ({ setUserOrganization, getUserOrganizations, userOrgs }) => { const setUserOrg = async (org: UserOrg) => { await setUserOrganization(org.orgId); window.location.href = config.appSubUrl + '/'; }; - useAsync(async () => { - setOrgs(await getUserOrgs()); - }, []); + useEffectOnce(() => { + getUserOrganizations(); + }); + return ( @@ -50,8 +52,8 @@ export const SelectOrgPage: FC = ({ setUserOrganization }) => { now. You can change this later at any time.

- {orgs && - orgs.map((org) => ( + {userOrgs && + userOrgs.map((org) => ( diff --git a/public/app/features/org/state/actions.test.ts b/public/app/features/org/state/actions.test.ts index 7a772a0268d..9dd97426e5f 100644 --- a/public/app/features/org/state/actions.test.ts +++ b/public/app/features/org/state/actions.test.ts @@ -1,6 +1,7 @@ -import { updateOrganization, setUserOrganization } from './actions'; +import { updateOrganization, setUserOrganization, getUserOrganizations } from './actions'; import { updateConfigurationSubtitle } from 'app/core/actions'; import { thunkTester } from 'test/core/thunk/thunkTester'; +import { OrgRole } from 'app/types'; const setup = () => { const initialState = { @@ -9,6 +10,7 @@ const setup = () => { id: 1, 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]); + }); + }); +}); diff --git a/public/app/features/org/state/actions.ts b/public/app/features/org/state/actions.ts index 554f153b8b2..6b01d1b2f91 100644 --- a/public/app/features/org/state/actions.ts +++ b/public/app/features/org/state/actions.ts @@ -1,6 +1,6 @@ import { ThunkResult } from 'app/types'; import { getBackendSrv } from '@grafana/runtime'; -import { organizationLoaded } from './reducers'; +import { organizationLoaded, userOrganizationsLoaded } from './reducers'; import { updateConfigurationSubtitle } from 'app/core/actions'; type OrganizationDependencies = { getBackendSrv: typeof getBackendSrv }; @@ -50,3 +50,14 @@ export function createOrganization( dispatch(setUserOrganization(result.orgId)); }; } + +export function getUserOrganizations( + dependencies: OrganizationDependencies = { getBackendSrv: getBackendSrv } +): ThunkResult { + return async (dispatch) => { + const result = await dependencies.getBackendSrv().get('/api/user/orgs'); + dispatch(userOrganizationsLoaded(result)); + + return result; + }; +} diff --git a/public/app/features/org/state/reducers.test.ts b/public/app/features/org/state/reducers.test.ts index 49982c12273..00d47a26c6c 100644 --- a/public/app/features/org/state/reducers.test.ts +++ b/public/app/features/org/state/reducers.test.ts @@ -1,6 +1,12 @@ import { reducerTester } from '../../../../test/core/redux/reducerTester'; -import { OrganizationState } from '../../../types'; -import { initialState, organizationLoaded, organizationReducer, setOrganizationName } from './reducers'; +import { OrganizationState, OrgRole } from '../../../types'; +import { + initialState, + organizationLoaded, + organizationReducer, + userOrganizationsLoaded, + setOrganizationName, +} from './reducers'; describe('organizationReducer', () => { describe('when organizationLoaded is dispatched', () => { @@ -10,6 +16,7 @@ describe('organizationReducer', () => { .whenActionIsDispatched(organizationLoaded({ id: 1, name: 'An org' })) .thenStateShouldEqual({ organization: { id: 1, name: 'An org' }, + userOrgs: [], }); }); }); @@ -21,6 +28,23 @@ describe('organizationReducer', () => { .whenActionIsDispatched(setOrganizationName('New Name')) .thenStateShouldEqual({ organization: { id: 1, name: 'New Name' }, + userOrgs: [], + }); + }); + }); + + describe('when userOrganizationsLoaded is dispatched', () => { + it('then state should be correct', () => { + reducerTester() + .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 }], }); }); }); diff --git a/public/app/features/org/state/reducers.ts b/public/app/features/org/state/reducers.ts index 0c459c58541..9ce9f058cd6 100644 --- a/public/app/features/org/state/reducers.ts +++ b/public/app/features/org/state/reducers.ts @@ -1,9 +1,10 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { Organization, OrganizationState } from 'app/types'; +import { Organization, OrganizationState, UserOrg } from 'app/types'; export const initialState: OrganizationState = { organization: {} as Organization, + userOrgs: [] as UserOrg[], }; const organizationSlice = createSlice({ @@ -16,10 +17,13 @@ const organizationSlice = createSlice({ setOrganizationName: (state, action: PayloadAction): OrganizationState => { return { ...state, organization: { ...state.organization, name: action.payload } }; }, + userOrganizationsLoaded: (state, action: PayloadAction): OrganizationState => { + return { ...state, userOrgs: action.payload }; + }, }, }); -export const { setOrganizationName, organizationLoaded } = organizationSlice.actions; +export const { setOrganizationName, organizationLoaded, userOrganizationsLoaded } = organizationSlice.actions; export const organizationReducer = organizationSlice.reducer; diff --git a/public/app/types/organization.ts b/public/app/types/organization.ts index da67ead34ae..926bced7bd5 100644 --- a/public/app/types/organization.ts +++ b/public/app/types/organization.ts @@ -1,3 +1,5 @@ +import { UserOrg } from 'app/types'; + export interface Organization { name: string; id: number; @@ -5,4 +7,5 @@ export interface Organization { export interface OrganizationState { organization: Organization; + userOrgs: UserOrg[]; }