diff --git a/plugins/rbac/src/components/RoleOverview/MembersCard.test.tsx b/plugins/rbac/src/components/RoleOverview/MembersCard.test.tsx index 1c54640567..d15d1c00d5 100644 --- a/plugins/rbac/src/components/RoleOverview/MembersCard.test.tsx +++ b/plugins/rbac/src/components/RoleOverview/MembersCard.test.tsx @@ -70,7 +70,7 @@ describe('MembersCard', () => { loading: false, data: useMembersMockData, error: undefined, - retry: () => {}, + retry: { roleRetry: jest.fn(), membersRetry: jest.fn() }, }); const { queryByText } = await renderInTestApp( , @@ -87,7 +87,7 @@ describe('MembersCard', () => { loading: false, data: [], error: undefined, - retry: () => {}, + retry: { roleRetry: jest.fn(), membersRetry: jest.fn() }, }); const { queryByText } = await renderInTestApp( , @@ -102,7 +102,7 @@ describe('MembersCard', () => { loading: false, data: [], error: { message: 'xyz' }, - retry: () => {}, + retry: { roleRetry: jest.fn(), membersRetry: jest.fn() }, }); const { queryByText } = await renderInTestApp( , @@ -122,7 +122,7 @@ describe('MembersCard', () => { loading: false, data: useMembersMockData, error: undefined, - retry: () => {}, + retry: { roleRetry: jest.fn(), membersRetry: jest.fn() }, }); const { getByTestId } = await renderInTestApp( , @@ -136,7 +136,7 @@ describe('MembersCard', () => { loading: false, data: useMembersMockData, error: undefined, - retry: () => {}, + retry: { roleRetry: jest.fn(), membersRetry: jest.fn() }, }); const { queryByTestId } = await renderInTestApp( , diff --git a/plugins/rbac/src/components/RoleOverview/MembersCard.tsx b/plugins/rbac/src/components/RoleOverview/MembersCard.tsx index 1215516cb4..725cef017a 100644 --- a/plugins/rbac/src/components/RoleOverview/MembersCard.tsx +++ b/plugins/rbac/src/components/RoleOverview/MembersCard.tsx @@ -59,7 +59,10 @@ export const MembersCard = ({ roleName }: MembersCardProps) => { icon: getRefreshIcon, tooltip: 'Refresh', isFreeAction: true, - onClick: () => retry(), + onClick: () => { + retry.roleRetry(); + retry.membersRetry(); + }, }, { icon: () => diff --git a/plugins/rbac/src/components/RoleOverview/PermissionsCard.test.tsx b/plugins/rbac/src/components/RoleOverview/PermissionsCard.test.tsx index 3801f613d4..bc37bbc855 100644 --- a/plugins/rbac/src/components/RoleOverview/PermissionsCard.test.tsx +++ b/plugins/rbac/src/components/RoleOverview/PermissionsCard.test.tsx @@ -50,7 +50,7 @@ describe('PermissionsCard', () => { mockPermissionPolicies.mockReturnValue({ loading: false, data: usePermissionPoliciesMockData, - retry: () => {}, + retry: { policiesRetry: jest.fn(), permissionPoliciesRetry: jest.fn() }, error: new Error(''), }); const { queryByText } = await renderInTestApp( @@ -65,7 +65,7 @@ describe('PermissionsCard', () => { mockPermissionPolicies.mockReturnValue({ loading: false, data: [], - retry: () => {}, + retry: { policiesRetry: jest.fn(), permissionPoliciesRetry: jest.fn() }, error: new Error(''), }); const { queryByText } = await renderInTestApp( @@ -79,7 +79,7 @@ describe('PermissionsCard', () => { mockPermissionPolicies.mockReturnValue({ loading: false, data: [], - retry: () => {}, + retry: { policiesRetry: jest.fn(), permissionPoliciesRetry: jest.fn() }, error: { message: '404', name: 'Not Found' }, }); const { queryByText } = await renderInTestApp( @@ -99,7 +99,7 @@ describe('PermissionsCard', () => { loading: false, data: [], error: new Error(''), - retry: () => {}, + retry: { policiesRetry: jest.fn(), permissionPoliciesRetry: jest.fn() }, }); const { getByTestId } = await renderInTestApp( , @@ -113,7 +113,7 @@ describe('PermissionsCard', () => { loading: false, data: [], error: new Error(''), - retry: () => {}, + retry: { policiesRetry: jest.fn(), permissionPoliciesRetry: jest.fn() }, }); const { queryByTestId } = await renderInTestApp( , diff --git a/plugins/rbac/src/components/RoleOverview/PermissionsCard.tsx b/plugins/rbac/src/components/RoleOverview/PermissionsCard.tsx index e106c15e3b..82b348a071 100644 --- a/plugins/rbac/src/components/RoleOverview/PermissionsCard.tsx +++ b/plugins/rbac/src/components/RoleOverview/PermissionsCard.tsx @@ -65,7 +65,10 @@ export const PermissionsCard = ({ entityReference }: PermissionsCardProps) => { icon: getRefreshIcon, tooltip: 'Refresh', isFreeAction: true, - onClick: () => retry(), + onClick: () => { + retry.permissionPoliciesRetry(); + retry.policiesRetry(); + }, }, { icon: () => getEditIcon(permissionResult.allowed, entityReference), diff --git a/plugins/rbac/src/hooks/useMembers.ts b/plugins/rbac/src/hooks/useMembers.ts index a92edfd887..7c5eb57533 100644 --- a/plugins/rbac/src/hooks/useMembers.ts +++ b/plugins/rbac/src/hooks/useMembers.ts @@ -1,11 +1,11 @@ import React from 'react'; -import { useAsync, useAsyncRetry } from 'react-use'; +import { useAsyncRetry, useInterval } from 'react-use'; import { stringifyEntityRef } from '@backstage/catalog-model'; import { useApi } from '@backstage/core-plugin-api'; import { rbacApiRef } from '../api/RBACBackendClient'; -import { MembersData } from '../types'; +import { MemberEntity, MembersData } from '../types'; import { getKindNamespaceName, getMembersFromGroup } from '../utils/rbac-utils'; const getErrorText = ( @@ -24,74 +24,90 @@ const getErrorText = ( return undefined; }; -export const useMembers = (roleName: string) => { +const getMemberData = ( + memberResource: MemberEntity | undefined, + ref: string, +) => { + if (memberResource) { + return { + name: + memberResource.spec.profile?.displayName ?? + memberResource.metadata.name, + type: memberResource.kind, + ref: { + namespace: memberResource.metadata.namespace as string, + kind: memberResource.kind.toLowerCase(), + name: memberResource.metadata.name, + }, + members: + memberResource.kind === 'Group' + ? getMembersFromGroup(memberResource) + : 0, + }; + } + const { kind, namespace, name } = getKindNamespaceName(ref); + return { + name, + type: kind === 'user' ? 'User' : ('Group' as 'User' | 'Group'), + ref: { + namespace, + kind, + name, + }, + members: 0, + }; +}; + +export const useMembers = (roleName: string, pollInterval?: number) => { const rbacApi = useApi(rbacApiRef); let data: MembersData[] = []; const { - loading: rolesLoading, value: role, - retry, + retry: roleRetry, error: roleError, } = useAsyncRetry(async () => { return await rbacApi.getRole(roleName); }); const { - loading: membersLoading, value: members, + retry: membersRetry, error: membersError, - } = useAsync(async () => { + } = useAsyncRetry(async () => { return await rbacApi.getMembers(); }); - const loading = rolesLoading && membersLoading; + const loading = !roleError && !membersError && !role && !members; data = React.useMemo( () => Array.isArray(role) ? role[0].memberReferences.reduce((acc: MembersData[], ref: string) => { - const memberResource = - Array.isArray(members) && - members.find(member => stringifyEntityRef(member) === ref); - if (memberResource) { - acc.push({ - name: - memberResource.spec.profile?.displayName ?? - memberResource.metadata.name, - type: memberResource.kind, - ref: { - namespace: memberResource.metadata.namespace as string, - kind: memberResource.kind.toLowerCase(), - name: memberResource.metadata.name, - }, - members: - memberResource.kind === 'Group' - ? getMembersFromGroup(memberResource) - : 0, - }); - } else { - const { kind, namespace, name } = getKindNamespaceName(ref); - acc.push({ - name, - type: kind === 'user' ? 'User' : 'Group', - ref: { - namespace, - kind, - name, - }, - members: 0, - }); - } + const memberResource: MemberEntity | undefined = Array.isArray( + members, + ) + ? members.find(member => stringifyEntityRef(member) === ref) + : undefined; + const memberData = getMemberData(memberResource, ref); + acc.push(memberData); return acc; }, []) : [], [role, members], ); + useInterval( + () => { + roleRetry(); + membersRetry(); + }, + loading ? null : pollInterval || 10000, + ); + return { loading, data, - retry, + retry: { roleRetry, membersRetry }, error: getErrorText(role, members) || roleError || membersError, }; }; diff --git a/plugins/rbac/src/hooks/usePermissionPolicies.ts b/plugins/rbac/src/hooks/usePermissionPolicies.ts index d85fd02e5f..87660f6dcd 100644 --- a/plugins/rbac/src/hooks/usePermissionPolicies.ts +++ b/plugins/rbac/src/hooks/usePermissionPolicies.ts @@ -1,50 +1,80 @@ import React from 'react'; -import { useAsync, useAsyncRetry } from 'react-use'; +import { useAsyncRetry, useInterval } from 'react-use'; import { useApi } from '@backstage/core-plugin-api'; import { rbacApiRef } from '../api/RBACBackendClient'; import { getPermissionsData } from '../utils/rbac-utils'; -export const usePermissionPolicies = (entityReference: string) => { +const getErrorText = ( + policies: any, + permissionPolicies: any, +): { name: number; message: string } | undefined => { + if (!Array.isArray(policies) && (policies as Response)?.statusText) { + return { + name: (policies as Response).status, + message: `Error fetching policies. ${(policies as Response).statusText}`, + }; + } else if ( + !Array.isArray(permissionPolicies) && + (permissionPolicies as Response)?.statusText + ) { + return { + name: (permissionPolicies as Response).status, + message: `Error fetching the plugins. ${ + (permissionPolicies as Response).statusText + }`, + }; + } + return undefined; +}; + +export const usePermissionPolicies = ( + entityReference: string, + pollInterval?: number, +) => { const rbacApi = useApi(rbacApiRef); const { - loading: policiesLoading, value: policies, - retry, - error, + retry: policiesRetry, + error: policiesError, } = useAsyncRetry(async () => { return await rbacApi.getAssociatedPolicies(entityReference); }); const { value: permissionPolicies, - loading: permissionPoliciesLoading, error: permissionPoliciesError, - } = useAsync(async () => { + retry: permissionPoliciesRetry, + } = useAsyncRetry(async () => { return await rbacApi.listPermissions(); }); - const loading = policiesLoading || permissionPoliciesLoading; + const loading = + !permissionPoliciesError && + !policiesError && + !policies && + !permissionPolicies; const data = React.useMemo(() => { const pp = Array.isArray(permissionPolicies) ? permissionPolicies : []; return Array.isArray(policies) ? getPermissionsData(policies, pp) : []; }, [policies, permissionPolicies]); + + useInterval( + () => { + policiesRetry(); + permissionPoliciesRetry(); + }, + loading ? null : pollInterval || 10000, + ); return { loading, data, - retry, - error: (error as Error) || - (permissionPoliciesError as Error) || { - name: (policies as Response)?.status, - message: `Error fetching policies. ${(policies as Response) - ?.statusText}`, - } || { - name: (permissionPolicies as Response)?.status, - message: `Error fetching permission policies. ${( - permissionPolicies as Response - )?.statusText}`, - }, + retry: { policiesRetry, permissionPoliciesRetry }, + error: + policiesError || + permissionPoliciesError || + getErrorText(policies, permissionPolicies), }; };