Skip to content

Commit

Permalink
feat: Support historic model versions
Browse files Browse the repository at this point in the history
  • Loading branch information
amattu2 committed Jan 23, 2025
1 parent fb343c1 commit afe3ee7
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 54 deletions.
2 changes: 1 addition & 1 deletion src/config/HeaderConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const HeaderSubLinks: Record<string, NavBarSubItem[]> = {
"Model Navigator": DataCommons.map((dc) => ({
id: `model-navigator-${dc.name}`,
name: `${dc.name}${dc.name.indexOf("Model") === -1 ? " Model" : ""}`,
link: `/model-navigator/${dc.name}`,
link: `/model-navigator/${dc.name}/latest`,
className: "navMobileSubItem",
})),

Expand Down
11 changes: 7 additions & 4 deletions src/content/ModelNavigator/Controller.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import { useParams } from "react-router-dom";
import { Navigate, useParams } from "react-router-dom";
import NavigatorView from "./NavigatorView";
import ErrorBoundary from "../../components/ErrorBoundary";
import { DataCommonProvider } from "../../components/Contexts/DataCommonContext";
Expand All @@ -8,12 +8,15 @@ import usePageTitle from "../../hooks/usePageTitle";
const ModelNavigatorController: React.FC = () => {
usePageTitle("Model Navigator");

const { dataCommon } = useParams<{ dataCommon: DataCommon["name"] }>();
const { model, version } = useParams<{ model: string; version?: string }>();
if (!version) {
return <Navigate to={`/model-navigator/${model}/latest`} />;
}

return (
<DataCommonProvider key={dataCommon} DataCommon={dataCommon}>
<DataCommonProvider key={model} DataCommon={model}>
<ErrorBoundary errorMessage="Unable to load the Model Navigator for the requested model">
<NavigatorView />
<NavigatorView version={version} />
</ErrorBoundary>
</DataCommonProvider>
);
Expand Down
17 changes: 12 additions & 5 deletions src/content/ModelNavigator/NavigatorView.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC } from "react";
import { FC } from "react";
import { Box } from "@mui/material";
// eslint-disable-next-line import/no-extraneous-dependencies -- Required to use legacy version from DMN
import { Provider } from "react-redux";
Expand All @@ -7,6 +7,13 @@ import SuspenseLoader from "../../components/SuspenseLoader";
import { Status, useDataCommonContext } from "../../components/Contexts/DataCommonContext";
import useBuildReduxStore from "../../hooks/useBuildReduxStore";

type ModelNavigatorProps = {
/**
* The version of the model to display
*/
version: string;
};

/**
* Encapsulates the Data Model Navigator component
*
Expand All @@ -15,9 +22,9 @@ import useBuildReduxStore from "../../hooks/useBuildReduxStore";
* - Building the Redux store for the Data Model Navigator
* - Rendering the Data Model Navigator
*
* @returns {JSX.Element}
* @returns The Model Navigator view
*/
const ModelNavigator: FC = () => {
const ModelNavigator: FC<ModelNavigatorProps> = ({ version = "latest" }) => {
const { status, DataCommon } = useDataCommonContext();
const [{ status: buildStatus, store }, , populate] = useBuildReduxStore();

Expand All @@ -26,12 +33,12 @@ const ModelNavigator: FC = () => {
}

if (status === Status.LOADED && buildStatus === "waiting") {
populate(DataCommon);
populate(DataCommon, version);
return <SuspenseLoader />;
}

if (!DataCommon || status === Status.ERROR || buildStatus === "error") {
throw new Error("Unable to build Model Navigator for the selected Data Common");
throw new Error("Oops! Unable to show the requested data model or model version.");
}

return (
Expand Down
7 changes: 4 additions & 3 deletions src/hooks/useBuildReduxStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export type ReduxStoreStatus = "waiting" | "loading" | "error" | "success";
export type ReduxStoreResult = [
{ status: ReduxStoreStatus; store: Store },
() => void,
(assets: DataCommon) => void,
(assets: DataCommon, modelVersion: string) => void,
];

const makeStore = (): Store => {
Expand Down Expand Up @@ -70,8 +70,9 @@ const useBuildReduxStore = (): ReduxStoreResult => {
* Injects the Data Model into the store
*
* @param datacommon The Data Model to inject assets from
* @param modelVersion The version of the Data Model to inject
*/
const populateStore = async (datacommon: DataCommon) => {
const populateStore = async (datacommon: DataCommon, modelVersion: string) => {
if (
!datacommon?.name ||
!datacommon?.assets ||
Expand All @@ -84,7 +85,7 @@ const useBuildReduxStore = (): ReduxStoreResult => {

setStatus("loading");

const assets = buildAssetUrls(datacommon);
const assets = buildAssetUrls(datacommon, modelVersion);
const response = await getModelExploreData(...assets.model_files)?.catch((e) => {
Logger.error(e);
return null;
Expand Down
6 changes: 3 additions & 3 deletions src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const Login = LazyLoader(lazy(() => import("./content/Login/Controller")));
const Questionnaire = LazyLoader(lazy(() => import("./content/questionnaire/Controller")));
const DataSubmissions = LazyLoader(lazy(() => import("./content/dataSubmissions/Controller")));
const Users = LazyLoader(lazy(() => import("./content/users/Controller")));
const DMN = LazyLoader(lazy(() => import("./content/modelNavigator/Controller")));
const ModelNavigator = LazyLoader(lazy(() => import("./content/ModelNavigator/Controller")));
const ReleaseNotes = LazyLoader(lazy(() => import("./content/ReleaseNotes/Controller")));
const Organizations = LazyLoader(lazy(() => import("./content/organizations/Controller")));
const Studies = LazyLoader(lazy(() => import("./content/studies/Controller")));
Expand All @@ -37,8 +37,8 @@ const routes: RouteObject[] = [
element: <Login />,
},
{
path: "/model-navigator/:dataCommon",
element: <DMN />,
path: "/model-navigator/:model/:version?",
element: <ModelNavigator />,
},
{
path: "/release-notes",
Expand Down
2 changes: 1 addition & 1 deletion src/types/DataCommon.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,5 @@ type ManifestAssets = {
*
* @example ["1.0", "1.1", "1.3"]
*/
versions: string[] | number[];
versions: string[];
};
56 changes: 44 additions & 12 deletions src/utils/dataModelUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ describe("buildAssetUrls cases", () => {
} as ManifestAssets,
} as DataCommon;

const result = utils.buildAssetUrls(dc);
const result = utils.buildAssetUrls(dc, "latest");

expect(result).toEqual({
model_files: [
Expand All @@ -142,7 +142,7 @@ describe("buildAssetUrls cases", () => {
} as ManifestAssets,
} as DataCommon;

const result = utils.buildAssetUrls(dc);
const result = utils.buildAssetUrls(dc, "latest");

expect(result.model_files).toEqual([
`${MODEL_FILE_REPO}prod/cache/test-name/1.0/model-file`,
Expand All @@ -163,7 +163,7 @@ describe("buildAssetUrls cases", () => {
} as ManifestAssets,
} as DataCommon;

const result = utils.buildAssetUrls(dc);
const result = utils.buildAssetUrls(dc, "latest");

expect(result.model_files).toEqual([]);
});
Expand All @@ -179,7 +179,7 @@ describe("buildAssetUrls cases", () => {
} as ManifestAssets,
} as DataCommon;

const result = utils.buildAssetUrls(dc);
const result = utils.buildAssetUrls(dc, "latest");

expect(result.readme).toEqual(null);
});
Expand All @@ -196,7 +196,7 @@ describe("buildAssetUrls cases", () => {
} as ManifestAssets,
} as DataCommon;

const result = utils.buildAssetUrls(dc);
const result = utils.buildAssetUrls(dc, "latest");

expect(result.navigator_icon).toEqual("genericLogo.png");
});
Expand All @@ -213,7 +213,7 @@ describe("buildAssetUrls cases", () => {
} as ManifestAssets,
} as DataCommon;

const result = utils.buildAssetUrls(dc);
const result = utils.buildAssetUrls(dc, "latest");

expect(result.navigator_icon).toEqual("genericLogo.png");
});
Expand All @@ -230,17 +230,17 @@ describe("buildAssetUrls cases", () => {
} as ManifestAssets,
} as DataCommon;

const result = utils.buildAssetUrls(dc);
const result = utils.buildAssetUrls(dc, "latest");

expect(result.navigator_icon).toEqual(
`${MODEL_FILE_REPO}prod/cache/test-name/1.0/custom-logo.png`
);
});

it("should not throw an exception if dealing with invalid data", () => {
expect(() => utils.buildAssetUrls(null)).not.toThrow();
expect(() => utils.buildAssetUrls({} as DataCommon)).not.toThrow();
expect(() => utils.buildAssetUrls(undefined)).not.toThrow();
expect(() => utils.buildAssetUrls(null, "latest")).not.toThrow();
expect(() => utils.buildAssetUrls({} as DataCommon, "latest")).not.toThrow();
expect(() => utils.buildAssetUrls(undefined, "latest")).not.toThrow();
});

it("should not throw an exception if `model_files` is not defined", () => {
Expand All @@ -253,8 +253,40 @@ describe("buildAssetUrls cases", () => {
} as ManifestAssets,
} as DataCommon;

expect(() => utils.buildAssetUrls(dc)).not.toThrow();
expect(utils.buildAssetUrls(dc)).toEqual(expect.objectContaining({ model_files: [] }));
expect(() => utils.buildAssetUrls(dc, "latest")).not.toThrow();
expect(utils.buildAssetUrls(dc, "latest")).toEqual(
expect.objectContaining({ model_files: [] })
);
});

it("should use the provided modelVersion if it is not 'latest'", () => {
const dc: DataCommon = {
name: "test-name",
assets: {
"current-version": "1.0",
"model-files": ["model-file", "prop-file"],
"readme-file": "readme-file",
"loading-file": "loading-file-zip-name",
} as ManifestAssets,
} as DataCommon;

const result = utils.buildAssetUrls(dc, "2.1");
expect(result.model_files).toEqual([
`${MODEL_FILE_REPO}prod/cache/test-name/2.1/model-file`,
`${MODEL_FILE_REPO}prod/cache/test-name/2.1/prop-file`,
]);

const result2 = utils.buildAssetUrls(dc, "1.0");
expect(result2.model_files).toEqual([
`${MODEL_FILE_REPO}prod/cache/test-name/1.0/model-file`,
`${MODEL_FILE_REPO}prod/cache/test-name/1.0/prop-file`,
]);

const result3 = utils.buildAssetUrls(dc, "latest");
expect(result3.model_files).toEqual([
`${MODEL_FILE_REPO}prod/cache/test-name/1.0/model-file`,
`${MODEL_FILE_REPO}prod/cache/test-name/1.0/prop-file`,
]);
});
});

Expand Down
48 changes: 23 additions & 25 deletions src/utils/dataModelUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,33 +31,31 @@ export const fetchManifest = async (): Promise<DataModelManifest> => {
/**
* Builds the asset URLs for the Data Model Navigator to import from
*
* @param dc The data common to build the asset URLs for
* @param model The Data Model (DataCommon) to build asset URLs for
* @param modelVersion The version of the Data Model to build asset URLs for
* @returns ModelAssetUrls
*/
export const buildAssetUrls = (dc: DataCommon): ModelAssetUrls => ({
model_files:
dc?.assets?.["model-files"]?.map(
(file) =>
`${MODEL_FILE_REPO}${env.REACT_APP_DEV_TIER || "prod"}/cache/${dc?.name}/${dc?.assets?.[
"current-version"
]}/${file}`
) || [],
readme: dc?.assets?.["readme-file"]
? `${MODEL_FILE_REPO}${env.REACT_APP_DEV_TIER || "prod"}/cache/${dc?.name}/${dc?.assets?.[
"current-version"
]}/${dc?.assets?.["readme-file"]}`
: null,
loading_file: dc?.assets?.["loading-file"]
? `${MODEL_FILE_REPO}${env.REACT_APP_DEV_TIER || "prod"}/cache/${dc?.name}/${dc?.assets?.[
"current-version"
]}/${dc?.assets?.["loading-file"]}`
: null,
navigator_icon: dc?.assets?.["model-navigator-logo"]
? `${MODEL_FILE_REPO}${env.REACT_APP_DEV_TIER || "prod"}/cache/${dc?.name}/${dc?.assets?.[
"current-version"
]}/${dc?.assets?.["model-navigator-logo"]}`
: GenericModelLogo,
});
export const buildAssetUrls = (model: DataCommon, modelVersion): ModelAssetUrls => {
const { name, assets } = model || {};
const version = modelVersion === "latest" ? assets?.["current-version"] : modelVersion;
const tier = env.REACT_APP_DEV_TIER || "prod";

return {
model_files:
assets?.["model-files"]?.map(
(file) => `${MODEL_FILE_REPO}${tier}/cache/${name}/${version}/${file}`
) || [],
readme: assets?.["readme-file"]
? `${MODEL_FILE_REPO}${tier}/cache/${name}/${version}/${assets?.["readme-file"]}`
: null,
loading_file: assets?.["loading-file"]
? `${MODEL_FILE_REPO}${tier}/cache/${name}/${version}/${assets?.["loading-file"]}`
: null,
navigator_icon: assets?.["model-navigator-logo"]
? `${MODEL_FILE_REPO}${tier}/cache/${name}/${version}/${assets?.["model-navigator-logo"]}`
: GenericModelLogo,
};
};

/**
* Helper function to SAFELY build a set of base filter containers for the Data Model Navigator
Expand Down

0 comments on commit afe3ee7

Please sign in to comment.