From d7362a97a0e17ecf2d18a7531e77817cb6ec9ced Mon Sep 17 00:00:00 2001 From: Jon Koops Date: Thu, 11 Nov 2021 17:04:04 +0100 Subject: [PATCH] Split realm context into two (#1523) --- src/App.tsx | 25 ++++---- .../data-loader/DataLoader.test.tsx | 8 +-- .../form-access/FormAccess.test.tsx | 8 +-- .../realm-selector/RealmSelector.tsx | 4 +- src/context/RealmsContext.tsx | 58 +++++++++++++++++++ src/context/realm-context/RealmContext.tsx | 43 +++----------- src/realm-settings/RealmSettingsTabs.tsx | 12 +++- src/realm/add/NewRealmForm.tsx | 6 +- 8 files changed, 96 insertions(+), 68 deletions(-) create mode 100644 src/context/RealmsContext.tsx diff --git a/src/App.tsx b/src/App.tsx index 2b4c70140a..dfc82ccec1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,6 +15,7 @@ import { routes, RouteDef } from "./route-config"; import { PageBreadCrumbs } from "./components/bread-crumb/PageBreadCrumbs"; import { ForbiddenSection } from "./ForbiddenSection"; import { SubGroups } from "./groups/SubGroupsContext"; +import { RealmsProvider } from "./context/RealmsContext"; import { RealmContextProvider } from "./context/realm-context/RealmContext"; import { ErrorRenderer } from "./components/error/ErrorRenderer"; import { AdminClient } from "./context/auth/AdminClient"; @@ -34,17 +35,19 @@ const AppContexts: FunctionComponent = ({ - - - - - - {children} - - - - - + + + + + + + {children} + + + + + + diff --git a/src/components/data-loader/DataLoader.test.tsx b/src/components/data-loader/DataLoader.test.tsx index 820b0c3ce1..a8bc92899a 100644 --- a/src/components/data-loader/DataLoader.test.tsx +++ b/src/components/data-loader/DataLoader.test.tsx @@ -47,13 +47,7 @@ export const MockAdminClient: FunctionComponent<{ mock?: object }> = ( } > - Promise.resolve(), - }} - > + {props.children} diff --git a/src/components/form-access/FormAccess.test.tsx b/src/components/form-access/FormAccess.test.tsx index 28dc38ce6c..aa1f6d9d2e 100644 --- a/src/components/form-access/FormAccess.test.tsx +++ b/src/components/form-access/FormAccess.test.tsx @@ -22,13 +22,7 @@ describe("FormAccess", () => { whoAmI: new WhoAmI(whoami as WhoAmIRepresentation), }} > - Promise.resolve(), - }} - > + diff --git a/src/components/realm-selector/RealmSelector.tsx b/src/components/realm-selector/RealmSelector.tsx index 6bb93fb72f..5b947722cd 100644 --- a/src/components/realm-selector/RealmSelector.tsx +++ b/src/components/realm-selector/RealmSelector.tsx @@ -16,6 +16,7 @@ import { useTranslation } from "react-i18next"; import { useHistory } from "react-router-dom"; import { useRealm } from "../../context/realm-context/RealmContext"; +import { useRealms } from "../../context/RealmsContext"; import { useWhoAmI } from "../../context/whoami/WhoAmI"; import { toDashboard } from "../../dashboard/routes/Dashboard"; import { toAddRealm } from "../../realm/routes/AddRealm"; @@ -25,7 +26,8 @@ import { RecentUsed } from "./recent-used"; import "./realm-selector.css"; export const RealmSelector = () => { - const { realm, realms } = useRealm(); + const { realm } = useRealm(); + const { realms } = useRealms(); const { whoAmI } = useWhoAmI(); const [open, setOpen] = useState(false); const [search, setSearch] = useState(""); diff --git a/src/context/RealmsContext.tsx b/src/context/RealmsContext.tsx new file mode 100644 index 0000000000..dcf0777e47 --- /dev/null +++ b/src/context/RealmsContext.tsx @@ -0,0 +1,58 @@ +import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; +import { sortBy } from "lodash"; +import React, { + createContext, + FunctionComponent, + useCallback, + useMemo, + useState, +} from "react"; +import { RecentUsed } from "../components/realm-selector/recent-used"; +import useRequiredContext from "../utils/useRequiredContext"; +import { useAdminClient, useFetch } from "./auth/AdminClient"; + +type RealmsContextProps = { + /** A list of all the realms. */ + realms: RealmRepresentation[]; + /** Refreshes the realms with the latest information. */ + refresh: () => Promise; +}; + +export const RealmsContext = createContext( + undefined +); + +export const RealmsProvider: FunctionComponent = ({ children }) => { + const adminClient = useAdminClient(); + const [realms, setRealms] = useState([]); + const recentUsed = useMemo(() => new RecentUsed(), []); + + function updateRealms(realms: RealmRepresentation[]) { + setRealms(sortBy(realms, "realm")); + recentUsed.clean(realms.map(({ realm }) => realm!)); + } + + useFetch( + () => adminClient.realms.find(), + (realms) => updateRealms(realms), + [] + ); + + const refresh = useCallback(async () => { + //this is needed otherwise the realm find function will not return + //new or renamed realms because of the cached realms in the token (perhaps?) + await adminClient.keycloak?.updateToken(Number.MAX_VALUE); + updateRealms(await adminClient.realms.find()); + }, []); + + const value = useMemo( + () => ({ realms, refresh }), + [realms, refresh] + ); + + return ( + {children} + ); +}; + +export const useRealms = () => useRequiredContext(RealmsContext); diff --git a/src/context/realm-context/RealmContext.tsx b/src/context/realm-context/RealmContext.tsx index 17a71bfc2c..a542effec1 100644 --- a/src/context/realm-context/RealmContext.tsx +++ b/src/context/realm-context/RealmContext.tsx @@ -1,6 +1,4 @@ -import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; -import _ from "lodash"; -import React, { FunctionComponent, useEffect, useMemo, useState } from "react"; +import React, { FunctionComponent, useEffect, useMemo } from "react"; import { useRouteMatch } from "react-router-dom"; import { RecentUsed } from "../../components/realm-selector/recent-used"; import { @@ -9,12 +7,10 @@ import { } from "../../dashboard/routes/Dashboard"; import environment from "../../environment"; import useRequiredContext from "../../utils/useRequiredContext"; -import { useAdminClient, useFetch } from "../auth/AdminClient"; +import { useAdminClient } from "../auth/AdminClient"; type RealmContextType = { realm: string; - realms: RealmRepresentation[]; - refresh: () => Promise; }; export const RealmContext = React.createContext( @@ -22,6 +18,8 @@ export const RealmContext = React.createContext( ); export const RealmContextProvider: FunctionComponent = ({ children }) => { + const adminClient = useAdminClient(); + const recentUsed = useMemo(() => new RecentUsed(), []); const routeMatch = useRouteMatch(DashboardRoute.path); const realmParam = routeMatch?.params.realm; const realm = useMemo( @@ -29,43 +27,16 @@ export const RealmContextProvider: FunctionComponent = ({ children }) => { [realmParam] ); - const [realms, setRealms] = useState([]); - const adminClient = useAdminClient(); - const recentUsed = new RecentUsed(); - - const updateRealmsList = (realms: RealmRepresentation[]) => { - setRealms(_.sortBy(realms, "realm")); - recentUsed.clean(realms.map((r) => r.realm!)); - }; - - useFetch( - () => adminClient.realms.find(), - (realms) => updateRealmsList(realms), - [] - ); - // Configure admin client to use selected realm when it changes. useEffect(() => adminClient.setConfig({ realmName: realm }), [realm]); // Keep track of recently used realms when selected realm changes. useEffect(() => recentUsed.setRecentUsed(realm), [realm]); + const value = useMemo(() => ({ realm }), [realm]); + return ( - { - //this is needed otherwise the realm find function will not return - //new or renamed realms because of the cached realms in the token (perhaps?) - await adminClient.keycloak?.updateToken(Number.MAX_VALUE); - const list = await adminClient.realms.find(); - updateRealmsList(list); - }, - }} - > - {children} - + {children} ); }; diff --git a/src/realm-settings/RealmSettingsTabs.tsx b/src/realm-settings/RealmSettingsTabs.tsx index e895ea32da..555c61642b 100644 --- a/src/realm-settings/RealmSettingsTabs.tsx +++ b/src/realm-settings/RealmSettingsTabs.tsx @@ -19,6 +19,7 @@ import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/de import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs"; import { useRealm } from "../context/realm-context/RealmContext"; +import { useRealms } from "../context/RealmsContext"; import { ViewHeader } from "../components/view-header/ViewHeader"; import { useAdminClient } from "../context/auth/AdminClient"; import { useServerInfo } from "../context/server-info/ServerInfoProvider"; @@ -48,6 +49,8 @@ import { toRealmSettings } from "./routes/RealmSettings"; import { LocalizationTab } from "./LocalizationTab"; import { HelpItem } from "../components/help-enabler/HelpItem"; import { DEFAULT_LOCALE } from "../i18n"; +import { toDashboard } from "../dashboard/routes/Dashboard"; +import environment from "../environment"; type RealmSettingsHeaderProps = { onChange: (value: boolean) => void; @@ -66,6 +69,7 @@ const RealmSettingsHeader = ({ }: RealmSettingsHeaderProps) => { const { t } = useTranslation("realm-settings"); const adminClient = useAdminClient(); + const { refresh: refreshRealms } = useRealms(); const { addAlert, addError } = useAlerts(); const history = useHistory(); const [partialImportOpen, setPartialImportOpen] = useState(false); @@ -90,7 +94,8 @@ const RealmSettingsHeader = ({ try { await adminClient.realms.del({ realm: realmName }); addAlert(t("deletedSuccess"), AlertVariant.success); - history.push("/master/"); + await refreshRealms(); + history.push(toDashboard({ realm: environment.masterRealm })); refresh(); } catch (error) { addError("realm-settings:deleteError", error); @@ -163,7 +168,8 @@ export const RealmSettingsTabs = ({ const { t } = useTranslation("realm-settings"); const adminClient = useAdminClient(); const { addAlert, addError } = useAlerts(); - const { realm: realmName, refresh: refreshRealm } = useRealm(); + const { realm: realmName } = useRealm(); + const { refresh: refreshRealms } = useRealms(); const history = useHistory(); const kpComponentTypes = @@ -215,7 +221,7 @@ export const RealmSettingsTabs = ({ setupForm(realm); const isRealmRenamed = realmName !== realm.realm; if (isRealmRenamed) { - await refreshRealm(); + await refreshRealms(); history.push(toRealmSettings({ realm: realm.realm! })); } addAlert(t("saveSuccess"), AlertVariant.success); diff --git a/src/realm/add/NewRealmForm.tsx b/src/realm/add/NewRealmForm.tsx index 146f5d2295..38e56c7733 100644 --- a/src/realm/add/NewRealmForm.tsx +++ b/src/realm/add/NewRealmForm.tsx @@ -17,7 +17,7 @@ import { FormAccess } from "../../components/form-access/FormAccess"; import { JsonFileUpload } from "../../components/json-file-upload/JsonFileUpload"; import { ViewHeader } from "../../components/view-header/ViewHeader"; import { useAdminClient } from "../../context/auth/AdminClient"; -import { useRealm } from "../../context/realm-context/RealmContext"; +import { useRealms } from "../../context/RealmsContext"; import { useWhoAmI } from "../../context/whoami/WhoAmI"; import { toDashboard } from "../../dashboard/routes/Dashboard"; @@ -25,7 +25,7 @@ export default function NewRealmForm() { const { t } = useTranslation("realm"); const history = useHistory(); const { refresh } = useWhoAmI(); - const { refresh: realmRefresh } = useRealm(); + const { refresh: refreshRealms } = useRealms(); const adminClient = useAdminClient(); const { addAlert, addError } = useAlerts(); @@ -45,7 +45,7 @@ export default function NewRealmForm() { addAlert(t("saveRealmSuccess"), AlertVariant.success); refresh(); - await realmRefresh(); + await refreshRealms(); history.push(toDashboard({ realm: realm.realm })); } catch (error) { addError("realm:saveRealmError", error);