diff --git a/src/hooks/useUserSelfTenantPermissions.js b/src/hooks/useUserSelfTenantPermissions.js new file mode 100644 index 000000000..7ffde2e84 --- /dev/null +++ b/src/hooks/useUserSelfTenantPermissions.js @@ -0,0 +1,51 @@ +import { useQuery } from 'react-query'; + +import { useStripes } from '../StripesContext'; +import { useNamespace } from '../components'; +import useOkapiKy from '../useOkapiKy'; + +const INITIAL_DATA = []; + +const useUserSelfTenantPermissions = ( + { tenantId }, + options = {}, +) => { + const stripes = useStripes(); + const ky = useOkapiKy(); + const api = ky.extend({ + hooks: { + beforeRequest: [(req) => req.headers.set('X-Okapi-Tenant', tenantId)] + } + }); + const [namespace] = useNamespace({ key: 'user-self-permissions' }); + + const user = stripes.user.user; + + const { + isFetching, + isLoading, + data, + } = useQuery( + [namespace, user?.id, tenantId], + ({ signal }) => { + return api.get( + 'users-keycloak/_self', + { signal }, + ).json(); + }, + { + enabled: Boolean(user?.id && tenantId) && stripes.hasInterface('users-keycloak'), + keepPreviousData: true, + ...options, + }, + ); + + return ({ + isFetching, + isLoading, + userPermissions: data?.permissions.permissions || INITIAL_DATA, + totalRecords: data?.permissions.permissions.length || 0, + }); +}; + +export default useUserSelfTenantPermissions; diff --git a/src/hooks/useUserSelfTenantPermissions.test.js b/src/hooks/useUserSelfTenantPermissions.test.js new file mode 100644 index 000000000..e8604bab7 --- /dev/null +++ b/src/hooks/useUserSelfTenantPermissions.test.js @@ -0,0 +1,71 @@ +import { renderHook, waitFor } from '@folio/jest-config-stripes/testing-library/react'; +import { + QueryClient, + QueryClientProvider, +} from 'react-query'; + +import permissions from 'fixtures/permissions'; +import useUserSelfTenantPermissions from './useUserSelfTenantPermissions'; +import useOkapiKy from '../useOkapiKy'; + +jest.mock('../useOkapiKy'); +jest.mock('../components', () => ({ + useNamespace: () => ([]), +})); +jest.mock('../StripesContext', () => ({ + useStripes: () => ({ + user: { + user: { + id: 'userId' + } + }, + hasInterface: () => true + }), +})); + +const queryClient = new QueryClient(); + +// eslint-disable-next-line react/prop-types +const wrapper = ({ children }) => ( + + {children} + +); + +const response = { + permissions: { permissions }, +}; + +describe('useUserSelfTenantPermissions', () => { + const getMock = jest.fn(() => ({ + json: () => Promise.resolve(response), + })); + const setHeaderMock = jest.fn(); + const kyMock = { + extend: jest.fn(({ hooks: { beforeRequest } }) => { + beforeRequest.forEach(handler => handler({ headers: { set: setHeaderMock } })); + + return { + get: getMock, + }; + }), + }; + + beforeEach(() => { + getMock.mockClear(); + useOkapiKy.mockClear().mockReturnValue(kyMock); + }); + + it('should fetch user permissions for specified tenant', async () => { + const options = { + userId: 'userId', + tenantId: 'tenantId', + }; + const { result } = renderHook(() => useUserSelfTenantPermissions(options), { wrapper }); + + await waitFor(() => !result.current.isLoading); + + expect(setHeaderMock).toHaveBeenCalledWith('X-Okapi-Tenant', options.tenantId); + expect(getMock).toHaveBeenCalledWith('users-keycloak/_self', expect.objectContaining({})); + }); +}); diff --git a/src/hooks/useUserTenantPermissionNames.js b/src/hooks/useUserTenantPermissionNames.js new file mode 100644 index 000000000..dc7c2057a --- /dev/null +++ b/src/hooks/useUserTenantPermissionNames.js @@ -0,0 +1,59 @@ +import { useQuery } from 'react-query'; + +import { useStripes } from '../StripesContext'; +import { useNamespace } from '../components'; +import useOkapiKy from '../useOkapiKy'; + +const INITIAL_DATA = []; + +const useUserTenantPermissionNames = ( + { tenantId }, + options = {}, +) => { + const stripes = useStripes(); + const ky = useOkapiKy(); + const api = ky.extend({ + hooks: { + beforeRequest: [(req) => req.headers.set('X-Okapi-Tenant', tenantId)] + } + }); + const [namespace] = useNamespace({ key: 'user-affiliation-permissions' }); + + const user = stripes.user.user; + + const searchParams = { + full: 'true', + indexField: 'userId', + }; + + const { + isFetching, + isLoading, + data = {}, + } = useQuery( + [namespace, user?.id, tenantId], + ({ signal }) => { + return api.get( + `perms/users/${user.id}/permissions`, + { + searchParams, + signal, + }, + ).json(); + }, + { + enabled: Boolean(user?.id && tenantId) && !stripes.hasInterface('roles'), + keepPreviousData: true, + ...options, + }, + ); + + return ({ + isFetching, + isLoading, + userPermissions: data.permissionNames || INITIAL_DATA, + totalRecords: data.totalRecords, + }); +}; + +export default useUserTenantPermissionNames; diff --git a/src/hooks/useUserTenantPermissionNames.test.js b/src/hooks/useUserTenantPermissionNames.test.js new file mode 100644 index 000000000..cc15aecda --- /dev/null +++ b/src/hooks/useUserTenantPermissionNames.test.js @@ -0,0 +1,72 @@ +import { renderHook, waitFor } from '@folio/jest-config-stripes/testing-library/react'; +import { + QueryClient, + QueryClientProvider, +} from 'react-query'; + +import permissions from 'fixtures/permissions'; +import useUserTenantPermissionNames from './useUserTenantPermissionNames'; +import useOkapiKy from '../useOkapiKy'; + +jest.mock('../useOkapiKy'); +jest.mock('../components', () => ({ + useNamespace: () => ([]), +})); +jest.mock('../StripesContext', () => ({ + useStripes: () => ({ + user: { + user: { + id: 'userId' + } + }, + hasInterface: () => false + }), +})); + +const queryClient = new QueryClient(); + +// eslint-disable-next-line react/prop-types +const wrapper = ({ children }) => ( + + {children} + +); + +const response = { + permissionNames: permissions, + totalRecords: permissions.length, +}; + +describe('useUserTenantPermissionNames', () => { + const getMock = jest.fn(() => ({ + json: () => Promise.resolve(response), + })); + const setHeaderMock = jest.fn(); + const kyMock = { + extend: jest.fn(({ hooks: { beforeRequest } }) => { + beforeRequest.forEach(handler => handler({ headers: { set: setHeaderMock } })); + + return { + get: getMock, + }; + }), + }; + + beforeEach(() => { + getMock.mockClear(); + useOkapiKy.mockClear().mockReturnValue(kyMock); + }); + + it('should fetch user permissions for specified tenant', async () => { + const options = { + userId: 'userId', + tenantId: 'tenantId', + }; + const { result } = renderHook(() => useUserTenantPermissionNames(options), { wrapper }); + + await waitFor(() => !result.current.isLoading); + + expect(setHeaderMock).toHaveBeenCalledWith('X-Okapi-Tenant', options.tenantId); + expect(getMock).toHaveBeenCalledWith(`perms/users/${options.userId}/permissions`, expect.objectContaining({})); + }); +}); diff --git a/src/hooks/useUserTenantPermissions.js b/src/hooks/useUserTenantPermissions.js index 80030eea1..4173160c3 100644 --- a/src/hooks/useUserTenantPermissions.js +++ b/src/hooks/useUserTenantPermissions.js @@ -1,58 +1,37 @@ -import { useQuery } from 'react-query'; - import { useStripes } from '../StripesContext'; -import { useNamespace } from '../components'; -import useOkapiKy from '../useOkapiKy'; - -const INITIAL_DATA = []; +import useUserSelfTenantPermissions from './useUserSelfTenantPermissions'; +import useUserTenantPermissionNames from './useUserTenantPermissionNames'; const useUserTenantPermissions = ( { tenantId }, options = {}, ) => { const stripes = useStripes(); - const ky = useOkapiKy(); - const api = ky.extend({ - hooks: { - beforeRequest: [(req) => req.headers.set('X-Okapi-Tenant', tenantId)] - } - }); - const [namespace] = useNamespace({ key: 'user-affiliation-permissions' }); - - const user = stripes.user.user; - const searchParams = { - full: 'true', - indexField: 'userId', - }; + const { + isFetching: isPermissionsFetching, + isLoading: isPermissionsLoading, + userPermissions: permissionsData = {}, + totalRecords: permissionsTotalRecords + } = useUserTenantPermissionNames({ tenantId }, options); const { - isFetching, - isLoading, - data = {}, - } = useQuery( - [namespace, user?.id, tenantId], - ({ signal }) => { - return api.get( - `perms/users/${user.id}/permissions`, - { - searchParams, - signal, - }, - ).json(); - }, - { - enabled: Boolean(user?.id && tenantId), - keepPreviousData: true, - ...options, - }, - ); + isFetching: isSelfPermissionsFetching, + isLoading: isSelfPermissionsLoading, + userPermissions:selfPermissionsData = {}, + totalRecords: selfPermissionsTotalRecords + } = useUserSelfTenantPermissions({ tenantId }, options); + + const isFetching = stripes.hasInterface('roles') ? isSelfPermissionsFetching : isPermissionsFetching; + const isLoading = stripes.hasInterface('roles') ? isSelfPermissionsLoading : isPermissionsLoading; + const userPermissions = stripes.hasInterface('roles') ? selfPermissionsData : permissionsData; + const totalRecords = stripes.hasInterface('roles') ? selfPermissionsTotalRecords : permissionsTotalRecords; return ({ isFetching, isLoading, - userPermissions: data.permissionNames || INITIAL_DATA, - totalRecords: data.totalRecords, + userPermissions, + totalRecords }); }; diff --git a/src/hooks/useUserTenantPermissions.test.js b/src/hooks/useUserTenantPermissions.test.js index e64b9c440..c3f4aa9bd 100644 --- a/src/hooks/useUserTenantPermissions.test.js +++ b/src/hooks/useUserTenantPermissions.test.js @@ -1,71 +1,74 @@ -import { renderHook, waitFor } from '@folio/jest-config-stripes/testing-library/react'; -import { - QueryClient, - QueryClientProvider, -} from 'react-query'; - -import permissions from 'fixtures/permissions'; +import { renderHook } from '@folio/jest-config-stripes/testing-library/react'; +import { useStripes } from '../StripesContext'; +import useUserSelfTenantPermissions from './useUserSelfTenantPermissions'; +import useUserTenantPermissionNames from './useUserTenantPermissionNames'; import useUserTenantPermissions from './useUserTenantPermissions'; -import useOkapiKy from '../useOkapiKy'; -jest.mock('../useOkapiKy'); -jest.mock('../components', () => ({ - useNamespace: () => ([]), -})); -jest.mock('../StripesContext', () => ({ - useStripes: () => ({ - user: { - user: { - id: 'userId' - } - } - }), -})); +jest.mock('../StripesContext'); +jest.mock('./useUserSelfTenantPermissions'); +jest.mock('./useUserTenantPermissionNames'); -const queryClient = new QueryClient(); +describe('useUserTenantPermissions', () => { + const tenantId = 'tenant-id'; + const options = {}; -// eslint-disable-next-line react/prop-types -const wrapper = ({ children }) => ( - - {children} - -); + beforeEach(() => { + useStripes.mockReturnValue({ + hasInterface: jest.fn() + }); + }); -const response = { - permissionNames: permissions, - totalRecords: permissions.length, -}; + it('should return _self permissions data when "roles" interface is present', () => { + useStripes().hasInterface.mockReturnValue(true); -describe('useUserTenantPermissions', () => { - const getMock = jest.fn(() => ({ - json: () => Promise.resolve(response), - })); - const setHeaderMock = jest.fn(); - const kyMock = { - extend: jest.fn(({ hooks: { beforeRequest } }) => { - beforeRequest.forEach(handler => handler({ headers: { set: setHeaderMock } })); + useUserSelfTenantPermissions.mockReturnValue({ + isFetching: true, + isLoading: true, + userPermissions: ['self'], + totalRecords: 1 + }); - return { - get: getMock, - }; - }), - }; + useUserTenantPermissionNames.mockReturnValue({ + isFetching: false, + isLoading: false, + userPermissions: ['permission name'], + totalRecords: 1 + }); - beforeEach(() => { - getMock.mockClear(); - useOkapiKy.mockClear().mockReturnValue(kyMock); + const { result } = renderHook(() => useUserTenantPermissions({ tenantId }, options)); + + expect(result.current).toStrictEqual({ + isFetching: true, + isLoading: true, + userPermissions: ['self'], + totalRecords: 1 + }); }); - it('should fetch user permissions for specified tenant', async () => { - const options = { - userId: 'userId', - tenantId: 'tenantId', - }; - const { result } = renderHook(() => useUserTenantPermissions(options), { wrapper }); + it('should return tenant permissions data when "roles" interface is NOT present', () => { + useStripes().hasInterface.mockReturnValue(false); + + useUserSelfTenantPermissions.mockReturnValue({ + isFetching: true, + isLoading: true, + userPermissions: ['self'], + totalRecords: 1 + }); + + useUserTenantPermissionNames.mockReturnValue({ + isFetching: false, + isLoading: false, + userPermissions: ['permission name'], + totalRecords: 1 + }); - await waitFor(() => !result.current.isLoading); + const { result } = renderHook(() => useUserTenantPermissions({ tenantId }, options)); - expect(setHeaderMock).toHaveBeenCalledWith('X-Okapi-Tenant', options.tenantId); - expect(getMock).toHaveBeenCalledWith(`perms/users/${options.userId}/permissions`, expect.objectContaining({})); + expect(result.current).toStrictEqual({ + isFetching: false, + isLoading: false, + userPermissions: ['permission name'], + totalRecords: 1 + }); }); });