V2Wizard/Repositories: Add payload_repositories to request and test it

Previously the V2 Wizard's request mapper was only adding the
custom_repositories field. We also need to add a nearly duplicate
payload_repositories field due to how the image-builder API works...
which is admittedly not intuitive.

Tests are also added to ensure that requests are generated correctly
when using the custom repositories feature.
This commit is contained in:
lucasgarfield 2024-02-29 11:24:06 +01:00 committed by Lucas Garfield
parent 08563b9dfa
commit e923cccd41
4 changed files with 180 additions and 3 deletions

View file

@ -38,9 +38,13 @@ import {
useListRepositoriesQuery,
} from '../../../../store/contentSourcesApi';
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
import { CustomRepository } from '../../../../store/imageBuilderApi';
import {
CustomRepository,
Repository,
} from '../../../../store/imageBuilderApi';
import {
changeCustomRepositories,
changePayloadRepositories,
selectArchitecture,
selectCustomRepositories,
selectDistribution,
@ -144,6 +148,22 @@ const convertSchemaToIBCustomRepo = (repo: ApiRepositoryResponseRead) => {
return imageBuilderRepo;
};
// Utility function to convert from Content Sources to Image Builder payload repo API schema
const convertSchemaToIBPayloadRepo = (repo: ApiRepositoryResponseRead) => {
const imageBuilderRepo: Repository = {
baseurl: repo.url,
rhsm: false,
check_gpg: false,
};
if (repo.gpg_key) {
imageBuilderRepo.gpgkey = repo.gpg_key;
imageBuilderRepo.check_gpg = true;
imageBuilderRepo.check_repo_gpg = repo.metadata_verification;
}
return imageBuilderRepo;
};
const Repositories = () => {
const dispatch = useAppDispatch();
@ -269,7 +289,12 @@ const Repositories = () => {
convertSchemaToIBCustomRepo(repo!)
);
const payloadRepositories = selectedRepos.map((repo) =>
convertSchemaToIBPayloadRepo(repo!)
);
dispatch(changeCustomRepositories(customRepositories));
dispatch(changePayloadRepositories(payloadRepositories));
};
const updateSelected = (selectedRepos: (string | undefined)[]) => {

View file

@ -26,12 +26,14 @@ import {
selectBaseUrl,
selectBlueprintDescription,
selectBlueprintName,
selectCustomRepositories,
selectDistribution,
selectGcpAccountType,
selectGcpEmail,
selectGcpShareMethod,
selectImageTypes,
selectPackages,
selectPayloadRepositories,
selectRegistrationType,
selectServerUrl,
} from '../../../store/wizardSlice';
@ -156,8 +158,8 @@ const getCustomizations = (state: RootState, orgID: string): Customizations => {
files: undefined,
subscription: getSubscription(state, orgID),
packages: getPackages(state),
payload_repositories: undefined,
custom_repositories: undefined,
payload_repositories: getPayloadRepositories(state),
custom_repositories: getCustomRepositories(state),
openscap: undefined,
filesystem: undefined,
users: undefined,
@ -219,3 +221,19 @@ const getSubscription = (
return { ...initialSubscription, insights: false, rhc: false };
}
};
const getCustomRepositories = (state: RootState) => {
const customRepositories = selectCustomRepositories(state);
if (customRepositories.length === 0) {
return undefined;
}
return customRepositories;
};
const getPayloadRepositories = (state: RootState) => {
const payloadRepositories = selectPayloadRepositories(state);
if (payloadRepositories.length === 0) {
return undefined;
}
return payloadRepositories;
};

View file

@ -6,6 +6,7 @@ import {
Distributions,
ImageRequest,
ImageTypes,
Repository,
} from './imageBuilderApi';
import { ActivationKeys } from './rhsmApi';
@ -69,6 +70,7 @@ type wizardState = {
repositories: {
customRepositories: CustomRepository[];
payloadRepositories: Repository[];
};
packages: IBPackageWithRepositoryInfo[];
details: {
@ -118,6 +120,7 @@ const initialState: wizardState = {
},
repositories: {
customRepositories: [],
payloadRepositories: [],
},
packages: [],
details: {
@ -220,6 +223,10 @@ export const selectCustomRepositories = (state: RootState) => {
return state.wizard.repositories.customRepositories;
};
export const selectPayloadRepositories = (state: RootState) => {
return state.wizard.repositories.payloadRepositories;
};
export const selectPackages = (state: RootState) => {
return state.wizard.packages;
};
@ -354,6 +361,9 @@ export const wizardSlice = createSlice({
) => {
state.repositories.customRepositories = action.payload;
},
changePayloadRepositories: (state, action: PayloadAction<Repository[]>) => {
state.repositories.payloadRepositories = action.payload;
},
addPackage: (state, action: PayloadAction<IBPackageWithRepositoryInfo>) => {
state.packages.push(action.payload);
},
@ -402,6 +412,7 @@ export const {
changeDisabledServices,
changeEnabledServices,
changeCustomRepositories,
changePayloadRepositories,
addPackage,
removePackage,
changeBlueprintName,

View file

@ -0,0 +1,123 @@
import { screen } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';
import { CREATE_BLUEPRINT } from '../../../../../constants';
import {
CreateBlueprintRequest,
CustomRepository,
Repository,
} from '../../../../../store/imageBuilderApi';
import { clickNext } from '../../../../testUtils';
import {
blueprintRequest,
clickRegisterLater,
enterBlueprintName,
interceptBlueprintRequest,
render,
} from '../../wizardTestUtils';
jest.mock('@redhat-cloud-services/frontend-components/useChrome', () => ({
useChrome: () => ({
auth: {
getUser: () => {
return {
identity: {
internal: {
org_id: 5,
},
},
};
},
},
isBeta: () => false,
isProd: () => true,
getEnvironment: () => 'prod',
}),
}));
const goToRepositoriesStep = async () => {
const bareMetalCheckBox = await screen.findByRole('checkbox', {
name: /bare metal installer checkbox/i,
});
await userEvent.click(bareMetalCheckBox);
await clickNext(); // Registration
await clickRegisterLater();
await clickNext(); // OpenSCAP
await clickNext(); // Custom repositories
};
const goToReviewStep = async () => {
await clickNext(); // Additional packages
await clickNext(); // Details
await enterBlueprintName();
await clickNext(); // Review
};
const selectFirstRepository = async () => {
await userEvent.click(
await screen.findByRole('checkbox', { name: /select row 0/i })
);
};
const deselectFirstRepository = async () => {
await userEvent.click(
await screen.findByRole('checkbox', { name: /select row 0/i })
);
};
describe('repositories request generated correctly', () => {
const expectedPayloadRepositories: Repository[] = [
{
baseurl: 'http://valid.link.to.repo.org/x86_64/',
check_gpg: true,
check_repo_gpg: false,
gpgkey:
'-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBGN9300BEAC1FLODu0cL6saMMHa7yJY1JZUc+jQUI/HdECQrrsTaPXlcc7nM\nykYMMv6amPqbnhH/R5BW2Ano+OMse+PXtUr0NXU4OcvxbnnXkrVBVUf8mXI9DzLZ\njw8KoD+4/s0BuzO78zAJF5uhuyHMAK0ll9v0r92kK45Fas9iZTfRFcqFAzvgjScf\n5jeBnbRs5U3UTz9mtDy802mk357o1A8BD0qlu3kANDpjLbORGWdAj21A6sMJDYXy\nHS9FBNV54daNcr+weky2L9gaF2yFjeu2rSEHCSfkbWfpSiVUx/bDTj7XS6XDOuJT\nJqvGS8jHqjHAIFBirhCA4cY/jLKxWyMr5N6IbXpPAYgt8/YYz2aOYVvdyB8tZ1u1\nkVsMYSGcvTBexZCn1cDkbO6I+waIlsc0uxGqUGBKF83AVYCQqOkBjF1uNnu9qefE\nkEc9obr4JZsAgnisboU25ss5ZJddKlmFMKSi66g4S5ChLEPFq7MB06PhLFioaD3L\nEXza7XitoW5VBwr0BSVKAHMC0T2xbm70zY06a6gQRlvr9a10lPmv4Tptc7xgQReg\nu1TlFPbrkGJ0d8O6vHQRAd3zdsNaVr4gX0Tg7UYiqT9ZUkP7hOc8PYXQ28hHrHTB\nA63MTq0aiPlJ/ivTuX8M6+Bi25dIV6N6IOUi/NQKIYxgovJCDSdCAAM0fQARAQAB\ntCFMdWNhcyBHYXJmaWVsZCA8bHVjYXNAcmVkaGF0LmNvbT6JAlcEEwEIAEEWIQTO\nQZeiHnXqdjmfUURc6PeuecS2PAUCY33fTQIbAwUJA8JnAAULCQgHAgIiAgYVCgkI\nCwIEFgIDAQIeBwIXgAAKCRBc6PeuecS2PCk3D/9jW7xrBB/2MQFKd5l+mNMFyKwc\nL9M/M5RFI9GaQRo55CwnPb0nnxOJR1V5GzZ/YGii53H2ose65CfBOE2L/F/RvKF0\nH9S9MInixlahzzKtV3TpDoZGk5oZIHEMuPmPS4XaHggolrzExY0ib0mQuBBE/uEV\n/HlyHEunBKPhTkAe+6Q+2dl22SUuVfWr4Uzlp65+DkdN3M37WI1a3Suhnef3rOSM\nV6puUzWRR7qcYs5C2In87AcYPn92P5ur1y/C32r8Ftg3fRWnEzI9QfRG52ojNOLK\nyGQ8ZC9PGe0q7VFcF7ridT/uzRU+NVKldbJg+rvBnszb1MjNuR7rUQHyvGmbsUVQ\nRCsgdovkee3lP4gfZHzk2SSLVSo0+NJRNaM90EmPk14Pgi/yfRSDGBVvLBbEanYI\nv1ZtdIPRyKi+/IaMOu/l7nayM/8RzghdU+0f1FAif5qf9nXuI13P8fqcqfu67gNd\nkh0UUF1XyR5UHHEZQQDqCuKEkZJ/+27jYlsG1ZiLb1odlIWoR44RP6k5OJl0raZb\nyLXbAfpITsXiJJBpCam9P9+XR5VSfgkqp5hIa7J8piN3DoMpoExg4PPQr6PbLAJy\nOUCOnuB7yYVbj0wYuMXTuyrcBHh/UymQnS8AMpQoEkCLWS/A/Hze/pD23LgiBoLY\nXIn5A2EOAf7t2IMSlA==\n=OanT\n-----END PGP PUBLIC KEY BLOCK-----',
rhsm: false,
},
];
const expectedCustomRepositories: CustomRepository[] = [
{
baseurl: ['http://valid.link.to.repo.org/x86_64/'],
check_gpg: true,
check_repo_gpg: false,
gpgkey: [
'-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBGN9300BEAC1FLODu0cL6saMMHa7yJY1JZUc+jQUI/HdECQrrsTaPXlcc7nM\nykYMMv6amPqbnhH/R5BW2Ano+OMse+PXtUr0NXU4OcvxbnnXkrVBVUf8mXI9DzLZ\njw8KoD+4/s0BuzO78zAJF5uhuyHMAK0ll9v0r92kK45Fas9iZTfRFcqFAzvgjScf\n5jeBnbRs5U3UTz9mtDy802mk357o1A8BD0qlu3kANDpjLbORGWdAj21A6sMJDYXy\nHS9FBNV54daNcr+weky2L9gaF2yFjeu2rSEHCSfkbWfpSiVUx/bDTj7XS6XDOuJT\nJqvGS8jHqjHAIFBirhCA4cY/jLKxWyMr5N6IbXpPAYgt8/YYz2aOYVvdyB8tZ1u1\nkVsMYSGcvTBexZCn1cDkbO6I+waIlsc0uxGqUGBKF83AVYCQqOkBjF1uNnu9qefE\nkEc9obr4JZsAgnisboU25ss5ZJddKlmFMKSi66g4S5ChLEPFq7MB06PhLFioaD3L\nEXza7XitoW5VBwr0BSVKAHMC0T2xbm70zY06a6gQRlvr9a10lPmv4Tptc7xgQReg\nu1TlFPbrkGJ0d8O6vHQRAd3zdsNaVr4gX0Tg7UYiqT9ZUkP7hOc8PYXQ28hHrHTB\nA63MTq0aiPlJ/ivTuX8M6+Bi25dIV6N6IOUi/NQKIYxgovJCDSdCAAM0fQARAQAB\ntCFMdWNhcyBHYXJmaWVsZCA8bHVjYXNAcmVkaGF0LmNvbT6JAlcEEwEIAEEWIQTO\nQZeiHnXqdjmfUURc6PeuecS2PAUCY33fTQIbAwUJA8JnAAULCQgHAgIiAgYVCgkI\nCwIEFgIDAQIeBwIXgAAKCRBc6PeuecS2PCk3D/9jW7xrBB/2MQFKd5l+mNMFyKwc\nL9M/M5RFI9GaQRo55CwnPb0nnxOJR1V5GzZ/YGii53H2ose65CfBOE2L/F/RvKF0\nH9S9MInixlahzzKtV3TpDoZGk5oZIHEMuPmPS4XaHggolrzExY0ib0mQuBBE/uEV\n/HlyHEunBKPhTkAe+6Q+2dl22SUuVfWr4Uzlp65+DkdN3M37WI1a3Suhnef3rOSM\nV6puUzWRR7qcYs5C2In87AcYPn92P5ur1y/C32r8Ftg3fRWnEzI9QfRG52ojNOLK\nyGQ8ZC9PGe0q7VFcF7ridT/uzRU+NVKldbJg+rvBnszb1MjNuR7rUQHyvGmbsUVQ\nRCsgdovkee3lP4gfZHzk2SSLVSo0+NJRNaM90EmPk14Pgi/yfRSDGBVvLBbEanYI\nv1ZtdIPRyKi+/IaMOu/l7nayM/8RzghdU+0f1FAif5qf9nXuI13P8fqcqfu67gNd\nkh0UUF1XyR5UHHEZQQDqCuKEkZJ/+27jYlsG1ZiLb1odlIWoR44RP6k5OJl0raZb\nyLXbAfpITsXiJJBpCam9P9+XR5VSfgkqp5hIa7J8piN3DoMpoExg4PPQr6PbLAJy\nOUCOnuB7yYVbj0wYuMXTuyrcBHh/UymQnS8AMpQoEkCLWS/A/Hze/pD23LgiBoLY\nXIn5A2EOAf7t2IMSlA==\n=OanT\n-----END PGP PUBLIC KEY BLOCK-----',
],
id: 'ae39f556-6986-478a-95d1-f9c7e33d066c',
name: '01-test-valid-repo',
},
];
test('with custom repositories', async () => {
await render();
await goToRepositoriesStep();
await selectFirstRepository();
await goToReviewStep();
const receivedRequest = await interceptBlueprintRequest(CREATE_BLUEPRINT);
const expectedRequest: CreateBlueprintRequest = {
...blueprintRequest,
customizations: {
custom_repositories: expectedCustomRepositories,
payload_repositories: expectedPayloadRepositories,
},
};
expect(receivedRequest).toEqual(expectedRequest);
});
test('deselecting a custom repository removes it from the request', async () => {
await render();
await goToRepositoriesStep();
await selectFirstRepository();
await deselectFirstRepository();
await goToReviewStep();
const receivedRequest = await interceptBlueprintRequest(CREATE_BLUEPRINT);
const expectedRequest = blueprintRequest;
expect(receivedRequest).toEqual(expectedRequest);
});
});