diff --git a/src/components/authentication/__tests__/__snapshots__/authentication.test.js.snap b/src/components/authentication/__tests__/__snapshots__/authentication.test.js.snap index cc0929d26..611853521 100644 --- a/src/components/authentication/__tests__/__snapshots__/authentication.test.js.snap +++ b/src/components/authentication/__tests__/__snapshots__/authentication.test.js.snap @@ -5,6 +5,7 @@ exports[`Authentication Component should allow being disabled: disabled 1`] = ` value={ { "authorized": { + "inventory": true, "subscriptions": true, }, "errorCodes": [], @@ -43,6 +44,7 @@ exports[`Authentication Component should render a component authorized: authoriz value={ { "authorized": { + "inventory": true, "subscriptions": true, }, "errorCodes": [], @@ -98,7 +100,10 @@ exports[`Authentication Component should render authorized via kessel when flag { ); expect(component).toMatchSnapshot('authorized'); + expect(component.props.value.authorized).toEqual({ + [helpers.UI_NAME]: true, + inventory: true + }); }); const mockUseChromeKesselOn = () => ({ visibilityFunctions: { featureFlag: () => true } }); @@ -235,6 +239,10 @@ describe('Authentication Component', () => { ); expect(component).toMatchSnapshot('kessel authorized'); + expect(component.props.value.authorized).toEqual({ + [helpers.UI_NAME]: true, + inventory: true + }); }); it('should render not authorized via kessel when flag is on', async () => { diff --git a/src/components/authentication/__tests__/useHasRelation.test.js b/src/components/authentication/__tests__/useHasRelation.test.js index 2a626ebe8..de70a9bfb 100644 --- a/src/components/authentication/__tests__/useHasRelation.test.js +++ b/src/components/authentication/__tests__/useHasRelation.test.js @@ -96,4 +96,18 @@ describe('useHasRelation', () => { }) ); }); + + it('should call checkSelf when enabled transitions from false to true', async () => { + mockCheckSelf.mockResolvedValue({ allowed: 'ALLOWED_TRUE' }); + + const { rerender, act: hookAct } = await renderHook((enabled = false) => + useHasRelation(Relation.INVENTORY_VIEW, { enabled }) + ); + + expect(mockCheckSelf).not.toHaveBeenCalled(); + + await hookAct(async () => rerender(true)); + + expect(mockCheckSelf).toHaveBeenCalledTimes(1); + }); }); diff --git a/src/components/authentication/authentication.js b/src/components/authentication/authentication.js index 4224f865d..768a34fed 100644 --- a/src/components/authentication/authentication.js +++ b/src/components/authentication/authentication.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { BinocularsIcon } from '@patternfly/react-icons'; import { Maintenance } from '@redhat-cloud-services/frontend-components/Maintenance'; import { NotAuthorized } from '@redhat-cloud-services/frontend-components/NotAuthorized'; @@ -55,6 +55,34 @@ const Authentication = ({ const isAuthorized = kesselEnabled ? kesselAuthorized : authorized[appName]; + /* + * Instance table links read session.authorized.inventory. RBAC v1 inventory is empty in v2 orgs + * (Kessel on or off), so overlay it whenever the user is allowed to see the app. + */ + const sessionData = useMemo( + () => ({ + ...data, + authorized: { + ...authorized, + ...(isAuthorized && { + [appName]: true, + inventory: true + }) + } + }), + [data, authorized, isAuthorized, appName] + ); + + helpers.browserExpose({ + authDebug: { + kesselEnabled, + kesselAuthorized, + kesselPending, + isAuthorized, + authorized: sessionData.authorized + } + }); + const renderContent = () => { if (isDisabled) { return ( @@ -92,7 +120,7 @@ const Authentication = ({ ); }; - return {renderContent()}; + return {renderContent()}; }; export { Authentication as default, Authentication }; diff --git a/src/components/authentication/useHasRelation.js b/src/components/authentication/useHasRelation.js index c301fc1fd..d9d108230 100644 --- a/src/components/authentication/useHasRelation.js +++ b/src/components/authentication/useHasRelation.js @@ -63,9 +63,9 @@ const useHasRelation = (relation, { enabled = true } = {}) => { return () => { cancelled = true; }; - // intentional: run once on mount, matches React Query one-shot behavior + // re-run if Chrome feature flags arrive after first paint // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [enabled]); return { has, isLoading }; };