diff --git a/packages/core/admin/ee/admin/hooks/useLicenseLimitNotification/index.js b/packages/core/admin/ee/admin/hooks/useLicenseLimitNotification/index.js index 7768ba4fc7..2fa8786289 100644 --- a/packages/core/admin/ee/admin/hooks/useLicenseLimitNotification/index.js +++ b/packages/core/admin/ee/admin/hooks/useLicenseLimitNotification/index.js @@ -19,18 +19,18 @@ const BILLING_SELF_HOSTED_URL = 'https://strapi.io/billing/request-seats'; const useLicenseLimitNotification = () => { const { formatMessage } = useIntl(); - let { license } = useLicenseLimits(); + let { license, isError, isLoading } = useLicenseLimits(); const toggleNotification = useNotification(); const { pathname } = useLocation(); + const { enforcementUserCount, permittedSeats, licenseLimitStatus, isHostedOnStrapiCloud } = + license; + useEffect(() => { - if (!license?.data) { + if (isError || isLoading) { return; } - const { enforcementUserCount, permittedSeats, licenseLimitStatus, isHostedOnStrapiCloud } = - license?.data ?? {}; - const shouldDisplayNotification = !isNil(permittedSeats) && !window.sessionStorage.getItem(`${STORAGE_KEY_PREFIX}-${pathname}`) && @@ -84,7 +84,18 @@ const useLicenseLimitNotification = () => { }, }); } - }, [toggleNotification, license.data, pathname, formatMessage]); + }, [ + toggleNotification, + license, + pathname, + formatMessage, + isLoading, + permittedSeats, + licenseLimitStatus, + enforcementUserCount, + isHostedOnStrapiCloud, + isError, + ]); }; export default useLicenseLimitNotification; diff --git a/packages/core/admin/ee/admin/hooks/useLicenseLimitNotification/tests/index.test.js b/packages/core/admin/ee/admin/hooks/useLicenseLimitNotification/tests/index.test.js index 0570e986aa..c06bfdafc9 100644 --- a/packages/core/admin/ee/admin/hooks/useLicenseLimitNotification/tests/index.test.js +++ b/packages/core/admin/ee/admin/hooks/useLicenseLimitNotification/tests/index.test.js @@ -54,9 +54,7 @@ describe('useLicenseLimitNotification', () => { it('should return if no license info is available', () => { useLicenseLimits.mockImplementationOnce(() => ({ - license: { - data: {}, - }, + license: {}, })); renderHook(() => useLicenseLimitNotification()); @@ -66,10 +64,8 @@ describe('useLicenseLimitNotification', () => { it('should not display notification if permittedSeat info is missing', () => { useLicenseLimits.mockImplementationOnce(() => ({ license: { - data: { - ...baseLicenseInfo, - permittedSeats: undefined, - }, + ...baseLicenseInfo, + permittedSeats: undefined, }, })); @@ -80,10 +76,8 @@ describe('useLicenseLimitNotification', () => { it('should not display notification if status is not AT_LIMIT or OVER_LIMIT', () => { useLicenseLimits.mockImplementationOnce(() => ({ license: { - data: { - ...baseLicenseInfo, - licenseLimitStatus: 'SOME_STRING', - }, + ...baseLicenseInfo, + licenseLimitStatus: 'SOME_STRING', }, })); @@ -117,10 +111,8 @@ describe('useLicenseLimitNotification', () => { it('should display a warning notification when license limit is at limit', () => { useLicenseLimits.mockImplementationOnce(() => ({ license: { - data: { - ...baseLicenseInfo, - licenseLimitStatus: 'OVER_LIMIT', - }, + ...baseLicenseInfo, + licenseLimitStatus: 'OVER_LIMIT', }, })); @@ -144,10 +136,8 @@ describe('useLicenseLimitNotification', () => { it('should have cloud billing url if is hosted on strapi cloud', () => { useLicenseLimits.mockImplementationOnce(() => ({ license: { - data: { - ...baseLicenseInfo, - isHostedOnStrapiCloud: true, - }, + ...baseLicenseInfo, + isHostedOnStrapiCloud: true, }, })); diff --git a/packages/core/admin/ee/admin/hooks/useLicenseLimits/index.js b/packages/core/admin/ee/admin/hooks/useLicenseLimits/index.js index 9d2a0ff734..0d1a1aaf2f 100644 --- a/packages/core/admin/ee/admin/hooks/useLicenseLimits/index.js +++ b/packages/core/admin/ee/admin/hooks/useLicenseLimits/index.js @@ -1,34 +1,3 @@ -import { useFetchClient, useRBAC } from '@strapi/helper-plugin'; -import { useQuery } from 'react-query'; -import { useSelector } from 'react-redux'; - -import { selectAdminPermissions } from '../../../../admin/src/pages/App/selectors'; - -const useLicenseLimits = () => { - const permissions = useSelector(selectAdminPermissions); - const rbac = useRBAC(permissions.settings.users); - - const { - isLoading: isRBACLoading, - allowedActions: { canRead, canCreate, canUpdate, canDelete }, - } = rbac; - - const isRBACAllowed = canRead && canCreate && canUpdate && canDelete; - - const { get } = useFetchClient(); - const fetchLicenseLimitInfo = async () => { - const { - data: { data }, - } = await get('/admin/license-limit-information'); - - return data; - }; - - const license = useQuery(['ee', 'license-limit-info'], fetchLicenseLimitInfo, { - enabled: !isRBACLoading && isRBACAllowed, - }); - - return { license }; -}; +import { useLicenseLimits } from './useLicenseLimits'; export default useLicenseLimits; diff --git a/packages/core/admin/ee/admin/hooks/useLicenseLimits/tests/index.test.js b/packages/core/admin/ee/admin/hooks/useLicenseLimits/tests/index.test.js index a01fa47b60..09172a416a 100644 --- a/packages/core/admin/ee/admin/hooks/useLicenseLimits/tests/index.test.js +++ b/packages/core/admin/ee/admin/hooks/useLicenseLimits/tests/index.test.js @@ -1,19 +1,32 @@ import React from 'react'; import { fixtures } from '@strapi/admin-test-utils'; -import { useFetchClient } from '@strapi/helper-plugin'; -import { renderHook } from '@testing-library/react'; -import { useQuery } from 'react-query'; +import { useRBAC } from '@strapi/helper-plugin'; +import { renderHook, waitFor } from '@testing-library/react'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { QueryClient, QueryClientProvider } from 'react-query'; import { Provider } from 'react-redux'; import { createStore } from 'redux'; import useLicenseLimits from '..'; +const server = setupServer( + ...[ + rest.get('*/license-limit-information', (req, res, ctx) => { + return res( + ctx.json({ + data: { + attribute: 1, + }, + }) + ); + }), + ] +); + jest.mock('@strapi/helper-plugin', () => ({ - // TODO: Replace with msw - useFetchClient: jest.fn(() => ({ - get: jest.fn(), - })), + ...jest.requireActual('@strapi/helper-plugin'), useRBAC: jest.fn(() => ({ isLoading: false, allowedActions: { @@ -25,55 +38,71 @@ jest.mock('@strapi/helper-plugin', () => ({ })), })); -// TODO: Replace with msw -jest.mock('react-query', () => ({ - useQuery: jest.fn(), -})); - const setup = (...args) => renderHook(() => useLicenseLimits(...args), { wrapper({ children }) { + const client = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + }, + }, + }); + return ( state, { admin_app: { permissions: fixtures.permissions.app }, })} > - {children} + {children} ); }, }); describe('useLicenseLimits', () => { + beforeAll(() => server.listen()); + + afterEach(() => { + server.resetHandlers(); + jest.clearAllMocks(); + }); + it('should fetch the license limit information', async () => { - const data = { data: { id: 1, name: 'Test License' } }; - useQuery.mockReturnValue({ - data: { id: 1, name: 'Test License' }, - isLoading: false, - }); - const { result } = setup(); - expect(useFetchClient).toHaveBeenCalled(); - expect(useQuery).toHaveBeenCalledWith(['ee', 'license-limit-info'], expect.any(Function), { - enabled: true, + expect(result.current.license).toEqual({}); + + await waitFor(() => expect(result.current.isLoading).toBeFalsy()); + + expect(result.current.license).toEqual({ + attribute: 1, }); - expect(result.current.license.data).toEqual(data.data); }); - it('data should be undefined if there is an API error', async () => { - // const data = { data: { id: 1, name: 'Test License' } }; - useQuery.mockReturnValue({ - isError: true, - }); + it.each(['canRead', 'canCreate', 'canUpdate', 'canDelete'])( + 'should not fetch the license limit information, when the user does not have the %s permissions', + async (permission) => { + const allowedActions = { + canRead: true, + canCreate: true, + canUpdate: true, + canDelete: true, + }; - const { result } = setup(); + allowedActions[permission] = false; - expect(useFetchClient).toHaveBeenCalled(); - expect(useQuery).toHaveBeenCalledWith(['ee', 'license-limit-info'], expect.any(Function), { - enabled: true, - }); - expect(result.current.license.data).toEqual(undefined); - }); + useRBAC.mockReturnValue({ + isLoading: false, + allowedActions, + }); + + const { result } = setup(); + + await waitFor(() => expect(result.current.isLoading).toBeFalsy()); + + expect(result.current.license).toEqual({}); + } + ); }); diff --git a/packages/core/admin/ee/admin/hooks/useLicenseLimits/useLicenseLimits.js b/packages/core/admin/ee/admin/hooks/useLicenseLimits/useLicenseLimits.js new file mode 100644 index 0000000000..ae6b705936 --- /dev/null +++ b/packages/core/admin/ee/admin/hooks/useLicenseLimits/useLicenseLimits.js @@ -0,0 +1,31 @@ +import { useFetchClient, useRBAC } from '@strapi/helper-plugin'; +import { useQuery } from 'react-query'; +import { useSelector } from 'react-redux'; + +import { selectAdminPermissions } from '../../../../admin/src/pages/App/selectors'; + +export function useLicenseLimits() { + const permissions = useSelector(selectAdminPermissions); + const { get } = useFetchClient(); + const { + isLoading: isRBACLoading, + allowedActions: { canRead, canCreate, canUpdate, canDelete }, + } = useRBAC(permissions.settings.users); + const hasPermissions = canRead && canCreate && canUpdate && canDelete; + + const { data, isError, isLoading } = useQuery( + ['ee', 'license-limit-info'], + async () => { + const { + data: { data }, + } = await get('/admin/license-limit-information'); + + return data; + }, + { + enabled: !isRBACLoading && hasPermissions, + } + ); + + return { license: data ?? {}, isError, isLoading }; +} diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ApplicationInfosPage/components/AdminSeatInfo/index.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ApplicationInfosPage/components/AdminSeatInfo/index.js index 019acddc83..08a1cd3f63 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ApplicationInfosPage/components/AdminSeatInfo/index.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ApplicationInfosPage/components/AdminSeatInfo/index.js @@ -13,11 +13,13 @@ const BILLING_SELF_HOSTED_URL = 'https://strapi.io/billing/request-seats'; const AdminSeatInfo = () => { const { formatMessage } = useIntl(); - const { license } = useLicenseLimits(); - const { licenseLimitStatus, enforcementUserCount, permittedSeats, isHostedOnStrapiCloud } = - license?.data ?? {}; + const { + license: { licenseLimitStatus, enforcementUserCount, permittedSeats, isHostedOnStrapiCloud }, + isError, + isLoading, + } = useLicenseLimits(); - if (!permittedSeats) { + if (isError || isLoading) { return null; } diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Users/ListPage/CreateAction/index.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/Users/ListPage/CreateAction/index.js index 0ab9df51d5..ff701c16cf 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Users/ListPage/CreateAction/index.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/Users/ListPage/CreateAction/index.js @@ -10,8 +10,15 @@ import { useLicenseLimits } from '../../../../../../hooks'; const CreateAction = ({ onClick }) => { const { formatMessage } = useIntl(); - const { license } = useLicenseLimits(); - const { permittedSeats, shouldStopCreate } = license?.data ?? {}; + const { + license: { permittedSeats, shouldStopCreate }, + isError, + isLoading, + } = useLicenseLimits(); + + if (isError || isLoading) { + return null; + } return (