Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions plugins/sandbox/src/api/RegistrationBackendClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type RegistrationBackendClientOptions = {

export interface UIConfig {
workatoWebHookURL?: string;
disabledIntegrations?: string[];
}

export interface RegistrationService {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,28 @@ describe('RegistrationBackendClient', () => {
);
});

it('should return UI config with disabledIntegrations', async () => {
const mockUIConfig = {
workatoWebHookURL: 'https://webhooks.test',
disabledIntegrations: ['openshift-console', 'devspaces'],
};

mockSecureFetchApi.fetch.mockResolvedValue(
createMockResponse({
ok: true,
json: () => Promise.resolve(mockUIConfig),
}),
);

const result = await client.getUIConfig();

expect(result).toEqual(mockUIConfig);
expect(result.disabledIntegrations).toEqual([
'openshift-console',
'devspaces',
]);
});

it('should return empty config if workatoWebHookURL is not present', async () => {
const mockUIConfig = {};

Expand All @@ -303,6 +325,7 @@ describe('RegistrationBackendClient', () => {

expect(result).toEqual({});
expect(result.workatoWebHookURL).toBeUndefined();
expect(result.disabledIntegrations).toBeUndefined();
});

it('should return empty config on unsuccessful response', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import React, { useMemo } from 'react';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import { Product, productData } from './productData';
import useGreenCorners from '../../hooks/useGreenCorners';
import { SandboxCatalogCard } from './SandboxCatalogCard';
import useProductURLs from '../../hooks/useProductURLs';
import { useSandboxContext } from '../../hooks/useSandboxContext';

export const SandboxCatalogGrid: React.FC = () => {
const { greenCorners, setGreenCorners } = useGreenCorners(productData);
const { disabledIntegrations } = useSandboxContext();
const enabledProducts = useMemo(
() => productData.filter(p => !(disabledIntegrations ?? []).includes(p.id)),
[disabledIntegrations],
);
const { greenCorners, setGreenCorners } = useGreenCorners(enabledProducts);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
const productURLs = useProductURLs();

const showGreenCorner = (id: Product) => {
Expand All @@ -31,9 +37,17 @@ export const SandboxCatalogGrid: React.FC = () => {
);
};

// Do not load the grid until we have a list of which integrations are
// disabled. Otherwise what happens is that the whole integration catalog
// is loaded, and once the UI configuration is fetched, the disabled
// integrations disappear.
if (disabledIntegrations === undefined) {
return null;
}

return (
<Grid container spacing={2} sx={{ maxWidth: '100%' }}>
{productData?.map(product => (
{enabledProducts.map(product => (
<Grid item xs="auto" sm="auto" md="auto" lg="auto" key={product.id}>
<Box sx={{ width: '330px', height: '372px' }}>
<SandboxCatalogCard
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ import { wrapInTestApp } from '@backstage/test-utils';
import { SandboxCatalogCard } from '../SandboxCatalogCard';
import useGreenCorners from '../../../hooks/useGreenCorners';
import useProductURLs from '../../../hooks/useProductURLs';
import { useSandboxContext } from '../../../hooks/useSandboxContext';
import { productData } from '../productData';

// Mock the hooks
jest.mock('../../../hooks/useGreenCorners');
jest.mock('../../../hooks/useProductURLs');
jest.mock('../../../hooks/useSandboxContext');

// Mock the SandboxCatalogCard component
jest.mock('../SandboxCatalogCard', () => ({
Expand Down Expand Up @@ -80,6 +82,10 @@ describe('SandboxCatalogGrid', () => {
beforeEach(() => {
jest.clearAllMocks();

(useSandboxContext as jest.Mock).mockReturnValue({
disabledIntegrations: [],
});

// Setup mock return values for hooks
(useGreenCorners as jest.Mock).mockReturnValue({
greenCorners: mockGreenCorners,
Expand Down Expand Up @@ -107,7 +113,7 @@ describe('SandboxCatalogGrid', () => {
expect(cards).toHaveLength(productData.length);
});

it('calls useGreenCorners hook with productData', () => {
it('calls useGreenCorners hook with enabled products', () => {
renderGrid();
expect(useGreenCorners).toHaveBeenCalledWith(productData);
});
Expand Down Expand Up @@ -170,4 +176,57 @@ describe('SandboxCatalogGrid', () => {
{ id: 'openshift-ai', show: true }, // Already true, unchanged
]);
});

it('renders nothing while UI config is loading', () => {
(useSandboxContext as jest.Mock).mockReturnValue({
disabledIntegrations: undefined,
});

(useGreenCorners as jest.Mock).mockReturnValue({
greenCorners: mockGreenCorners,
setGreenCorners: mockSetGreenCorners,
});

const { container } = renderGrid();

const cards = screen.queryAllByTestId('catalog-card');
expect(cards).toHaveLength(0);
expect(container.querySelector('.MuiGrid-container')).toBeNull();
expect(useGreenCorners).toHaveBeenCalledWith(productData);
});

it('hides cards for disabled integrations', () => {
(useSandboxContext as jest.Mock).mockReturnValue({
disabledIntegrations: ['openshift-ai'],
});

(useGreenCorners as jest.Mock).mockReturnValue({
greenCorners: [{ id: 'openshift-console', show: false }],
setGreenCorners: mockSetGreenCorners,
});

renderGrid();

const cards = screen.getAllByTestId('catalog-card');
expect(cards).toHaveLength(1);

const firstCallProps = (SandboxCatalogCard as jest.Mock).mock.calls[0][0];
expect(firstCallProps.id).toBe('openshift-console');
});

it('hides all cards when all integrations are disabled', () => {
(useSandboxContext as jest.Mock).mockReturnValue({
disabledIntegrations: ['openshift-console', 'openshift-ai'],
});

(useGreenCorners as jest.Mock).mockReturnValue({
greenCorners: [],
setGreenCorners: mockSetGreenCorners,
});

renderGrid();

const cards = screen.queryAllByTestId('catalog-card');
expect(cards).toHaveLength(0);
});
});
25 changes: 21 additions & 4 deletions plugins/sandbox/src/hooks/useSandboxContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ interface SandboxContextType {
ansibleStatus: AnsibleStatus;
segmentTrackClick?: (data: SegmentTrackingData) => Promise<void>;
marketoWebhookURL?: string;
disabledIntegrations?: string[];
}

const SandboxContext = createContext<SandboxContextType | undefined>(undefined);
Expand All @@ -73,6 +74,9 @@ export const SandboxProvider: React.FC<{ children: React.ReactNode }> = ({
const registerApi = useApi(registerApiRef);
const [segmentWriteKey, setSegmentWriteKey] = useState<string>();
const [marketoWebhookURL, setMarketoWebhookURL] = useState<string>();
const [disabledIntegrations, setDisabledIntegrations] = useState<
string[] | undefined
>();
const [statusUnknown, setStatusUnknown] = React.useState(true);
const [userFound, setUserFound] = useState<boolean>(false);
const [userData, setData] = useState<SignupData | undefined>(undefined);
Expand Down Expand Up @@ -228,12 +232,24 @@ export const SandboxProvider: React.FC<{ children: React.ReactNode }> = ({
fetchSegmentWriteKey();
}, [registerApi, isProd]);

// Fetch Marketo webhook URL from UI config
// Fetch the marketo URL and the disabled integrations from the registration
// service.
useEffect(() => {
const fetchUIConfig = async () => {
const uiConfig = await registerApi.getUIConfig();
if (uiConfig.workatoWebHookURL) {
setMarketoWebhookURL(uiConfig.workatoWebHookURL);
try {
const uiConfig = await registerApi.getUIConfig();
if (uiConfig.workatoWebHookURL) {
setMarketoWebhookURL(uiConfig.workatoWebHookURL);
}
setDisabledIntegrations(
Array.isArray(uiConfig.disabledIntegrations)
? uiConfig.disabledIntegrations
: [],
);
} catch (err) {
// eslint-disable-next-line no-console
console.error('Error fetching UI config:', err);
setDisabledIntegrations([]);
}
};
fetchUIConfig();
Expand Down Expand Up @@ -291,6 +307,7 @@ export const SandboxProvider: React.FC<{ children: React.ReactNode }> = ({
ansibleStatus,
segmentTrackClick: segmentAnalytics.trackClick,
marketoWebhookURL,
disabledIntegrations,
}}
>
{children}
Expand Down
Loading