From 041a3c27d4ecfe8e8dd0b5ab40c0a5dc2f4c8697 Mon Sep 17 00:00:00 2001 From: Stijn Coolen Date: Tue, 16 Nov 2021 16:49:07 +0100 Subject: [PATCH] Update to version 6 of react-router (Issue #1869, PR #1879) # Description This PR has 2 parts. The first part is using the new api provided by react-router v6. All of the routing logic changes are related to this. It's mostly replacing references to history with location and navigate. Typing for routing has also changed a bit (RouteMatch, useRouteParams). The second part is changing the way an environment change is handled. Most of the changes are related to removing the environment from the constructor. And making sure the environmentHandler has an environment to provide to the QueryManagers. closes #1869 # Self Check: Strike through any lines that are not applicable (`~~line~~`) then check the box - [x] Attached issue to pull request - [x] Changelog entry - [x] Code is clear and sufficiently documented - [x] Sufficient test cases (reproduces the bug/tests the requested feature) - [x] Correct, in line with design - [ ] ~~End user documentation is included or an issue is created for end-user documentation (add ref to issue here: )~~ # Reviewer Checklist: - [ ] Sufficient test cases (reproduces the bug/tests the requested feature) - [ ] Code is clear and sufficiently documented - [ ] Correct, in line with design --- .../unreleased/1869-react-router-update.yml | 4 + cypress/integration/service_catalog.spec.js | 6 ++ package.json | 8 +- src/Core/Contracts/EnvironmentHandler.ts | 12 ++- src/Core/Contracts/PageManager.ts | 4 +- src/Core/Contracts/RouteManager.ts | 7 +- src/Core/Domain/Route.ts | 41 +++---- src/Data/API/FileFetcherImpl.ts | 10 +- src/Data/Common/UrlState/helpers.mocked.ts | 13 --- src/Data/Common/UrlState/helpers.ts | 14 +-- src/Data/Common/UrlState/useUrlState.ts | 6 +- .../UrlState/useUrlStateWithExpansion.spec.ts | 5 +- .../UrlState/useUrlStateWithExpansion.ts | 10 +- .../UrlState/useUrlStateWithFilter.spec.ts | 5 +- .../Common/UrlState/useUrlStateWithFilter.ts | 6 +- .../UrlState/useUrlStateWithPageSize.spec.ts | 5 +- .../UrlState/useUrlStateWithPageSize.ts | 6 +- .../UrlState/useUrlStateWithSort.spec.ts | 5 +- .../Common/UrlState/useUrlStateWithSort.ts | 6 +- .../UrlState/useUrlStateWithString.spec.ts | 5 +- .../Common/UrlState/useUrlStateWithString.ts | 6 +- .../Callbacks/GetCallbacks/QueryManager.ts | 20 +--- .../Managers/CompileDetails/QueryManager.ts | 10 +- .../Managers/CompileReports/QueryManager.ts | 16 ++- src/Data/Managers/Diagnostics/QueryManager.ts | 10 +- .../EnvironmentDetails/QueryManager.ts | 16 ++- .../GetEnvironmentSettings/QueryManager.ts | 10 +- .../GetEnvironmentSettings/StateHelper.ts | 4 +- src/Data/Managers/Events/QueryManager.ts | 12 +-- .../Managers/GetEnvironments/QueryManager.ts | 15 +-- src/Data/Managers/GetInstance/QueryManager.ts | 10 +- .../Managers/GetInstanceLogs/QueryManager.ts | 10 +- src/Data/Managers/GetProjects/QueryManager.ts | 22 +--- .../Managers/GetServerStatus/QueryManager.ts | 15 +-- .../Helpers/PrimaryContinuousQueryManager.ts | 33 ++---- .../PrimaryContinuousQueryManagerWithEnv.ts | 88 +++++++++++++++ .../Helpers/PrimaryOneTimeQueryManager.ts | 23 +--- .../PrimaryOneTimeQueryManagerWithEnv.ts | 69 ++++++++++++ src/Data/Managers/Helpers/index.ts | 2 + src/Data/Managers/Helpers/types.ts | 41 +++++++ .../Managers/InstanceConfig/QueryManager.ts | 19 ++-- .../InstanceResources/QueryManager.ts | 10 +- .../Managers/ResourceDetails/QueryManager.ts | 10 +- .../Managers/ResourceHistory/QueryManager.ts | 14 ++- .../Managers/ResourceLogs/QueryManager.ts | 14 ++- src/Data/Managers/Resources/QueryManager.ts | 14 ++- src/Data/Managers/Service/QueryManager.ts | 14 ++- .../Managers/ServiceConfig/QueryManager.ts | 19 ++-- .../Managers/ServiceInstances/QueryManager.ts | 10 +- src/Data/Managers/Services/QueryManager.ts | 14 ++- src/Data/Resolvers/QueryManagerResolver.ts | 57 ++++------ src/Injector.tsx | 11 +- src/Test/Inject/dependencies.ts | 3 + src/Test/Mock/InstantApiHelper.ts | 4 +- src/Test/Mock/MockEnvironmentHandler.ts | 13 +++ src/Test/Mock/index.ts | 1 + .../EnvironmentProvider.tsx | 27 ----- .../Components/EnvironmentProvider/index.ts | 1 - src/UI/Components/index.ts | 1 - src/UI/Dependency/DependencyResolver.tsx | 8 +- .../Dummy/DummyEnvironmentHandler.ts | 3 + src/UI/Dependency/Dummy/DummyRouteManager.ts | 4 +- src/UI/Dependency/EnvironmentHandler.test.ts | 18 +++- src/UI/Dependency/EnvironmentHandler.ts | 33 +++--- .../CompileDetails/CompileDetails.test.tsx | 19 ++-- src/UI/Pages/CompileDetails/Page.tsx | 5 +- .../CompileReports/CompileReports.test.tsx | 3 +- .../CreateEnvironmentForm.test.tsx | 7 +- .../Pages/CreateInstance/CreateInstance.tsx | 8 +- src/UI/Pages/CreateInstance/Page.tsx | 6 +- src/UI/Pages/Diagnose/Diagnose.stories.tsx | 5 +- src/UI/Pages/Diagnose/Diagnose.test.tsx | 22 ++-- src/UI/Pages/Diagnose/Page.tsx | 6 +- src/UI/Pages/EditInstance/EditForm.tsx | 8 +- .../EditInstance/EditInstancePage.test.tsx | 66 +++++++----- src/UI/Pages/EditInstance/Page.tsx | 6 +- src/UI/Pages/Events/Events.stories.tsx | 3 +- src/UI/Pages/Events/Events.test.tsx | 12 +-- src/UI/Pages/Events/Page.tsx | 8 +- .../Pages/Events/Spec/EventsPageComposer.tsx | 3 +- src/UI/Pages/NotFound/NotFound.tsx | 6 +- src/UI/Pages/PageRouter.tsx | 33 ------ src/UI/Pages/ResourceDetails/Page.tsx | 5 +- .../ResourceDetailsView.test.tsx | 3 +- .../HistoryTab/ResourceHistoryView.test.tsx | 4 +- .../ResourceDetails/Tabs/LogTab/View.spec.tsx | 8 +- src/UI/Pages/Resources/Page.test.tsx | 3 +- src/UI/Pages/ServiceCatalog/Page.test.tsx | 74 +++++++++++-- .../ServiceCatalog/Spec/CallbacksTab.spec.tsx | 6 +- .../Spec/CallbacksView.spec.tsx | 8 +- .../ServiceCatalog/Spec/ConfigTab.spec.tsx | 6 +- src/UI/Pages/ServiceInstanceHistory/Page.tsx | 8 +- .../ServiceInstanceHistory.stories.tsx | 3 +- .../ServiceInstanceHistory.test.tsx | 23 ++-- .../InventoryTable.stories.tsx | 3 +- .../ServiceInventory/InventoryTable.test.tsx | 6 +- src/UI/Pages/ServiceInventory/Page.tsx | 6 +- .../ServiceInventory.stories.tsx | 9 +- .../ServiceInventory.test.tsx | 6 +- .../ServiceInventory/ServiceInventory.tsx | 11 +- .../Spec/ServiceInventoryPrepper.tsx | 15 +-- .../TableProvider.stories.tsx | 3 +- .../ServiceInventory/Tabs/ConfigTab.test.tsx | 30 +++--- .../Tabs/ResourcesTab.stories.tsx | 3 +- .../Tabs/ResourcesTab.test.tsx | 3 +- .../Settings/Tabs/Configuration/Tab.test.tsx | 20 ++-- src/UI/Pages/index.ts | 1 - .../AppLayout/EnvironmentControls.test.tsx | 7 +- .../EnvSelector/EnvSelectorWithData.tsx | 4 +- .../EnvSelector/EnvironmentSelector.test.tsx | 2 +- .../Toolbar/EnvSelector/Provider.tsx | 4 +- src/UI/Root/EnvSpecificContentLayout.tsx | 56 +++++----- src/UI/Root/Navigation.tsx | 8 +- .../PrimaryPageManager.tsx} | 36 ++++--- src/UI/Root/app.test.tsx | 5 +- src/UI/Root/app.tsx | 67 ++++++++---- src/UI/Routing/Crumb.ts | 6 +- src/UI/Routing/PrimaryBaseUrlManager.ts | 2 +- src/UI/Routing/PrimaryRouteManager.test.ts | 18 ++-- src/UI/Routing/PrimaryRouteManager.ts | 32 +++--- src/UI/Routing/SearchSanitizer/Provider.tsx | 13 ++- src/UI/Routing/Utils.ts | 13 ++- yarn.lock | 100 +++--------------- 123 files changed, 947 insertions(+), 851 deletions(-) create mode 100644 changelogs/unreleased/1869-react-router-update.yml delete mode 100644 src/Data/Common/UrlState/helpers.mocked.ts create mode 100644 src/Data/Managers/Helpers/PrimaryContinuousQueryManagerWithEnv.ts create mode 100644 src/Data/Managers/Helpers/PrimaryOneTimeQueryManagerWithEnv.ts create mode 100644 src/Data/Managers/Helpers/types.ts create mode 100644 src/Test/Mock/MockEnvironmentHandler.ts delete mode 100644 src/UI/Components/EnvironmentProvider/EnvironmentProvider.tsx delete mode 100644 src/UI/Components/EnvironmentProvider/index.ts delete mode 100644 src/UI/Pages/PageRouter.tsx rename src/UI/{Pages/PrimaryPageManager.ts => Root/PrimaryPageManager.tsx} (50%) diff --git a/changelogs/unreleased/1869-react-router-update.yml b/changelogs/unreleased/1869-react-router-update.yml new file mode 100644 index 000000000..366685e8a --- /dev/null +++ b/changelogs/unreleased/1869-react-router-update.yml @@ -0,0 +1,4 @@ +description: Update to version 6 of react-router +issue-nr: 1869 +change-type: patch +destination-branches: [master, iso4] diff --git a/cypress/integration/service_catalog.spec.js b/cypress/integration/service_catalog.spec.js index 9c0b1f20b..1b96017f5 100644 --- a/cypress/integration/service_catalog.spec.js +++ b/cypress/integration/service_catalog.spec.js @@ -49,6 +49,12 @@ describe("Service catalog", function () { cy.contains("Lifecycle States").click(); + /** + * @NOTE This assertion indirectly verifies that no full page rerender is triggered. + * We do not have the prop unMountOnExit set to true for the Tabs component. + * This means, the TabContent of previous viewed tabs is not destroyed. + * So here we correctly assume there are 2 TabContent components in the DOM. + */ cy.get("#e2e_service-expand") .find(".pf-c-tab-content") .should("have.length", 2); diff --git a/package.json b/package.json index 8608314de..deceb9e66 100644 --- a/package.json +++ b/package.json @@ -62,13 +62,13 @@ "@types/styled-components": "^5.1.15", "@types/webpack": "^5.28.0", "@types/xml-formatter": "^2.1.1", - "clean-css": "^5.2.2", - "copy-webpack-plugin": "^9.1.0", - "css-minimizer-webpack-plugin": "^3.1.3", "@typescript-eslint/eslint-plugin": "^5.3.1", "@typescript-eslint/parser": "^5.4.0", "babel-loader": "^8.2.3", + "clean-css": "^5.2.2", + "copy-webpack-plugin": "^9.1.0", "css-loader": "^6.5.1", + "css-minimizer-webpack-plugin": "^3.1.3", "cypress": "^9.0.0", "cypress-multi-reporters": "^1.5.0", "eslint": "^7.32.0", @@ -139,7 +139,7 @@ "process": "^0.11.10", "qs": "^6.10.1", "react-keycloak": "^6.1.1", - "react-router-dom": "^5.3.0", + "react-router-dom": "^6.0.1", "react-syntax-highlighter": "^15.4.4", "styled-components": "^5.3.3", "xml-formatter": "^2.5.1" diff --git a/src/Core/Contracts/EnvironmentHandler.ts b/src/Core/Contracts/EnvironmentHandler.ts index b19f149fd..57a29a1f6 100644 --- a/src/Core/Contracts/EnvironmentHandler.ts +++ b/src/Core/Contracts/EnvironmentHandler.ts @@ -1,6 +1,16 @@ +import { Location } from "history"; import { FlatEnvironment } from "@/Core/Domain"; export interface EnvironmentHandler { - set(environmentId: string): void; + set(location: Location, environmentId: string): void; useSelected(): FlatEnvironment | undefined; + /** + * If this function is called, it means the environment is required. + * If it is required, the environment should be defined. + * If it is not defined, something is wrong with the code. + * This should never happen during runtime. + * @returns the environment id + * @throws error when there is no environment defined + */ + useId(): string; } diff --git a/src/Core/Contracts/PageManager.ts b/src/Core/Contracts/PageManager.ts index 683203a7e..8e9f6f1d3 100644 --- a/src/Core/Contracts/PageManager.ts +++ b/src/Core/Contracts/PageManager.ts @@ -1,8 +1,8 @@ -import { ComponentType } from "react"; +import { ReactElement } from "react"; import { Route } from "@/Core/Domain"; export interface Page extends Route { - component: ComponentType; + element: ReactElement | null; } export interface PageManager { diff --git a/src/Core/Contracts/RouteManager.ts b/src/Core/Contracts/RouteManager.ts index 4bc646dd5..09f0a6655 100644 --- a/src/Core/Contracts/RouteManager.ts +++ b/src/Core/Contracts/RouteManager.ts @@ -1,13 +1,12 @@ -import { RouteKind, Route, RouteParams } from "@/Core/Domain"; +import { RouteKind, Route, RouteParams, RouteMatch } from "@/Core/Domain"; export type RouteDictionary = Record; -export type MatchedParams = Record; export interface RouteManager { getRoutes(): Route[]; getRouteDictionary(): RouteDictionary; getRoute(routeKind: RouteKind): Route; - getUrl(kind: RouteKind, params: RouteParams): string; + getUrl(kind: RouteKind, params: RouteParams): string; /** * Gets the closest url in the lineage without params. * When switching environments, we can't go to pages with params, @@ -21,5 +20,5 @@ export interface RouteManager { * @param routes the buildup of parent routes as this is a recursive function */ getLineageFromRoute(route: Route, routes?: Route[]): Route[]; - getRouteWithParamsFromUrl(url: string): [Route, MatchedParams] | undefined; + getRouteMatchFromUrl(url: string): RouteMatch | undefined; } diff --git a/src/Core/Domain/Route.ts b/src/Core/Domain/Route.ts index 0c778421f..2fffd2ad8 100644 --- a/src/Core/Domain/Route.ts +++ b/src/Core/Domain/Route.ts @@ -28,23 +28,28 @@ export interface Route { clearEnv?: boolean; } -interface RouteParamsManifest { - Catalog: undefined; - Inventory: { service: string }; - CreateInstance: { service: string }; - EditInstance: { service: string; instance: string }; - History: { service: string; instance: string }; - Events: { service: string; instance: string }; - Diagnose: { service: string; instance: string }; - Resources: undefined; - ResourceHistory: { resourceId: string }; - ResourceLogs: { resourceId: string }; - CompileReports: undefined; - CompileDetails: { id: string }; - Home: undefined; - ResourceDetails: { resourceId: string }; - Settings: undefined; - CreateEnvironment: undefined; +interface RouteParamKeysManifest { + Inventory: "service"; + CreateInstance: "service"; + EditInstance: "service" | "instance"; + History: "service" | "instance"; + Events: "service" | "instance"; + Diagnose: "service" | "instance"; + ResourceHistory: "resourceId"; + ResourceLogs: "resourceId"; + CompileDetails: "id"; + ResourceDetails: "resourceId"; } -export type RouteParams = RouteParamsManifest[R]; +export type RouteParamKeys = + K extends keyof RouteParamKeysManifest ? RouteParamKeysManifest[K] : never; + +export type RouteParams = + K extends keyof RouteParamKeysManifest + ? Record + : undefined; + +export interface RouteMatch { + route: Route; + params: RouteParams; +} diff --git a/src/Data/API/FileFetcherImpl.ts b/src/Data/API/FileFetcherImpl.ts index c90a18748..c880cc464 100644 --- a/src/Data/API/FileFetcherImpl.ts +++ b/src/Data/API/FileFetcherImpl.ts @@ -1,5 +1,4 @@ -import { FileFetcher, Either, Maybe } from "@/Core"; -import { BaseApiHelper } from "./BaseApiHelper"; +import { FileFetcher, Either, Maybe, ApiHelper } from "@/Core"; interface RawResponse { data?: { @@ -11,10 +10,7 @@ interface RawResponse { export class FileFetcherImpl implements FileFetcher { private environment: Maybe.Type = Maybe.none(); - constructor( - private readonly baseApiHelper: BaseApiHelper, - environment?: string - ) { + constructor(private readonly apiHelper: ApiHelper, environment?: string) { if (typeof environment === "undefined") return; this.environment = Maybe.some(environment); } @@ -38,7 +34,7 @@ export class FileFetcherImpl implements FileFetcher { async get(fileId: string): Promise> { return this.unpack( - await this.baseApiHelper.get( + await this.apiHelper.get( this.getUrl(fileId), this.getEnvironment() ) diff --git a/src/Data/Common/UrlState/helpers.mocked.ts b/src/Data/Common/UrlState/helpers.mocked.ts deleted file mode 100644 index 4a7ba0fec..000000000 --- a/src/Data/Common/UrlState/helpers.mocked.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Location, History } from "./helpers"; - -export const mockedHistory: History = { - replace() { - return undefined; - }, -}; - -export const getMockedLocation = (search: string): Location => ({ - pathname: "", - search, - hash: "", -}); diff --git a/src/Data/Common/UrlState/helpers.ts b/src/Data/Common/UrlState/helpers.ts index 0d73db8a9..c5111477f 100644 --- a/src/Data/Common/UrlState/helpers.ts +++ b/src/Data/Common/UrlState/helpers.ts @@ -1,4 +1,4 @@ -import { useHistory, useLocation } from "react-router-dom"; +import { useNavigate, useLocation } from "react-router-dom"; import { RouteKind } from "@/Core"; export interface Location { @@ -7,9 +7,7 @@ export interface Location { hash: string; } -export interface History { - replace(to: string): void; -} +export type Replace = (path: string) => void; export type Update = (data: Data) => void; @@ -25,7 +23,7 @@ export interface StateConfig { type Handler = ( config: ConfigType, location: Location, - history: History + replace: Replace ) => ReturnValue; type ProvidedHandler = ( @@ -37,7 +35,9 @@ export const provide = ( ): ProvidedHandler => { return (config) => { const location = useLocation(); - const history = useHistory(); - return handler(config, location, history); + const navigate = useNavigate(); + return handler(config, location, (path) => + navigate(path, { replace: true }) + ); }; }; diff --git a/src/Data/Common/UrlState/useUrlState.ts b/src/Data/Common/UrlState/useUrlState.ts index c50655442..1b57bd2aa 100644 --- a/src/Data/Common/UrlState/useUrlState.ts +++ b/src/Data/Common/UrlState/useUrlState.ts @@ -1,6 +1,6 @@ import { isObject } from "@/Core"; import { SearchHelper } from "@/UI/Routing"; -import { provide, Location, History, Update, StateConfig } from "./helpers"; +import { provide, Location, Replace, Update, StateConfig } from "./helpers"; const searchHelper = new SearchHelper(); @@ -9,7 +9,7 @@ export const useUrlState = provide(handleUrlState); export function handleUrlState( config: StateConfig, location: Location, - history: History + replace: Replace ): [Data, Update] { const parsedSearch = searchHelper.parse(location.search); const state = getKeyOrEmpty(parsedSearch, "state"); @@ -43,7 +43,7 @@ export function handleUrlState( }, }); - history.replace(`${location.pathname}${newSearch}${location.hash}`); + replace(`${location.pathname}${newSearch}${location.hash}`); }; return [currentValue, setValue]; diff --git a/src/Data/Common/UrlState/useUrlStateWithExpansion.spec.ts b/src/Data/Common/UrlState/useUrlStateWithExpansion.spec.ts index 9b95b78e1..67147aba6 100644 --- a/src/Data/Common/UrlState/useUrlStateWithExpansion.spec.ts +++ b/src/Data/Common/UrlState/useUrlStateWithExpansion.spec.ts @@ -1,4 +1,3 @@ -import { getMockedLocation, mockedHistory } from "./helpers.mocked"; import { handleUrlStateWithExpansion } from "./useUrlStateWithExpansion"; test.each` @@ -11,8 +10,8 @@ test.each` async ({ search, expectedValue }) => { const [value] = handleUrlStateWithExpansion( { route: "Inventory" }, - getMockedLocation(search), - mockedHistory + { pathname: "", search, hash: "" }, + () => undefined ); expect(value).toEqual(expectedValue); } diff --git a/src/Data/Common/UrlState/useUrlStateWithExpansion.ts b/src/Data/Common/UrlState/useUrlStateWithExpansion.ts index 1fa77aa80..5091aca09 100644 --- a/src/Data/Common/UrlState/useUrlStateWithExpansion.ts +++ b/src/Data/Common/UrlState/useUrlStateWithExpansion.ts @@ -1,6 +1,6 @@ import { isEqual, identity } from "lodash"; import { toggleValueInList } from "@/Core"; -import { provide, Location, History, StateConfig, Update } from "./helpers"; +import { provide, Location, Replace, StateConfig, Update } from "./helpers"; import { handleUrlState } from "./useUrlState"; type IsExpanded = (id: string) => boolean; @@ -16,7 +16,7 @@ export const useUrlStateWithExpansion = provide( export function handleUrlStateWithExpansion( config: Config, location: Location, - history: History + replace: Replace ): [string[], Update] { return handleUrlState( { @@ -28,19 +28,19 @@ export function handleUrlStateWithExpansion( equals: isEqual, }, location, - history + replace ); } function handleUrlStateWithExpansionWrapped( config: Config, location: Location, - history: History + replace: Replace ): [IsExpanded, OnExpansion] { const [expandedKeys, setExpandedKeys] = handleUrlStateWithExpansion( config, location, - history + replace ); return [ diff --git a/src/Data/Common/UrlState/useUrlStateWithFilter.spec.ts b/src/Data/Common/UrlState/useUrlStateWithFilter.spec.ts index b19549cdb..0408b90dc 100644 --- a/src/Data/Common/UrlState/useUrlStateWithFilter.spec.ts +++ b/src/Data/Common/UrlState/useUrlStateWithFilter.spec.ts @@ -1,4 +1,3 @@ -import { getMockedLocation, mockedHistory } from "./helpers.mocked"; import { handleUrlStateWithFilter } from "./useUrlStateWithFilter"; const from = { @@ -24,8 +23,8 @@ test.each` async ({ search, expectedValue }) => { const [value] = handleUrlStateWithFilter( { route: "Inventory" }, - getMockedLocation(search), - mockedHistory + { pathname: "", search, hash: "" }, + () => undefined ); expect(value).toEqual(expectedValue); } diff --git a/src/Data/Common/UrlState/useUrlStateWithFilter.ts b/src/Data/Common/UrlState/useUrlStateWithFilter.ts index c48727e2d..79111e151 100644 --- a/src/Data/Common/UrlState/useUrlStateWithFilter.ts +++ b/src/Data/Common/UrlState/useUrlStateWithFilter.ts @@ -1,6 +1,6 @@ import { isEqual, pickBy } from "lodash"; import { isObject, DateRange, isNotUndefined } from "@/Core"; -import { provide, Location, History, StateConfig, Update } from "./helpers"; +import { provide, Location, StateConfig, Update, Replace } from "./helpers"; import { handleUrlState } from "./useUrlState"; export const useUrlStateWithFilter = provide(handleUrlStateWithFilter); @@ -8,7 +8,7 @@ export const useUrlStateWithFilter = provide(handleUrlStateWithFilter); export function handleUrlStateWithFilter( config: Pick, "route"> & { dateRangeKey?: string }, location: Location, - history: History + replace: Replace ): [Data, Update] { const serialize = (data: Data): string | Data => { if (!config.dateRangeKey) return data; @@ -43,6 +43,6 @@ export function handleUrlStateWithFilter( parse, }, location, - history + replace ); } diff --git a/src/Data/Common/UrlState/useUrlStateWithPageSize.spec.ts b/src/Data/Common/UrlState/useUrlStateWithPageSize.spec.ts index 0d420a919..09b4e4e20 100644 --- a/src/Data/Common/UrlState/useUrlStateWithPageSize.spec.ts +++ b/src/Data/Common/UrlState/useUrlStateWithPageSize.spec.ts @@ -1,5 +1,4 @@ import { PageSize } from "@/Core"; -import { getMockedLocation, mockedHistory } from "./helpers.mocked"; import { handleUrlStateWithPageSize } from "./useUrlStateWithPageSize"; test.each` @@ -11,8 +10,8 @@ test.each` async ({ search, expectedValue }) => { const [value] = handleUrlStateWithPageSize( { route: "Inventory" }, - getMockedLocation(search), - mockedHistory + { pathname: "", search, hash: "" }, + () => undefined ); expect(value).toEqual(expectedValue); } diff --git a/src/Data/Common/UrlState/useUrlStateWithPageSize.ts b/src/Data/Common/UrlState/useUrlStateWithPageSize.ts index b5284a158..c07d6723c 100644 --- a/src/Data/Common/UrlState/useUrlStateWithPageSize.ts +++ b/src/Data/Common/UrlState/useUrlStateWithPageSize.ts @@ -1,5 +1,5 @@ import { PageSize } from "@/Core"; -import { provide, Location, History, StateConfig, Update } from "./helpers"; +import { provide, Location, Replace, StateConfig, Update } from "./helpers"; import { handleUrlState } from "./useUrlState"; export const useUrlStateWithPageSize = provide(handleUrlStateWithPageSize); @@ -7,7 +7,7 @@ export const useUrlStateWithPageSize = provide(handleUrlStateWithPageSize); export function handleUrlStateWithPageSize( config: Pick, "route">, location: Location, - history: History + replace: Replace ): [PageSize.Type, Update] { return handleUrlState( { @@ -19,6 +19,6 @@ export function handleUrlStateWithPageSize( equals: PageSize.equals, }, location, - history + replace ); } diff --git a/src/Data/Common/UrlState/useUrlStateWithSort.spec.ts b/src/Data/Common/UrlState/useUrlStateWithSort.spec.ts index 3337996a2..5b8d2cbbe 100644 --- a/src/Data/Common/UrlState/useUrlStateWithSort.spec.ts +++ b/src/Data/Common/UrlState/useUrlStateWithSort.spec.ts @@ -1,4 +1,3 @@ -import { getMockedLocation, mockedHistory } from "./helpers.mocked"; import { handleUrlStateWithSort } from "./useUrlStateWithSort"; test.each` @@ -10,8 +9,8 @@ test.each` async ({ search, expectedValue }) => { const [value] = handleUrlStateWithSort( { default: { name: "timestamp", order: "desc" }, route: "Inventory" }, - getMockedLocation(search), - mockedHistory + { pathname: "", search, hash: "" }, + () => undefined ); expect(value).toEqual(expectedValue); } diff --git a/src/Data/Common/UrlState/useUrlStateWithSort.ts b/src/Data/Common/UrlState/useUrlStateWithSort.ts index ac51fdb16..bdd331feb 100644 --- a/src/Data/Common/UrlState/useUrlStateWithSort.ts +++ b/src/Data/Common/UrlState/useUrlStateWithSort.ts @@ -1,5 +1,5 @@ import { Sort } from "@/Core"; -import { provide, Location, History, StateConfig, Update } from "./helpers"; +import { provide, Location, Replace, StateConfig, Update } from "./helpers"; import { handleUrlState } from "./useUrlState"; export const useUrlStateWithSort = provide(handleUrlStateWithSort); @@ -7,7 +7,7 @@ export const useUrlStateWithSort = provide(handleUrlStateWithSort); export function handleUrlStateWithSort( config: Pick, "route" | "default">, location: Location, - history: History + replace: Replace ): [Sort.Type, Update] { return handleUrlState( { @@ -19,6 +19,6 @@ export function handleUrlStateWithSort( equals: Sort.equals, }, location, - history + replace ); } diff --git a/src/Data/Common/UrlState/useUrlStateWithString.spec.ts b/src/Data/Common/UrlState/useUrlStateWithString.spec.ts index fdf6efcc6..cf27c5c85 100644 --- a/src/Data/Common/UrlState/useUrlStateWithString.spec.ts +++ b/src/Data/Common/UrlState/useUrlStateWithString.spec.ts @@ -1,4 +1,3 @@ -import { getMockedLocation, mockedHistory } from "./helpers.mocked"; import { handleUrlStateWithString } from "./useUrlStateWithString"; test.each` @@ -10,8 +9,8 @@ test.each` async ({ search, expectedValue }) => { const [value] = handleUrlStateWithString( { default: "Info", key: "tab", route: "Inventory" }, - getMockedLocation(search), - mockedHistory + { pathname: "", search, hash: "" }, + () => undefined ); expect(value).toEqual(expectedValue); } diff --git a/src/Data/Common/UrlState/useUrlStateWithString.ts b/src/Data/Common/UrlState/useUrlStateWithString.ts index ba9aefac9..d73fb2504 100644 --- a/src/Data/Common/UrlState/useUrlStateWithString.ts +++ b/src/Data/Common/UrlState/useUrlStateWithString.ts @@ -1,5 +1,5 @@ import { identity } from "lodash"; -import { provide, Location, History, StateConfig, Update } from "./helpers"; +import { provide, Location, Replace, StateConfig, Update } from "./helpers"; import { handleUrlState } from "./useUrlState"; export const useUrlStateWithString = provide(handleUrlStateWithString); @@ -7,7 +7,7 @@ export const useUrlStateWithString = provide(handleUrlStateWithString); export function handleUrlStateWithString( config: Pick, "default" | "key" | "route">, location: Location, - history: History + replace: Replace ): [Data, Update] { return handleUrlState( { @@ -19,6 +19,6 @@ export function handleUrlStateWithString( equals: (a: Data, b: Data) => a === b, }, location, - history + replace ); } diff --git a/src/Data/Managers/Callbacks/GetCallbacks/QueryManager.ts b/src/Data/Managers/Callbacks/GetCallbacks/QueryManager.ts index c6ce1e459..09daceee8 100644 --- a/src/Data/Managers/Callbacks/GetCallbacks/QueryManager.ts +++ b/src/Data/Managers/Callbacks/GetCallbacks/QueryManager.ts @@ -1,22 +1,10 @@ import { identity } from "lodash"; import { ApiHelper, StateHelper } from "@/Core"; import { getUrl } from "@/Data/Managers/Callbacks/getUrl"; -import { PrimaryOneTimeQueryManager } from "@/Data/Managers/Helpers"; +import { PrimaryOneTimeQueryManagerWithEnv } from "@/Data/Managers/Helpers"; -export class CallbacksQueryManager extends PrimaryOneTimeQueryManager<"GetCallbacks"> { - constructor( - apiHelper: ApiHelper, - stateHelper: StateHelper<"GetCallbacks">, - environment: string - ) { - super( - apiHelper, - stateHelper, - () => [environment], - "GetCallbacks", - getUrl, - identity, - environment - ); +export class CallbacksQueryManager extends PrimaryOneTimeQueryManagerWithEnv<"GetCallbacks"> { + constructor(apiHelper: ApiHelper, stateHelper: StateHelper<"GetCallbacks">) { + super(apiHelper, stateHelper, () => [], "GetCallbacks", getUrl, identity); } } diff --git a/src/Data/Managers/CompileDetails/QueryManager.ts b/src/Data/Managers/CompileDetails/QueryManager.ts index 7625b67e7..313463298 100644 --- a/src/Data/Managers/CompileDetails/QueryManager.ts +++ b/src/Data/Managers/CompileDetails/QueryManager.ts @@ -1,13 +1,12 @@ import { identity } from "lodash"; import { StateHelper, Scheduler, ApiHelper } from "@/Core"; -import { PrimaryContinuousQueryManager } from "@/Data/Managers/Helpers"; +import { PrimaryContinuousQueryManagerWithEnv } from "@/Data/Managers/Helpers"; -export class CompileDetailsQueryManager extends PrimaryContinuousQueryManager<"GetCompileDetails"> { +export class CompileDetailsQueryManager extends PrimaryContinuousQueryManagerWithEnv<"GetCompileDetails"> { constructor( apiHelper: ApiHelper, stateHelper: StateHelper<"GetCompileDetails">, - scheduler: Scheduler, - environment: string + scheduler: Scheduler ) { super( apiHelper, @@ -17,8 +16,7 @@ export class CompileDetailsQueryManager extends PrimaryContinuousQueryManager<"G ({ id }) => [id], "GetCompileDetails", ({ id }) => `/api/v2/compilereport/${id}`, - identity, - environment + identity ); } } diff --git a/src/Data/Managers/CompileReports/QueryManager.ts b/src/Data/Managers/CompileReports/QueryManager.ts index 2b3407b33..b49159ea1 100644 --- a/src/Data/Managers/CompileReports/QueryManager.ts +++ b/src/Data/Managers/CompileReports/QueryManager.ts @@ -1,23 +1,22 @@ import { Scheduler, StateHelper, CompileReportParams, ApiHelper } from "@/Core"; import { getPaginationHandlers, - PrimaryContinuousQueryManager, + PrimaryContinuousQueryManagerWithEnv, } from "@/Data/Managers/Helpers"; import { getUrl } from "./getUrl"; -export class CompileReportsQueryManager extends PrimaryContinuousQueryManager<"GetCompileReports"> { +export class CompileReportsQueryManager extends PrimaryContinuousQueryManagerWithEnv<"GetCompileReports"> { constructor( apiHelper: ApiHelper, stateHelper: StateHelper<"GetCompileReports">, - scheduler: Scheduler, - environment: string + scheduler: Scheduler ) { super( apiHelper, stateHelper, scheduler, - () => environment, - ({ pageSize, sort, filter }) => [ + (query, environment) => environment, + ({ pageSize, sort, filter }, environment) => [ environment, pageSize.value, sort?.name, @@ -25,7 +24,7 @@ export class CompileReportsQueryManager extends PrimaryContinuousQueryManager<"G stringifyFilter(filter), ], "GetCompileReports", - getUrl, + (query) => getUrl(query), ({ data, links, metadata }, setUrl) => { if (typeof links === "undefined") { return { data: data, handlers: {}, metadata }; @@ -35,8 +34,7 @@ export class CompileReportsQueryManager extends PrimaryContinuousQueryManager<"G handlers: getPaginationHandlers(links, metadata, setUrl), metadata, }; - }, - environment + } ); } } diff --git a/src/Data/Managers/Diagnostics/QueryManager.ts b/src/Data/Managers/Diagnostics/QueryManager.ts index 45ea2b9c9..4a6a139d3 100644 --- a/src/Data/Managers/Diagnostics/QueryManager.ts +++ b/src/Data/Managers/Diagnostics/QueryManager.ts @@ -1,13 +1,12 @@ import { identity } from "lodash"; import { StateHelper, Scheduler, ApiHelper } from "@/Core"; -import { PrimaryContinuousQueryManager } from "@/Data/Managers/Helpers"; +import { PrimaryContinuousQueryManagerWithEnv } from "@/Data/Managers/Helpers"; -export class DiagnosticsQueryManager extends PrimaryContinuousQueryManager<"GetDiagnostics"> { +export class DiagnosticsQueryManager extends PrimaryContinuousQueryManagerWithEnv<"GetDiagnostics"> { constructor( apiHelper: ApiHelper, stateHelper: StateHelper<"GetDiagnostics">, - scheduler: Scheduler, - environment: string + scheduler: Scheduler ) { super( apiHelper, @@ -18,8 +17,7 @@ export class DiagnosticsQueryManager extends PrimaryContinuousQueryManager<"GetD "GetDiagnostics", ({ service_entity, id }) => `/lsm/v1/service_inventory/${service_entity}/${id}/diagnose`, - identity, - environment + identity ); } } diff --git a/src/Data/Managers/EnvironmentDetails/QueryManager.ts b/src/Data/Managers/EnvironmentDetails/QueryManager.ts index ce8659962..b531c1b35 100644 --- a/src/Data/Managers/EnvironmentDetails/QueryManager.ts +++ b/src/Data/Managers/EnvironmentDetails/QueryManager.ts @@ -1,24 +1,22 @@ import { identity } from "lodash"; import { StateHelper, Scheduler, ApiHelper } from "@/Core"; -import { PrimaryContinuousQueryManager } from "@/Data/Managers/Helpers"; +import { PrimaryContinuousQueryManagerWithEnv } from "@/Data/Managers/Helpers"; -export class EnvironmentDetailsQueryManager extends PrimaryContinuousQueryManager<"GetEnvironmentDetails"> { +export class EnvironmentDetailsQueryManager extends PrimaryContinuousQueryManagerWithEnv<"GetEnvironmentDetails"> { constructor( apiHelper: ApiHelper, stateHelper: StateHelper<"GetEnvironmentDetails">, - scheduler: Scheduler, - environment: string + scheduler: Scheduler ) { super( apiHelper, stateHelper, scheduler, - () => `env-${environment}`, - () => [environment], + (query, environment) => `env-${environment}`, + (query, environment) => [environment], "GetEnvironmentDetails", - () => `/api/v2/environment/${environment}`, - identity, - "" + (query, environment) => `/api/v2/environment/${environment}`, + identity ); } } diff --git a/src/Data/Managers/EnvironmentSettings/GetEnvironmentSettings/QueryManager.ts b/src/Data/Managers/EnvironmentSettings/GetEnvironmentSettings/QueryManager.ts index 033875bde..28e33a235 100644 --- a/src/Data/Managers/EnvironmentSettings/GetEnvironmentSettings/QueryManager.ts +++ b/src/Data/Managers/EnvironmentSettings/GetEnvironmentSettings/QueryManager.ts @@ -1,12 +1,11 @@ import { identity } from "lodash"; import { StateHelper, ApiHelper } from "@/Core"; -import { PrimaryOneTimeQueryManager } from "@/Data/Managers/Helpers"; +import { PrimaryOneTimeQueryManagerWithEnv } from "@/Data/Managers/Helpers"; -export class GetEnvironmentSettingsQueryManager extends PrimaryOneTimeQueryManager<"GetEnvironmentSettings"> { +export class GetEnvironmentSettingsQueryManager extends PrimaryOneTimeQueryManagerWithEnv<"GetEnvironmentSettings"> { constructor( apiHelper: ApiHelper, - stateHelper: StateHelper<"GetEnvironmentSettings">, - environment: string + stateHelper: StateHelper<"GetEnvironmentSettings"> ) { super( apiHelper, @@ -14,8 +13,7 @@ export class GetEnvironmentSettingsQueryManager extends PrimaryOneTimeQueryManag () => [], "GetEnvironmentSettings", () => `/api/v2/environment_settings`, - identity, - environment + identity ); } } diff --git a/src/Data/Managers/EnvironmentSettings/GetEnvironmentSettings/StateHelper.ts b/src/Data/Managers/EnvironmentSettings/GetEnvironmentSettings/StateHelper.ts index 1bb860739..98936f5ca 100644 --- a/src/Data/Managers/EnvironmentSettings/GetEnvironmentSettings/StateHelper.ts +++ b/src/Data/Managers/EnvironmentSettings/GetEnvironmentSettings/StateHelper.ts @@ -12,11 +12,11 @@ export class GetEnvironmentSettingsStateHelper extends PrimaryStateHelper<"GetEn data ); store.dispatch.environmentSettings.setData({ - environment: this.environment, + environment, value: unwrapped, }); }, - (state) => state.environmentSettings.byEnv[this.environment] + (state) => state.environmentSettings.byEnv[environment] ); } } diff --git a/src/Data/Managers/Events/QueryManager.ts b/src/Data/Managers/Events/QueryManager.ts index 99a39bd4b..2c7e10d87 100644 --- a/src/Data/Managers/Events/QueryManager.ts +++ b/src/Data/Managers/Events/QueryManager.ts @@ -1,16 +1,15 @@ import { StateHelper, Scheduler, EventParams, ApiHelper } from "@/Core"; import { getPaginationHandlers, - PrimaryContinuousQueryManager, + PrimaryContinuousQueryManagerWithEnv, } from "@/Data/Managers/Helpers"; import { getUrl } from "./getUrl"; -export class EventsQueryManager extends PrimaryContinuousQueryManager<"GetInstanceEvents"> { +export class EventsQueryManager extends PrimaryContinuousQueryManagerWithEnv<"GetInstanceEvents"> { constructor( apiHelper: ApiHelper, stateHelper: StateHelper<"GetInstanceEvents">, - scheduler: Scheduler, - environment: string + scheduler: Scheduler ) { super( apiHelper, @@ -25,7 +24,7 @@ export class EventsQueryManager extends PrimaryContinuousQueryManager<"GetInstan pageSize.value, ], "GetInstanceEvents", - getUrl, + (query) => getUrl(query), ({ data, links, metadata }, setUrl) => { if (typeof links === "undefined") { return { data: data, handlers: {}, metadata }; @@ -35,8 +34,7 @@ export class EventsQueryManager extends PrimaryContinuousQueryManager<"GetInstan handlers: getPaginationHandlers(links, metadata, setUrl), metadata, }; - }, - environment + } ); } } diff --git a/src/Data/Managers/GetEnvironments/QueryManager.ts b/src/Data/Managers/GetEnvironments/QueryManager.ts index 76c403848..c670cddea 100644 --- a/src/Data/Managers/GetEnvironments/QueryManager.ts +++ b/src/Data/Managers/GetEnvironments/QueryManager.ts @@ -1,5 +1,5 @@ import { identity } from "lodash"; -import { StateHelper, Query, RemoteData, ApiHelper } from "@/Core"; +import { StateHelper, ApiHelper } from "@/Core"; import { PrimaryOneTimeQueryManager } from "@/Data/Managers/Helpers"; import { getUrl } from "./getUrl"; @@ -14,18 +14,7 @@ export class GetEnvironmentsQueryManager extends PrimaryOneTimeQueryManager<"Get () => [], "GetEnvironments", getUrl, - identity, - "" - ); - } - - async update( - query: Query.SubQuery<"GetEnvironments">, - url: string - ): Promise { - this.stateHelper.set( - RemoteData.fromEither(await this.apiHelper.getWithoutEnvironment(url)), - query + identity ); } } diff --git a/src/Data/Managers/GetInstance/QueryManager.ts b/src/Data/Managers/GetInstance/QueryManager.ts index ca00fcd42..d24dcf502 100644 --- a/src/Data/Managers/GetInstance/QueryManager.ts +++ b/src/Data/Managers/GetInstance/QueryManager.ts @@ -1,13 +1,12 @@ import { identity } from "lodash"; import { StateHelper, Scheduler, ApiHelper } from "@/Core"; -import { PrimaryContinuousQueryManager } from "@/Data/Managers/Helpers"; +import { PrimaryContinuousQueryManagerWithEnv } from "@/Data/Managers/Helpers"; -export class ServiceInstanceQueryManager extends PrimaryContinuousQueryManager<"GetServiceInstance"> { +export class ServiceInstanceQueryManager extends PrimaryContinuousQueryManagerWithEnv<"GetServiceInstance"> { constructor( apiHelper: ApiHelper, stateHelper: StateHelper<"GetServiceInstance">, - scheduler: Scheduler, - environment: string + scheduler: Scheduler ) { super( apiHelper, @@ -18,8 +17,7 @@ export class ServiceInstanceQueryManager extends PrimaryContinuousQueryManager<" "GetServiceInstance", ({ service_entity, id }) => `/lsm/v1/service_inventory/${service_entity}/${id}`, - identity, - environment + identity ); } } diff --git a/src/Data/Managers/GetInstanceLogs/QueryManager.ts b/src/Data/Managers/GetInstanceLogs/QueryManager.ts index c7723b906..49ea17ce8 100644 --- a/src/Data/Managers/GetInstanceLogs/QueryManager.ts +++ b/src/Data/Managers/GetInstanceLogs/QueryManager.ts @@ -1,13 +1,12 @@ import { identity } from "lodash"; import { ApiHelper, Scheduler, StateHelper } from "@/Core"; -import { PrimaryContinuousQueryManager } from "@/Data/Managers/Helpers"; +import { PrimaryContinuousQueryManagerWithEnv } from "@/Data/Managers/Helpers"; -export class GetInstanceLogsQueryManager extends PrimaryContinuousQueryManager<"GetInstanceLogs"> { +export class GetInstanceLogsQueryManager extends PrimaryContinuousQueryManagerWithEnv<"GetInstanceLogs"> { constructor( apiHelper: ApiHelper, stateHelper: StateHelper<"GetInstanceLogs">, - scheduler: Scheduler, - environment: string + scheduler: Scheduler ) { super( apiHelper, @@ -18,8 +17,7 @@ export class GetInstanceLogsQueryManager extends PrimaryContinuousQueryManager<" "GetInstanceLogs", ({ service_entity, id }) => `/lsm/v1/service_inventory/${service_entity}/${id}/log`, - identity, - environment + identity ); } } diff --git a/src/Data/Managers/GetProjects/QueryManager.ts b/src/Data/Managers/GetProjects/QueryManager.ts index 185a9683e..b8404b9c6 100644 --- a/src/Data/Managers/GetProjects/QueryManager.ts +++ b/src/Data/Managers/GetProjects/QueryManager.ts @@ -1,28 +1,10 @@ import { identity } from "lodash"; -import { StateHelper, Query, RemoteData, ApiHelper } from "@/Core"; +import { StateHelper, ApiHelper } from "@/Core"; import { PrimaryOneTimeQueryManager } from "@/Data/Managers/Helpers"; import { getUrl } from "./getUrl"; export class GetProjectsQueryManager extends PrimaryOneTimeQueryManager<"GetProjects"> { constructor(apiHelper: ApiHelper, stateHelper: StateHelper<"GetProjects">) { - super( - apiHelper, - stateHelper, - () => [], - "GetProjects", - getUrl, - identity, - "" - ); - } - - async update( - query: Query.SubQuery<"GetProjects">, - url: string - ): Promise { - this.stateHelper.set( - RemoteData.fromEither(await this.apiHelper.getWithoutEnvironment(url)), - query - ); + super(apiHelper, stateHelper, () => [], "GetProjects", getUrl, identity); } } diff --git a/src/Data/Managers/GetServerStatus/QueryManager.ts b/src/Data/Managers/GetServerStatus/QueryManager.ts index f344f964e..3b5e98966 100644 --- a/src/Data/Managers/GetServerStatus/QueryManager.ts +++ b/src/Data/Managers/GetServerStatus/QueryManager.ts @@ -1,5 +1,5 @@ import { identity } from "lodash"; -import { StateHelper, Query, RemoteData, ApiHelper } from "@/Core"; +import { StateHelper, ApiHelper } from "@/Core"; import { PrimaryOneTimeQueryManager } from "@/Data/Managers/Helpers"; export class GetServerStatusQueryManager extends PrimaryOneTimeQueryManager<"GetServerStatus"> { @@ -13,18 +13,7 @@ export class GetServerStatusQueryManager extends PrimaryOneTimeQueryManager<"Get () => [], "GetServerStatus", () => `/api/v1/serverstatus`, - identity, - "" - ); - } - - async update( - query: Query.SubQuery<"GetServerStatus">, - url: string - ): Promise { - this.stateHelper.set( - RemoteData.fromEither(await this.apiHelper.getWithoutEnvironment(url)), - query + identity ); } } diff --git a/src/Data/Managers/Helpers/PrimaryContinuousQueryManager.ts b/src/Data/Managers/Helpers/PrimaryContinuousQueryManager.ts index fafb8fab2..2e558f3fe 100644 --- a/src/Data/Managers/Helpers/PrimaryContinuousQueryManager.ts +++ b/src/Data/Managers/Helpers/PrimaryContinuousQueryManager.ts @@ -15,19 +15,7 @@ import { Scheduler, ApiHelper, } from "@/Core"; - -type GetUnique = ( - query: Query.SubQuery -) => string; - -type GetDependencies = ( - query: Query.SubQuery -) => (string | number | boolean | undefined)[]; - -type Data = [ - RemoteData.Type, Query.UsedData>, - () => void -]; +import { GetDependencies, Data, GetUnique, GetUrl, ToUsed } from "./types"; export class PrimaryContinuousQueryManager implements ContinuousQueryManager @@ -39,17 +27,16 @@ export class PrimaryContinuousQueryManager private readonly getUnique: GetUnique, private readonly getDependencies: GetDependencies, private readonly kind: Kind, - private readonly getUrl: (query: Query.SubQuery) => string, - private readonly toUsed: ( - data: Query.Data, - setUrl: (url: string) => void - ) => Query.UsedData, - private readonly environment: string + private readonly getUrl: GetUrl, + private readonly toUsed: ToUsed ) {} - async update(query: Query.SubQuery, url: string): Promise { + private async update( + query: Query.SubQuery, + url: string + ): Promise { this.stateHelper.set( - RemoteData.fromEither(await this.apiHelper.get(url, this.environment)), + RemoteData.fromEither(await this.apiHelper.getWithoutEnvironment(url)), query ); } @@ -63,7 +50,7 @@ export class PrimaryContinuousQueryManager const task = { effect: async () => - RemoteData.fromEither(await this.apiHelper.get(url, this.environment)), + RemoteData.fromEither(await this.apiHelper.getWithoutEnvironment(url)), update: (data) => this.stateHelper.set(data, query), }; @@ -74,7 +61,7 @@ export class PrimaryContinuousQueryManager return () => { this.scheduler.unregister(this.getUnique(query)); }; - }, [url, this.environment]); + }, [url]); return [ RemoteData.mapSuccess( diff --git a/src/Data/Managers/Helpers/PrimaryContinuousQueryManagerWithEnv.ts b/src/Data/Managers/Helpers/PrimaryContinuousQueryManagerWithEnv.ts new file mode 100644 index 000000000..9e01be66a --- /dev/null +++ b/src/Data/Managers/Helpers/PrimaryContinuousQueryManagerWithEnv.ts @@ -0,0 +1,88 @@ +/** + * @DANGER Disabling these hooks rules is dangerous for an entire file. + * When you edit this file, turn the rule off so you know you are not missing anything. + */ + +/* eslint-disable react-hooks/rules-of-hooks, react-hooks/exhaustive-deps */ + +import { useContext, useEffect, useState } from "react"; +import { + RemoteData, + Query, + ContinuousQueryManager, + QueryManagerKind, + StateHelper, + Scheduler, + ApiHelper, +} from "@/Core"; +import { DependencyContext } from "@/UI"; +import { + Data, + GetUniqueWithEnv, + GetDependenciesWithEnv, + GetUrlWithEnv, + ToUsed, +} from "./types"; + +export class PrimaryContinuousQueryManagerWithEnv + implements ContinuousQueryManager +{ + constructor( + private readonly apiHelper: ApiHelper, + private readonly stateHelper: StateHelper, + private readonly scheduler: Scheduler, + private readonly getUnique: GetUniqueWithEnv, + private readonly getDependencies: GetDependenciesWithEnv, + private readonly kind: Kind, + private readonly getUrl: GetUrlWithEnv, + private readonly toUsed: ToUsed + ) {} + + private async update( + query: Query.SubQuery, + url: string, + environment: string + ): Promise { + this.stateHelper.set( + RemoteData.fromEither(await this.apiHelper.get(url, environment)), + query + ); + } + + useContinuous(query: Query.SubQuery): Data { + const { environmentHandler } = useContext(DependencyContext); + const environment = environmentHandler.useId(); + const [url, setUrl] = useState(this.getUrl(query, environment)); + + useEffect(() => { + setUrl(this.getUrl(query, environment)); + }, this.getDependencies(query, environment)); + + const task = { + effect: async () => + RemoteData.fromEither(await this.apiHelper.get(url, environment)), + update: (data) => this.stateHelper.set(data, query), + }; + + useEffect(() => { + this.stateHelper.set(RemoteData.loading(), query); + this.update(query, url, environment); + this.scheduler.register(this.getUnique(query, environment), task); + return () => { + this.scheduler.unregister(this.getUnique(query, environment)); + }; + }, [url, environment]); + + return [ + RemoteData.mapSuccess( + (data) => this.toUsed(data, setUrl), + this.stateHelper.getHooked(query) + ), + () => this.update(query, url, environment), + ]; + } + + matches(query: Query.SubQuery, kind: QueryManagerKind): boolean { + return query.kind === this.kind && kind === "Continuous"; + } +} diff --git a/src/Data/Managers/Helpers/PrimaryOneTimeQueryManager.ts b/src/Data/Managers/Helpers/PrimaryOneTimeQueryManager.ts index 6ad26f5f2..573183334 100644 --- a/src/Data/Managers/Helpers/PrimaryOneTimeQueryManager.ts +++ b/src/Data/Managers/Helpers/PrimaryOneTimeQueryManager.ts @@ -14,15 +14,7 @@ import { StateHelper, ApiHelper, } from "@/Core"; - -type GetDependencies = ( - query: Query.SubQuery -) => (string | number | boolean | undefined)[]; - -type Data = [ - RemoteData.Type, Query.UsedData>, - () => void -]; +import { GetDependencies, Data, GetUrl, ToUsed } from "./types"; export class PrimaryOneTimeQueryManager implements OneTimeQueryManager @@ -32,23 +24,18 @@ export class PrimaryOneTimeQueryManager protected readonly stateHelper: StateHelper, private readonly getDependencies: GetDependencies, private readonly kind: Kind, - private readonly getUrl: (query: Query.SubQuery) => string, - private readonly toUsed: ( - data: Query.Data, - setUrl: (url: string) => void - ) => Query.UsedData, - private readonly environment: string + private readonly getUrl: GetUrl, + private readonly toUsed: ToUsed ) {} async update(query: Query.SubQuery, url: string): Promise { this.stateHelper.set( - RemoteData.fromEither(await this.apiHelper.get(url, this.environment)), + RemoteData.fromEither(await this.apiHelper.getWithoutEnvironment(url)), query ); } useOneTime(query: Query.SubQuery): Data { - const { environment } = this; const [url, setUrl] = useState(this.getUrl(query)); useEffect(() => { @@ -58,7 +45,7 @@ export class PrimaryOneTimeQueryManager useEffect(() => { this.stateHelper.set(RemoteData.loading(), query); this.update(query, url); - }, [url, environment]); + }, [url]); return [ RemoteData.mapSuccess( diff --git a/src/Data/Managers/Helpers/PrimaryOneTimeQueryManagerWithEnv.ts b/src/Data/Managers/Helpers/PrimaryOneTimeQueryManagerWithEnv.ts new file mode 100644 index 000000000..2eababfb3 --- /dev/null +++ b/src/Data/Managers/Helpers/PrimaryOneTimeQueryManagerWithEnv.ts @@ -0,0 +1,69 @@ +/** + * @DANGER Disabling these hooks rules is dangerous for an entire file. + * When you edit this file, turn the rule off so you know you are not missing anything. + */ + +/* eslint-disable react-hooks/rules-of-hooks, react-hooks/exhaustive-deps */ + +import { useContext, useEffect, useState } from "react"; +import { + RemoteData, + Query, + OneTimeQueryManager, + QueryManagerKind, + StateHelper, + ApiHelper, +} from "@/Core"; +import { DependencyContext } from "@/UI"; +import { Data, GetDependenciesWithEnv, GetUrlWithEnv, ToUsed } from "./types"; + +export class PrimaryOneTimeQueryManagerWithEnv + implements OneTimeQueryManager +{ + constructor( + protected readonly apiHelper: ApiHelper, + protected readonly stateHelper: StateHelper, + private readonly getDependencies: GetDependenciesWithEnv, + private readonly kind: Kind, + private readonly getUrl: GetUrlWithEnv, + private readonly toUsed: ToUsed + ) {} + + async update( + query: Query.SubQuery, + url: string, + environment: string + ): Promise { + this.stateHelper.set( + RemoteData.fromEither(await this.apiHelper.get(url, environment)), + query + ); + } + + useOneTime(query: Query.SubQuery): Data { + const { environmentHandler } = useContext(DependencyContext); + const environment = environmentHandler.useId(); + const [url, setUrl] = useState(this.getUrl(query, environment)); + + useEffect(() => { + setUrl(this.getUrl(query, environment)); + }, this.getDependencies(query, environment)); + + useEffect(() => { + this.stateHelper.set(RemoteData.loading(), query); + this.update(query, url, environment); + }, [url, environment]); + + return [ + RemoteData.mapSuccess( + (d) => this.toUsed(d, setUrl), + this.stateHelper.getHooked(query) + ), + () => this.update(query, url, environment), + ]; + } + + matches(query: Query.SubQuery, kind: QueryManagerKind): boolean { + return query.kind === this.kind && kind === "OneTime"; + } +} diff --git a/src/Data/Managers/Helpers/index.ts b/src/Data/Managers/Helpers/index.ts index cae8f1074..74a39e6bc 100644 --- a/src/Data/Managers/Helpers/index.ts +++ b/src/Data/Managers/Helpers/index.ts @@ -1,3 +1,5 @@ export * from "./PrimaryContinuousQueryManager"; +export * from "./PrimaryContinuousQueryManagerWithEnv"; export * from "./PrimaryOneTimeQueryManager"; +export * from "./PrimaryOneTimeQueryManagerWithEnv"; export * from "./getPaginationHandlers"; diff --git a/src/Data/Managers/Helpers/types.ts b/src/Data/Managers/Helpers/types.ts new file mode 100644 index 000000000..ba3a168fb --- /dev/null +++ b/src/Data/Managers/Helpers/types.ts @@ -0,0 +1,41 @@ +import { Query, RemoteData } from "@/Core"; + +export type Data = [ + RemoteData.Type, Query.UsedData>, + () => void +]; + +type GetDependenciesWith< + Kind extends Query.Kind, + WithEnv +> = WithEnv extends true + ? ( + query: Query.SubQuery, + environment: string + ) => (string | number | boolean | undefined)[] + : (query: Query.SubQuery) => (string | number | boolean | undefined)[]; + +export type GetDependencies = GetDependenciesWith< + Kind, + false +>; +export type GetDependenciesWithEnv = + GetDependenciesWith; + +type GetUniqueWith = WithEnv extends true + ? (query: Query.SubQuery, environment: string) => string + : (query: Query.SubQuery) => string; + +export type GetUnique = GetUniqueWith; +export type GetUniqueWithEnv = GetUniqueWith< + Kind, + true +>; + +export type GetUrl = GetUniqueWith; +export type GetUrlWithEnv = GetUniqueWith; + +export type ToUsed = ( + data: Query.Data, + setUrl: (url: string) => void +) => Query.UsedData; diff --git a/src/Data/Managers/InstanceConfig/QueryManager.ts b/src/Data/Managers/InstanceConfig/QueryManager.ts index 1cf5b891a..c036c607e 100644 --- a/src/Data/Managers/InstanceConfig/QueryManager.ts +++ b/src/Data/Managers/InstanceConfig/QueryManager.ts @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { useContext, useEffect } from "react"; import { OneTimeQueryManager, Query, @@ -7,6 +7,7 @@ import { ConfigFinalizer, ApiHelper, } from "@/Core"; +import { DependencyContext } from "@/UI/Dependency"; type Data = RemoteData.Type< Query.Error<"GetInstanceConfig">, @@ -19,8 +20,7 @@ export class InstanceConfigQueryManager constructor( private readonly apiHelper: ApiHelper, private readonly stateHelper: StateHelper<"GetInstanceConfig">, - private readonly configFinalizer: ConfigFinalizer<"GetInstanceConfig">, - private readonly environment: string + private readonly configFinalizer: ConfigFinalizer<"GetInstanceConfig"> ) {} private getConfigUrl({ @@ -39,22 +39,25 @@ export class InstanceConfigQueryManager private async update( query: Query.SubQuery<"GetInstanceConfig">, - url: string + url: string, + environment: string ): Promise { this.stateHelper.set( - RemoteData.fromEither(await this.apiHelper.get(url, this.environment)), + RemoteData.fromEither(await this.apiHelper.get(url, environment)), query ); } useOneTime(query: Query.SubQuery<"GetInstanceConfig">): [Data, () => void] { + /* eslint-disable-next-line react-hooks/rules-of-hooks */ + const { environmentHandler } = useContext(DependencyContext); + const environment = environmentHandler.useId(); const { service_entity } = query; - const { environment } = this; /* eslint-disable-next-line react-hooks/rules-of-hooks */ useEffect(() => { this.initialize(query); - this.update(query, this.getConfigUrl(query)); + this.update(query, this.getConfigUrl(query), environment); }, [environment]); /* eslint-disable-line react-hooks/exhaustive-deps */ return [ @@ -62,7 +65,7 @@ export class InstanceConfigQueryManager this.stateHelper.getHooked(query), service_entity ), - () => this.update(query, this.getConfigUrl(query)), + () => this.update(query, this.getConfigUrl(query), environment), ]; } diff --git a/src/Data/Managers/InstanceResources/QueryManager.ts b/src/Data/Managers/InstanceResources/QueryManager.ts index 1d0ff70f2..e6c431493 100644 --- a/src/Data/Managers/InstanceResources/QueryManager.ts +++ b/src/Data/Managers/InstanceResources/QueryManager.ts @@ -1,13 +1,12 @@ import { identity } from "lodash"; import { StateHelper, Scheduler, ApiHelper } from "@/Core"; -import { PrimaryContinuousQueryManager } from "@/Data/Managers/Helpers"; +import { PrimaryContinuousQueryManagerWithEnv } from "@/Data/Managers/Helpers"; -export class InstanceResourcesQueryManager extends PrimaryContinuousQueryManager<"GetInstanceResources"> { +export class InstanceResourcesQueryManager extends PrimaryContinuousQueryManagerWithEnv<"GetInstanceResources"> { constructor( apiHelper: ApiHelper, stateHelper: StateHelper<"GetInstanceResources">, - scheduler: Scheduler, - environment: string + scheduler: Scheduler ) { super( apiHelper, @@ -18,8 +17,7 @@ export class InstanceResourcesQueryManager extends PrimaryContinuousQueryManager "GetInstanceResources", ({ service_entity, id, version }) => `/lsm/v1/service_inventory/${service_entity}/${id}/resources?current_version=${version}`, - identity, - environment + identity ); } } diff --git a/src/Data/Managers/ResourceDetails/QueryManager.ts b/src/Data/Managers/ResourceDetails/QueryManager.ts index 61567c1ce..81056994e 100644 --- a/src/Data/Managers/ResourceDetails/QueryManager.ts +++ b/src/Data/Managers/ResourceDetails/QueryManager.ts @@ -1,13 +1,12 @@ import { identity } from "lodash"; import { StateHelper, Scheduler, ApiHelper } from "@/Core"; -import { PrimaryContinuousQueryManager } from "@/Data/Managers/Helpers"; +import { PrimaryContinuousQueryManagerWithEnv } from "@/Data/Managers/Helpers"; -export class ResourceDetailsQueryManager extends PrimaryContinuousQueryManager<"GetResourceDetails"> { +export class ResourceDetailsQueryManager extends PrimaryContinuousQueryManagerWithEnv<"GetResourceDetails"> { constructor( apiHelper: ApiHelper, stateHelper: StateHelper<"GetResourceDetails">, - scheduler: Scheduler, - environment: string + scheduler: Scheduler ) { super( apiHelper, @@ -17,8 +16,7 @@ export class ResourceDetailsQueryManager extends PrimaryContinuousQueryManager<" ({ id }) => [id], "GetResourceDetails", ({ id }) => `/api/v2/resource/${id}`, - identity, - environment + identity ); } } diff --git a/src/Data/Managers/ResourceHistory/QueryManager.ts b/src/Data/Managers/ResourceHistory/QueryManager.ts index 442029efc..4d28a2848 100644 --- a/src/Data/Managers/ResourceHistory/QueryManager.ts +++ b/src/Data/Managers/ResourceHistory/QueryManager.ts @@ -1,23 +1,22 @@ import { Scheduler, StateHelper, ApiHelper } from "@/Core"; import { getPaginationHandlers, - PrimaryContinuousQueryManager, + PrimaryContinuousQueryManagerWithEnv, } from "@/Data/Managers/Helpers"; import { getUrl } from "./getUrl"; -export class ResourceHistoryQueryManager extends PrimaryContinuousQueryManager<"GetResourceHistory"> { +export class ResourceHistoryQueryManager extends PrimaryContinuousQueryManagerWithEnv<"GetResourceHistory"> { constructor( apiHelper: ApiHelper, stateHelper: StateHelper<"GetResourceHistory">, - scheduler: Scheduler, - environment: string + scheduler: Scheduler ) { super( apiHelper, stateHelper, scheduler, - () => environment, - ({ sort, pageSize }) => [ + (query, environment) => environment, + ({ sort, pageSize }, environment) => [ environment, pageSize.value, sort?.name, @@ -33,8 +32,7 @@ export class ResourceHistoryQueryManager extends PrimaryContinuousQueryManager<" handlers: getPaginationHandlers(links, metadata, setUrl), metadata, }; - }, - environment + } ); } } diff --git a/src/Data/Managers/ResourceLogs/QueryManager.ts b/src/Data/Managers/ResourceLogs/QueryManager.ts index 888958cc8..ec41f6096 100644 --- a/src/Data/Managers/ResourceLogs/QueryManager.ts +++ b/src/Data/Managers/ResourceLogs/QueryManager.ts @@ -1,23 +1,22 @@ import { Scheduler, StateHelper, ResourceLogFilter, ApiHelper } from "@/Core"; import { getPaginationHandlers, - PrimaryContinuousQueryManager, + PrimaryContinuousQueryManagerWithEnv, } from "@/Data/Managers/Helpers"; import { getUrl } from "./getUrl"; -export class ResourceLogsQueryManager extends PrimaryContinuousQueryManager<"GetResourceLogs"> { +export class ResourceLogsQueryManager extends PrimaryContinuousQueryManagerWithEnv<"GetResourceLogs"> { constructor( apiHelper: ApiHelper, stateHelper: StateHelper<"GetResourceLogs">, - scheduler: Scheduler, - environment: string + scheduler: Scheduler ) { super( apiHelper, stateHelper, scheduler, - () => environment, - ({ pageSize, filter, sort }) => [ + (query, environment) => environment, + ({ pageSize, filter, sort }, environment) => [ environment, pageSize.value, stringifyFilter(filter), @@ -34,8 +33,7 @@ export class ResourceLogsQueryManager extends PrimaryContinuousQueryManager<"Get handlers: getPaginationHandlers(links, metadata, setUrl), metadata, }; - }, - environment + } ); } } diff --git a/src/Data/Managers/Resources/QueryManager.ts b/src/Data/Managers/Resources/QueryManager.ts index 05f3ab544..8587a78bb 100644 --- a/src/Data/Managers/Resources/QueryManager.ts +++ b/src/Data/Managers/Resources/QueryManager.ts @@ -1,23 +1,22 @@ import { Scheduler, StateHelper, ResourceParams, ApiHelper } from "@/Core"; import { getPaginationHandlers, - PrimaryContinuousQueryManager, + PrimaryContinuousQueryManagerWithEnv, } from "@/Data/Managers/Helpers"; import { getUrl } from "./getUrl"; -export class ResourcesQueryManager extends PrimaryContinuousQueryManager<"GetResources"> { +export class ResourcesQueryManager extends PrimaryContinuousQueryManagerWithEnv<"GetResources"> { constructor( apiHelper: ApiHelper, stateHelper: StateHelper<"GetResources">, - scheduler: Scheduler, - environment: string + scheduler: Scheduler ) { super( apiHelper, stateHelper, scheduler, - () => environment, - ({ filter, sort, pageSize }) => [ + (query, environment) => environment, + ({ filter, sort, pageSize }, environment) => [ environment, pageSize.value, sort?.name, @@ -35,8 +34,7 @@ export class ResourcesQueryManager extends PrimaryContinuousQueryManager<"GetRes handlers: getPaginationHandlers(links, metadata, setUrl), metadata, }; - }, - environment + } ); } } diff --git a/src/Data/Managers/Service/QueryManager.ts b/src/Data/Managers/Service/QueryManager.ts index 16c32b611..9f9363f13 100644 --- a/src/Data/Managers/Service/QueryManager.ts +++ b/src/Data/Managers/Service/QueryManager.ts @@ -1,25 +1,23 @@ import { identity } from "lodash"; import { KeyMaker, StateHelper, Scheduler, ApiHelper } from "@/Core"; -import { PrimaryContinuousQueryManager } from "@/Data/Managers/Helpers"; +import { PrimaryContinuousQueryManagerWithEnv } from "@/Data/Managers/Helpers"; -export class ServiceQueryManager extends PrimaryContinuousQueryManager<"GetService"> { +export class ServiceQueryManager extends PrimaryContinuousQueryManagerWithEnv<"GetService"> { constructor( apiHelper: ApiHelper, stateHelper: StateHelper<"GetService">, scheduler: Scheduler, - keyMaker: KeyMaker<[string, string]>, - environment: string + keyMaker: KeyMaker<[string, string]> ) { super( apiHelper, stateHelper, scheduler, - ({ name }) => keyMaker.make([environment, name]), - ({ name }) => [name, environment], + ({ name }, environment) => keyMaker.make([environment, name]), + ({ name }, environment) => [name, environment], "GetService", ({ name }) => `/lsm/v1/service_catalog/${name}?instance_summary=True`, - identity, - environment + identity ); } } diff --git a/src/Data/Managers/ServiceConfig/QueryManager.ts b/src/Data/Managers/ServiceConfig/QueryManager.ts index 6e556c3c1..45f4c6fa3 100644 --- a/src/Data/Managers/ServiceConfig/QueryManager.ts +++ b/src/Data/Managers/ServiceConfig/QueryManager.ts @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { useContext, useEffect } from "react"; import { OneTimeQueryManager, Query, @@ -7,6 +7,7 @@ import { ConfigFinalizer, ApiHelper, } from "@/Core"; +import { DependencyContext } from "@/UI"; type Data = RemoteData.Type< Query.Error<"GetServiceConfig">, @@ -19,8 +20,7 @@ export class ServiceConfigQueryManager constructor( private readonly apiHelper: ApiHelper, private readonly stateHelper: StateHelper<"GetServiceConfig">, - private readonly configFinalizer: ConfigFinalizer<"GetServiceConfig">, - private readonly environment: string + private readonly configFinalizer: ConfigFinalizer<"GetServiceConfig"> ) {} private getConfigUrl({ name }: Query.SubQuery<"GetServiceConfig">): string { @@ -36,27 +36,30 @@ export class ServiceConfigQueryManager private async update( query: Query.SubQuery<"GetServiceConfig">, - url: string + url: string, + environment: string ): Promise { this.stateHelper.set( - RemoteData.fromEither(await this.apiHelper.get(url, this.environment)), + RemoteData.fromEither(await this.apiHelper.get(url, environment)), query ); } useOneTime(query: Query.SubQuery<"GetServiceConfig">): [Data, () => void] { - const { environment } = this; + /* eslint-disable-next-line react-hooks/rules-of-hooks */ + const { environmentHandler } = useContext(DependencyContext); + const environment = environmentHandler.useId(); const { name } = query; /* eslint-disable-next-line react-hooks/rules-of-hooks */ useEffect(() => { this.initialize(query); - this.update(query, this.getConfigUrl(query)); + this.update(query, this.getConfigUrl(query), environment); }, [environment]); /* eslint-disable-line react-hooks/exhaustive-deps */ return [ this.configFinalizer.finalize(this.stateHelper.getHooked(query), name), - () => this.update(query, this.getConfigUrl(query)), + () => this.update(query, this.getConfigUrl(query), environment), ]; } diff --git a/src/Data/Managers/ServiceInstances/QueryManager.ts b/src/Data/Managers/ServiceInstances/QueryManager.ts index a70072bbc..5e8f8feda 100644 --- a/src/Data/Managers/ServiceInstances/QueryManager.ts +++ b/src/Data/Managers/ServiceInstances/QueryManager.ts @@ -6,16 +6,15 @@ import { } from "@/Core"; import { getPaginationHandlers, - PrimaryContinuousQueryManager, + PrimaryContinuousQueryManagerWithEnv, } from "@/Data/Managers/Helpers"; import { getUrl } from "./getUrl"; -export class ServiceInstancesQueryManager extends PrimaryContinuousQueryManager<"GetServiceInstances"> { +export class ServiceInstancesQueryManager extends PrimaryContinuousQueryManagerWithEnv<"GetServiceInstances"> { constructor( apiHelper: ApiHelper, stateHelper: StateHelper<"GetServiceInstances">, - scheduler: Scheduler, - environment: string + scheduler: Scheduler ) { super( apiHelper, @@ -40,8 +39,7 @@ export class ServiceInstancesQueryManager extends PrimaryContinuousQueryManager< handlers: getPaginationHandlers(links, metadata, setUrl), metadata, }; - }, - environment + } ); } } diff --git a/src/Data/Managers/Services/QueryManager.ts b/src/Data/Managers/Services/QueryManager.ts index 12635d2c9..39f212313 100644 --- a/src/Data/Managers/Services/QueryManager.ts +++ b/src/Data/Managers/Services/QueryManager.ts @@ -1,24 +1,22 @@ import { identity } from "lodash"; import { Scheduler, StateHelper, ApiHelper } from "@/Core"; -import { PrimaryContinuousQueryManager } from "@/Data/Managers/Helpers"; +import { PrimaryContinuousQueryManagerWithEnv } from "@/Data/Managers/Helpers"; -export class ServicesQueryManager extends PrimaryContinuousQueryManager<"GetServices"> { +export class ServicesQueryManager extends PrimaryContinuousQueryManagerWithEnv<"GetServices"> { constructor( apiHelper: ApiHelper, stateHelper: StateHelper<"GetServices">, - scheduler: Scheduler, - environment: string + scheduler: Scheduler ) { super( apiHelper, stateHelper, scheduler, - () => environment, - () => [environment], + ({ kind }) => kind, + () => [], "GetServices", () => `/lsm/v1/service_catalog?instance_summary=True`, - identity, - environment + identity ); } } diff --git a/src/Data/Resolvers/QueryManagerResolver.ts b/src/Data/Resolvers/QueryManagerResolver.ts index d8b6e9baf..63748dacc 100644 --- a/src/Data/Resolvers/QueryManagerResolver.ts +++ b/src/Data/Resolvers/QueryManagerResolver.ts @@ -101,116 +101,97 @@ export class QueryManagerResolver implements ManagerResolver { return [ new GetEnvironmentSettingsQueryManager( this.apiHelper, - new GetEnvironmentSettingsStateHelper(this.store, environment), - environment + new GetEnvironmentSettingsStateHelper(this.store, environment) ), new ServicesQueryManager( this.apiHelper, new ServicesStateHelper(this.store, environment), - scheduler, - environment + scheduler ), new ServiceQueryManager( this.apiHelper, serviceStateHelper, scheduler, - serviceKeyMaker, - environment + serviceKeyMaker ), new ServiceInstancesQueryManager( this.apiHelper, new ServiceInstancesStateHelper(this.store, environment), - scheduler, - environment + scheduler ), new ServiceConfigQueryManager( this.apiHelper, new ServiceConfigStateHelper(this.store), - new ServiceConfigFinalizer(serviceStateHelper), - environment + new ServiceConfigFinalizer(serviceStateHelper) ), new InstanceResourcesQueryManager( this.apiHelper, new InstanceResourcesStateHelper(this.store), - scheduler, - environment + scheduler ), new EventsQueryManager( this.apiHelper, new EventsStateHelper(this.store), - scheduler, - environment + scheduler ), new GetInstanceLogsQueryManager( this.apiHelper, new GetInstanceLogsStateHelper(this.store), - scheduler, - environment + scheduler ), new InstanceConfigQueryManager( this.apiHelper, new InstanceConfigStateHelper(this.store), - new InstanceConfigFinalizer(serviceStateHelper), - environment + new InstanceConfigFinalizer(serviceStateHelper) ), new DiagnosticsQueryManager( this.apiHelper, new DiagnosticsStateHelper(this.store), - scheduler, - environment + scheduler ), new ResourcesQueryManager( this.apiHelper, new ResourcesStateHelper(this.store, environment), - scheduler, - environment + scheduler ), new ResourceDetailsQueryManager( this.apiHelper, new ResourceDetailsStateHelper(this.store), - scheduler, - environment + scheduler ), new ResourceHistoryQueryManager( this.apiHelper, new ResourceHistoryStateHelper(this.store), - scheduler, - environment + scheduler ), new EnvironmentDetailsQueryManager( this.apiHelper, new EnvironmentDetailsStateHelper(this.store, environment), - scheduler, - environment + scheduler ), new ServiceInstanceQueryManager( this.apiHelper, new ServiceInstanceStateHelper(this.store), - scheduler, - environment + scheduler ), new CallbacksQueryManager( this.apiHelper, - new CallbacksStateHelper(this.store, environment), - environment + new CallbacksStateHelper(this.store, environment) ), new CompileReportsQueryManager( this.apiHelper, new CompileReportsStateHelper(this.store, environment), - scheduler, - environment + scheduler ), new CompileDetailsQueryManager( this.apiHelper, new CompileDetailsStateHelper(this.store), - scheduler, - environment + scheduler ), new ResourceLogsQueryManager( this.apiHelper, new ResourceLogsStateHelper(this.store), - scheduler, - environment + scheduler ), ]; } diff --git a/src/Injector.tsx b/src/Injector.tsx index b44809ea8..804b131e8 100644 --- a/src/Injector.tsx +++ b/src/Injector.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { useHistory } from "react-router-dom"; +import { useLocation, useNavigate } from "react-router-dom"; import { BaseApiHelper, FileFetcherImpl } from "@/Data/API"; import { KeycloakAuthHelper } from "@/Data/Auth"; import { PrimaryFeatureManager } from "@/Data/Common"; @@ -25,7 +25,8 @@ interface Props { } export const Injector: React.FC = ({ store, children, keycloak }) => { - const history = useHistory(); + const location = useLocation(); + const navigate = useNavigate(); const baseUrlManager = new PrimaryBaseUrlManager(location.pathname); const consoleBaseUrl = baseUrlManager.getConsoleBaseUrl(); const baseUrl = baseUrlManager.getBaseUrl(process.env.API_BASEURL); @@ -45,7 +46,11 @@ export const Injector: React.FC = ({ store, children, keycloak }) => { const urlManager = new UrlManagerImpl(featureManager, baseUrl); const fileFetcher = new FileFetcherImpl(apiHelper); const environmentModifier = new EnvironmentModifierImpl(); - const environmentHandler = new EnvironmentHandlerImpl(history, routeManager); + const environmentHandler = new EnvironmentHandlerImpl( + useLocation, + navigate, + routeManager + ); return ( implements ApiHelper { ); } getWithoutEnvironment(): Promise> { - throw new Error("Method not implemented."); + return Outcome.handle( + this.outcome as Outcome.Type + ); } post(): Promise> { throw new Error("Method not implemented."); diff --git a/src/Test/Mock/MockEnvironmentHandler.ts b/src/Test/Mock/MockEnvironmentHandler.ts new file mode 100644 index 000000000..d1efb70c5 --- /dev/null +++ b/src/Test/Mock/MockEnvironmentHandler.ts @@ -0,0 +1,13 @@ +import { EnvironmentHandler, FlatEnvironment } from "@/Core"; + +export class MockEnvironmentHandler implements EnvironmentHandler { + useId(): string { + return "env"; + } + set(): void { + throw new Error("Method not implemented."); + } + useSelected(): FlatEnvironment { + throw new Error("Method not implemented."); + } +} diff --git a/src/Test/Mock/index.ts b/src/Test/Mock/index.ts index 576f6cf6d..229f47edf 100644 --- a/src/Test/Mock/index.ts +++ b/src/Test/Mock/index.ts @@ -11,3 +11,4 @@ export * from "./MockEnvironmentModifier"; export * from "./MockFeatureManager"; export * as Outcome from "./Outcome"; export * from "./MockCommandManager"; +export * from "./MockEnvironmentHandler"; diff --git a/src/UI/Components/EnvironmentProvider/EnvironmentProvider.tsx b/src/UI/Components/EnvironmentProvider/EnvironmentProvider.tsx deleted file mode 100644 index c4e36d0eb..000000000 --- a/src/UI/Components/EnvironmentProvider/EnvironmentProvider.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React, { useContext } from "react"; -import { ErrorView } from "@/UI/Components/ErrorView"; -import { DependencyContext } from "@/UI/Dependency"; -import { words } from "@/UI/words"; - -interface Props { - Wrapper: React.FC; - Dependant: React.FC<{ environment: string }>; -} - -export const EnvironmentProvider: React.FunctionComponent = ({ - Wrapper, - Dependant, -}) => { - const { environmentHandler } = useContext(DependencyContext); - - const environment = environmentHandler.useSelected(); - if (environment) { - return ; - } else { - return ( - - - - ); - } -}; diff --git a/src/UI/Components/EnvironmentProvider/index.ts b/src/UI/Components/EnvironmentProvider/index.ts deleted file mode 100644 index bc0ceaaf0..000000000 --- a/src/UI/Components/EnvironmentProvider/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./EnvironmentProvider"; diff --git a/src/UI/Components/index.ts b/src/UI/Components/index.ts index 853403948..051879fed 100644 --- a/src/UI/Components/index.ts +++ b/src/UI/Components/index.ts @@ -11,7 +11,6 @@ export * from "./DeleteForm"; export * from "./Description"; export * from "./DictEditor"; export * from "./EmptyView"; -export * from "./EnvironmentProvider"; export * from "./ErrorView"; export * from "./EventIcon"; export * from "./EventsTable"; diff --git a/src/UI/Dependency/DependencyResolver.tsx b/src/UI/Dependency/DependencyResolver.tsx index b4738993e..a44fccbba 100644 --- a/src/UI/Dependency/DependencyResolver.tsx +++ b/src/UI/Dependency/DependencyResolver.tsx @@ -1,9 +1,11 @@ import React, { useContext } from "react"; import { DependencyContext } from "./Dependency"; -export const DependencyResolver: React.FC<{ environment: string }> = ({ - environment, -}) => { +interface Props { + environment: string; +} + +export const DependencyResolver: React.FC = ({ environment }) => { const { queryResolver, commandResolver, diff --git a/src/UI/Dependency/Dummy/DummyEnvironmentHandler.ts b/src/UI/Dependency/Dummy/DummyEnvironmentHandler.ts index a9c600886..3d0ae1298 100644 --- a/src/UI/Dependency/Dummy/DummyEnvironmentHandler.ts +++ b/src/UI/Dependency/Dummy/DummyEnvironmentHandler.ts @@ -1,6 +1,9 @@ import { EnvironmentHandler, FlatEnvironment } from "@/Core"; export class DummyEnvironmentHandler implements EnvironmentHandler { + useId(): string { + throw new Error("Method not implemented."); + } set(): void { throw new Error("Method not implemented."); } diff --git a/src/UI/Dependency/Dummy/DummyRouteManager.ts b/src/UI/Dependency/Dummy/DummyRouteManager.ts index 4b88138b7..bbe6b4aa4 100644 --- a/src/UI/Dependency/Dummy/DummyRouteManager.ts +++ b/src/UI/Dependency/Dummy/DummyRouteManager.ts @@ -1,7 +1,7 @@ -import { RouteManager, Route, RouteKind, MatchedParams } from "@/Core"; +import { RouteManager, Route, RouteKind, RouteMatch } from "@/Core"; export class DummyRouteManager implements RouteManager { - getRouteWithParamsFromUrl(): [Route, MatchedParams] | undefined { + getRouteMatchFromUrl(): RouteMatch | undefined { throw new Error("Method not implemented."); } getLineageFromRoute(): Route[] { diff --git a/src/UI/Dependency/EnvironmentHandler.test.ts b/src/UI/Dependency/EnvironmentHandler.test.ts index 239f2e226..e12926ba4 100644 --- a/src/UI/Dependency/EnvironmentHandler.test.ts +++ b/src/UI/Dependency/EnvironmentHandler.test.ts @@ -17,15 +17,25 @@ test("EnvironmentHandler updates environment correctly", () => { .getActions() .environments.setEnvironments(RemoteData.success(Environment.filterable)); - const environmentHandler = new EnvironmentHandlerImpl(history, routeManager); - environmentHandler.set(env.id); + const environmentHandler = new EnvironmentHandlerImpl( + () => history.location, + history.push, + routeManager + ); + environmentHandler.set(history.location, env.id); + expect(history.location.search).toEqual(`?env=${env.id}`); }); test("EnvironmentHandler determines selected environment correctly", () => { const history = createMemoryHistory(); - const environmentHandler = new EnvironmentHandlerImpl(history, routeManager); + const environmentHandler = new EnvironmentHandlerImpl( + () => history.location, + (path) => history.push(path), + routeManager + ); + expect( environmentHandler.determineSelected( RemoteData.notAsked(), @@ -47,7 +57,7 @@ test("EnvironmentHandler determines selected environment correctly", () => { ) ).toEqual(Environment.filterable[0]); - environmentHandler.set(Environment.filterable[1].id); + environmentHandler.set(history.location, Environment.filterable[1].id); expect( environmentHandler.determineSelected( RemoteData.success(Environment.filterable), diff --git a/src/UI/Dependency/EnvironmentHandler.ts b/src/UI/Dependency/EnvironmentHandler.ts index 80dbca7b5..2fabb0634 100644 --- a/src/UI/Dependency/EnvironmentHandler.ts +++ b/src/UI/Dependency/EnvironmentHandler.ts @@ -1,5 +1,4 @@ -import { useLocation } from "react-router-dom"; -import { History } from "history"; +import { Location } from "history"; import { EnvironmentHandler, FlatEnvironment, @@ -11,30 +10,36 @@ import { SearchHelper } from "@/UI/Routing/SearchHelper"; export class EnvironmentHandlerImpl implements EnvironmentHandler { constructor( - private readonly history: History, + private readonly useLocation: () => Location, + private readonly navigate: (pathname: string) => void, private readonly routeManager: RouteManager ) {} - public set(environmentId: string): void { - const params = new URLSearchParams(this.history.location.search); + set(location: Location, environmentId: string): void { + const { pathname, search } = location; + const params = new URLSearchParams(search); if (params.get("env") !== environmentId) { params.set("env", environmentId); - this.history.push({ - pathname: this.routeManager.getRelatedUrlWithoutParams( - this.history.location.pathname - ), - search: `?${params}`, - }); + this.navigate( + this.routeManager.getRelatedUrlWithoutParams(pathname) + `?${params}` + ); } } - public useSelected(): FlatEnvironment | undefined { + useId(): string { + const environment = this.useSelected(); + if (typeof environment === "undefined") { + throw new Error("environment required but missing"); + } + return environment.id; + } + + useSelected(): FlatEnvironment | undefined { /* eslint-disable-next-line react-hooks/rules-of-hooks */ const allEnvironments = useStoreState( (state) => state.environments.environments ); - /* eslint-disable-next-line react-hooks/rules-of-hooks */ - const { search } = useLocation(); + const { search } = this.useLocation(); return this.determineSelected(allEnvironments, search); } diff --git a/src/UI/Pages/CompileDetails/CompileDetails.test.tsx b/src/UI/Pages/CompileDetails/CompileDetails.test.tsx index a47ace2b6..2826cfd4e 100644 --- a/src/UI/Pages/CompileDetails/CompileDetails.test.tsx +++ b/src/UI/Pages/CompileDetails/CompileDetails.test.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { MemoryRouter } from "react-router-dom"; import { render, screen } from "@testing-library/react"; import { StoreProvider } from "easy-peasy"; import { Either } from "@/Core"; @@ -12,6 +13,7 @@ import { import { CompileDetailsData, DeferredApiHelper, + dependencies, DynamicQueryManagerResolver, StaticScheduler, } from "@/Test"; @@ -28,8 +30,7 @@ function setup() { new CompileDetailsQueryManager( apiHelper, new CompileDetailsStateHelper(store), - scheduler, - "environment" + scheduler ), ]) ); @@ -40,11 +41,15 @@ function setup() { ); const component = ( - - - - - + + + + + + + ); return { component, apiHelper, scheduler }; diff --git a/src/UI/Pages/CompileDetails/Page.tsx b/src/UI/Pages/CompileDetails/Page.tsx index 3fb09a25a..c66466d39 100644 --- a/src/UI/Pages/CompileDetails/Page.tsx +++ b/src/UI/Pages/CompileDetails/Page.tsx @@ -1,12 +1,11 @@ import React from "react"; -import { useParams } from "react-router-dom"; -import { RouteParams } from "@/Core"; import { PageSectionWithTitle } from "@/UI/Components"; +import { useRouteParams } from "@/UI/Routing"; import { words } from "@/UI/words"; import { CompileDetails } from "./CompileDetails"; export const Page: React.FC = () => { - const { id } = useParams>(); + const { id } = useRouteParams<"CompileDetails">(); return ( diff --git a/src/UI/Pages/CompileReports/CompileReports.test.tsx b/src/UI/Pages/CompileReports/CompileReports.test.tsx index 12fba5a2d..ebcbe6d16 100644 --- a/src/UI/Pages/CompileReports/CompileReports.test.tsx +++ b/src/UI/Pages/CompileReports/CompileReports.test.tsx @@ -30,8 +30,7 @@ function setup() { new CompileReportsQueryManager( apiHelper, new CompileReportsStateHelper(store, "environment"), - scheduler, - "environment" + scheduler ), ]) ); diff --git a/src/UI/Pages/CreateEnvironment/CreateEnvironmentForm.test.tsx b/src/UI/Pages/CreateEnvironment/CreateEnvironmentForm.test.tsx index dc19ef713..03058dd80 100644 --- a/src/UI/Pages/CreateEnvironment/CreateEnvironmentForm.test.tsx +++ b/src/UI/Pages/CreateEnvironment/CreateEnvironmentForm.test.tsx @@ -16,6 +16,7 @@ import { } from "@/Data"; import { DeferredApiHelper, + dependencies, DynamicCommandManagerResolver, DynamicQueryManagerResolver, Project, @@ -44,9 +45,11 @@ function setup() { ); const component = ( - + - + diff --git a/src/UI/Pages/CreateInstance/CreateInstance.tsx b/src/UI/Pages/CreateInstance/CreateInstance.tsx index d5b92ea50..646adf430 100644 --- a/src/UI/Pages/CreateInstance/CreateInstance.tsx +++ b/src/UI/Pages/CreateInstance/CreateInstance.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useContext, useState } from "react"; -import { useHistory } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; import { Alert, AlertActionCloseButton, @@ -24,13 +24,13 @@ export const CreateInstance: React.FC<{ serviceEntity: ServiceModel }> = ({ useContext(DependencyContext); const [errorMessage, setErrorMessage] = useState(""); const isHalted = environmentModifier.useIsHalted(); - const history = useHistory(); + const navigate = useNavigate(); const url = `${routeManager.getUrl("Inventory", { service: serviceEntity.name, })}?env=${serviceEntity.environment}`; const handleRedirect = useCallback( - () => history.push(url), - [history] /* eslint-disable-line react-hooks/exhaustive-deps */ + () => navigate(url), + [navigate] /* eslint-disable-line react-hooks/exhaustive-deps */ ); const trigger = commandResolver.getTrigger<"CreateInstance">({ diff --git a/src/UI/Pages/CreateInstance/Page.tsx b/src/UI/Pages/CreateInstance/Page.tsx index 2c32a0958..0d74f14c4 100644 --- a/src/UI/Pages/CreateInstance/Page.tsx +++ b/src/UI/Pages/CreateInstance/Page.tsx @@ -1,8 +1,8 @@ import React, { useContext } from "react"; -import { useParams } from "react-router-dom"; -import { RemoteData, RouteParams } from "@/Core"; +import { RemoteData } from "@/Core"; import { PageSectionWithTitle, ErrorView, LoadingView } from "@/UI/Components"; import { DependencyContext } from "@/UI/Dependency"; +import { useRouteParams } from "@/UI/Routing"; import { words } from "@/UI/words"; import { CreateInstance } from "./CreateInstance"; @@ -16,7 +16,7 @@ const PageWrapper: React.FC = ({ children, ...props }) => ( ); export const Page: React.FC = () => { - const { service: serviceName } = useParams>(); + const { service: serviceName } = useRouteParams<"CreateInstance">(); const { queryResolver } = useContext(DependencyContext); const [data, retry] = queryResolver.useContinuous<"GetService">({ diff --git a/src/UI/Pages/Diagnose/Diagnose.stories.tsx b/src/UI/Pages/Diagnose/Diagnose.stories.tsx index e3ecd7fca..91bfc1583 100644 --- a/src/UI/Pages/Diagnose/Diagnose.stories.tsx +++ b/src/UI/Pages/Diagnose/Diagnose.stories.tsx @@ -27,7 +27,7 @@ export default { const Template: React.FC<{ diagnostics: RawDiagnostics }> = ({ diagnostics, }) => { - const { service_instance_id, environment } = InstanceLog.a; + const { service_instance_id } = InstanceLog.a; const store = getStoreInstance(); const queryResolver = new QueryResolverImpl( new DynamicQueryManagerResolver([ @@ -37,8 +37,7 @@ const Template: React.FC<{ diagnostics: RawDiagnostics }> = ({ data: { data: diagnostics }, }), new DiagnosticsStateHelper(store), - new StaticScheduler(), - environment + new StaticScheduler() ), ]) ); diff --git a/src/UI/Pages/Diagnose/Diagnose.test.tsx b/src/UI/Pages/Diagnose/Diagnose.test.tsx index c47b30729..d1bf6abf2 100644 --- a/src/UI/Pages/Diagnose/Diagnose.test.tsx +++ b/src/UI/Pages/Diagnose/Diagnose.test.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { MemoryRouter } from "react-router-dom"; import { render, screen } from "@testing-library/react"; import { StoreProvider } from "easy-peasy"; import { Either } from "@/Core"; @@ -28,22 +29,23 @@ function setup() { new DiagnosticsQueryManager( apiHelper, new DiagnosticsStateHelper(store), - scheduler, - "environment" + scheduler ), ]) ); dependencies.urlManager.setEnvironment("environment"); const component = ( - - - - - + + + + + + + ); return { component, apiHelper, scheduler }; diff --git a/src/UI/Pages/Diagnose/Page.tsx b/src/UI/Pages/Diagnose/Page.tsx index 923986ca8..2eb1edb6a 100644 --- a/src/UI/Pages/Diagnose/Page.tsx +++ b/src/UI/Pages/Diagnose/Page.tsx @@ -1,7 +1,6 @@ import React from "react"; -import { useParams } from "react-router-dom"; -import { RouteParams } from "@/Core"; import { PageSectionWithTitle, ServiceProvider } from "@/UI/Components"; +import { useRouteParams } from "@/UI/Routing"; import { words } from "@/UI/words"; import { Diagnose } from "./Diagnose"; @@ -12,8 +11,7 @@ const Wrapper: React.FC = ({ children, ...props }) => ( ); export const Page: React.FC = () => { - const { service: serviceName, instance } = - useParams>(); + const { service: serviceName, instance } = useRouteParams<"Diagnose">(); return ( = ({ serviceEntity, instance }) => { ); const isHalted = environmentModifier.useIsHalted(); const [errorMessage, setErrorMessage] = useState(""); - const history = useHistory(); + const navigate = useNavigate(); const url = `${routeManager.getUrl("Inventory", { service: serviceEntity.name, })}?env=${serviceEntity.environment}`; const handleRedirect = useCallback( - () => history.push(url), - [history] /* eslint-disable-line react-hooks/exhaustive-deps */ + () => navigate(url), + [navigate] /* eslint-disable-line react-hooks/exhaustive-deps */ ); const attributeInputConverter = new AttributeInputConverterImpl(); const currentAttributes = diff --git a/src/UI/Pages/EditInstance/EditInstancePage.test.tsx b/src/UI/Pages/EditInstance/EditInstancePage.test.tsx index 5d0f0b523..91981849e 100644 --- a/src/UI/Pages/EditInstance/EditInstancePage.test.tsx +++ b/src/UI/Pages/EditInstance/EditInstancePage.test.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { MemoryRouter } from "react-router-dom"; import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { StoreProvider } from "easy-peasy"; @@ -10,7 +11,6 @@ import { ServiceInstanceQueryManager, TriggerInstanceUpdateCommandManager, AttributeResultConverterImpl, - BaseApiHelper, CommandResolverImpl, } from "@/Data"; import { @@ -24,7 +24,6 @@ import { dependencies, } from "@/Test"; import { DependencyProvider } from "@/UI/Dependency"; - import { EditInstancePage } from "./EditInstancePage"; function setup() { @@ -36,14 +35,13 @@ function setup() { new ServiceInstanceQueryManager( apiHelper, new ServiceInstanceStateHelper(store), - scheduler, - "environment" + scheduler ), ]) ); const commandManager = new TriggerInstanceUpdateCommandManager( - new BaseApiHelper(), + apiHelper, new AttributeResultConverterImpl(), Service.a.environment ); @@ -52,21 +50,23 @@ function setup() { ); const component = ( - - - - - + + + + + + + ); return { component, apiHelper, scheduler }; @@ -90,6 +90,7 @@ test("Edit Instance View shows failed table", async () => { test("EditInstance View shows success form", async () => { const { component, apiHelper } = setup(); render(component); + const { service_entity, id, version } = ServiceInstance.nestedEditable; expect( await screen.findByRole("generic", { name: "EditInstance-Loading" }) @@ -100,14 +101,23 @@ test("EditInstance View shows success form", async () => { expect( await screen.findByRole("generic", { name: "EditInstance-Success" }) ).toBeInTheDocument(); - const bandwidthField = await screen.findByText("bandwidth"); + + const bandwidthField = screen.getByText("bandwidth"); expect(bandwidthField).toBeVisible(); + userEvent.type(bandwidthField, "2"); - userEvent.click(await screen.findByText("Confirm")); - expect(fetchMock.mock.calls).toHaveLength(1); - const [, requestInit] = fetchMock.mock.calls[0]; - expect(requestInit?.body).toBeTruthy(); - expect( - JSON.parse(requestInit?.body as string)["attributes"]["bandwidth"] - ).toEqual("2"); + userEvent.click(screen.getByText("Confirm")); + + expect(apiHelper.pendingRequests).toHaveLength(1); + expect(apiHelper.pendingRequests[0]).toEqual({ + method: "PATCH", + url: `/lsm/v1/service_inventory/${service_entity}/${id}?current_version=${version}`, + body: { + attributes: { + bandwidth: "2", + circuits: [{}], + }, + }, + environment: "environment_id_a", + }); }); diff --git a/src/UI/Pages/EditInstance/Page.tsx b/src/UI/Pages/EditInstance/Page.tsx index c6af66826..7011f7e33 100644 --- a/src/UI/Pages/EditInstance/Page.tsx +++ b/src/UI/Pages/EditInstance/Page.tsx @@ -1,7 +1,6 @@ import React from "react"; -import { useParams } from "react-router-dom"; -import { RouteParams } from "@/Core"; import { PageSectionWithTitle, ServiceProvider } from "@/UI/Components"; +import { useRouteParams } from "@/UI/Routing"; import { words } from "@/UI/words"; import { EditInstancePage } from "./EditInstancePage"; @@ -15,8 +14,7 @@ const PageWrapper: React.FC = ({ children, ...props }) => ( ); export const Page: React.FC = () => { - const { service: serviceName, instance } = - useParams>(); + const { service: serviceName, instance } = useRouteParams<"EditInstance">(); return ( = ({ events }) => { }, }), new EventsStateHelper(store), - scheduler, - InstanceLog.a.environment + scheduler ), ]) ); diff --git a/src/UI/Pages/Events/Events.test.tsx b/src/UI/Pages/Events/Events.test.tsx index a2aea7a72..fc7c419a5 100644 --- a/src/UI/Pages/Events/Events.test.tsx +++ b/src/UI/Pages/Events/Events.test.tsx @@ -11,6 +11,7 @@ import { } from "@/Data"; import { DeferredApiHelper, + dependencies, DynamicQueryManagerResolver, Service, StaticScheduler, @@ -22,26 +23,19 @@ function setup() { const store = getStoreInstance(); const scheduler = new StaticScheduler(); const apiHelper = new DeferredApiHelper(); - const instance = { - id: "4a4a6d14-8cd0-4a16-bc38-4b768eb004e3", - service_entity: "vlan-assignment", - version: 4, - environment: "34a961ba-db3c-486e-8d85-1438d8e88909", - }; const queryResolver = new QueryResolverImpl( new DynamicQueryManagerResolver([ new EventsQueryManager( apiHelper, new EventsStateHelper(store), - scheduler, - instance.environment + scheduler ), ]) ); const component = ( - + ( ); const Wrapped: React.FC<{ service: ServiceModel }> = ({ service }) => { - const { instance } = useParams>(); + const { instance } = useRouteParams<"Events">(); return ( @@ -21,7 +21,7 @@ const Wrapped: React.FC<{ service: ServiceModel }> = ({ service }) => { }; export const Page: React.FC = () => { - const { service: serviceName } = useParams>(); + const { service: serviceName } = useRouteParams<"Events">(); return ( { +export const NotFound: React.FC = () => { + useDocumentTitle("404 Page Not Found"); const { routeManager } = React.useContext(DependencyContext); return ( diff --git a/src/UI/Pages/PageRouter.tsx b/src/UI/Pages/PageRouter.tsx deleted file mode 100644 index 179009805..000000000 --- a/src/UI/Pages/PageRouter.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { useContext } from "react"; -import { Route, Switch } from "react-router-dom"; -import { useDocumentTitle } from "@/UI/Routing"; -import { DependencyContext } from ".."; -import { NotFound } from "./NotFound"; -import { PrimaryPageManager } from "./PrimaryPageManager"; - -export const PageRouter: React.FC = () => { - const { routeManager } = useContext(DependencyContext); - const pages = new PrimaryPageManager( - routeManager.getRouteDictionary() - ).getPages(); - - return ( - - {pages.map(({ path, label, kind, component }) => ( - - ))} - - - ); -}; - -const PageNotFound = ({ title }: { title: string }) => { - useDocumentTitle(title); - return ; -}; diff --git a/src/UI/Pages/ResourceDetails/Page.tsx b/src/UI/Pages/ResourceDetails/Page.tsx index fe635212c..c5e2a8526 100644 --- a/src/UI/Pages/ResourceDetails/Page.tsx +++ b/src/UI/Pages/ResourceDetails/Page.tsx @@ -1,12 +1,11 @@ import React from "react"; -import { useParams } from "react-router-dom"; -import { RouteParams } from "@/Core"; import { PageSectionWithTitle } from "@/UI/Components"; +import { useRouteParams } from "@/UI/Routing"; import { words } from "@/UI/words"; import { ResourceDetailsView } from "./ResourceDetailsView"; export const Page: React.FC = () => { - const { resourceId } = useParams>(); + const { resourceId } = useRouteParams<"ResourceDetails">(); return ( diff --git a/src/UI/Pages/ResourceDetails/ResourceDetailsView.test.tsx b/src/UI/Pages/ResourceDetails/ResourceDetailsView.test.tsx index d27c671f9..069642905 100644 --- a/src/UI/Pages/ResourceDetails/ResourceDetailsView.test.tsx +++ b/src/UI/Pages/ResourceDetails/ResourceDetailsView.test.tsx @@ -30,8 +30,7 @@ function setup() { new ResourceDetailsQueryManager( apiHelper, new ResourceDetailsStateHelper(store), - scheduler, - environment + scheduler ), ]) ); diff --git a/src/UI/Pages/ResourceDetails/Tabs/HistoryTab/ResourceHistoryView.test.tsx b/src/UI/Pages/ResourceDetails/Tabs/HistoryTab/ResourceHistoryView.test.tsx index f2a156965..f814b91e0 100644 --- a/src/UI/Pages/ResourceDetails/Tabs/HistoryTab/ResourceHistoryView.test.tsx +++ b/src/UI/Pages/ResourceDetails/Tabs/HistoryTab/ResourceHistoryView.test.tsx @@ -24,14 +24,12 @@ function setup() { const store = getStoreInstance(); const scheduler = new StaticScheduler(); const apiHelper = new DeferredApiHelper(); - const environment = "34a961ba-db3c-486e-8d85-1438d8e88909"; const queryResolver = new QueryResolverImpl( new DynamicQueryManagerResolver([ new ResourceHistoryQueryManager( apiHelper, new ResourceHistoryStateHelper(store), - scheduler, - environment + scheduler ), ]) ); diff --git a/src/UI/Pages/ResourceDetails/Tabs/LogTab/View.spec.tsx b/src/UI/Pages/ResourceDetails/Tabs/LogTab/View.spec.tsx index 04bfefe8f..360f92098 100644 --- a/src/UI/Pages/ResourceDetails/Tabs/LogTab/View.spec.tsx +++ b/src/UI/Pages/ResourceDetails/Tabs/LogTab/View.spec.tsx @@ -12,24 +12,22 @@ import { } from "@/Data"; import { DynamicQueryManagerResolver, - Service, StaticScheduler, ResourceLogs, DeferredApiHelper, + dependencies, } from "@/Test"; import { DependencyProvider } from "@/UI/Dependency"; import { View } from "./View"; function setup() { const store = getStoreInstance(); - const environment = Service.a.environment; const apiHelper = new DeferredApiHelper(); const resourceLogsStateHelper = new ResourceLogsStateHelper(store); const resourceLogsQueryManager = new ResourceLogsQueryManager( apiHelper, resourceLogsStateHelper, - new StaticScheduler(), - environment + new StaticScheduler() ); const queryResolver = new QueryResolverImpl( @@ -38,7 +36,7 @@ function setup() { const component = ( - + diff --git a/src/UI/Pages/Resources/Page.test.tsx b/src/UI/Pages/Resources/Page.test.tsx index 9686d490c..a58b0f9b0 100644 --- a/src/UI/Pages/Resources/Page.test.tsx +++ b/src/UI/Pages/Resources/Page.test.tsx @@ -30,8 +30,7 @@ function setup() { new ResourcesQueryManager( apiHelper, new ResourcesStateHelper(store, environment), - scheduler, - environment + scheduler ), ]) ); diff --git a/src/UI/Pages/ServiceCatalog/Page.test.tsx b/src/UI/Pages/ServiceCatalog/Page.test.tsx index 24e290e6b..2c2a70852 100644 --- a/src/UI/Pages/ServiceCatalog/Page.test.tsx +++ b/src/UI/Pages/ServiceCatalog/Page.test.tsx @@ -1,8 +1,9 @@ import React from "react"; -import { MemoryRouter } from "react-router-dom"; -import { fireEvent, render, screen } from "@testing-library/react"; +import { Link, MemoryRouter, useLocation, useNavigate } from "react-router-dom"; +import { act, fireEvent, render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; import { StoreProvider } from "easy-peasy"; -import { Either } from "@/Core"; +import { Either, RemoteData } from "@/Core"; import { QueryResolverImpl, ServicesQueryManager, @@ -17,12 +18,15 @@ import { dependencies, DynamicCommandManagerResolver, DynamicQueryManagerResolver, + Environment, Service, StaticScheduler, } from "@/Test"; -import { DependencyProvider } from "@/UI/Dependency"; +import { DependencyProvider, EnvironmentHandlerImpl } from "@/UI/Dependency"; import { Page } from "./Page"; +const [env1, env2] = Environment.filterable.map((env) => env.id); + function setup() { const store = getStoreInstance(); const scheduler = new StaticScheduler(); @@ -30,9 +34,8 @@ function setup() { const servicesHelper = new ServicesQueryManager( apiHelper, - new ServicesStateHelper(store, Service.a.environment), - scheduler, - Service.a.environment + new ServicesStateHelper(store, env1), + scheduler ); const queryResolver = new QueryResolverImpl( @@ -40,19 +43,43 @@ function setup() { ); const commandManager = new DeleteServiceCommandManager( new BaseApiHelper(), - Service.a.environment + env1 ); const commandResolver = new CommandResolverImpl( new DynamicCommandManagerResolver([commandManager]) ); + const environmentHandler = new EnvironmentHandlerImpl( + useLocation, + (...args) => useNavigate()(...args), + dependencies.routeManager + ); + + store.dispatch.environments.setEnvironments( + RemoteData.success(Environment.filterable) + ); + + const linkToEnv2 = ( + + + + ); + const component = ( - + + {linkToEnv2} @@ -141,3 +168,30 @@ test("ServiceCatalog removes service after deletion", async () => { await screen.findByRole("region", { name: "ServiceCatalog-Empty" }) ).toBeInTheDocument(); }); + +test("GIVEN ServiceCatalog WHEN new environment selected THEN new query is triggered", async () => { + const { component, apiHelper } = setup(); + render(component); + + expect(apiHelper.pendingRequests).toHaveLength(1); + expect(apiHelper.resolvedRequests).toHaveLength(0); + expect(apiHelper.pendingRequests[0]).toEqual({ + method: "GET", + url: "/lsm/v1/service_catalog?instance_summary=True", + environment: env1, + }); + + await act(async () => { + await apiHelper.resolve(Either.right({ data: [Service.a] })); + }); + + userEvent.click(screen.getByText("change environment")); + + expect(apiHelper.pendingRequests).toHaveLength(1); + expect(apiHelper.resolvedRequests).toHaveLength(1); + expect(apiHelper.pendingRequests[0]).toEqual({ + method: "GET", + url: "/lsm/v1/service_catalog?instance_summary=True", + environment: env2, + }); +}); diff --git a/src/UI/Pages/ServiceCatalog/Spec/CallbacksTab.spec.tsx b/src/UI/Pages/ServiceCatalog/Spec/CallbacksTab.spec.tsx index 8118ecb77..6c576714b 100644 --- a/src/UI/Pages/ServiceCatalog/Spec/CallbacksTab.spec.tsx +++ b/src/UI/Pages/ServiceCatalog/Spec/CallbacksTab.spec.tsx @@ -39,14 +39,12 @@ function setup() { const servicesQueryManager = new ServicesQueryManager( apiHelper, new ServicesStateHelper(store, environment), - scheduler, - environment + scheduler ); const callbacksStateHelper = new CallbacksStateHelper(store, environment); const callbacksQueryManager = new CallbacksQueryManager( apiHelper, - callbacksStateHelper, - environment + callbacksStateHelper ); const queryResolver = new QueryResolverImpl( diff --git a/src/UI/Pages/ServiceCatalog/Spec/CallbacksView.spec.tsx b/src/UI/Pages/ServiceCatalog/Spec/CallbacksView.spec.tsx index 627d7775f..a8b1c7f70 100644 --- a/src/UI/Pages/ServiceCatalog/Spec/CallbacksView.spec.tsx +++ b/src/UI/Pages/ServiceCatalog/Spec/CallbacksView.spec.tsx @@ -20,6 +20,7 @@ import { Service, Callback, DeferredApiHelper, + dependencies, } from "@/Test"; import { DependencyProvider } from "@/UI/Dependency"; import { CallbacksView } from "@/UI/Pages/ServiceCatalog/Tabs/Callbacks"; @@ -31,8 +32,7 @@ function setup() { const callbacksStateHelper = new CallbacksStateHelper(store, environment); const callbacksQueryManager = new CallbacksQueryManager( apiHelper, - callbacksStateHelper, - environment + callbacksStateHelper ); const queryResolver = new QueryResolverImpl( @@ -65,7 +65,9 @@ function setup() { const component = ( - + diff --git a/src/UI/Pages/ServiceCatalog/Spec/ConfigTab.spec.tsx b/src/UI/Pages/ServiceCatalog/Spec/ConfigTab.spec.tsx index 2647be396..40232531c 100644 --- a/src/UI/Pages/ServiceCatalog/Spec/ConfigTab.spec.tsx +++ b/src/UI/Pages/ServiceCatalog/Spec/ConfigTab.spec.tsx @@ -39,8 +39,7 @@ function setup() { const servicesHelper = new ServicesQueryManager( apiHelper, new ServicesStateHelper(store, Service.a.environment), - scheduler, - Service.a.environment + scheduler ); const serviceConfigStateHelper = new ServiceConfigStateHelper(store); const serviceConfigQueryManager = new ServiceConfigQueryManager( @@ -52,8 +51,7 @@ function setup() { new ServiceKeyMaker(), Service.a.environment ) - ), - Service.a.environment + ) ); // { data: Service.a.config } diff --git a/src/UI/Pages/ServiceInstanceHistory/Page.tsx b/src/UI/Pages/ServiceInstanceHistory/Page.tsx index 895fe7e00..26b334295 100644 --- a/src/UI/Pages/ServiceInstanceHistory/Page.tsx +++ b/src/UI/Pages/ServiceInstanceHistory/Page.tsx @@ -1,8 +1,8 @@ import React from "react"; -import { useParams } from "react-router-dom"; import { Card } from "@patternfly/react-core"; -import { RouteParams, ServiceModel } from "@/Core"; +import { ServiceModel } from "@/Core"; import { PageSectionWithTitle, ServiceProvider } from "@/UI/Components"; +import { useRouteParams } from "@/UI/Routing"; import { words } from "@/UI/words"; import { ServiceInstanceHistory } from "./ServiceInstanceHistory"; @@ -13,7 +13,7 @@ const Wrapper: React.FC = ({ children, ...props }) => ( ); const Wrapped: React.FC<{ service: ServiceModel }> = ({ service }) => { - const { instance } = useParams>(); + const { instance } = useRouteParams<"History">(); return ( @@ -22,7 +22,7 @@ const Wrapped: React.FC<{ service: ServiceModel }> = ({ service }) => { }; export const Page: React.FC = () => { - const { service: serviceName } = useParams>(); + const { service: serviceName } = useRouteParams<"History">(); return ( = ({ logs }) => { data: { data: logs }, }), new GetInstanceLogsStateHelper(store), - new StaticScheduler(), - Service.a.environment + new StaticScheduler() ), ]) ); diff --git a/src/UI/Pages/ServiceInstanceHistory/ServiceInstanceHistory.test.tsx b/src/UI/Pages/ServiceInstanceHistory/ServiceInstanceHistory.test.tsx index 3108b8547..5509c4ad6 100644 --- a/src/UI/Pages/ServiceInstanceHistory/ServiceInstanceHistory.test.tsx +++ b/src/UI/Pages/ServiceInstanceHistory/ServiceInstanceHistory.test.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { MemoryRouter } from "react-router-dom"; import { render, screen } from "@testing-library/react"; import { StoreProvider } from "easy-peasy"; import { @@ -8,6 +9,7 @@ import { getStoreInstance, } from "@/Data"; import { + dependencies, DynamicQueryManagerResolver, InstantApiHelper, Service, @@ -27,21 +29,22 @@ it("ServiceInstanceHistory renders", async () => { data: { data: [] }, }), new GetInstanceLogsStateHelper(store), - new StaticScheduler(), - Service.a.environment + new StaticScheduler() ), ]) ); render( - - - - - + + + + + + + ); expect( diff --git a/src/UI/Pages/ServiceInventory/InventoryTable.stories.tsx b/src/UI/Pages/ServiceInventory/InventoryTable.stories.tsx index c0bdcb441..a6a2e2b98 100644 --- a/src/UI/Pages/ServiceInventory/InventoryTable.stories.tsx +++ b/src/UI/Pages/ServiceInventory/InventoryTable.stories.tsx @@ -33,8 +33,7 @@ const Template: Story> = (args) => { data: { data: [] }, }), new InstanceResourcesStateHelper(store), - new StaticScheduler(), - "env" + new StaticScheduler() ), ]) ); diff --git a/src/UI/Pages/ServiceInventory/InventoryTable.test.tsx b/src/UI/Pages/ServiceInventory/InventoryTable.test.tsx index 20706e7d4..0d4ffc701 100644 --- a/src/UI/Pages/ServiceInventory/InventoryTable.test.tsx +++ b/src/UI/Pages/ServiceInventory/InventoryTable.test.tsx @@ -43,8 +43,7 @@ test("InventoryTable can be expanded", async () => { }, }), new InstanceResourcesStateHelper(store), - new StaticScheduler(), - "env" + new StaticScheduler() ), ]) ); @@ -90,8 +89,7 @@ test("ServiceInventory can show resources for instance", async () => { }, }), new InstanceResourcesStateHelper(store), - new StaticScheduler(), - "env" + new StaticScheduler() ), ]) ); diff --git a/src/UI/Pages/ServiceInventory/Page.tsx b/src/UI/Pages/ServiceInventory/Page.tsx index c9da8e5e9..57b05b163 100644 --- a/src/UI/Pages/ServiceInventory/Page.tsx +++ b/src/UI/Pages/ServiceInventory/Page.tsx @@ -1,13 +1,13 @@ import React from "react"; -import { useParams } from "react-router-dom"; -import { RouteParams, ServiceModel } from "@/Core"; +import { ServiceModel } from "@/Core"; import { ServiceProvider } from "@/UI/Components"; +import { useRouteParams } from "@/UI/Routing"; import { Chart } from "./Components"; import { ServiceInventory } from "./ServiceInventory"; import { Wrapper } from "./Wrapper"; export const Page: React.FC = () => { - const { service: serviceName } = useParams>(); + const { service: serviceName } = useRouteParams<"Inventory">(); return ( { }, }), new ServiceInstancesStateHelper(store, Service.a.environment), - scheduler, - Service.a.environment + scheduler ); const resourcesHelper = new InstanceResourcesQueryManager( apiHelper, new InstanceResourcesStateHelper(store), - scheduler, - Service.a.environment + scheduler ); const catchAllCommandManager = new MockCommandManager(); @@ -91,8 +89,7 @@ export const Failed: React.FC = () => { error: "fake error message", }), new ServiceInstancesStateHelper(store, Service.a.environment), - scheduler, - Service.a.environment + scheduler ); const queryResolver = new QueryResolverImpl( diff --git a/src/UI/Pages/ServiceInventory/ServiceInventory.test.tsx b/src/UI/Pages/ServiceInventory/ServiceInventory.test.tsx index 0e41b4876..56aad49ff 100644 --- a/src/UI/Pages/ServiceInventory/ServiceInventory.test.tsx +++ b/src/UI/Pages/ServiceInventory/ServiceInventory.test.tsx @@ -41,15 +41,13 @@ function setup(service = Service.a) { const serviceInstancesHelper = new ServiceInstancesQueryManager( apiHelper, new ServiceInstancesStateHelper(store, service.environment), - scheduler, - service.environment + scheduler ); const resourcesHelper = new InstanceResourcesQueryManager( apiHelper, new InstanceResourcesStateHelper(store), - scheduler, - service.environment + scheduler ); const queryResolver = new QueryResolverImpl( diff --git a/src/UI/Pages/ServiceInventory/ServiceInventory.tsx b/src/UI/Pages/ServiceInventory/ServiceInventory.tsx index 3af8c91d7..189cddd43 100644 --- a/src/UI/Pages/ServiceInventory/ServiceInventory.tsx +++ b/src/UI/Pages/ServiceInventory/ServiceInventory.tsx @@ -1,11 +1,5 @@ import React, { useContext, ReactElement } from "react"; -import { useParams } from "react-router-dom"; -import { - RemoteData, - ServiceModel, - ServiceInstanceParams, - RouteParams, -} from "@/Core"; +import { RemoteData, ServiceModel, ServiceInstanceParams } from "@/Core"; import { useUrlStateWithFilter, useUrlStateWithPageSize, @@ -20,6 +14,7 @@ import { ServiceProvider, } from "@/UI/Components"; import { DependencyContext } from "@/UI/Dependency"; +import { useRouteParams } from "@/UI/Routing"; import { words } from "@/UI/words"; import { Chart, TableControls } from "./Components"; import { TableProvider } from "./TableProvider"; @@ -31,7 +26,7 @@ const Wrapper: React.FC = ({ children, ...props }) => ( ); export const Page: React.FC = () => { - const { service: serviceName } = useParams>(); + const { service: serviceName } = useRouteParams<"Inventory">(); return ( + = (args) => { data: { data: [] }, }), new InstanceResourcesStateHelper(store), - new StaticScheduler(), - Service.a.environment + new StaticScheduler() ), ]) ); diff --git a/src/UI/Pages/ServiceInventory/Tabs/ConfigTab.test.tsx b/src/UI/Pages/ServiceInventory/Tabs/ConfigTab.test.tsx index ae94db565..3ae2cc41b 100644 --- a/src/UI/Pages/ServiceInventory/Tabs/ConfigTab.test.tsx +++ b/src/UI/Pages/ServiceInventory/Tabs/ConfigTab.test.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { MemoryRouter } from "react-router-dom"; import { render, screen, act } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { StoreProvider } from "easy-peasy"; @@ -22,6 +23,7 @@ import { } from "@/Data"; import { DeferredApiHelper, + dependencies, DynamicCommandManagerResolver, DynamicQueryManagerResolver, InstantApiHelper, @@ -67,8 +69,7 @@ function setup( serviceKeyMaker, Service.a.environment ) - ), - Service.a.environment + ) ); const queryResolver = new QueryResolverImpl( @@ -86,17 +87,20 @@ function setup( ); const component = ( - - - - - + + + + + + + ); return { diff --git a/src/UI/Pages/ServiceInventory/Tabs/ResourcesTab.stories.tsx b/src/UI/Pages/ServiceInventory/Tabs/ResourcesTab.stories.tsx index 8cf193b6c..d166410bc 100644 --- a/src/UI/Pages/ServiceInventory/Tabs/ResourcesTab.stories.tsx +++ b/src/UI/Pages/ServiceInventory/Tabs/ResourcesTab.stories.tsx @@ -35,8 +35,7 @@ const Template: React.FC<{ new InstanceResourcesQueryManager( new InstantApiHelper(outcome), new InstanceResourcesStateHelper(store), - new StaticScheduler(), - "34a961ba-db3c-486e-8d85-1438d8e88909" + new StaticScheduler() ), ]) ); diff --git a/src/UI/Pages/ServiceInventory/Tabs/ResourcesTab.test.tsx b/src/UI/Pages/ServiceInventory/Tabs/ResourcesTab.test.tsx index 50fdb1028..f743ca0ee 100644 --- a/src/UI/Pages/ServiceInventory/Tabs/ResourcesTab.test.tsx +++ b/src/UI/Pages/ServiceInventory/Tabs/ResourcesTab.test.tsx @@ -27,8 +27,7 @@ function setup() { new InstanceResourcesQueryManager( apiHelper, new InstanceResourcesStateHelper(store), - scheduler, - "34a961ba-db3c-486e-8d85-1438d8e88909" + scheduler ), ]) ); diff --git a/src/UI/Pages/Settings/Tabs/Configuration/Tab.test.tsx b/src/UI/Pages/Settings/Tabs/Configuration/Tab.test.tsx index 8dbb23514..5d8d282b7 100644 --- a/src/UI/Pages/Settings/Tabs/Configuration/Tab.test.tsx +++ b/src/UI/Pages/Settings/Tabs/Configuration/Tab.test.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { MemoryRouter } from "react-router-dom"; import { act, render, screen, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { StoreProvider } from "easy-peasy"; @@ -16,6 +17,7 @@ import { } from "@/Data"; import { DeferredApiHelper, + dependencies, DynamicCommandManagerResolver, DynamicQueryManagerResolver, EnvironmentSettings, @@ -30,8 +32,7 @@ function setup() { new DynamicQueryManagerResolver([ new GetEnvironmentSettingsQueryManager( apiHelper, - new GetEnvironmentSettingsStateHelper(store, "env"), - "env" + new GetEnvironmentSettingsStateHelper(store, "env") ), ]) ); @@ -56,12 +57,17 @@ function setup() { ), ]) ); + const component = ( - - - - - + + + + + + + ); return { component, apiHelper }; diff --git a/src/UI/Pages/index.ts b/src/UI/Pages/index.ts index 09d2f5f6d..2deb38334 100644 --- a/src/UI/Pages/index.ts +++ b/src/UI/Pages/index.ts @@ -6,7 +6,6 @@ export * from "./Resources"; export * from "./ServiceCatalog"; export * from "./ServiceInstanceHistory"; export * from "./ServiceInventory"; -export * from "./PageRouter"; export * from "./CompileReports"; export * from "./Home"; export * from "./CreateEnvironment"; diff --git a/src/UI/Root/AppLayout/EnvironmentControls.test.tsx b/src/UI/Root/AppLayout/EnvironmentControls.test.tsx index 0d5ba6b5b..f8f8f6ac1 100644 --- a/src/UI/Root/AppLayout/EnvironmentControls.test.tsx +++ b/src/UI/Root/AppLayout/EnvironmentControls.test.tsx @@ -24,7 +24,6 @@ import { StaticScheduler, } from "@/Test"; import { DependencyProvider } from "@/UI/Dependency"; -import { EnvironmentModifierImpl } from "@/UI/Dependency/EnvironmentModifier"; import { EnvironmentControls } from "./EnvironmentControls"; function setup() { @@ -38,8 +37,7 @@ function setup() { const environmentDetailsQueryManager = new EnvironmentDetailsQueryManager( apiHelper, environmentDetailsStateHelper, - scheduler, - EnvironmentDetails.a.id + scheduler ); const queryResolver = new QueryResolverImpl( @@ -76,13 +74,12 @@ function setup() { ); const component = ( - + diff --git a/src/UI/Root/AppLayout/Toolbar/EnvSelector/EnvSelectorWithData.tsx b/src/UI/Root/AppLayout/Toolbar/EnvSelector/EnvSelectorWithData.tsx index 03b738e33..8e0a16eae 100644 --- a/src/UI/Root/AppLayout/Toolbar/EnvSelector/EnvSelectorWithData.tsx +++ b/src/UI/Root/AppLayout/Toolbar/EnvSelector/EnvSelectorWithData.tsx @@ -1,5 +1,5 @@ import React, { useContext } from "react"; -import { Redirect } from "react-router"; +import { Navigate } from "react-router-dom"; import { EmptyState, EmptyStateIcon, @@ -57,7 +57,7 @@ export const EnvSelectorWithData: React.FC = ({ /> ); } else { - return ; + return ; } }, }, diff --git a/src/UI/Root/AppLayout/Toolbar/EnvSelector/EnvironmentSelector.test.tsx b/src/UI/Root/AppLayout/Toolbar/EnvSelector/EnvironmentSelector.test.tsx index da155bf3f..78ea29a87 100644 --- a/src/UI/Root/AppLayout/Toolbar/EnvSelector/EnvironmentSelector.test.tsx +++ b/src/UI/Root/AppLayout/Toolbar/EnvSelector/EnvironmentSelector.test.tsx @@ -12,7 +12,7 @@ import { EnvSelectorWithData as EnvironmentSelector } from "./EnvSelectorWithDat test("GIVEN EnvironmentSelector WHEN there are no environments THEN redirects", async () => { const history = createMemoryHistory(); render( - + { + const location = useLocation(); const { environmentHandler, queryResolver } = useContext(DependencyContext); const onSelectEnvironment = (item: EnvironmentSelectorItem) => { - environmentHandler.set(item.environmentId); + environmentHandler.set(location, item.environmentId); }; const [data] = queryResolver.useOneTime<"GetEnvironments">({ kind: "GetEnvironments", diff --git a/src/UI/Root/EnvSpecificContentLayout.tsx b/src/UI/Root/EnvSpecificContentLayout.tsx index 53521dae6..4b2c6ea91 100644 --- a/src/UI/Root/EnvSpecificContentLayout.tsx +++ b/src/UI/Root/EnvSpecificContentLayout.tsx @@ -1,10 +1,10 @@ -import React from "react"; +import React, { useContext } from "react"; import { Page, PageSidebar } from "@patternfly/react-core"; -import { EnvironmentProvider } from "@/UI/Components"; -import { DependencyResolver } from "@/UI/Dependency"; -import { PageRouter } from "@/UI/Pages"; +import { ErrorView } from "@/UI/Components/ErrorView"; +import { DependencyResolver, DependencyContext } from "@/UI/Dependency"; import { AppWrapper } from "@/UI/Root/AppLayout/AppWrapper"; import { Sidebar } from "@/UI/Root/AppLayout/Sidebar"; +import { words } from "@/UI/words"; import { PageBreadcrumbs } from "./PageBreadcrumbs"; interface Props { @@ -15,7 +15,10 @@ interface Props { export const EnvSpecificContentLayout: React.FC = ({ keycloak, shouldUseAuth, + children, }) => { + const { environmentHandler } = useContext(DependencyContext); + const environment = environmentHandler.useSelected(); const [isNavOpen, setIsNavOpen] = React.useState(true); const [isMobileView, setIsMobileView] = React.useState(false); const [isNavOpenMobile, setIsNavOpenMobile] = React.useState(false); @@ -37,29 +40,28 @@ export const EnvSpecificContentLayout: React.FC = ({ onToggle={onToggle} withEnv > - <>{children}} - Dependant={({ environment }) => ( - <> - - } - onPageResize={onPageResize} - sidebar={ - } - isNavOpen={isMobileView ? isNavOpenMobile : isNavOpen} - theme="dark" - /> - } - style={{ gridArea: "mainpage", overflow: "hidden" }} - > - - - - )} - /> + {!environment ? ( + + ) : ( + <> + + } + onPageResize={onPageResize} + sidebar={ + } + isNavOpen={isMobileView ? isNavOpenMobile : isNavOpen} + theme="dark" + /> + } + style={{ gridArea: "mainpage", overflow: "hidden" }} + > + {children} + + + )} ); }; diff --git a/src/UI/Root/Navigation.tsx b/src/UI/Root/Navigation.tsx index 7cd899666..8212d231d 100644 --- a/src/UI/Root/Navigation.tsx +++ b/src/UI/Root/Navigation.tsx @@ -45,19 +45,21 @@ interface Link { } const Item: React.FC = ({ id, label, url, external }) => ( - + {external ? ( {label} ) : ( + "pf-c-nav__link" + (isActive ? " pf-m-current" : "") + } + end > {label} diff --git a/src/UI/Pages/PrimaryPageManager.ts b/src/UI/Root/PrimaryPageManager.tsx similarity index 50% rename from src/UI/Pages/PrimaryPageManager.ts rename to src/UI/Root/PrimaryPageManager.tsx index d42d74065..c889b4116 100644 --- a/src/UI/Pages/PrimaryPageManager.ts +++ b/src/UI/Root/PrimaryPageManager.tsx @@ -1,4 +1,6 @@ +import React from "react"; import { PageManager, Page, RouteDictionary } from "@/Core"; +import { CompileDetailsPage } from "@/UI/Pages/CompileDetails"; import { CompileReportsPage } from "@/UI/Pages/CompileReports"; import { CreateInstancePage } from "@/UI/Pages/CreateInstance"; import { DiagnosePage } from "@/UI/Pages/Diagnose"; @@ -10,31 +12,39 @@ import { ServiceCatalogPage } from "@/UI/Pages/ServiceCatalog"; import { ServiceInstanceHistoryPage } from "@/UI/Pages/ServiceInstanceHistory"; import { ServiceInventoryPage } from "@/UI/Pages/ServiceInventory"; import { SettingsPage } from "@/UI/Pages/Settings"; -import { CompileDetailsPage } from "./CompileDetails"; export class PrimaryPageManager implements PageManager { constructor(private readonly routeDictionary: RouteDictionary) {} getPages(): Page[] { return [ - { ...this.routeDictionary.Catalog, component: ServiceCatalogPage }, - { ...this.routeDictionary.Inventory, component: ServiceInventoryPage }, - { ...this.routeDictionary.CreateInstance, component: CreateInstancePage }, - { ...this.routeDictionary.EditInstance, component: EditInstancePage }, + { ...this.routeDictionary.Catalog, element: }, + { ...this.routeDictionary.Inventory, element: }, + { + ...this.routeDictionary.CreateInstance, + element: , + }, + { ...this.routeDictionary.EditInstance, element: }, { ...this.routeDictionary.History, - component: ServiceInstanceHistoryPage, + element: , + }, + { ...this.routeDictionary.Diagnose, element: }, + { ...this.routeDictionary.Events, element: }, + { ...this.routeDictionary.Resources, element: }, + { + ...this.routeDictionary.CompileReports, + element: , + }, + { + ...this.routeDictionary.CompileDetails, + element: , }, - { ...this.routeDictionary.Diagnose, component: DiagnosePage }, - { ...this.routeDictionary.Events, component: EventsPage }, - { ...this.routeDictionary.Resources, component: ResourcesPage }, - { ...this.routeDictionary.CompileReports, component: CompileReportsPage }, - { ...this.routeDictionary.CompileDetails, component: CompileDetailsPage }, { ...this.routeDictionary.ResourceDetails, - component: ResourceDetailsPage, + element: , }, - { ...this.routeDictionary.Settings, component: SettingsPage }, + { ...this.routeDictionary.Settings, element: }, ]; } } diff --git a/src/UI/Root/app.test.tsx b/src/UI/Root/app.test.tsx index ec23fe557..461ec079e 100644 --- a/src/UI/Root/app.test.tsx +++ b/src/UI/Root/app.test.tsx @@ -1,8 +1,8 @@ import React from "react"; import { MemoryRouter } from "react-router"; +import { useLocation, useNavigate } from "react-router-dom"; import { act, render, screen } from "@testing-library/react"; import { StoreProvider } from "easy-peasy"; -import { createMemoryHistory } from "history"; import Keycloak from "keycloak-js"; import { Either } from "@/Core"; import { @@ -43,7 +43,8 @@ function setup() { ); const environmentHandler = new EnvironmentHandlerImpl( - createMemoryHistory(), + useLocation, + (...args) => useNavigate()(...args), dependencies.routeManager ); diff --git a/src/UI/Root/app.tsx b/src/UI/Root/app.tsx index b04269cf2..0c6dd5c6e 100644 --- a/src/UI/Root/app.tsx +++ b/src/UI/Root/app.tsx @@ -1,15 +1,16 @@ import React, { useContext } from "react"; import "@patternfly/react-core/dist/styles/base.css"; import { KeycloakProvider } from "react-keycloak"; -import { Route, Switch } from "react-router-dom"; +import { Route, Routes } from "react-router-dom"; import { Spinner, Bullseye } from "@patternfly/react-core"; import { KeycloakInitOptions } from "keycloak-js"; import { DependencyContext } from "@/UI/Dependency"; -import { Home, CreateEnvironmentPage } from "@/UI/Pages"; +import { Home, CreateEnvironmentPage, NotFound } from "@/UI/Pages"; import { SearchSanitizer } from "@/UI/Routing"; import { BaseLayout } from "./BaseLayout"; import { EnvSpecificContentLayout } from "./EnvSpecificContentLayout"; import { Initializer } from "./Initializer"; +import { PrimaryPageManager } from "./PrimaryPageManager"; interface AuthProps { keycloak: Keycloak.KeycloakInstance; @@ -18,31 +19,57 @@ interface AuthProps { export const App: React.FC = ({ keycloak, shouldUseAuth }) => { const { routeManager } = useContext(DependencyContext); + const pages = new PrimaryPageManager( + routeManager.getRouteDictionary() + ).getPages(); + return ( - - - - - - + + + + + } + /> - - - - - - + + + } + /> + {pages.map(({ path, kind, element }) => ( + + {element} + + } + key={kind} /> - - + ))} + + + + } + /> + diff --git a/src/UI/Routing/Crumb.ts b/src/UI/Routing/Crumb.ts index 2019bcbb3..5109c49f3 100644 --- a/src/UI/Routing/Crumb.ts +++ b/src/UI/Routing/Crumb.ts @@ -9,9 +9,9 @@ interface Crumb { } export function getCrumbs(routeManager: RouteManager, url: string): Crumb[] { - const routeWithParams = routeManager.getRouteWithParamsFromUrl(url); - if (typeof routeWithParams === "undefined") return []; - const [route, params] = routeWithParams; + const routeMatch = routeManager.getRouteMatchFromUrl(url); + if (typeof routeMatch === "undefined") return []; + const { route, params } = routeMatch; const lineage = routeManager.getLineageFromRoute(route); return lineage.map(({ kind, label, path }, idx) => ({ kind, diff --git a/src/UI/Routing/PrimaryBaseUrlManager.ts b/src/UI/Routing/PrimaryBaseUrlManager.ts index d3af8ed5b..f4e02b0bf 100644 --- a/src/UI/Routing/PrimaryBaseUrlManager.ts +++ b/src/UI/Routing/PrimaryBaseUrlManager.ts @@ -14,6 +14,6 @@ export class PrimaryBaseUrlManager implements BaseUrlManager { } getBaseUrl(forcedUrl?: string): string { - return forcedUrl || this.getConsoleBaseUrl().replace("/console", ""); + return forcedUrl || this.getConsoleBaseUrl().replace(this.ANCHOR, ""); } } diff --git a/src/UI/Routing/PrimaryRouteManager.test.ts b/src/UI/Routing/PrimaryRouteManager.test.ts index c257681ac..da2bd2810 100644 --- a/src/UI/Routing/PrimaryRouteManager.test.ts +++ b/src/UI/Routing/PrimaryRouteManager.test.ts @@ -42,17 +42,17 @@ it.each` ); it.each` - url | result | resultTxt - ${"/"} | ${[Home, {}]} | ${"[Home, {}]"} - ${"/lsm/catalog"} | ${[Catalog, {}]} | ${"[Catalog, {}]"} - ${"/lsm/catalog/xyz/inventory"} | ${[Inventory, { service: "xyz" }]} | ${"[Inventory, {service: 'xyz'}]"} - ${"/lsm/catalog/xyz/inventory/add"} | ${[CreateInstance, { service: "xyz" }]} | ${"[CreateInstance, {service: 'xyz'}]"} - ${"/lsm/catalog/xyz/inventory/123/history"} | ${[History, { service: "xyz", instance: "123" }]} | ${"[History, {service: 'xyz', instance: '123'}]"} - ${"/lsm/catalog/xyz/inventory/123/events"} | ${[Events, { service: "xyz", instance: "123" }]} | ${"[Events, {service: 'xyz', instance: '123'}]"} - ${"/lsm/catalog/xyz/inventory/123/diagnose"} | ${[Diagnose, { service: "xyz", instance: "123" }]} | ${"[Diagnose, {service: 'xyz', instance: '123'}]"} + url | result | resultTxt + ${"/"} | ${{ route: Home, params: undefined }} | ${"Home(undefined)"} + ${"/lsm/catalog"} | ${{ route: Catalog, params: undefined }} | ${"Catalog(undefined)"} + ${"/lsm/catalog/xyz/inventory"} | ${{ route: Inventory, params: { service: "xyz" } }} | ${"Inventory({service: 'xyz'})"} + ${"/lsm/catalog/xyz/inventory/add"} | ${{ route: CreateInstance, params: { service: "xyz" } }} | ${"CreateInstance({service: 'xyz'})"} + ${"/lsm/catalog/xyz/inventory/123/history"} | ${{ route: History, params: { service: "xyz", instance: "123" } }} | ${"History({service: 'xyz', instance: '123'})"} + ${"/lsm/catalog/xyz/inventory/123/events"} | ${{ route: Events, params: { service: "xyz", instance: "123" } }} | ${"Events({service: 'xyz', instance: '123'})"} + ${"/lsm/catalog/xyz/inventory/123/diagnose"} | ${{ route: Diagnose, params: { service: "xyz", instance: "123" } }} | ${"Diagnose({service: 'xyz', instance: '123'})"} `( "GIVEN getRouteWithParamsFromUrl WHEN passed '$url' THEN returns $resultTxt", ({ url, result }) => { - expect(routeManager.getRouteWithParamsFromUrl(url)).toEqual(result); + expect(routeManager.getRouteMatchFromUrl(url)).toEqual(result); } ); diff --git a/src/UI/Routing/PrimaryRouteManager.ts b/src/UI/Routing/PrimaryRouteManager.ts index 2482746de..c4ec2c054 100644 --- a/src/UI/Routing/PrimaryRouteManager.ts +++ b/src/UI/Routing/PrimaryRouteManager.ts @@ -1,11 +1,11 @@ -import { generatePath, matchPath, match } from "react-router-dom"; +import { generatePath, matchPath, PathMatch } from "react-router-dom"; import { RouteDictionary, RouteManager, Route, RouteKind, RouteParams, - MatchedParams, + RouteMatch, } from "@/Core"; import { paths } from "./Paths"; @@ -42,13 +42,13 @@ export class PrimaryRouteManager implements RouteManager { } getRelatedUrlWithoutParams(pathname: string): string { - const routeAndParams = this.getRouteWithParamsFromUrl(pathname); - if (typeof routeAndParams === "undefined") { + const routeMatch = this.getRouteMatchFromUrl(pathname); + if (typeof routeMatch === "undefined") { return this.getUrl("Home", undefined); } - const [currentRoute] = routeAndParams; - if (!this.routeHasParams(currentRoute)) return pathname; - const parent = this.getParentWithoutParams(currentRoute); + const { route } = routeMatch; + if (!this.routeHasParams(route)) return pathname; + const parent = this.getParentWithoutParams(route); if (typeof parent === "undefined") return this.getUrl("Home", undefined); return this.getUrl(parent.kind, undefined); } @@ -103,22 +103,28 @@ export class PrimaryRouteManager implements RouteManager { } } - getUrl(kind: RouteKind, params: RouteParams): string { + getUrl(kind: RouteKind, params: RouteParams): string { const route = this.getRoute(kind); return generatePath(route.path, params); } - getRouteWithParamsFromUrl(url: string): [Route, MatchedParams] | undefined { + getRouteMatchFromUrl(url: string): RouteMatch | undefined { const routeMatchPairs = this.getRoutes().map((route) => [ route, - matchPath(url, { path: route.path, exact: true }), + matchPath(route.path, url), ]); const routeWithMatch = routeMatchPairs.find( - (pair): pair is [Route, match] => pair[1] !== null + (pair): pair is [Route, PathMatch] => pair[1] !== null ); if (typeof routeWithMatch === "undefined") return undefined; - const [page, match] = routeWithMatch; - return [page, match.params]; + const [route, match] = routeWithMatch; + return { + route, + params: + Object.entries(match.params).length <= 0 + ? undefined + : (match.params as RouteParams), + }; } } diff --git a/src/UI/Routing/SearchSanitizer/Provider.tsx b/src/UI/Routing/SearchSanitizer/Provider.tsx index 1d7bd846c..a34bda570 100644 --- a/src/UI/Routing/SearchSanitizer/Provider.tsx +++ b/src/UI/Routing/SearchSanitizer/Provider.tsx @@ -1,5 +1,5 @@ import React, { useContext, useEffect } from "react"; -import { useHistory, useLocation } from "react-router"; +import { useNavigate, useLocation } from "react-router"; import { RouteManager } from "@/Core"; import { DependencyContext } from "@/UI/Dependency"; import { Kind } from "@/UI/Routing/Kind"; @@ -8,7 +8,7 @@ import { SearchSanitizer } from "./SearchSanitizer"; export const Provider: React.FC = ({ children }) => { const { routeManager } = useContext(DependencyContext); const { pathname, search, hash } = useLocation(); - const history = useHistory(); + const navigate = useNavigate(); const sanitizer = new SearchSanitizer(routeManager); const [sanitizedSearch, routeKind] = getSearchResult( @@ -20,9 +20,9 @@ export const Provider: React.FC = ({ children }) => { useEffect(() => { if (sanitizedSearch !== null && sanitizedSearch !== search) { - history.replace(`${pathname}${sanitizedSearch}${hash}`); + navigate(`${pathname}${sanitizedSearch}${hash}`, { replace: true }); } - }, [history, hash, pathname, sanitizedSearch, search]); + }, [navigate, hash, pathname, sanitizedSearch, search]); if (routeKind !== null) { return sanitizer.isSanitized(routeKind, search) ? <>{children} : null; @@ -36,8 +36,7 @@ const getSearchResult = ( pathname: string, search: string ): [string | null, Kind | null] => { - const match = routeManager.getRouteWithParamsFromUrl(pathname); + const match = routeManager.getRouteMatchFromUrl(pathname); if (typeof match === "undefined") return [null, null]; - const [route] = match; - return [sanitizer.sanitize(route.kind, search), route.kind]; + return [sanitizer.sanitize(match.route.kind, search), match.route.kind]; }; diff --git a/src/UI/Routing/Utils.ts b/src/UI/Routing/Utils.ts index f6c10d8eb..93fbdf753 100644 --- a/src/UI/Routing/Utils.ts +++ b/src/UI/Routing/Utils.ts @@ -1,6 +1,6 @@ import { useContext, useEffect } from "react"; -import { useHistory, useLocation } from "react-router"; -import { RouteParams } from "@/Core"; +import { useNavigate, useLocation, useParams } from "react-router-dom"; +import { RouteKind, RouteParams } from "@/Core"; import { DependencyContext } from "@/UI/Dependency"; import { Kind } from "./Kind"; @@ -16,14 +16,19 @@ type NavigateTo = ( export const useNavigateTo = (): NavigateTo => { const { routeManager } = useContext(DependencyContext); const { search } = useLocation(); - const history = useHistory(); + const navigate = useNavigate(); return (routeKind, params, newSearch) => { const pathname = routeManager.getUrl(routeKind, params); - history.push(`${pathname}?${newSearch || search}`); + navigate(`${pathname}?${newSearch || search}`); }; }; +export const useRouteParams = (): RouteParams => { + const params = useParams(); + return params as RouteParams; +}; + /** * A custom hook for setting the page title * @param title diff --git a/yarn.lock b/yarn.lock index 186abc107..70c4ec663 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1384,7 +1384,7 @@ core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.17", "@babel/runtime@^7.15.4", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2", "@babel/runtime@^7.9.6": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.17", "@babel/runtime@^7.15.4", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2", "@babel/runtime@^7.9.6": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== @@ -8606,25 +8606,13 @@ highlight.js@^10.1.1, highlight.js@^10.4.1, highlight.js@~10.7.0: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.2.tgz#89319b861edc66c48854ed1e6da21ea89f847360" integrity sha512-oFLl873u4usRM9K63j4ME9u3etNF0PLiJhSQ8rdfuL51Wn3zkD6drf9ZW0dOzjnZI22YYG24z30JcmfCZjMgYg== -history@5.1.0: +history@5.1.0, history@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/history/-/history-5.1.0.tgz#2e93c09c064194d38d52ed62afd0afc9d9b01ece" integrity sha512-zPuQgPacm2vH2xdORvGGz1wQMuHSIB56yNAy5FnLuwOwgSYyPKptJtcMm6Ev+hRGeS+GzhbmRacHzvlESbFwDg== dependencies: "@babel/runtime" "^7.7.6" -history@^4.9.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" - integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== - dependencies: - "@babel/runtime" "^7.1.2" - loose-envify "^1.2.0" - resolve-pathname "^3.0.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" - value-equal "^1.0.1" - hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -8634,7 +8622,7 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0: +hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -9561,11 +9549,6 @@ is-wsl@^2.1.1, is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -10619,7 +10602,7 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -10980,14 +10963,6 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -mini-create-react-context@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e" - integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ== - dependencies: - "@babel/runtime" "^7.12.1" - tiny-warning "^1.0.3" - mini-css-extract-plugin@^2.4.4: version "2.4.4" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.4.tgz#c7e5d2d931dcf100ae50ae949ba757c506b54b0f" @@ -11951,13 +11926,6 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= -path-to-regexp@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== - dependencies: - isarray "0.0.1" - path-to-regexp@^2.2.1: version "2.4.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.4.0.tgz#35ce7f333d5616f1c1e1bfe266c3aba2e5b2e704" @@ -12973,7 +12941,7 @@ react-inspector@^5.1.0: is-dom "^1.0.0" prop-types "^15.0.0" -react-is@^16.3.2, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.3.2, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -13024,34 +12992,20 @@ react-refresh@^0.8.3: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg== -react-router-dom@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.0.tgz#da1bfb535a0e89a712a93b97dd76f47ad1f32363" - integrity sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ== +react-router-dom@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.0.1.tgz#958d5deac8932ce209001ba7a11d5ae605148856" + integrity sha512-fiE+PzFTrof5q8Z/+RHzuiin9/U/q5KY2adlHClwYexbY0DqJnHcC/0U9yv3Amz9em2/bcK7X8mk7+zxB+qhvg== dependencies: - "@babel/runtime" "^7.12.13" - history "^4.9.0" - loose-envify "^1.3.1" - prop-types "^15.6.2" - react-router "5.2.1" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" + history "^5.1.0" + react-router "6.0.1" -react-router@5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.1.tgz#4d2e4e9d5ae9425091845b8dbc6d9d276239774d" - integrity sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ== - dependencies: - "@babel/runtime" "^7.12.13" - history "^4.9.0" - hoist-non-react-statics "^3.1.0" - loose-envify "^1.3.1" - mini-create-react-context "^0.4.0" - path-to-regexp "^1.7.0" - prop-types "^15.6.2" - react-is "^16.6.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" +react-router@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.0.1.tgz#3fe93ad11f91fb55e242a42628414cb47219e652" + integrity sha512-O3iab52icFnQaHWONZr50CcjRlf3gx8CCjPQ0YxN8xEuEklRJNgoZSeoYFYz0fLvA4cpnhc306Nd8BYgL4QZrQ== + dependencies: + history "^5.1.0" react-sizeme@^3.0.1: version "3.0.1" @@ -13489,11 +13443,6 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-pathname@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" - integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== - resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -14836,16 +14785,6 @@ tiny-emitter@^2.0.0: resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== -tiny-invariant@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" - integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== - -tiny-warning@^1.0.0, tiny-warning@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" - integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== - tippy.js@5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-5.1.2.tgz#5ac91233c59ab482ef5988cffe6e08bd26528e66" @@ -15497,11 +15436,6 @@ validator@^13.6.0: resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== -value-equal@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" - integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== - vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"