diff --git a/src/app/middleware/telemetryMiddleware.ts b/src/app/middleware/telemetryMiddleware.ts index 423ae21d3..0b7141071 100644 --- a/src/app/middleware/telemetryMiddleware.ts +++ b/src/app/middleware/telemetryMiddleware.ts @@ -38,9 +38,7 @@ const telemetryMiddleware = componentNames.FETCH_PERMISSIONS_ACTION, state.sampleQuery, action.response.error, - { - HasRequestUrl: action.response.hasUrl - } + {} ); break; } diff --git a/src/app/services/actions/autocomplete-action-creators.spec.ts b/src/app/services/actions/autocomplete-action-creators.spec.ts index fb2f7f1de..1075eb1ec 100644 --- a/src/app/services/actions/autocomplete-action-creators.spec.ts +++ b/src/app/services/actions/autocomplete-action-creators.spec.ts @@ -56,8 +56,10 @@ const mockState: IRootState = { }, scopes: { pending: false, - data: [], - hasUrl: false, + data: { + fullPermissions: [], + specificPermissions: [] + }, error: null }, history: [], diff --git a/src/app/services/actions/permissions-action-creator.spec.ts b/src/app/services/actions/permissions-action-creator.spec.ts index e4af12e29..9099e1a34 100644 --- a/src/app/services/actions/permissions-action-creator.spec.ts +++ b/src/app/services/actions/permissions-action-creator.spec.ts @@ -1,12 +1,12 @@ import { FETCH_SCOPES_ERROR, FETCH_SCOPES_PENDING, - FETCH_SCOPES_SUCCESS, - QUERY_GRAPH_STATUS -} from '../redux-constants'; + QUERY_GRAPH_STATUS, + FETCH_FULL_SCOPES_SUCCESS +} from '../../../app/services/redux-constants'; import { - fetchScopesSuccess, fetchScopesPending, fetchScopesError, getPermissionsScopeType, fetchScopes, + fetchFullScopesSuccess, fetchScopesPending, fetchScopesError, getPermissionsScopeType, fetchScopes, consentToScopes } from './permissions-action-creator'; @@ -59,8 +59,10 @@ const mockState: IRootState = { }, scopes: { pending: false, - data: [], - hasUrl: false, + data: { + fullPermissions: [], + specificPermissions: [] + }, error: null }, history: [], @@ -133,24 +135,19 @@ describe('tests permissions action creators', () => { it('Tests if FETCH_SCOPES_SUCCESS is dispatched when fetchScopesSuccess is called', () => { // Arrange const response: IPermissionsResponse = { - hasUrl: true, - scopes: [ - { - value: '', - consentDescription: '', - isAdmin: false, - consented: true - } - ] + scopes: { + fullPermissions: [], + specificPermissions: [] + } } const expectedAction = { - type: FETCH_SCOPES_SUCCESS, + type: FETCH_FULL_SCOPES_SUCCESS, response } // Act - const action = fetchScopesSuccess(response); + const action = fetchFullScopesSuccess(response); // Assert expect(action).toEqual(expectedAction); @@ -159,7 +156,6 @@ describe('tests permissions action creators', () => { it('Tests if FETCH_SCOPES_ERROR is dispatched when fetchScopesError is called', () => { // Arrange const response = { - hasUrl: true, error: {} } diff --git a/src/app/services/actions/permissions-action-creator.ts b/src/app/services/actions/permissions-action-creator.ts index 081cba7d2..756cbef4e 100644 --- a/src/app/services/actions/permissions-action-creator.ts +++ b/src/app/services/actions/permissions-action-creator.ts @@ -14,7 +14,8 @@ import { ACCOUNT_TYPE, PERMS_SCOPE } from '../graph-constants'; import { FETCH_SCOPES_ERROR, FETCH_SCOPES_PENDING, - FETCH_SCOPES_SUCCESS + FETCH_FULL_SCOPES_SUCCESS, + FETCH_URL_SCOPES_SUCCESS } from '../redux-constants'; import { getAuthTokenSuccess, @@ -23,13 +24,20 @@ import { import { getProfileInfo } from './profile-action-creators'; import { setQueryResponseStatus } from './query-status-action-creator'; -export function fetchScopesSuccess(response: object): IAction { +export function fetchFullScopesSuccess(response: object): IAction { return { - type: FETCH_SCOPES_SUCCESS, + type: FETCH_FULL_SCOPES_SUCCESS, response }; } +export function fetchUrlScopesSuccess(response: Object): IAction { + return { + type: FETCH_URL_SCOPES_SUCCESS, + response + } +} + export function fetchScopesPending(): any { return { type: FETCH_SCOPES_PENDING @@ -45,7 +53,6 @@ export function fetchScopesError(response: object): IAction { export function fetchScopes(): Function { return async (dispatch: Function, getState: Function) => { - let hasUrl = false; // whether permissions are for a specific url try { const { devxApi, permissionsPanelOpen, profile, sampleQuery: query }: IRootState = getState(); let permissionsUrl = `${devxApi.baseUrl}/permissions`; @@ -62,7 +69,6 @@ export function fetchScopes(): Function { // eslint-disable-next-line max-len permissionsUrl = `${permissionsUrl}?requesturl=/${requestUrl}&method=${query.selectedVerb}&scopeType=${scopeType}`; - hasUrl = true; } if (devxApi.parameters) { @@ -81,18 +87,19 @@ export function fetchScopes(): Function { const response = await fetch(permissionsUrl, options); if (response.ok) { const scopes = await response.json(); - return dispatch( - fetchScopesSuccess({ - hasUrl, - scopes - }) - ); + + return permissionsPanelOpen ? dispatch(fetchFullScopesSuccess({ + scopes: { fullPermissions: scopes } + })) : + dispatch(fetchUrlScopesSuccess({ + scopes: { specificPermissions: scopes } + })); } + throw response; } catch (error) { return dispatch( fetchScopesError({ - hasUrl, error }) ); diff --git a/src/app/services/actions/resource-explorer-action-creators.spec.ts b/src/app/services/actions/resource-explorer-action-creators.spec.ts index 3e7f546a6..11f503146 100644 --- a/src/app/services/actions/resource-explorer-action-creators.spec.ts +++ b/src/app/services/actions/resource-explorer-action-creators.spec.ts @@ -51,8 +51,10 @@ const mockState: IRootState = { }, scopes: { pending: false, - data: [], - hasUrl: false, + data: { + fullPermissions: [], + specificPermissions: [] + }, error: null }, history: [], diff --git a/src/app/services/reducers/permissions-reducer.spec.ts b/src/app/services/reducers/permissions-reducer.spec.ts index 724ea65b5..94703ae65 100644 --- a/src/app/services/reducers/permissions-reducer.spec.ts +++ b/src/app/services/reducers/permissions-reducer.spec.ts @@ -1,27 +1,37 @@ import { scopes } from '../../../app/services/reducers/permissions-reducer'; -import { FETCH_SCOPES_ERROR, FETCH_SCOPES_PENDING, FETCH_SCOPES_SUCCESS } from '../../../app/services/redux-constants'; +import { + FETCH_SCOPES_ERROR, FETCH_SCOPES_PENDING, + FETCH_FULL_SCOPES_SUCCESS, + FETCH_URL_SCOPES_SUCCESS +} from '../../../app/services/redux-constants'; const initialState = { pending: false, - data: [], - hasUrl: false, + data: { + fullPermissions: [], + specificPermissions: [] + }, error: null }; describe('Permissions reducer', () => { - it('should handle FETCH_SCOPES_SUCCESS', () => { + it('should handle FETCH_FULL_SCOPES_SUCCESS', () => { const action = { - type: FETCH_SCOPES_SUCCESS, + type: FETCH_FULL_SCOPES_SUCCESS, response: { - hasUrl: false, - scopes: ['profile.read', 'profile.write', 'email.read', 'email.write'] + scopes: { + specificPermissions: [], + fullPermissions: ['profile.read', 'profile.write', 'email.read', 'email.write'] + } } } const expectedState = { pending: false, - data: ['profile.read', 'profile.write', 'email.read', 'email.write'], - hasUrl: false, + data: { + fullPermissions: ['profile.read', 'profile.write', 'email.read', 'email.write'], + specificPermissions: [] + }, error: null } @@ -29,6 +39,30 @@ describe('Permissions reducer', () => { expect(newState).toEqual(expectedState); }); + it('should handle FETCH_URL_SCOPES_SUCCESS', () => { + const action = { + type: FETCH_URL_SCOPES_SUCCESS, + response: { + scopes: { + specificPermissions: ['profile.read', 'profile.write', 'email.read', 'email.write'], + fullPermissions: [] + } + } + } + + const expectedState = { + pending: false, + data: { + fullPermissions: [], + specificPermissions: ['profile.read', 'profile.write', 'email.read', 'email.write'] + }, + error: null + } + + const newState = scopes(initialState, action); + expect(newState).toEqual(expectedState); + }) + it('should handle FETCH_SCOPES_ERROR', () => { const action = { type: FETCH_SCOPES_ERROR, @@ -36,8 +70,7 @@ describe('Permissions reducer', () => { } const expectedState = { pending: false, - data: [], - hasUrl: false, + data: {}, error: 'error' } @@ -53,8 +86,10 @@ describe('Permissions reducer', () => { const expectedState = { pending: true, - data: [], - hasUrl: false, + data: { + fullPermissions: [], + specificPermissions: [] + }, error: null } diff --git a/src/app/services/reducers/permissions-reducer.ts b/src/app/services/reducers/permissions-reducer.ts index 08a771032..44b0a1013 100644 --- a/src/app/services/reducers/permissions-reducer.ts +++ b/src/app/services/reducers/permissions-reducer.ts @@ -1,37 +1,46 @@ import { IAction } from '../../../types/action'; import { IPermissionsResponse, IScopes } from '../../../types/permissions'; -import { FETCH_SCOPES_ERROR, FETCH_SCOPES_PENDING, FETCH_SCOPES_SUCCESS } from '../redux-constants'; +import { + FETCH_SCOPES_ERROR, FETCH_SCOPES_PENDING, FETCH_FULL_SCOPES_SUCCESS, + FETCH_URL_SCOPES_SUCCESS +} from '../redux-constants'; const initialState: IScopes = { pending: false, - data: [], - hasUrl: false, + data: { + specificPermissions: [], + fullPermissions: [] + }, error: null }; export function scopes(state: IScopes = initialState, action: IAction): any { switch (action.type) { - case FETCH_SCOPES_SUCCESS: - const response: IPermissionsResponse = { ...action.response as IPermissionsResponse }; + case FETCH_FULL_SCOPES_SUCCESS: + let response: IPermissionsResponse = { ...action.response as IPermissionsResponse }; return { pending: false, - data: response.scopes, - hasUrl: response.hasUrl, + data: { ...state.data, fullPermissions: response.scopes.fullPermissions }, error: null }; + case FETCH_URL_SCOPES_SUCCESS: + response = { ...action.response as IPermissionsResponse }; + return { + pending: false, + data: { ...state.data, specificPermissions: response.scopes.specificPermissions }, + error: null + } case FETCH_SCOPES_ERROR: return { pending: false, error: action.response, - hasUrl: false, - data: [] + data: {} }; case FETCH_SCOPES_PENDING: return { pending: true, - data: [], - error: null, - hasUrl: false + data: state.data, + error: null }; default: return state; diff --git a/src/app/services/redux-constants.ts b/src/app/services/redux-constants.ts index 27c5a5a72..81f066345 100644 --- a/src/app/services/redux-constants.ts +++ b/src/app/services/redux-constants.ts @@ -27,7 +27,8 @@ export const FETCH_ADAPTIVE_CARD_SUCCESS = 'FETCH_ADAPTIVE_CARD_SUCCESS'; export const FETCH_ADAPTIVE_CARD_PENDING = 'FETCH_ADAPTIVE_CARD_PENDING'; export const FETCH_ADAPTIVE_CARD_ERROR = 'FETCH_ADAPTIVE_CARD_ERROR'; export const CLEAR_TERMS_OF_USE = 'CLEAR_TERMS_OF_USE'; -export const FETCH_SCOPES_SUCCESS = 'SCOPES_FETCH_SUCCESS'; +export const FETCH_FULL_SCOPES_SUCCESS = 'FULL_SCOPES_FETCH_SUCCESS'; +export const FETCH_URL_SCOPES_SUCCESS = 'FETCH_URL_SCOPES_SUCCESS'; export const FETCH_SCOPES_ERROR = 'SCOPES_FETCH_ERROR'; export const FETCH_SCOPES_PENDING = 'FETCH_SCOPES_PENDING'; export const GET_CONSENT_ERROR = 'GET_CONSENT_ERROR'; diff --git a/src/app/views/query-runner/request/permissions/PanelList.tsx b/src/app/views/query-runner/request/permissions/PanelList.tsx index d491fb913..76e5859e2 100644 --- a/src/app/views/query-runner/request/permissions/PanelList.tsx +++ b/src/app/views/query-runner/request/permissions/PanelList.tsx @@ -34,8 +34,13 @@ const PanelList = ({ messages, columns, classes, selection, renderItemColumn, renderDetailsHeader, renderCustomCheckbox }: IPanelList) => { + const sortedPermissions = (permissionsToSort: IPermission[]) : IPermission[] => { + return permissionsToSort.sort(dynamicSort('value', SortOrder.ASC)); + } + const { consentedScopes, scopes, authToken } = useSelector((state: IRootState) => state); - const [permissions, setPermissions] = useState(scopes.data.sort(dynamicSort('value', SortOrder.ASC))); + const { fullPermissions } = scopes.data; + const [permissions, setPermissions] = useState(sortedPermissions(fullPermissions)); const permissionsList: any[] = []; const tokenPresent = !!authToken.token; @@ -50,11 +55,11 @@ const PanelList = ({ messages, }); const searchValueChanged = (event: any, value?: string): void => { - let filteredPermissions = scopes.data; + let filteredPermissions = scopes.data.fullPermissions; if (value) { const keyword = value.toLowerCase(); - filteredPermissions = scopes.data.filter((permission: IPermission) => { + filteredPermissions = fullPermissions.filter((permission: IPermission) => { const name = permission.value.toLowerCase(); return name.includes(keyword); }); @@ -65,7 +70,7 @@ const PanelList = ({ messages, const groups = generateGroupsFromList(permissionsList, 'groupName'); - const _onRenderGroupHeader = (props: any): any => { + const onRenderGroupHeader = (props: any): any => { if (props) { return ( @@ -101,7 +106,7 @@ const PanelList = ({ messages, compact={true} groupProps={{ showEmptyGroups: false, - onRenderHeader: _onRenderGroupHeader + onRenderHeader: onRenderGroupHeader }} ariaLabelForSelectionColumn={messages['Toggle selection'] || 'Toggle selection'} ariaLabelForSelectAllCheckbox={messages['Toggle selection for all items'] || 'Toggle selection for all items'} diff --git a/src/app/views/query-runner/request/permissions/Permission.spec.tsx b/src/app/views/query-runner/request/permissions/Permission.spec.tsx index 3aefdc573..cc4ef1ab8 100644 --- a/src/app/views/query-runner/request/permissions/Permission.spec.tsx +++ b/src/app/views/query-runner/request/permissions/Permission.spec.tsx @@ -42,7 +42,6 @@ const renderPermission = (args?: any) => { consented: true } ], - hasUrl: true, error: null }, panel: args?.panel || false, @@ -67,20 +66,36 @@ const renderPermission = (args?: any) => { } const permissionState: IPermissionState = { - permissions: [ - { - value: 'profile.read', - isAdmin: false, - consentDescription: 'Read your profile', - consented: true - }, - { - value: 'profile.write', - isAdmin: false, - consentDescription: 'Write your profile', - consented: true - } - ] + permissions: { + specificPermissions: [ + { + value: 'profile.read', + isAdmin: false, + consentDescription: 'Read your profile', + consented: true + }, + { + value: 'profile.write', + isAdmin: false, + consentDescription: 'Write your profile', + consented: true + } + ], + fullPermissions: [ + { + value: 'profile.read', + isAdmin: false, + consentDescription: 'Read your profile', + consented: true + }, + { + value: 'profile.write', + isAdmin: false, + consentDescription: 'Write your profile', + consented: true + } + ] + } } const allProps = { ...permissionProps, ...permissionState }; diff --git a/src/app/views/query-runner/request/permissions/Permission.tsx b/src/app/views/query-runner/request/permissions/Permission.tsx index 848e4ff34..814fd80e4 100644 --- a/src/app/views/query-runner/request/permissions/Permission.tsx +++ b/src/app/views/query-runner/request/permissions/Permission.tsx @@ -31,7 +31,10 @@ export class Permission extends Component { constructor(props: IPermissionProps) { super(props); this.state = { - permissions: [] + permissions: { + specificPermissions: [], + fullPermissions: [] + } }; } diff --git a/src/app/views/query-runner/request/permissions/TabList.spec.tsx b/src/app/views/query-runner/request/permissions/TabList.spec.tsx index bcb986586..5e1b330de 100644 --- a/src/app/views/query-runner/request/permissions/TabList.spec.tsx +++ b/src/app/views/query-runner/request/permissions/TabList.spec.tsx @@ -53,20 +53,36 @@ jest.mock('react-redux', () => { consentedScopes: ['profile.read', 'profile.write', 'mail.read', 'mail.write'], scopes: { pending: false, - data: [ - { - value: 'profile.read', - isAdmin: false, - consentDescription: 'Read your profile', - consented: true - }, - { - value: 'profile.write', - isAdmin: false, - consentDescription: 'Write your profile', - consented: true - } - ] + data: { + specificPermissions: [ + { + value: 'profile.read', + isAdmin: false, + consentDescription: 'Read your profile', + consented: true + }, + { + value: 'profile.write', + isAdmin: false, + consentDescription: 'Write your profile', + consented: true + } + ], + fullPermissions: [ + { + value: 'profile.read', + isAdmin: false, + consentDescription: 'Read your profile', + consented: true + }, + { + value: 'profile.write', + isAdmin: false, + consentDescription: 'Write your profile', + consented: true + } + ] + } }, authToken: { pending: false, @@ -84,6 +100,6 @@ console.warn = jest.fn() describe('Renders permissions tab', () => { it('Renders Modify Permissions Tab without crasing', () => { const { getByText } = renderTabList(); - getByText(/Permissions for the query are missing on this tab/) + getByText(/One of the following permissions is required to run the query/) }) }) \ No newline at end of file diff --git a/src/app/views/query-runner/request/permissions/TabList.tsx b/src/app/views/query-runner/request/permissions/TabList.tsx index 484ab68eb..a46e0017a 100644 --- a/src/app/views/query-runner/request/permissions/TabList.tsx +++ b/src/app/views/query-runner/request/permissions/TabList.tsx @@ -19,7 +19,7 @@ interface ITabList { const TabList = ({ columns, classes, renderItemColumn, renderDetailsHeader, maxHeight }: ITabList) => { const dispatch = useDispatch(); const { consentedScopes, scopes, authToken } = useSelector((state: IRootState) => state); - const permissions: IPermission[] = scopes.hasUrl ? scopes.data : []; + const permissions: IPermission[] = scopes.data.specificPermissions; const tokenPresent = !!authToken.token; const [isHoverOverPermissionsList, setIsHoverOverPermissionsList] = useState(false); @@ -45,11 +45,7 @@ const TabList = ({ columns, classes, renderItemColumn, renderDetailsHeader, maxH ) } - if (tokenPresent && !scopes.hasUrl) { - return displayNoPermissionsFoundMessage(); - } - - if (!tokenPresent && !scopes.hasUrl) { + if (!tokenPresent) { return displayNotSignedInMessage(); } diff --git a/src/types/permissions.ts b/src/types/permissions.ts index 66b563108..0cfc56276 100644 --- a/src/types/permissions.ts +++ b/src/types/permissions.ts @@ -23,21 +23,28 @@ export interface IPermissionProps { actions?: { fetchScopes: Function; consentToScopes: Function; - }; + } } export interface IPermissionState { - permissions: IPermission[]; + permissions: { + specificPermissions: IPermission[]; + fullPermissions: IPermission[]; + }; } export interface IPermissionsResponse { - hasUrl: boolean; - scopes: IPermission[]; + scopes: { + specificPermissions: IPermission[]; + fullPermissions: IPermission[]; + } } export interface IScopes { pending: boolean; - data: IPermission[]; - hasUrl: boolean; + data: { + specificPermissions: IPermission[]; + fullPermissions: IPermission[]; + }; error: any | null; }