diff --git a/CHANGELOG.md b/CHANGELOG.md index f635f6ae0..b9d833f85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ * Wait longer before declaring a rotation request to be stale. Refs STCOR-895. * Send the stored central tenant name in the header on logout. Refs STCOR-900. * Provide `` and `stripes.hasAnyPermission()`. Refs STCOR-910. +* Use the `users-keycloak/_self` endpoint conditionally when the `users-keycloak` interface is present; otherwise, use `bl-users/_self` within `useUserTenantPermissions`. Refs STCOR-905. + ## [10.2.0](https://github.com/folio-org/stripes-core/tree/v10.2.0) (2024-10-11) [Full Changelog](https://github.com/folio-org/stripes-core/compare/v10.1.1...v10.2.0) diff --git a/src/hooks/useUserSelfTenantPermissions.js b/src/hooks/useUserSelfTenantPermissions.js deleted file mode 100644 index 253784fc3..000000000 --- a/src/hooks/useUserSelfTenantPermissions.js +++ /dev/null @@ -1,53 +0,0 @@ -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, - isFetched, - 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, - isFetched, - 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 deleted file mode 100644 index e8604bab7..000000000 --- a/src/hooks/useUserSelfTenantPermissions.test.js +++ /dev/null @@ -1,71 +0,0 @@ -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 deleted file mode 100644 index 6ca306967..000000000 --- a/src/hooks/useUserTenantPermissionNames.js +++ /dev/null @@ -1,61 +0,0 @@ -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, - isFetched, - 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, - isFetched, - 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 deleted file mode 100644 index cc15aecda..000000000 --- a/src/hooks/useUserTenantPermissionNames.test.js +++ /dev/null @@ -1,72 +0,0 @@ -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 35339b400..91b5a503e 100644 --- a/src/hooks/useUserTenantPermissions.js +++ b/src/hooks/useUserTenantPermissions.js @@ -1,41 +1,51 @@ +import { useQuery } from 'react-query'; + import { useStripes } from '../StripesContext'; -import useUserSelfTenantPermissions from './useUserSelfTenantPermissions'; -import useUserTenantPermissionNames from './useUserTenantPermissionNames'; +import { useNamespace } from '../components'; +import useOkapiKy from '../useOkapiKy'; + +const INITIAL_DATA = []; 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-self-permissions' }); - const { - isFetching: isPermissionsFetching, - isFetched: isPermissionsFetched, - isLoading: isPermissionsLoading, - userPermissions: permissionsData = {}, - totalRecords: permissionsTotalRecords - } = useUserTenantPermissionNames({ tenantId }, options); + const permPath = stripes.hasInterface('users-keycloak') ? 'users-keycloak' : 'bl-users'; const { - isFetching: isSelfPermissionsFetching, - isFetched: isSelfPermissionsFetched, - isLoading: isSelfPermissionsLoading, - userPermissions:selfPermissionsData = {}, - totalRecords: selfPermissionsTotalRecords - } = useUserSelfTenantPermissions({ tenantId }, options); - - const isFetching = stripes.hasInterface('roles') ? isSelfPermissionsFetching : isPermissionsFetching; - const isFetched = stripes.hasInterface('roles') ? isSelfPermissionsFetched : isPermissionsFetched; - const isLoading = stripes.hasInterface('roles') ? isSelfPermissionsLoading : isPermissionsLoading; - const userPermissions = stripes.hasInterface('roles') ? selfPermissionsData : permissionsData; - const totalRecords = stripes.hasInterface('roles') ? selfPermissionsTotalRecords : permissionsTotalRecords; + isFetching, + isFetched, + isLoading, + data, + } = useQuery( + [namespace, tenantId], + ({ signal }) => { + return api.get( + `${permPath}/_self?expandPermissions=true`, + { signal }, + ).json(); + }, + { + keepPreviousData: true, + ...options, + }, + ); return ({ isFetching, isFetched, isLoading, - userPermissions, - totalRecords + userPermissions: data?.permissions.permissions || INITIAL_DATA, + totalRecords: data?.permissions.permissions.length || 0, }); }; diff --git a/src/hooks/useUserTenantPermissions.test.js b/src/hooks/useUserTenantPermissions.test.js index c9cf7b060..18be2f44f 100644 --- a/src/hooks/useUserTenantPermissions.test.js +++ b/src/hooks/useUserTenantPermissions.test.js @@ -1,80 +1,65 @@ -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 { renderHook, waitFor } from '@folio/jest-config-stripes/testing-library/react'; +import { + QueryClient, + QueryClientProvider, +} from 'react-query'; -jest.mock('../StripesContext'); -jest.mock('./useUserSelfTenantPermissions'); -jest.mock('./useUserTenantPermissionNames'); +import permissions from 'fixtures/permissions'; +import useUserTenantPermissions from './useUserTenantPermissions'; +import useOkapiKy from '../useOkapiKy'; -describe('useUserTenantPermissions', () => { - const tenantId = 'tenant-id'; - const options = {}; +jest.mock('../useOkapiKy'); +jest.mock('../components', () => ({ + useNamespace: () => ([]), +})); +jest.mock('../StripesContext', () => ({ + useStripes: () => ({ + hasInterface: () => true + }), +})); - beforeEach(() => { - useStripes.mockReturnValue({ - hasInterface: jest.fn() - }); - }); +const queryClient = new QueryClient(); - it('should return _self permissions data when "roles" interface is present', () => { - useStripes().hasInterface.mockReturnValue(true); +// eslint-disable-next-line react/prop-types +const wrapper = ({ children }) => ( + + {children} + +); - useUserSelfTenantPermissions.mockReturnValue({ - isFetching: true, - isFetched: true, - isLoading: true, - userPermissions: ['self'], - totalRecords: 1 - }); +const response = { + permissions: { permissions }, +}; - useUserTenantPermissionNames.mockReturnValue({ - isFetching: false, - isFetched: false, - isLoading: false, - userPermissions: ['permission name'], - totalRecords: 1 - }); +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 } })); - const { result } = renderHook(() => useUserTenantPermissions({ tenantId }, options)); + return { + get: getMock, + }; + }), + }; - expect(result.current).toStrictEqual({ - isFetching: true, - isFetched: true, - isLoading: true, - userPermissions: ['self'], - totalRecords: 1 - }); + beforeEach(() => { + getMock.mockClear(); + useOkapiKy.mockClear().mockReturnValue(kyMock); }); - it('should return tenant permissions data when "roles" interface is NOT present', () => { - useStripes().hasInterface.mockReturnValue(false); - - useUserSelfTenantPermissions.mockReturnValue({ - isFetching: true, - isFetched: true, - isLoading: true, - userPermissions: ['self'], - totalRecords: 1 - }); - - useUserTenantPermissionNames.mockReturnValue({ - isFetching: false, - isFetched: false, - isLoading: false, - userPermissions: ['permission name'], - totalRecords: 1 - }); + it('should fetch user permissions for specified tenant', async () => { + const options = { + tenantId: 'tenantId', + }; + const { result } = renderHook(() => useUserTenantPermissions(options), { wrapper }); - const { result } = renderHook(() => useUserTenantPermissions({ tenantId }, options)); + await waitFor(() => !result.current.isLoading); - expect(result.current).toStrictEqual({ - isFetching: false, - isFetched: false, - isLoading: false, - userPermissions: ['permission name'], - totalRecords: 1 - }); + expect(setHeaderMock).toHaveBeenCalledWith('X-Okapi-Tenant', options.tenantId); + expect(getMock).toHaveBeenCalledWith('users-keycloak/_self?expandPermissions=true', expect.objectContaining({})); }); });