Skip to content

Commit ead78e2

Browse files
authored
fix(rbac): fix the roles table to also watch policies (#1057)
1 parent f413fd1 commit ead78e2

File tree

5 files changed

+115
-23
lines changed

5 files changed

+115
-23
lines changed

plugins/rbac/src/api/RBACBackendClient.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { getKindNamespaceName } from '../utils/rbac-utils';
1616
// @public
1717
export type RBACAPI = {
1818
getUserAuthorization: () => Promise<{ status: string }>;
19-
getRoles: () => Promise<Role[]>;
19+
getRoles: () => Promise<Role[] | Response>;
2020
getPolicies: () => Promise<RoleBasedPolicy[] | Response>;
2121
getAssociatedPolicies: (
2222
entityReference: string,
@@ -78,6 +78,11 @@ export class RBACBackendClient implements RBACAPI {
7878
...(idToken && { Authorization: `Bearer ${idToken}` }),
7979
},
8080
});
81+
82+
if (jsonResponse.status !== 200 && jsonResponse.status !== 204) {
83+
return jsonResponse;
84+
}
85+
8186
return jsonResponse.json();
8287
}
8388

plugins/rbac/src/components/RbacPage.test.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ describe('RbacPage', () => {
3737
mockUseRoles.mockReturnValue({
3838
loading: true,
3939
data: [],
40-
retry: jest.fn(),
40+
error: {
41+
rolesError: '',
42+
policiesError: '',
43+
},
44+
retry: { roleRetry: jest.fn(), policiesRetry: jest.fn() },
4145
createRoleAllowed: false,
4246
createRoleLoading: false,
4347
});

plugins/rbac/src/components/RolesList/RolesList.test.tsx

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ describe('RolesList', () => {
6363
mockUseRoles.mockReturnValue({
6464
loading: false,
6565
data: useRolesMockData,
66-
retry: jest.fn(),
66+
error: {
67+
rolesError: '',
68+
policiesError: '',
69+
},
70+
retry: { roleRetry: jest.fn(), policiesRetry: jest.fn() },
6771
createRoleAllowed: false,
6872
createRoleLoading: false,
6973
});
@@ -80,12 +84,17 @@ describe('RolesList', () => {
8084
mockUseRoles.mockReturnValue({
8185
loading: false,
8286
data: [],
83-
retry: jest.fn(),
87+
error: {
88+
rolesError: '',
89+
policiesError: '',
90+
},
91+
retry: { roleRetry: jest.fn(), policiesRetry: jest.fn() },
8492
createRoleAllowed: false,
8593
createRoleLoading: false,
8694
});
87-
const { getByTestId } = await renderInTestApp(<RolesList />);
95+
const { getByTestId, queryByText } = await renderInTestApp(<RolesList />);
8896
expect(getByTestId('roles-table-empty')).not.toBeNull();
97+
expect(queryByText('Something went wrong')).not.toBeInTheDocument();
8998
});
9099

91100
it('should show delete icon if user is authorized to delete roles', async () => {
@@ -96,7 +105,11 @@ describe('RolesList', () => {
96105
mockUseRoles.mockReturnValue({
97106
loading: false,
98107
data: useRolesMockData,
99-
retry: jest.fn(),
108+
error: {
109+
rolesError: '',
110+
policiesError: '',
111+
},
112+
retry: { roleRetry: jest.fn(), policiesRetry: jest.fn() },
100113
createRoleAllowed: false,
101114
createRoleLoading: false,
102115
});
@@ -128,7 +141,11 @@ describe('RolesList', () => {
128141
},
129142
},
130143
],
131-
retry: jest.fn(),
144+
error: {
145+
rolesError: '',
146+
policiesError: '',
147+
},
148+
retry: { roleRetry: jest.fn(), policiesRetry: jest.fn() },
132149
createRoleAllowed: false,
133150
createRoleLoading: false,
134151
});
@@ -160,7 +177,11 @@ describe('RolesList', () => {
160177
},
161178
},
162179
],
163-
retry: jest.fn(),
180+
error: {
181+
rolesError: '',
182+
policiesError: '',
183+
},
184+
retry: { roleRetry: jest.fn(), policiesRetry: jest.fn() },
164185
createRoleAllowed: true,
165186
createRoleLoading: false,
166187
});
@@ -175,7 +196,11 @@ describe('RolesList', () => {
175196
mockUseRoles.mockReturnValue({
176197
loading: false,
177198
data: useRolesMockData,
178-
retry: jest.fn(),
199+
error: {
200+
rolesError: '',
201+
policiesError: '',
202+
},
203+
retry: { roleRetry: jest.fn(), policiesRetry: jest.fn() },
179204
createRoleAllowed: false,
180205
createRoleLoading: false,
181206
});
@@ -192,7 +217,11 @@ describe('RolesList', () => {
192217
mockUseRoles.mockReturnValue({
193218
loading: false,
194219
data: useRolesMockData,
195-
retry: jest.fn(),
220+
error: {
221+
rolesError: '',
222+
policiesError: '',
223+
},
224+
retry: { roleRetry: jest.fn(), policiesRetry: jest.fn() },
196225
createRoleAllowed: true,
197226
createRoleLoading: false,
198227
});
@@ -209,12 +238,35 @@ describe('RolesList', () => {
209238
mockUseRoles.mockReturnValue({
210239
loading: false,
211240
data: useRolesMockData,
212-
retry: jest.fn(),
241+
error: {
242+
rolesError: '',
243+
policiesError: '',
244+
},
245+
retry: { roleRetry: jest.fn(), policiesRetry: jest.fn() },
213246
createRoleAllowed: false,
214247
createRoleLoading: false,
215248
});
216249
const { getByTestId } = await renderInTestApp(<RolesList />);
217250

218251
expect(getByTestId('create-role-warning')).not.toBeNull();
219252
});
253+
254+
it('should show error message when there is an error fetching the roles', async () => {
255+
RequirePermissionMock.mockImplementation(props => <>{props.children}</>);
256+
mockUsePermission.mockReturnValue({ loading: false, allowed: true });
257+
mockUseRoles.mockReturnValue({
258+
loading: true,
259+
data: [],
260+
error: {
261+
rolesError: 'Something went wrong',
262+
policiesError: '',
263+
},
264+
retry: { roleRetry: jest.fn(), policiesRetry: jest.fn() },
265+
createRoleAllowed: false,
266+
createRoleLoading: false,
267+
});
268+
269+
const { queryByText } = await renderInTestApp(<RolesList />);
270+
expect(queryByText('Something went wrong')).toBeInTheDocument();
271+
});
220272
});

plugins/rbac/src/components/RolesList/RolesList.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22

3-
import { Table } from '@backstage/core-components';
3+
import { Table, WarningPanel } from '@backstage/core-components';
44

55
import { makeStyles } from '@material-ui/core';
66

@@ -27,12 +27,13 @@ export const RolesList = () => {
2727

2828
const [roles, setRoles] = React.useState<number | undefined>();
2929
const classes = useStyles();
30-
const { loading, data, retry, createRoleAllowed, createRoleLoading } =
30+
const { loading, data, retry, createRoleAllowed, createRoleLoading, error } =
3131
useRoles();
3232

3333
const closeDialog = () => {
3434
setOpenDialog(false);
35-
retry();
35+
retry.roleRetry();
36+
retry.policiesRetry();
3637
};
3738

3839
const onAlertClose = () => {
@@ -49,6 +50,19 @@ export const RolesList = () => {
4950
createRoleAllowed={createRoleAllowed}
5051
createRoleLoading={createRoleLoading}
5152
/>
53+
{(error?.rolesError || error?.policiesError) && (
54+
<div style={{ paddingBottom: '16px' }}>
55+
<WarningPanel
56+
message={error.rolesError || error.policiesError}
57+
title={
58+
error.rolesError
59+
? 'Something went wrong while fetching the roles'
60+
: 'Something went wrong while fetching the permission policies'
61+
}
62+
severity="error"
63+
/>
64+
</div>
65+
)}
5266
<Table
5367
title={
5468
!loading && data?.length

plugins/rbac/src/hooks/useRoles.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,24 @@ export const useRoles = (
2424
data: RolesData[];
2525
createRoleLoading: boolean;
2626
createRoleAllowed: boolean;
27-
retry: () => void;
27+
error: {
28+
rolesError: string;
29+
policiesError: string;
30+
};
31+
retry: { roleRetry: () => void; policiesRetry: () => void };
2832
} => {
2933
const rbacApi = useApi(rbacApiRef);
3034
const {
31-
loading: rolesLoading,
3235
value: roles,
3336
retry: roleRetry,
37+
error: rolesError,
3438
} = useAsyncRetry(async () => await rbacApi.getRoles());
3539

36-
const { loading: policiesLoading, value: policies } = useAsyncRetry(
37-
async () => await rbacApi.getPolicies(),
38-
[],
39-
);
40+
const {
41+
value: policies,
42+
retry: policiesRetry,
43+
error: policiesError,
44+
} = useAsyncRetry(async () => await rbacApi.getPolicies(), []);
4045

4146
const deletePermissionResult = usePermission({
4247
permission: policyEntityDeletePermission,
@@ -67,7 +72,7 @@ export const useRoles = (
6772
});
6873
const data: RolesData[] = React.useMemo(
6974
() =>
70-
roles && roles?.length > 0
75+
Array.isArray(roles) && roles?.length > 0
7176
? roles.reduce((acc: RolesData[], role: Role) => {
7277
const permissions = getPermissions(
7378
role.name,
@@ -107,19 +112,31 @@ export const useRoles = (
107112
catalogEntityReadPermissionResult,
108113
],
109114
);
110-
const loading = rolesLoading && policiesLoading;
115+
const loading = !rolesError && !policiesError && !roles && !policies;
116+
111117
useInterval(
112118
() => {
113119
roleRetry();
120+
policiesRetry();
114121
},
115122
loading ? null : pollInterval || 10000,
116123
);
117124

118125
return {
119126
loading,
120127
data,
128+
error: {
129+
rolesError: (rolesError?.message ||
130+
(typeof roles === 'object'
131+
? (roles as any as Response)?.statusText
132+
: '')) as string,
133+
policiesError: (policiesError?.message ||
134+
(typeof policies === 'object'
135+
? (policies as any as Response)?.statusText
136+
: '')) as string,
137+
},
121138
createRoleLoading,
122139
createRoleAllowed,
123-
retry: roleRetry,
140+
retry: { roleRetry, policiesRetry },
124141
};
125142
};

0 commit comments

Comments
 (0)