diff --git a/src/AppCockpit.tsx b/src/AppCockpit.tsx
index 4c825be28c..be31af6c53 100644
--- a/src/AppCockpit.tsx
+++ b/src/AppCockpit.tsx
@@ -12,6 +12,8 @@ import { HashRouter } from 'react-router-dom';
import './AppCockpit.scss';
import { NotReady, RequireAdmin } from './Components/Cockpit';
+import { PlatformProvider } from './context/platform';
+import { onPremPlatform } from './context/platform/onprem';
import { Router } from './Router';
import { onPremStore as store } from './store';
import { useGetComposerSocketStatus } from './Utilities/useComposerStatus';
@@ -41,11 +43,13 @@ const Application = () => {
};
const ImageBuilder = () => (
-
-
-
-
-
+
+
+
+
+
+
+
);
diff --git a/src/AppEntry.tsx b/src/AppEntry.tsx
index 514c33828d..5303d3f36c 100644
--- a/src/AppEntry.tsx
+++ b/src/AppEntry.tsx
@@ -3,11 +3,15 @@ import React from 'react';
import { Provider } from 'react-redux';
import App from './App';
+import { PlatformProvider } from './context/platform';
+import { hostedPlatform } from './context/platform/hosted';
import { serviceStore as store } from './store';
const ImageBuilder = () => (
-
+
+
+
);
diff --git a/src/Components/Blueprints/BlueprintDiffModal.tsx b/src/Components/Blueprints/BlueprintDiffModal.tsx
index b37b4c7517..96d499c3d9 100644
--- a/src/Components/Blueprints/BlueprintDiffModal.tsx
+++ b/src/Components/Blueprints/BlueprintDiffModal.tsx
@@ -10,7 +10,7 @@ import {
ModalVariant,
} from '@patternfly/react-core';
-import { useGetBlueprintQuery } from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
import { selectSelectedBlueprintId } from '@/store/slices/blueprint';
import { BuildImagesButton } from './BuildImagesButton';
@@ -31,6 +31,9 @@ const BlueprintDiffModal = ({
isOpen,
onClose,
}: blueprintDiffProps) => {
+ const {
+ queries: { useGetBlueprintQuery },
+ } = usePlatform();
const selectedBlueprintId = useAppSelector(selectSelectedBlueprintId);
const { data: baseBlueprint } = useGetBlueprintQuery(
diff --git a/src/Components/Blueprints/BlueprintsPagination.tsx b/src/Components/Blueprints/BlueprintsPagination.tsx
index c8061b8d8c..1b000f20f0 100644
--- a/src/Components/Blueprints/BlueprintsPagination.tsx
+++ b/src/Components/Blueprints/BlueprintsPagination.tsx
@@ -3,10 +3,8 @@ import React from 'react';
import { Pagination, PaginationVariant } from '@patternfly/react-core';
import { OnSetPage } from '@patternfly/react-core/dist/esm/components/Pagination/Pagination';
-import {
- GetBlueprintsApiArg,
- useGetBlueprintsQuery,
-} from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
+import { GetBlueprintsApiArg } from '@/store/api/backend';
import {
selectBlueprintSearchInput,
selectLimit,
@@ -18,6 +16,9 @@ import {
import { useAppDispatch, useAppSelector } from '../../store/hooks';
const BlueprintsPagination = () => {
+ const {
+ queries: { useGetBlueprintsQuery },
+ } = usePlatform();
const blueprintSearchInput = useAppSelector(selectBlueprintSearchInput);
const blueprintsOffset = useAppSelector(selectOffset) || 0;
const blueprintsLimit = useAppSelector(selectLimit) || 10;
diff --git a/src/Components/Blueprints/BlueprintsSideBar.tsx b/src/Components/Blueprints/BlueprintsSideBar.tsx
index 1600978d8e..95a07f7064 100644
--- a/src/Components/Blueprints/BlueprintsSideBar.tsx
+++ b/src/Components/Blueprints/BlueprintsSideBar.tsx
@@ -18,12 +18,8 @@ import { PlusCircleIcon, SearchIcon } from '@patternfly/react-icons';
import { SVGIconProps } from '@patternfly/react-icons/dist/esm/createIcon';
import useChrome from '@redhat-cloud-services/frontend-components/useChrome';
-import {
- BlueprintItem,
- GetBlueprintsApiArg,
- imageBuilderApi,
- useGetBlueprintsQuery,
-} from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
+import { BlueprintItem, GetBlueprintsApiArg } from '@/store/api/backend';
import {
selectBlueprintSearchInput,
selectLimit,
@@ -57,6 +53,9 @@ type emptyBlueprintStateProps = {
};
const BlueprintsSidebar = () => {
+ const {
+ queries: { useGetBlueprintsQuery },
+ } = usePlatform();
const { analytics, auth } = useChrome();
const { userData } = useGetUser(auth);
const isOnPremise = useAppSelector(selectIsOnPremise);
@@ -181,6 +180,9 @@ const BlueprintsSidebar = () => {
};
const BlueprintSearch = ({ blueprintsTotal }: blueprintSearchProps) => {
+ const {
+ api: { backendApi },
+ } = usePlatform();
const blueprintSearchInput = useAppSelector(selectBlueprintSearchInput);
const dispatch = useAppDispatch();
const [localSearchValue, setLocalSearchValue] = useState(
@@ -190,7 +192,7 @@ const BlueprintSearch = ({ blueprintsTotal }: blueprintSearchProps) => {
useEffect(() => {
dispatch(setBlueprintsOffset(0));
- dispatch(imageBuilderApi.util.invalidateTags([{ type: 'Blueprints' }]));
+ dispatch(backendApi.util.invalidateTags([{ type: 'Blueprints' }]));
dispatch(
setBlueprintSearchInput(
debouncedSearchValue.length > 0 ? debouncedSearchValue : undefined,
diff --git a/src/Components/Blueprints/BuildImagesButton.tsx b/src/Components/Blueprints/BuildImagesButton.tsx
index ed77ce48c7..e3833abb57 100644
--- a/src/Components/Blueprints/BuildImagesButton.tsx
+++ b/src/Components/Blueprints/BuildImagesButton.tsx
@@ -18,7 +18,8 @@ import { MenuToggleElement } from '@patternfly/react-core/dist/esm/components/Me
import useChrome from '@redhat-cloud-services/frontend-components/useChrome';
import { skipToken } from '@reduxjs/toolkit/query';
-import { ImageTypes, useGetBlueprintQuery } from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
+import { ImageTypes } from '@/store/api/backend';
import { selectSelectedBlueprintId } from '@/store/slices/blueprint';
import { selectIsOnPremise } from '@/store/slices/env';
@@ -35,6 +36,9 @@ type BuildImagesButtonPropTypes = {
};
export const BuildImagesButton = ({ children }: BuildImagesButtonPropTypes) => {
+ const {
+ queries: { useGetBlueprintQuery },
+ } = usePlatform();
const selectedBlueprintId = useAppSelector(selectSelectedBlueprintId);
const [deselectedTargets, setDeselectedTargets] = useState([]);
const { trigger: buildBlueprint, isLoading: imageBuildLoading } =
diff --git a/src/Components/Blueprints/DeleteBlueprintModal.tsx b/src/Components/Blueprints/DeleteBlueprintModal.tsx
index 60ff59ee33..179cc9f221 100644
--- a/src/Components/Blueprints/DeleteBlueprintModal.tsx
+++ b/src/Components/Blueprints/DeleteBlueprintModal.tsx
@@ -10,11 +10,8 @@ import {
} from '@patternfly/react-core';
import useChrome from '@redhat-cloud-services/frontend-components/useChrome';
-import {
- backendApi,
- GetBlueprintsApiArg,
- useGetBlueprintsQuery,
-} from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
+import { GetBlueprintsApiArg } from '@/store/api/backend';
import {
selectBlueprintSearchInput,
selectLimit,
@@ -43,6 +40,10 @@ interface DeleteBlueprintModalProps {
export const DeleteBlueprintModal: React.FunctionComponent<
DeleteBlueprintModalProps
> = ({ setShowDeleteModal, isOpen }: DeleteBlueprintModalProps) => {
+ const {
+ queries: { useGetBlueprintsQuery },
+ api: { backendApi },
+ } = usePlatform();
const selectedBlueprintId = useAppSelector(selectSelectedBlueprintId);
const blueprintSearchInput = useAppSelector(selectBlueprintSearchInput);
const blueprintsOffset = useAppSelector(selectOffset) || PAGINATION_OFFSET;
diff --git a/src/Components/CreateImageWizard/steps/ImageOutput/components/ImageSourceSelect.tsx b/src/Components/CreateImageWizard/steps/ImageOutput/components/ImageSourceSelect.tsx
index a4a7f95a62..30194c0864 100644
--- a/src/Components/CreateImageWizard/steps/ImageOutput/components/ImageSourceSelect.tsx
+++ b/src/Components/CreateImageWizard/steps/ImageOutput/components/ImageSourceSelect.tsx
@@ -19,10 +19,8 @@ import {
import { SyncAltIcon } from '@patternfly/react-icons';
import { RHEL_10_IMAGE_MODE_IMAGE } from '@/constants';
-import {
- BootcDistributionItem,
- useGetDistributionsQuery,
-} from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
+import { BootcDistributionItem } from '@/store/api/backend';
import { Distributions } from '@/store/api/backend/hosted';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
import { selectIsOnPremise } from '@/store/slices/env';
@@ -63,6 +61,9 @@ const InfoMessageContent = ({ source }: { source: string }) => {
};
const ImageSourceSelect = () => {
+ const {
+ queries: { useGetDistributionsQuery },
+ } = usePlatform();
const dispatch = useAppDispatch();
const isOnPremise = useAppSelector(selectIsOnPremise);
const arch = useAppSelector(selectArchitecture);
diff --git a/src/Components/CreateImageWizard/steps/ImageOutput/components/TargetEnvironment.tsx b/src/Components/CreateImageWizard/steps/ImageOutput/components/TargetEnvironment.tsx
index ac57068963..e98a18f61b 100644
--- a/src/Components/CreateImageWizard/steps/ImageOutput/components/TargetEnvironment.tsx
+++ b/src/Components/CreateImageWizard/steps/ImageOutput/components/TargetEnvironment.tsx
@@ -11,14 +11,10 @@ import {
Tooltip,
} from '@patternfly/react-core';
+import { usePlatform } from '@/context/platform';
import { useTargetEnvironmentCategories } from '@/Hooks';
import { rhsmApi } from '@/store/api';
-import {
- BootcDistributionItem,
- ImageTypes,
- useGetArchitecturesQuery,
- useGetDistributionsQuery,
-} from '@/store/api/backend';
+import { BootcDistributionItem, ImageTypes } from '@/store/api/backend';
import { useCustomizationRestrictions } from '@/store/api/distributions';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
import {
@@ -67,6 +63,9 @@ const createLabelWithTooltip = (
};
const TargetEnvironment = () => {
+ const {
+ queries: { useGetArchitecturesQuery, useGetDistributionsQuery },
+ } = usePlatform();
const arch = useAppSelector(selectArchitecture);
const environments = useAppSelector(selectImageTypes);
const distribution = useAppSelector(selectDistribution);
diff --git a/src/Components/CreateImageWizard/steps/ImageOutput/tests/ImageSourceSelect.test.tsx b/src/Components/CreateImageWizard/steps/ImageOutput/tests/ImageSourceSelect.test.tsx
index ffd9109a78..ec19770d5e 100644
--- a/src/Components/CreateImageWizard/steps/ImageOutput/tests/ImageSourceSelect.test.tsx
+++ b/src/Components/CreateImageWizard/steps/ImageOutput/tests/ImageSourceSelect.test.tsx
@@ -27,12 +27,19 @@ import ImageSourceSelect from '../components/ImageSourceSelect';
const mockRefetch = vi.fn();
const mockUseGetDistributionsQuery = vi.fn();
-vi.mock('@/store/api/backend', async (importOriginal) => {
- const actual = await importOriginal();
+vi.mock('@/context/platform', async (importOriginal) => {
+ const actual = await importOriginal();
+ const { mockPlatform } = await import('@/context/platform/tests/mocks');
return {
...actual,
- useGetDistributionsQuery: (...args: unknown[]) =>
- mockUseGetDistributionsQuery(...args),
+ usePlatform: () => ({
+ ...mockPlatform,
+ queries: {
+ ...mockPlatform.queries,
+ useGetDistributionsQuery: (...args: unknown[]) =>
+ mockUseGetDistributionsQuery(...args),
+ },
+ }),
};
});
diff --git a/src/Components/CreateImageWizard/steps/Kernel/components/KernelArguments.tsx b/src/Components/CreateImageWizard/steps/Kernel/components/KernelArguments.tsx
index 9bedf583dc..e1a1377d08 100644
--- a/src/Components/CreateImageWizard/steps/Kernel/components/KernelArguments.tsx
+++ b/src/Components/CreateImageWizard/steps/Kernel/components/KernelArguments.tsx
@@ -5,7 +5,7 @@ import { FormGroup, HelperText, HelperTextItem } from '@patternfly/react-core';
import LabelInput from '@/Components/CreateImageWizard/LabelInput';
import { useKernelValidation } from '@/Components/CreateImageWizard/utilities/useValidation';
import { isKernelArgumentValid } from '@/Components/CreateImageWizard/validators';
-import { useGetOscapCustomizationsQuery } from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
import { useAppSelector } from '@/store/hooks';
import {
addKernelArg,
@@ -16,6 +16,9 @@ import {
} from '@/store/slices/wizard';
const KernelArguments = () => {
+ const {
+ queries: { useGetOscapCustomizationsQuery },
+ } = usePlatform();
const kernelAppend = useAppSelector(selectKernel).append;
const stepValidation = useKernelValidation();
diff --git a/src/Components/CreateImageWizard/steps/Locale/index.tsx b/src/Components/CreateImageWizard/steps/Locale/index.tsx
index 2bd07bd6bb..425ac76ef2 100644
--- a/src/Components/CreateImageWizard/steps/Locale/index.tsx
+++ b/src/Components/CreateImageWizard/steps/Locale/index.tsx
@@ -3,7 +3,7 @@ import React, { useMemo } from 'react';
import { Content, Spinner, Title } from '@patternfly/react-core';
import { CustomizationLabels } from '@/Components/sharedComponents/CustomizationLabels';
-import { useGetArchitecturesQuery } from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
import { useSearchLanguagePacks } from '@/store/api/contentSources';
import { useAppSelector } from '@/store/hooks';
import {
@@ -17,6 +17,9 @@ import KeyboardDropDown from './components/KeyboardDropDown';
import LanguagesDropDown from './components/LanguagesDropDown';
const LocaleStep = () => {
+ const {
+ queries: { useGetArchitecturesQuery },
+ } = usePlatform();
const distribution = useAppSelector(selectDistribution);
const arch = useAppSelector(selectArchitecture);
const candidateLangpacks = useAppSelector(selectLocaleLangpackCandidates);
diff --git a/src/Components/CreateImageWizard/steps/Oscap/components/ProfileSelector.tsx b/src/Components/CreateImageWizard/steps/Oscap/components/ProfileSelector.tsx
index f7317a74d9..a664e7ecae 100644
--- a/src/Components/CreateImageWizard/steps/Oscap/components/ProfileSelector.tsx
+++ b/src/Components/CreateImageWizard/steps/Oscap/components/ProfileSelector.tsx
@@ -15,14 +15,12 @@ import {
} from '@patternfly/react-core';
import { TimesIcon } from '@patternfly/react-icons';
+import { usePlatform } from '@/context/platform';
import {
DistributionProfileItem,
DistributionProfileResponse,
OpenScap,
OpenScapProfile,
- useBackendPrefetch,
- useGetOscapCustomizationsQuery,
- useLazyGetOscapCustomizationsQuery,
} from '@/store/api/backend';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
import { selectIsOnPremise } from '@/store/slices/env';
@@ -60,6 +58,13 @@ const ProfileSelector = ({
isSuccess,
refetch,
}: ProfileSelectorProps) => {
+ const {
+ queries: {
+ useGetOscapCustomizationsQuery,
+ useLazyGetOscapCustomizationsQuery,
+ },
+ api: { useBackendPrefetch },
+ } = usePlatform();
const isOnPremise = useAppSelector(selectIsOnPremise);
const profileID = useAppSelector(selectComplianceProfileID);
const release = removeBetaFromRelease(useAppSelector(selectDistribution));
diff --git a/src/Components/CreateImageWizard/steps/Oscap/index.tsx b/src/Components/CreateImageWizard/steps/Oscap/index.tsx
index 35bcd524e9..4156c141fb 100644
--- a/src/Components/CreateImageWizard/steps/Oscap/index.tsx
+++ b/src/Components/CreateImageWizard/steps/Oscap/index.tsx
@@ -26,12 +26,8 @@ import {
FIRST_BOOT_SERVICE,
OSCAP_URL,
} from '@/constants';
+import { usePlatform } from '@/context/platform';
import { useGetUser } from '@/Hooks';
-import {
- useBackendPrefetch,
- useGetOscapCustomizationsQuery,
- useGetOscapProfilesQuery,
-} from '@/store/api/backend';
import { useSecuritySummary } from '@/store/api/backend/hooks';
import { usePoliciesQuery } from '@/store/api/compliance';
import { useCustomizationRestrictions } from '@/store/api/distributions';
@@ -71,6 +67,10 @@ import { removeBetaFromRelease } from './removeBetaFromRelease';
import ExternalLinkButton from '../../utilities/ExternalLinkButton';
const OscapContent = () => {
+ const {
+ queries: { useGetOscapProfilesQuery, useGetOscapCustomizationsQuery },
+ api: { useBackendPrefetch },
+ } = usePlatform();
const dispatch = useAppDispatch();
const registrationType = useAppSelector(selectRegistrationType);
const complianceType = useAppSelector(selectComplianceType);
diff --git a/src/Components/CreateImageWizard/steps/Packages/components/PackageSearch.tsx b/src/Components/CreateImageWizard/steps/Packages/components/PackageSearch.tsx
index 5baf48103b..9a97c611ad 100644
--- a/src/Components/CreateImageWizard/steps/Packages/components/PackageSearch.tsx
+++ b/src/Components/CreateImageWizard/steps/Packages/components/PackageSearch.tsx
@@ -30,6 +30,7 @@ import {
ContentOrigin,
EPEL_10_REPO_DEFINITION,
} from '@/constants';
+import { usePlatform } from '@/context/platform';
import {
Module,
useGetArchitecturesQuery,
@@ -42,7 +43,6 @@ import {
useListRepositoriesQuery,
useSearchPackageGroupMutation,
useSearchRepositoryModuleStreamsMutation,
- useSearchRpmMutation,
} from '@/store/api/contentSources';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
import { selectIsOnPremise } from '@/store/slices/env';
@@ -100,6 +100,9 @@ const PackageSearch = ({
activeStream,
setActiveStream,
}: PackageSearchProps) => {
+ const {
+ mutations: { useSearchRpmMutation },
+ } = usePlatform();
const dispatch = useAppDispatch();
const { analytics, isBeta } = useChrome();
@@ -254,13 +257,16 @@ const PackageSearch = ({
}
if (debouncedSearchTerm.length > 1 && isSuccessDistroRepositories) {
if (isOnPremise) {
+ // On-prem uses a different request shape with packages/architecture/distribution
+ // fields that don't exist in the hosted ApiContentUnitSearchRequest type.
+ // The on-prem implementation accepts these fields at runtime via PlatformContext.
searchDistroRpms({
apiContentUnitSearchRequest: {
packages: [debouncedSearchTerm],
architecture: arch,
distribution,
},
- });
+ } as Parameters[0]);
} else {
searchDistroRpms({
apiContentUnitSearchRequest: {
diff --git a/src/Components/CreateImageWizard/steps/Repositories/components/Repositories.tsx b/src/Components/CreateImageWizard/steps/Repositories/components/Repositories.tsx
index 421b03c033..c3cf0061c0 100644
--- a/src/Components/CreateImageWizard/steps/Repositories/components/Repositories.tsx
+++ b/src/Components/CreateImageWizard/steps/Repositories/components/Repositories.tsx
@@ -16,12 +16,12 @@ import { ExternalLinkAltIcon } from '@patternfly/react-icons';
import { Table, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table';
import { CONTENT_URL, ContentOrigin } from '@/constants';
+import { usePlatform } from '@/context/platform';
import {
ApiRepositoryResponseRead,
useGetTemplateQuery,
useListRepositoriesQuery,
useListRepositoryParametersQuery,
- useListSnapshotsByDateMutation,
} from '@/store/api/contentSources';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
import {
@@ -70,6 +70,9 @@ import {
} from '../repositoriesUtilities';
const Repositories = () => {
+ const {
+ mutations: { useListSnapshotsByDateMutation },
+ } = usePlatform();
const dispatch = useAppDispatch();
const arch = useAppSelector(selectArchitecture);
diff --git a/src/Components/CreateImageWizard/steps/Review/Footer/CreateDropdown.tsx b/src/Components/CreateImageWizard/steps/Review/Footer/CreateDropdown.tsx
index af852c65f7..9d7282e8b3 100644
--- a/src/Components/CreateImageWizard/steps/Review/Footer/CreateDropdown.tsx
+++ b/src/Components/CreateImageWizard/steps/Review/Footer/CreateDropdown.tsx
@@ -82,7 +82,7 @@ export const CreateSaveAndBuildBtn = ({
}
if (requestBody) {
const blueprint = (await createBlueprint({
- createBlueprintRequest: requestBody,
+ createBlueprintRequest: requestBody as CreateBlueprintRequest,
})) as CreateBlueprintResponse;
buildBlueprint({ id: blueprint.id, body: {} });
@@ -181,7 +181,7 @@ export const CreateSaveButton = ({
}
if (requestBody) {
const blueprint = (await createBlueprint({
- createBlueprintRequest: requestBody,
+ createBlueprintRequest: requestBody as CreateBlueprintRequest,
})) as CreateBlueprintResponse;
dispatch(setBlueprintId(blueprint.id));
}
diff --git a/src/Components/CreateImageWizard/steps/Review/Footer/EditDropdown.tsx b/src/Components/CreateImageWizard/steps/Review/Footer/EditDropdown.tsx
index 158464bde6..3cf86cd7bd 100644
--- a/src/Components/CreateImageWizard/steps/Review/Footer/EditDropdown.tsx
+++ b/src/Components/CreateImageWizard/steps/Review/Footer/EditDropdown.tsx
@@ -78,7 +78,7 @@ export const EditSaveAndBuildBtn = ({
if (requestBody) {
await updateBlueprint({
id: blueprintId,
- createBlueprintRequest: requestBody,
+ createBlueprintRequest: requestBody as CreateBlueprintRequest,
});
}
buildBlueprint({ id: blueprintId, body: {} });
@@ -127,7 +127,7 @@ export const EditSaveButton = ({
if (requestBody) {
updateBlueprint({
id: blueprintId,
- createBlueprintRequest: requestBody,
+ createBlueprintRequest: requestBody as CreateBlueprintRequest,
});
}
};
diff --git a/src/Components/CreateImageWizard/steps/Services/components/ServicesInputs.tsx b/src/Components/CreateImageWizard/steps/Services/components/ServicesInputs.tsx
index 7d7b515d9a..fa446ec6bb 100644
--- a/src/Components/CreateImageWizard/steps/Services/components/ServicesInputs.tsx
+++ b/src/Components/CreateImageWizard/steps/Services/components/ServicesInputs.tsx
@@ -5,7 +5,7 @@ import { FormGroup, HelperText, HelperTextItem } from '@patternfly/react-core';
import LabelInput from '@/Components/CreateImageWizard/LabelInput';
import { useServicesValidation } from '@/Components/CreateImageWizard/utilities/useValidation';
import { isServiceValid } from '@/Components/CreateImageWizard/validators';
-import { useGetOscapCustomizationsQuery } from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
import { useAppSelector } from '@/store/hooks';
import {
addDisabledService,
@@ -20,6 +20,9 @@ import {
} from '@/store/slices/wizard';
const ServicesInput = () => {
+ const {
+ queries: { useGetOscapCustomizationsQuery },
+ } = usePlatform();
const disabledServices = useAppSelector(selectServices).disabled;
const maskedServices = useAppSelector(selectServices).masked;
const enabledServices = useAppSelector(selectServices).enabled;
diff --git a/src/Components/CreateImageWizard/tests/helpers.tsx b/src/Components/CreateImageWizard/tests/helpers.tsx
index 25a452f32d..b7378504f9 100644
--- a/src/Components/CreateImageWizard/tests/helpers.tsx
+++ b/src/Components/CreateImageWizard/tests/helpers.tsx
@@ -5,6 +5,8 @@ import { render, screen } from '@testing-library/react';
import { Provider } from 'react-redux';
import { createMemoryRouter, RouterProvider } from 'react-router-dom';
+import { PlatformProvider } from '@/context/platform';
+import { hostedPlatform } from '@/context/platform/hosted';
import { RootState, serviceMiddleware, serviceReducer } from '@/store';
import CreateImageWizard from '../CreateImageWizard';
@@ -36,12 +38,14 @@ export const renderWithQueryParams = async (
render(
-
+
+
+
,
);
diff --git a/src/Components/CreateImageWizard/utilities/useValidation.tsx b/src/Components/CreateImageWizard/utilities/useValidation.tsx
index c3e24692b7..a9f357d8ca 100644
--- a/src/Components/CreateImageWizard/utilities/useValidation.tsx
+++ b/src/Components/CreateImageWizard/utilities/useValidation.tsx
@@ -3,10 +3,8 @@ import React, { useEffect, useState } from 'react';
import { CheckCircleIcon } from '@patternfly/react-icons';
import { jwtDecode } from 'jwt-decode';
-import {
- BlueprintsResponse,
- useLazyGetBlueprintsQuery,
-} from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
+import { BlueprintsResponse } from '@/store/api/backend';
import { useShowActivationKeyQuery } from '@/store/api/rhsm';
import { selectIsOnPremise } from '@/store/slices/env';
import {
@@ -1098,6 +1096,9 @@ const countCharacterTypes = (value: string) => {
};
export function useDetailsValidation(): StepValidation {
+ const {
+ queries: { useLazyGetBlueprintsQuery },
+ } = usePlatform();
const name = useAppSelector(selectBlueprintName);
const description = useAppSelector(selectBlueprintDescription);
const blueprintId = useAppSelector(selectBlueprintId);
diff --git a/src/Components/ImagesTable/ImageDetails.tsx b/src/Components/ImagesTable/ImageDetails.tsx
index 9a720fb3c4..a838218198 100644
--- a/src/Components/ImagesTable/ImageDetails.tsx
+++ b/src/Components/ImagesTable/ImageDetails.tsx
@@ -12,10 +12,10 @@ import {
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
import useChrome from '@redhat-cloud-services/frontend-components/useChrome';
+import { usePlatform } from '@/context/platform';
import {
ComposesResponseItem,
GcpUploadRequestOptions,
- useGetComposeStatusQuery,
} from '@/store/api/backend';
import { selectIsOnPremise } from '@/store/slices/env';
@@ -45,6 +45,9 @@ type AwsDetailsPropTypes = {
};
export const AwsDetails = ({ compose }: AwsDetailsPropTypes) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const options = compose.request.image_requests[0].upload_request.options;
const { analytics, auth } = useChrome();
@@ -176,6 +179,9 @@ type AzureDetailsPropTypes = {
};
export const AzureDetails = ({ compose }: AzureDetailsPropTypes) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const { data: composeStatus } = useGetComposeStatusQuery({
composeId: compose.id,
});
@@ -256,6 +262,9 @@ type GcpDetailsPropTypes = {
};
export const GcpDetails = ({ compose }: GcpDetailsPropTypes) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const { data: composeStatus } = useGetComposeStatusQuery({
composeId: compose.id,
});
@@ -344,6 +353,9 @@ type OciDetailsPropTypes = {
};
export const OciDetails = ({ compose }: OciDetailsPropTypes) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const { data: composeStatus } = useGetComposeStatusQuery({
composeId: compose.id,
});
diff --git a/src/Components/ImagesTable/ImagesTable.tsx b/src/Components/ImagesTable/ImagesTable.tsx
index 6c87300b24..b2788dd739 100644
--- a/src/Components/ImagesTable/ImagesTable.tsx
+++ b/src/Components/ImagesTable/ImagesTable.tsx
@@ -31,16 +31,13 @@ import useChrome from '@redhat-cloud-services/frontend-components/useChrome';
import cockpit from 'cockpit';
import { useDispatch } from 'react-redux';
+import { usePlatform } from '@/context/platform';
import {
BlueprintItem,
ComposesResponseItem,
GetBlueprintComposesApiArg,
GetBlueprintsApiArg,
LocalUploadStatus,
- useGetBlueprintComposesQuery,
- useGetBlueprintsQuery,
- useGetComposesQuery,
- useGetComposeStatusQuery,
} from '@/store/api/backend';
import {
selectBlueprintSearchInput,
@@ -96,6 +93,13 @@ import { GcpLaunchModal } from '../Launch/GcpLaunchModal';
import { OciLaunchModal } from '../Launch/OciLaunchModal';
const ImagesTable = () => {
+ const {
+ queries: {
+ useGetBlueprintsQuery,
+ useGetBlueprintComposesQuery,
+ useGetComposesQuery,
+ },
+ } = usePlatform();
const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(10);
@@ -322,6 +326,9 @@ type ImagesTableRowPropTypes = {
};
const ImagesTableRow = ({ compose, rowIndex }: ImagesTableRowPropTypes) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const [pollingInterval, setPollingInterval] = useState(
STATUS_POLLING_INTERVAL,
);
@@ -580,6 +587,9 @@ const Row = ({
details,
instance,
}: RowPropTypes) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const { analytics, auth } = useChrome();
const { userData } = useGetUser(auth);
const isOnPremise = useAppSelector(selectIsOnPremise);
diff --git a/src/Components/ImagesTable/ImagesTableToolbar.tsx b/src/Components/ImagesTable/ImagesTableToolbar.tsx
index 86e1e0e3a4..081cd2b613 100644
--- a/src/Components/ImagesTable/ImagesTableToolbar.tsx
+++ b/src/Components/ImagesTable/ImagesTableToolbar.tsx
@@ -13,13 +13,11 @@ import {
ToolbarItem,
} from '@patternfly/react-core';
+import { usePlatform } from '@/context/platform';
import {
BlueprintItem,
Distributions,
GetBlueprintComposesApiArg,
- useGetBlueprintComposesQuery,
- useGetBlueprintQuery,
- useGetBlueprintsQuery,
} from '@/store/api/backend';
import {
selectBlueprintSearchInput,
@@ -58,6 +56,13 @@ const ImagesTableToolbar: React.FC = ({
setPage,
onPerPageSelect,
}: imagesTableToolbarProps) => {
+ const {
+ queries: {
+ useGetBlueprintQuery,
+ useGetBlueprintComposesQuery,
+ useGetBlueprintsQuery,
+ },
+ } = usePlatform();
const isOnPremise = useAppSelector(selectIsOnPremise);
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [showDiffModal, setShowDiffModal] = useState(false);
diff --git a/src/Components/ImagesTable/Instance.tsx b/src/Components/ImagesTable/Instance.tsx
index f1073cc89c..a012644512 100644
--- a/src/Components/ImagesTable/Instance.tsx
+++ b/src/Components/ImagesTable/Instance.tsx
@@ -6,11 +6,11 @@ import { Button, Skeleton } from '@patternfly/react-core';
import useChrome from '@redhat-cloud-services/frontend-components/useChrome';
import cockpit from 'cockpit';
+import { usePlatform } from '@/context/platform';
import {
ComposesResponseItem,
ImageTypes,
LocalUploadStatus,
- useGetComposeStatusQuery,
} from '@/store/api/backend';
import { selectIsOnPremise } from '@/store/slices/env';
@@ -32,6 +32,9 @@ export const AwsS3Instance = ({
compose,
isExpired,
}: AwsS3InstancePropTypes) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const { analytics } = useChrome();
const isOnPremise = useAppSelector(selectIsOnPremise);
@@ -121,6 +124,9 @@ const VM_INSTALLABLE_IMAGE_TYPES: ImageTypes[] = [
];
export const LocalInstance = ({ compose }: LocalInstancePropTypes) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const isMachinesAvailable = useCockpitMachinesAvailable();
const { data: composeStatus, isSuccess } = useGetComposeStatusQuery({
composeId: compose.id,
diff --git a/src/Components/ImagesTable/Status.tsx b/src/Components/ImagesTable/Status.tsx
index 3c2d75bf39..fdec39757d 100644
--- a/src/Components/ImagesTable/Status.tsx
+++ b/src/Components/ImagesTable/Status.tsx
@@ -24,12 +24,12 @@ import {
PendingIcon,
} from '@patternfly/react-icons';
+import { usePlatform } from '@/context/platform';
import {
ComposerComposesResponseItem,
ComposesResponseItem,
ComposeStatus,
ComposeStatusError,
- useGetComposeStatusQuery,
} from '@/store/api/backend';
import {
@@ -42,6 +42,9 @@ type ComposeStatusPropTypes = {
};
export const AwsDetailsStatus = ({ compose }: ComposeStatusPropTypes) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const { data, isSuccess } = useGetComposeStatusQuery({
composeId: compose.id,
});
@@ -77,6 +80,9 @@ type CloudStatusPropTypes = {
};
export const CloudStatus = ({ compose }: CloudStatusPropTypes) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const { data, isSuccess } = useGetComposeStatusQuery({
composeId: compose.id,
});
@@ -145,6 +151,9 @@ export const ExpiringStatus = ({
isExpired,
timeToExpiration,
}: ExpiringStatusPropTypes) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const { data: composeStatus, isSuccess } = useGetComposeStatusQuery({
composeId: compose.id,
});
@@ -237,6 +246,9 @@ type LocalStatusPropTypes = {
};
export const LocalStatus = ({ compose }: LocalStatusPropTypes) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const { data: composeStatus, isSuccess } = useGetComposeStatusQuery({
composeId: compose.id,
});
diff --git a/src/Components/LandingPage/tests/helpers.tsx b/src/Components/LandingPage/tests/helpers.tsx
index 3302a5f269..6df6354742 100644
--- a/src/Components/LandingPage/tests/helpers.tsx
+++ b/src/Components/LandingPage/tests/helpers.tsx
@@ -4,6 +4,9 @@ import { render } from '@testing-library/react';
import { Provider } from 'react-redux';
import { createMemoryRouter, RouterProvider } from 'react-router-dom';
+import { PlatformProvider } from '@/context/platform';
+import { hostedPlatform } from '@/context/platform/hosted';
+import { onPremPlatform } from '@/context/platform/onprem';
import { createTestStore, type RenderWithReduxOptions } from '@/test/testUtils';
import LandingPage from '../LandingPage';
@@ -22,14 +25,18 @@ export const renderLandingPage = (options: RenderWithReduxOptions = {}) => {
initialEntries: ['/'],
});
+ const isOnPremise = options.preloadedState?.env?.isOnPremise;
+ const platform = isOnPremise ? onPremPlatform : hostedPlatform;
const view = render(
-
+
+
+
,
);
diff --git a/src/Components/Launch/AWSLaunchModal.tsx b/src/Components/Launch/AWSLaunchModal.tsx
index 5038365b44..a70df493cb 100644
--- a/src/Components/Launch/AWSLaunchModal.tsx
+++ b/src/Components/Launch/AWSLaunchModal.tsx
@@ -15,10 +15,8 @@ import {
} from '@patternfly/react-core';
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
-import {
- ComposesResponseItem,
- useGetComposeStatusQuery,
-} from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
+import { ComposesResponseItem } from '@/store/api/backend';
import { isAwsUploadRequestOptions } from '../../store/typeGuards';
@@ -27,6 +25,9 @@ type LaunchProps = {
};
export const AWSLaunchModal = ({ compose }: LaunchProps) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const [isModalOpen, setIsModalOpen] = useState(false);
const { data, isSuccess, isFetching } = useGetComposeStatusQuery({
composeId: compose.id,
diff --git a/src/Components/Launch/AzureLaunchModal.tsx b/src/Components/Launch/AzureLaunchModal.tsx
index 739c340871..bb6966f54b 100644
--- a/src/Components/Launch/AzureLaunchModal.tsx
+++ b/src/Components/Launch/AzureLaunchModal.tsx
@@ -15,10 +15,8 @@ import {
} from '@patternfly/react-core';
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
-import {
- ComposesResponseItem,
- useGetComposeStatusQuery,
-} from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
+import { ComposesResponseItem } from '@/store/api/backend';
import { isAzureUploadStatus } from '../../store/typeGuards';
@@ -27,6 +25,9 @@ type LaunchProps = {
};
export const AzureLaunchModal = ({ compose }: LaunchProps) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const [isModalOpen, setIsModalOpen] = useState(false);
const { data, isSuccess, isFetching } = useGetComposeStatusQuery({
composeId: compose.id,
diff --git a/src/Components/Launch/GcpLaunchModal.tsx b/src/Components/Launch/GcpLaunchModal.tsx
index 3fc0aa8f84..a95bf0903f 100644
--- a/src/Components/Launch/GcpLaunchModal.tsx
+++ b/src/Components/Launch/GcpLaunchModal.tsx
@@ -18,10 +18,8 @@ import {
} from '@patternfly/react-core';
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
-import {
- ComposesResponseItem,
- useGetComposeStatusQuery,
-} from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
+import { ComposesResponseItem } from '@/store/api/backend';
import { generateDefaultName } from './useGenerateDefaultName';
@@ -34,6 +32,9 @@ import { parseGcpSharedWith } from '../ImagesTable/ImageDetails';
type LaunchProps = { compose: ComposesResponseItem };
export const GcpLaunchModal = ({ compose }: LaunchProps) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const [isModalOpen, setIsModalOpen] = useState(false);
const [customerProjectId, setCustomerProjectId] = useState('');
const { data, isSuccess, isFetching } = useGetComposeStatusQuery({
diff --git a/src/Components/Launch/OciLaunchModal.tsx b/src/Components/Launch/OciLaunchModal.tsx
index d6a72e270d..3933da4230 100644
--- a/src/Components/Launch/OciLaunchModal.tsx
+++ b/src/Components/Launch/OciLaunchModal.tsx
@@ -18,10 +18,8 @@ import {
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
import { useNavigate } from 'react-router-dom';
-import {
- ComposesResponseItem,
- useGetComposeStatusQuery,
-} from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
+import { ComposesResponseItem } from '@/store/api/backend';
import { selectPathResolver } from '@/store/slices/env';
import { useAppSelector } from '../../store/hooks';
@@ -33,6 +31,9 @@ type LaunchProps = {
};
export const OciLaunchModal = ({ isExpired, compose }: LaunchProps) => {
+ const {
+ queries: { useGetComposeStatusQuery },
+ } = usePlatform();
const [isModalOpen, setIsModalOpen] = useState(false);
const { data, isSuccess, isFetching } = useGetComposeStatusQuery({
composeId: compose.id,
diff --git a/src/Components/sharedComponents/ImageBuilderHeader.tsx b/src/Components/sharedComponents/ImageBuilderHeader.tsx
index e301a29981..bb1e4322b1 100644
--- a/src/Components/sharedComponents/ImageBuilderHeader.tsx
+++ b/src/Components/sharedComponents/ImageBuilderHeader.tsx
@@ -8,7 +8,7 @@ import {
PageHeaderTitle,
} from '@redhat-cloud-services/frontend-components';
-import { useBackendPrefetch } from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
import { selectIsOnPremise } from '@/store/slices/env';
import { selectDistribution } from '@/store/slices/wizard';
import { openWizardModal } from '@/store/slices/wizardModal';
@@ -72,6 +72,9 @@ export const ImageBuilderHeader = ({
const dispatch = useAppDispatch();
const isOnPremise = useAppSelector(selectIsOnPremise);
+ const {
+ api: { useBackendPrefetch },
+ } = usePlatform();
const distribution = useAppSelector(selectDistribution);
const prefetchTargets = useBackendPrefetch('getArchitectures');
diff --git a/src/Hooks/MutationNotifications/useComposeBPWithNotification.tsx b/src/Hooks/MutationNotifications/useComposeBPWithNotification.tsx
index 00f49ee123..a1cfed5da3 100644
--- a/src/Hooks/MutationNotifications/useComposeBPWithNotification.tsx
+++ b/src/Hooks/MutationNotifications/useComposeBPWithNotification.tsx
@@ -1,8 +1,11 @@
-import { useComposeBlueprintMutation } from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
import { useMutationWithNotification } from './useMutationWithNotification';
export const useComposeBPWithNotification = () => {
+ const {
+ mutations: { useComposeBlueprintMutation },
+ } = usePlatform();
const { trigger: composeBlueprint, ...rest } = useMutationWithNotification(
useComposeBlueprintMutation,
{
diff --git a/src/Hooks/MutationNotifications/useCreateBPWithNotification.tsx b/src/Hooks/MutationNotifications/useCreateBPWithNotification.tsx
index de8c71b36d..9a637314af 100644
--- a/src/Hooks/MutationNotifications/useCreateBPWithNotification.tsx
+++ b/src/Hooks/MutationNotifications/useCreateBPWithNotification.tsx
@@ -1,4 +1,4 @@
-import { useCreateBlueprintMutation } from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
import {
HookOptions,
@@ -6,8 +6,10 @@ import {
} from './useMutationWithNotification';
export const useCreateBPWithNotification = (options?: HookOptions) => {
+ const {
+ mutations: { useCreateBlueprintMutation },
+ } = usePlatform();
const { trigger: createBlueprint, ...rest } = useMutationWithNotification(
- // @ts-expect-error TODO: this will need to be revisited
useCreateBlueprintMutation,
{
options,
diff --git a/src/Hooks/MutationNotifications/useDeleteBPWithNotification.tsx b/src/Hooks/MutationNotifications/useDeleteBPWithNotification.tsx
index 43dd18cd7a..48f755337a 100644
--- a/src/Hooks/MutationNotifications/useDeleteBPWithNotification.tsx
+++ b/src/Hooks/MutationNotifications/useDeleteBPWithNotification.tsx
@@ -1,4 +1,4 @@
-import { useDeleteBlueprintMutation } from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
import {
HookOptions,
@@ -6,6 +6,9 @@ import {
} from './useMutationWithNotification';
export const useDeleteBPWithNotification = (options?: HookOptions) => {
+ const {
+ mutations: { useDeleteBlueprintMutation },
+ } = usePlatform();
const { trigger: deleteBlueprint, ...rest } = useMutationWithNotification(
useDeleteBlueprintMutation,
{
diff --git a/src/Hooks/MutationNotifications/useUpdateBPWithNotification.tsx b/src/Hooks/MutationNotifications/useUpdateBPWithNotification.tsx
index b273e9bf99..fc901307f3 100644
--- a/src/Hooks/MutationNotifications/useUpdateBPWithNotification.tsx
+++ b/src/Hooks/MutationNotifications/useUpdateBPWithNotification.tsx
@@ -1,4 +1,4 @@
-import { useUpdateBlueprintMutation } from '@/store/api/backend';
+import { usePlatform } from '@/context/platform';
import {
HookOptions,
@@ -6,8 +6,10 @@ import {
} from './useMutationWithNotification';
export const useUpdateBPWithNotification = (options?: HookOptions) => {
+ const {
+ mutations: { useUpdateBlueprintMutation },
+ } = usePlatform();
const { trigger: updateBlueprint, ...rest } = useMutationWithNotification(
- // @ts-expect-error TODO: this will need to be revisited
useUpdateBlueprintMutation,
{
options,
diff --git a/src/context/platform/hosted.ts b/src/context/platform/hosted.ts
new file mode 100644
index 0000000000..3bdfaa46d9
--- /dev/null
+++ b/src/context/platform/hosted.ts
@@ -0,0 +1,77 @@
+import useChrome from '@redhat-cloud-services/frontend-components/useChrome';
+import { useFlag as useUnleashFlag } from '@unleash/proxy-client-react';
+
+import { imageBuilderApi } from '@/store/api/backend/hosted/enhancedImageBuilderApi';
+import {
+ useComposeBlueprintMutation,
+ useCreateBlueprintMutation,
+ useDeleteBlueprintMutation,
+ useGetArchitecturesQuery,
+ useGetBlueprintComposesQuery,
+ useGetBlueprintQuery,
+ useGetBlueprintsQuery,
+ useGetComposesQuery,
+ useGetComposeStatusQuery,
+ useGetDistributionsQuery,
+ useGetOscapCustomizationsQuery,
+ useGetOscapProfilesQuery,
+ useLazyGetBlueprintsQuery,
+ useLazyGetOscapCustomizationsQuery,
+ useUpdateBlueprintMutation,
+} from '@/store/api/backend/hosted/imageBuilderApi';
+import {
+ contentSourcesApi,
+ useListSnapshotsByDateMutation,
+ useSearchRpmMutation,
+} from '@/store/api/contentSources/hosted';
+
+import type { PlatformHooks } from './types';
+
+// Hoisted stable function references to avoid per-call allocations
+const isBetaTrue = () => true;
+const isBetaFalse = () => false;
+
+const useHostedGetEnvironment = () => {
+ const { isBeta, isProd, getEnvironment } = useChrome();
+ if (isBeta() || getEnvironment() === 'qa') {
+ return { isBeta: isBetaTrue, isProd };
+ }
+ return { isBeta: isBetaFalse, isProd };
+};
+
+export const hostedPlatform: PlatformHooks = {
+ queries: {
+ // PlatformHooks uses the on-prem type which accepts wider input.
+ // The hosted version is narrower, but runtime behavior is identical.
+ useGetArchitecturesQuery:
+ useGetArchitecturesQuery as unknown as PlatformHooks['queries']['useGetArchitecturesQuery'],
+ useGetDistributionsQuery:
+ useGetDistributionsQuery as unknown as PlatformHooks['queries']['useGetDistributionsQuery'],
+ useGetBlueprintQuery,
+ useGetBlueprintsQuery,
+ useLazyGetBlueprintsQuery,
+ useGetOscapProfilesQuery,
+ useGetOscapCustomizationsQuery,
+ useLazyGetOscapCustomizationsQuery,
+ useGetComposesQuery,
+ useGetBlueprintComposesQuery,
+ useGetComposeStatusQuery,
+ },
+ mutations: {
+ useCreateBlueprintMutation,
+ useUpdateBlueprintMutation,
+ useDeleteBlueprintMutation,
+ useComposeBlueprintMutation,
+ useSearchRpmMutation,
+ useListSnapshotsByDateMutation,
+ },
+ env: {
+ useFlag: useUnleashFlag,
+ useGetEnvironment: useHostedGetEnvironment,
+ },
+ api: {
+ backendApi: imageBuilderApi,
+ contentSourcesApi,
+ useBackendPrefetch: imageBuilderApi.usePrefetch,
+ },
+};
diff --git a/src/context/platform/index.ts b/src/context/platform/index.ts
new file mode 100644
index 0000000000..a8466a90a3
--- /dev/null
+++ b/src/context/platform/index.ts
@@ -0,0 +1,15 @@
+import { createContext, useContext } from 'react';
+
+import type { PlatformHooks } from './types';
+
+const PlatformContext = createContext(null);
+
+export const usePlatform = (): PlatformHooks => {
+ const ctx = useContext(PlatformContext);
+ if (ctx === null) {
+ throw new Error('usePlatform must be used within a PlatformProvider');
+ }
+ return ctx;
+};
+
+export const PlatformProvider = PlatformContext.Provider;
diff --git a/src/context/platform/onprem.ts b/src/context/platform/onprem.ts
new file mode 100644
index 0000000000..7732891e42
--- /dev/null
+++ b/src/context/platform/onprem.ts
@@ -0,0 +1,74 @@
+import {
+ useComposeBlueprintMutation,
+ useCreateBlueprintMutation,
+ useDeleteBlueprintMutation,
+ useGetArchitecturesQuery,
+ useGetBlueprintComposesQuery,
+ useGetBlueprintQuery,
+ useGetBlueprintsQuery,
+ useGetComposesQuery,
+ useGetComposeStatusQuery,
+ useGetDistributionsQuery,
+ useGetOscapCustomizationsQuery,
+ useGetOscapProfilesQuery,
+ useLazyGetBlueprintsQuery,
+ useLazyGetOscapCustomizationsQuery,
+ useUpdateBlueprintMutation,
+} from '@/store/api/backend/onprem/composerApi';
+import { composerApi } from '@/store/api/backend/onprem/enhancedComposerApi';
+import {
+ contentSourcesApi,
+ useListSnapshotsByDateMutation,
+ useSearchRpmMutation,
+} from '@/store/api/contentSources/onprem';
+
+import type { PlatformHooks } from './types';
+
+// Feature flag defaults for on-premises — Unleash is not available.
+// All flags default to false (default-deny). Add cases here when a
+// flag-gated feature needs to be enabled for on-prem users.
+// NOTE: useGetEnvironment.ts has its own onPremFlag with a switch statement.
+// That copy will be removed when Task 7 migrates useFlag to usePlatform().env.
+export const onPremFlag = (_flag: string): boolean => {
+ return false;
+};
+
+// On-prem environment is static — hoist to avoid allocations per call
+const onPremEnv = { isBeta: () => false, isProd: () => true };
+
+// On-prem hooks use a different base query and reducer path than hosted,
+// making them structurally incompatible with the hosted-anchored PlatformHooks
+// type despite identical arg/response shapes. UseQuery is invariant in D
+// so a single object-level cast is needed.
+export const onPremPlatform = {
+ queries: {
+ useGetArchitecturesQuery,
+ useGetDistributionsQuery,
+ useGetBlueprintQuery,
+ useGetBlueprintsQuery,
+ useLazyGetBlueprintsQuery,
+ useGetOscapProfilesQuery,
+ useGetOscapCustomizationsQuery,
+ useLazyGetOscapCustomizationsQuery,
+ useGetComposesQuery,
+ useGetBlueprintComposesQuery,
+ useGetComposeStatusQuery,
+ },
+ mutations: {
+ useCreateBlueprintMutation,
+ useUpdateBlueprintMutation,
+ useDeleteBlueprintMutation,
+ useComposeBlueprintMutation,
+ useSearchRpmMutation,
+ useListSnapshotsByDateMutation,
+ },
+ env: {
+ useFlag: onPremFlag,
+ useGetEnvironment: () => onPremEnv,
+ },
+ api: {
+ backendApi: composerApi,
+ contentSourcesApi,
+ useBackendPrefetch: composerApi.usePrefetch,
+ },
+} as unknown as PlatformHooks;
diff --git a/src/context/platform/tests/PlatformContext.test.tsx b/src/context/platform/tests/PlatformContext.test.tsx
new file mode 100644
index 0000000000..2a7ce2bf7b
--- /dev/null
+++ b/src/context/platform/tests/PlatformContext.test.tsx
@@ -0,0 +1,32 @@
+import React from 'react';
+
+import { renderHook } from '@testing-library/react';
+import { describe, expect, it, vi } from 'vitest';
+
+import { PlatformProvider, usePlatform } from '@/context/platform';
+
+import { mockPlatform } from './mocks';
+
+describe('PlatformContext', () => {
+ it('throws when usePlatform is called outside PlatformProvider', () => {
+ // React logs to console.error before the error propagates;
+ // suppress it so the global setup spy doesn't treat it as a failure.
+ const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
+
+ expect(() => {
+ renderHook(() => usePlatform());
+ }).toThrow('usePlatform must be used within a PlatformProvider');
+
+ spy.mockRestore();
+ });
+
+ it('provides the platform object when wrapped in PlatformProvider', () => {
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
+ {children}
+ );
+
+ const { result } = renderHook(() => usePlatform(), { wrapper });
+ expect(result.current).toBe(mockPlatform);
+ expect(result.current.env.useFlag('test')).toBe(false);
+ });
+});
diff --git a/src/context/platform/tests/mocks/fixtures.ts b/src/context/platform/tests/mocks/fixtures.ts
new file mode 100644
index 0000000000..667656a0d0
--- /dev/null
+++ b/src/context/platform/tests/mocks/fixtures.ts
@@ -0,0 +1,11 @@
+import type { PlatformHooks } from '@/context/platform/types';
+
+export const mockPlatform = {
+ queries: {},
+ mutations: {},
+ env: {
+ useFlag: () => false,
+ useGetEnvironment: () => ({ isBeta: () => false, isProd: () => true }),
+ },
+ api: {},
+} as unknown as PlatformHooks;
diff --git a/src/context/platform/tests/mocks/index.ts b/src/context/platform/tests/mocks/index.ts
new file mode 100644
index 0000000000..995b5bc6e1
--- /dev/null
+++ b/src/context/platform/tests/mocks/index.ts
@@ -0,0 +1 @@
+export * from './fixtures';
diff --git a/src/context/platform/types.ts b/src/context/platform/types.ts
new file mode 100644
index 0000000000..52e7c5f7be
--- /dev/null
+++ b/src/context/platform/types.ts
@@ -0,0 +1,66 @@
+import type { imageBuilderApi } from '@/store/api/backend/hosted/enhancedImageBuilderApi';
+import type {
+ useComposeBlueprintMutation as useHostedComposeBlueprintMutation,
+ useCreateBlueprintMutation as useHostedCreateBlueprintMutation,
+ useDeleteBlueprintMutation as useHostedDeleteBlueprintMutation,
+ useGetBlueprintComposesQuery as useHostedGetBlueprintComposesQuery,
+ useGetBlueprintQuery as useHostedGetBlueprintQuery,
+ useGetBlueprintsQuery as useHostedGetBlueprintsQuery,
+ useGetComposesQuery as useHostedGetComposesQuery,
+ useGetComposeStatusQuery as useHostedGetComposeStatusQuery,
+ useGetOscapCustomizationsQuery as useHostedGetOscapCustomizationsQuery,
+ useGetOscapProfilesQuery as useHostedGetOscapProfilesQuery,
+ useLazyGetBlueprintsQuery as useHostedLazyGetBlueprintsQuery,
+ useLazyGetOscapCustomizationsQuery as useHostedLazyGetOscapCustomizationsQuery,
+ useUpdateBlueprintMutation as useHostedUpdateBlueprintMutation,
+} from '@/store/api/backend/hosted/imageBuilderApi';
+import type {
+ useGetArchitecturesQuery as useComposerGetArchitecturesQuery,
+ useGetDistributionsQuery as useComposerGetDistributionsQuery,
+} from '@/store/api/backend/onprem/composerApi';
+import type {
+ contentSourcesApi,
+ useListSnapshotsByDateMutation as useHostedListSnapshotsByDateMutation,
+ useSearchRpmMutation as useHostedSearchRpmMutation,
+} from '@/store/api/contentSources/hosted/contentSourcesApi';
+
+// The useGetArchitecturesQuery and useGetDistributionsQuery types are
+// widened to the composer (on-prem) versions because they accept wider
+// input types. The response shapes are identical across platforms.
+type UseGetArchitecturesQuery = typeof useComposerGetArchitecturesQuery;
+type UseGetDistributionsQuery = typeof useComposerGetDistributionsQuery;
+
+type PlatformHooks = {
+ queries: {
+ useGetArchitecturesQuery: UseGetArchitecturesQuery;
+ useGetDistributionsQuery: UseGetDistributionsQuery;
+ useGetBlueprintQuery: typeof useHostedGetBlueprintQuery;
+ useGetBlueprintsQuery: typeof useHostedGetBlueprintsQuery;
+ useLazyGetBlueprintsQuery: typeof useHostedLazyGetBlueprintsQuery;
+ useGetOscapProfilesQuery: typeof useHostedGetOscapProfilesQuery;
+ useGetOscapCustomizationsQuery: typeof useHostedGetOscapCustomizationsQuery;
+ useLazyGetOscapCustomizationsQuery: typeof useHostedLazyGetOscapCustomizationsQuery;
+ useGetComposesQuery: typeof useHostedGetComposesQuery;
+ useGetBlueprintComposesQuery: typeof useHostedGetBlueprintComposesQuery;
+ useGetComposeStatusQuery: typeof useHostedGetComposeStatusQuery;
+ };
+ mutations: {
+ useCreateBlueprintMutation: typeof useHostedCreateBlueprintMutation;
+ useUpdateBlueprintMutation: typeof useHostedUpdateBlueprintMutation;
+ useDeleteBlueprintMutation: typeof useHostedDeleteBlueprintMutation;
+ useComposeBlueprintMutation: typeof useHostedComposeBlueprintMutation;
+ useSearchRpmMutation: typeof useHostedSearchRpmMutation;
+ useListSnapshotsByDateMutation: typeof useHostedListSnapshotsByDateMutation;
+ };
+ env: {
+ useFlag: (flag: string) => boolean;
+ useGetEnvironment: () => { isBeta: () => boolean; isProd: () => boolean };
+ };
+ api: {
+ backendApi: typeof imageBuilderApi;
+ contentSourcesApi: typeof contentSourcesApi;
+ useBackendPrefetch: typeof imageBuilderApi.usePrefetch;
+ };
+};
+
+export type { PlatformHooks };
diff --git a/src/test/renderUtils.jsx b/src/test/renderUtils.jsx
index 964b5f206e..b3e9d50e2e 100644
--- a/src/test/renderUtils.jsx
+++ b/src/test/renderUtils.jsx
@@ -6,6 +6,9 @@ import { Provider } from 'react-redux';
import { createMemoryRouter, RouterProvider } from 'react-router-dom';
import LandingPage from '../Components/LandingPage/LandingPage';
+import { PlatformProvider } from '../context/platform';
+import { hostedPlatform } from '../context/platform/hosted';
+import { onPremPlatform } from '../context/platform/onprem';
import {
serviceMiddleware as middleware,
onPremMiddleware as onPremMiddleware,
@@ -52,14 +55,18 @@ export const renderCustomRoutesWithReduxRouter = async (
initialEntries: [resolveRelPath(route)],
});
+ const platform = process.env.IS_ON_PREMISE ? onPremPlatform : hostedPlatform;
+
render(
-
+
+
+
,
);
diff --git a/src/test/testUtils/renderUtils.tsx b/src/test/testUtils/renderUtils.tsx
index 4882b933f9..633686f496 100644
--- a/src/test/testUtils/renderUtils.tsx
+++ b/src/test/testUtils/renderUtils.tsx
@@ -4,6 +4,9 @@ import { configureStore, EnhancedStore } from '@reduxjs/toolkit';
import { render, RenderResult } from '@testing-library/react';
import { Provider } from 'react-redux';
+import { PlatformProvider } from '@/context/platform';
+import { hostedPlatform } from '@/context/platform/hosted';
+import { onPremPlatform } from '@/context/platform/onprem';
import {
onPremMiddleware,
onPremReducer,
@@ -29,7 +32,13 @@ export const renderWithRedux = (
options: RenderWithReduxOptions = {},
): RenderWithReduxResult => {
const store = createTestStore(wizardStateOverrides, options);
- const view = render({component});
+ const isOnPremise = options.preloadedState?.env?.isOnPremise;
+ const platform = isOnPremise ? onPremPlatform : hostedPlatform;
+ const view = render(
+
+ {component}
+ ,
+ );
return { ...view, store };
};