Fixes with realm list (#576)
* only query realms endpoint once currently the realm list query is expensive and we only need it once. * fixed refresh issues * fixed tests * fixed imports * fixed types * removed strange non code * reload instead * added more realm tests
This commit is contained in:
parent
e1d53c640e
commit
78f843cdcc
23 changed files with 219 additions and 802 deletions
|
@ -4,16 +4,18 @@ import CreateRealmPage from "../support/pages/admin_console/CreateRealmPage";
|
|||
import Masthead from "../support/pages/admin_console/Masthead";
|
||||
import AdminClient from "../support/util/AdminClient";
|
||||
import { keycloakBefore } from "../support/util/keycloak_before";
|
||||
import RealmSelector from "../support/pages/admin_console/RealmSelector";
|
||||
|
||||
const masthead = new Masthead();
|
||||
const loginPage = new LoginPage();
|
||||
const sidebarPage = new SidebarPage();
|
||||
const createRealmPage = new CreateRealmPage();
|
||||
const realmSelector = new RealmSelector();
|
||||
|
||||
describe("Realms test", function () {
|
||||
describe("Realms test", () => {
|
||||
const testRealmName = "Test realm";
|
||||
describe("Realm creation", function () {
|
||||
beforeEach(function () {
|
||||
describe("Realm creation", () => {
|
||||
beforeEach(() => {
|
||||
keycloakBefore();
|
||||
loginPage.logIn();
|
||||
});
|
||||
|
@ -23,7 +25,7 @@ describe("Realms test", function () {
|
|||
await client.deleteRealm(testRealmName);
|
||||
});
|
||||
|
||||
it("should fail creating Master realm", function () {
|
||||
it("should fail creating Master realm", () => {
|
||||
sidebarPage.goToCreateRealm();
|
||||
createRealmPage.fillRealmName("master").createRealm();
|
||||
|
||||
|
@ -32,14 +34,14 @@ describe("Realms test", function () {
|
|||
);
|
||||
});
|
||||
|
||||
it("should create Test realm", function () {
|
||||
it("should create Test realm", () => {
|
||||
sidebarPage.goToCreateRealm();
|
||||
createRealmPage.fillRealmName(testRealmName).createRealm();
|
||||
|
||||
masthead.checkNotificationMessage("Realm created");
|
||||
});
|
||||
|
||||
it("should change to Test realm", function () {
|
||||
it("should change to Test realm", () => {
|
||||
sidebarPage.getCurrentRealm().should("eq", "Master");
|
||||
|
||||
sidebarPage
|
||||
|
@ -48,4 +50,29 @@ describe("Realms test", function () {
|
|||
.should("eq", testRealmName);
|
||||
});
|
||||
});
|
||||
|
||||
describe("More then 5 realms", () => {
|
||||
const realmNames = ["One", "Two", "Three", "Four", "Five"];
|
||||
|
||||
beforeEach(() => {
|
||||
keycloakBefore();
|
||||
loginPage.logIn();
|
||||
for (const realmName of realmNames) {
|
||||
sidebarPage.goToCreateRealm();
|
||||
createRealmPage.fillRealmName(realmName).createRealm();
|
||||
sidebarPage.goToClients();
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
const client = new AdminClient();
|
||||
for (const realmName of realmNames) {
|
||||
await client.deleteRealm(realmName);
|
||||
}
|
||||
});
|
||||
|
||||
it("switch to searchable realm selector", () => {
|
||||
realmSelector.openRealmContextSelector().shouldContainAll(realmNames);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
27
cypress/support/pages/admin_console/RealmSelector.ts
Normal file
27
cypress/support/pages/admin_console/RealmSelector.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
const expect = chai.expect;
|
||||
|
||||
export default class RealmSelector {
|
||||
private realmSelector = "realmSelector";
|
||||
private realmContextSelector = ".keycloak__realm_selector__context_selector";
|
||||
|
||||
shouldContainAll(realmsList: string[]) {
|
||||
cy.getId(this.realmSelector)
|
||||
.scrollIntoView()
|
||||
.get("ul")
|
||||
.should((realms) => {
|
||||
for (let index = 0; index < realmsList.length; index++) {
|
||||
const realmName = realmsList[index];
|
||||
expect(realms).to.contain(realmName);
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
openRealmContextSelector() {
|
||||
cy.getId(this.realmSelector).scrollIntoView();
|
||||
cy.get(this.realmContextSelector).click();
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,64 +1,47 @@
|
|||
export default class SidebarPage {
|
||||
realmsDrpDwn: string;
|
||||
realmsList: string;
|
||||
createRealmBtn: string;
|
||||
clientsBtn: string;
|
||||
clientScopesBtn: string;
|
||||
realmRolesBtn: string;
|
||||
usersBtn: string;
|
||||
groupsBtn: string;
|
||||
sessionsBtn: string;
|
||||
eventsBtn: string;
|
||||
realmSettingsBtn: string;
|
||||
authenticationBtn: string;
|
||||
identityProvidersBtn: string;
|
||||
userFederationBtn: string;
|
||||
private realmsDrpDwn = "realmSelectorToggle";
|
||||
private realmsList = "realmSelector";
|
||||
private createRealmBtn = "add-realm";
|
||||
|
||||
constructor() {
|
||||
this.realmsDrpDwn = "#realm-select-toggle";
|
||||
this.realmsList = "#realm-select ul";
|
||||
this.createRealmBtn = "#realm-select li:last-child a";
|
||||
private clientsBtn = "#nav-item-clients";
|
||||
private clientScopesBtn = "#nav-item-client-scopes";
|
||||
private realmRolesBtn = "#nav-item-roles";
|
||||
private usersBtn = "#nav-item-users";
|
||||
private groupsBtn = "#nav-item-groups";
|
||||
private sessionsBtn = "#nav-item-sessions";
|
||||
private eventsBtn = "#nav-item-events";
|
||||
|
||||
this.clientsBtn = "#nav-item-clients";
|
||||
this.clientScopesBtn = "#nav-item-client-scopes";
|
||||
this.realmRolesBtn = "#nav-item-roles";
|
||||
this.usersBtn = "#nav-item-users";
|
||||
this.groupsBtn = "#nav-item-groups";
|
||||
this.sessionsBtn = "#nav-item-sessions";
|
||||
this.eventsBtn = "#nav-item-events";
|
||||
|
||||
this.realmSettingsBtn = "#nav-item-realm-settings";
|
||||
this.authenticationBtn = "#nav-item-authentication";
|
||||
this.identityProvidersBtn = "#nav-item-identity-providers";
|
||||
this.userFederationBtn = "#nav-item-user-federation";
|
||||
}
|
||||
private realmSettingsBtn = "#nav-item-realm-settings";
|
||||
private authenticationBtn = "#nav-item-authentication";
|
||||
private identityProvidersBtn = "#nav-item-identity-providers";
|
||||
private userFederationBtn = "#nav-item-user-federation";
|
||||
|
||||
getCurrentRealm() {
|
||||
return cy.get(this.realmsDrpDwn).invoke("text");
|
||||
return cy.getId(this.realmsDrpDwn).scrollIntoView().invoke("text");
|
||||
}
|
||||
|
||||
goToRealm(realmName: string) {
|
||||
cy.get(this.realmsDrpDwn).click();
|
||||
cy.get(this.realmsList).contains(realmName).click();
|
||||
cy.getId(this.realmsDrpDwn).scrollIntoView().click();
|
||||
cy.getId(this.realmsList).get("ul").contains(realmName).click();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
goToCreateRealm() {
|
||||
cy.get(this.realmsDrpDwn).click();
|
||||
cy.get(this.createRealmBtn).click();
|
||||
cy.getId(this.realmsDrpDwn).scrollIntoView().click();
|
||||
cy.getId(this.createRealmBtn).click();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
goToClients() {
|
||||
cy.get(this.clientsBtn).click();
|
||||
cy.get(this.clientsBtn).scrollIntoView().click();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
goToClientScopes() {
|
||||
cy.get(this.clientScopesBtn).click();
|
||||
cy.get(this.clientScopesBtn).scrollIntoView().click();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
|
27
src/App.tsx
27
src/App.tsx
|
@ -6,7 +6,7 @@ import {
|
|||
Switch,
|
||||
useParams,
|
||||
} from "react-router-dom";
|
||||
import { ErrorBoundary, useErrorHandler } from "react-error-boundary";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
|
||||
import { Header } from "./PageHeader";
|
||||
import { PageNav } from "./PageNav";
|
||||
|
@ -21,9 +21,7 @@ import { PageBreadCrumbs } from "./components/bread-crumb/PageBreadCrumbs";
|
|||
import { ForbiddenSection } from "./ForbiddenSection";
|
||||
import { SubGroups } from "./groups/SubGroupsContext";
|
||||
import { useRealm } from "./context/realm-context/RealmContext";
|
||||
import { useAdminClient, asyncStateFetch } from "./context/auth/AdminClient";
|
||||
import { ErrorRenderer } from "./components/error/ErrorRenderer";
|
||||
import { RecentUsed } from "./components/realm-selector/recent-used";
|
||||
|
||||
export const mainPageContentId = "kc-main-content-page-container";
|
||||
|
||||
|
@ -39,28 +37,11 @@ const AppContexts = ({ children }: { children: ReactNode }) => (
|
|||
</AccessContextProvider>
|
||||
);
|
||||
|
||||
// set the realm form the path if it's one of the know realms
|
||||
// set the realm form the path
|
||||
const RealmPathSelector = ({ children }: { children: ReactNode }) => {
|
||||
const { setRealm } = useRealm();
|
||||
const { realm } = useParams<{ realm: string }>();
|
||||
const adminClient = useAdminClient();
|
||||
const handleError = useErrorHandler();
|
||||
const recentUsed = new RecentUsed();
|
||||
|
||||
useEffect(
|
||||
() =>
|
||||
asyncStateFetch(
|
||||
() => adminClient.realms.find(),
|
||||
(realms) => {
|
||||
recentUsed.clean(realms.map((r) => r.realm!));
|
||||
if (realms.findIndex((r) => r.realm == realm) !== -1) {
|
||||
setRealm(realm);
|
||||
}
|
||||
},
|
||||
handleError
|
||||
),
|
||||
[]
|
||||
);
|
||||
useEffect(() => setRealm(realm), []);
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
@ -88,7 +69,7 @@ export const App = () => {
|
|||
>
|
||||
<ErrorBoundary
|
||||
FallbackComponent={ErrorRenderer}
|
||||
onReset={() => (location.href = "/")}
|
||||
onReset={window.location.reload}
|
||||
>
|
||||
<Switch>
|
||||
{routes(() => {}).map((route, i) => (
|
||||
|
|
|
@ -14,12 +14,12 @@ export const KeycloakAdminConsole = ({
|
|||
adminClient,
|
||||
}: KeycloakAdminConsoleProps) => {
|
||||
return (
|
||||
<RealmContextProvider>
|
||||
<AdminClient.Provider value={adminClient}>
|
||||
<WhoAmIContextProvider>
|
||||
<AdminClient.Provider value={adminClient}>
|
||||
<WhoAmIContextProvider>
|
||||
<RealmContextProvider>
|
||||
<App />
|
||||
</WhoAmIContextProvider>
|
||||
</AdminClient.Provider>
|
||||
</RealmContextProvider>
|
||||
</RealmContextProvider>
|
||||
</WhoAmIContextProvider>
|
||||
</AdminClient.Provider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -18,7 +18,6 @@ import { WhoAmIContext } from "./context/whoami/WhoAmI";
|
|||
import { HelpContext, HelpHeader } from "./components/help-enabler/HelpHeader";
|
||||
import { Link, useHistory } from "react-router-dom";
|
||||
import { useAdminClient } from "./context/auth/AdminClient";
|
||||
import { useRealm } from "./context/realm-context/RealmContext";
|
||||
|
||||
export const Header = () => {
|
||||
const adminClient = useAdminClient();
|
||||
|
@ -59,13 +58,11 @@ export const Header = () => {
|
|||
const ServerInfoDropdownItem = () => {
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const { setRealm } = useRealm();
|
||||
return (
|
||||
<DropdownItem
|
||||
key="server info"
|
||||
onClick={() => {
|
||||
history.push("/master/");
|
||||
setRealm("master");
|
||||
}}
|
||||
>
|
||||
{t("realmInfo")}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from "react";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import _ from "lodash";
|
||||
import {
|
||||
Nav,
|
||||
NavItem,
|
||||
|
@ -12,8 +11,6 @@ import {
|
|||
|
||||
import { RealmSelector } from "./components/realm-selector/RealmSelector";
|
||||
import { useRealm } from "./context/realm-context/RealmContext";
|
||||
import { DataLoader } from "./components/data-loader/DataLoader";
|
||||
import { useAdminClient } from "./context/auth/AdminClient";
|
||||
import { useAccess } from "./context/access/Access";
|
||||
import { routes } from "./route-config";
|
||||
|
||||
|
@ -21,10 +18,6 @@ export const PageNav: React.FunctionComponent = () => {
|
|||
const { t } = useTranslation("common");
|
||||
const { hasAccess, hasSomeAccess } = useAccess();
|
||||
const { realm } = useRealm();
|
||||
const adminClient = useAdminClient();
|
||||
const realmLoader = async () => {
|
||||
return _.sortBy(await adminClient.realms.find(), "realm");
|
||||
};
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
|
@ -83,13 +76,9 @@ export const PageNav: React.FunctionComponent = () => {
|
|||
nav={
|
||||
<Nav onSelect={onSelect}>
|
||||
<NavList>
|
||||
<DataLoader loader={realmLoader} deps={[realm]}>
|
||||
{(realmList) => (
|
||||
<NavItem className="keycloak__page_nav__nav_item__realm-selector">
|
||||
<RealmSelector realmList={realmList || []} />
|
||||
</NavItem>
|
||||
)}
|
||||
</DataLoader>
|
||||
<NavItem className="keycloak__page_nav__nav_item__realm-selector">
|
||||
<RealmSelector />
|
||||
</NavItem>
|
||||
</NavList>
|
||||
{isOnAddRealm() && (
|
||||
<NavGroup title="">
|
||||
|
|
|
@ -26,15 +26,15 @@ import {
|
|||
AllClientScopes,
|
||||
AllClientScopeType,
|
||||
} from "../components/client-scope/ClientScopeTypes";
|
||||
import KeycloakAdminClient from "keycloak-admin";
|
||||
import { ChangeTypeDialog } from "./ChangeTypeDialog";
|
||||
|
||||
import "./client-scope.css";
|
||||
|
||||
type ClientScopeDefaultOptionalType = ClientScopeRepresentation & {
|
||||
type: AllClientScopeType;
|
||||
};
|
||||
|
||||
import "./client-scope.css";
|
||||
import KeycloakAdminClient from "keycloak-admin";
|
||||
import { ChangeTypeDialog } from "./ChangeTypeDialog";
|
||||
|
||||
const castAdminClient = (adminClient: KeycloakAdminClient) =>
|
||||
(adminClient.clientScopes as unknown) as {
|
||||
[index: string]: Function;
|
||||
|
|
|
@ -18,6 +18,8 @@ import {
|
|||
} from "../../components/client-scope/ClientScopeTypes";
|
||||
import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable";
|
||||
|
||||
import "./client-scopes.css";
|
||||
|
||||
export type AddScopeDialogProps = {
|
||||
clientScopes: ClientScopeRepresentation[];
|
||||
open: boolean;
|
||||
|
@ -27,8 +29,6 @@ export type AddScopeDialogProps = {
|
|||
) => void;
|
||||
};
|
||||
|
||||
import "./client-scopes.css";
|
||||
|
||||
export const AddScopeDialog = ({
|
||||
clientScopes,
|
||||
open,
|
||||
|
|
|
@ -32,7 +32,7 @@ exports[`Group BreadCrumbs tests couple of crumbs 1`] = `
|
|||
href="//groups"
|
||||
onClick={[Function]}
|
||||
>
|
||||
groups
|
||||
Groups
|
||||
</a>
|
||||
</LinkAnchor>
|
||||
</Link>
|
||||
|
|
|
@ -17,7 +17,14 @@ describe("<FormAccess />", () => {
|
|||
<WhoAmIContext.Provider
|
||||
value={{ refresh: () => {}, whoAmI: new WhoAmI("master", whoami) }}
|
||||
>
|
||||
<RealmContext.Provider value={{ realm, setRealm: () => {} }}>
|
||||
<RealmContext.Provider
|
||||
value={{
|
||||
realm,
|
||||
setRealm: () => {},
|
||||
realms: [],
|
||||
refresh: () => Promise.resolve(),
|
||||
}}
|
||||
>
|
||||
<AccessContextProvider>
|
||||
<FormAccess role="manage-clients">
|
||||
<FormGroup label="test" fieldId="field">
|
||||
|
|
|
@ -16,24 +16,20 @@ import {
|
|||
} from "@patternfly/react-core";
|
||||
import { CheckIcon } from "@patternfly/react-icons";
|
||||
|
||||
import { toUpperCase } from "../../util";
|
||||
import RealmRepresentation from "keycloak-admin/lib/defs/realmRepresentation";
|
||||
import { toUpperCase } from "../../util";
|
||||
import { useRealm } from "../../context/realm-context/RealmContext";
|
||||
import { WhoAmIContext } from "../../context/whoami/WhoAmI";
|
||||
import { RecentUsed } from "./recent-used";
|
||||
|
||||
import "./realm-selector.css";
|
||||
|
||||
type RealmSelectorProps = {
|
||||
realmList: RealmRepresentation[];
|
||||
};
|
||||
|
||||
export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
|
||||
const { realm, setRealm } = useRealm();
|
||||
export const RealmSelector = () => {
|
||||
const { realm, setRealm, realms } = useRealm();
|
||||
const { whoAmI } = useContext(WhoAmIContext);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [search, setSearch] = useState("");
|
||||
const [filteredItems, setFilteredItems] = useState(realmList);
|
||||
const [filteredItems, setFilteredItems] = useState<RealmRepresentation[]>();
|
||||
const history = useHistory();
|
||||
const { t } = useTranslation("common");
|
||||
const recentUsed = new RecentUsed();
|
||||
|
@ -47,6 +43,7 @@ export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
|
|||
|
||||
const AddRealm = () => (
|
||||
<Button
|
||||
data-testid="add-realm"
|
||||
component="div"
|
||||
isBlock
|
||||
onClick={() => {
|
||||
|
@ -59,30 +56,31 @@ export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
|
|||
);
|
||||
|
||||
const onFilter = () => {
|
||||
const filtered =
|
||||
search === ""
|
||||
? realmList
|
||||
: realmList.filter(
|
||||
(r) => r.realm!.toLowerCase().indexOf(search.toLowerCase()) !== -1
|
||||
);
|
||||
setFilteredItems(filtered || []);
|
||||
if (search === "") {
|
||||
setFilteredItems(undefined);
|
||||
} else {
|
||||
const filtered = realms.filter(
|
||||
(r) => r.realm!.toLowerCase().indexOf(search.toLowerCase()) !== -1
|
||||
);
|
||||
setFilteredItems(filtered);
|
||||
}
|
||||
};
|
||||
|
||||
const selectRealm = (realm: string) => {
|
||||
setRealm(realm);
|
||||
setOpen(!open);
|
||||
history.push(`/${realm}/`);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
onFilter();
|
||||
}, [search]);
|
||||
|
||||
const dropdownItems = realmList.map((r) => (
|
||||
const dropdownItems = realms.map((r) => (
|
||||
<DropdownItem
|
||||
key={`realm-dropdown-item-${r.realm}`}
|
||||
onClick={() => {
|
||||
selectRealm(r.realm!);
|
||||
history.push(`/${realm}/`);
|
||||
}}
|
||||
>
|
||||
<RealmText value={r.realm!} />
|
||||
|
@ -104,8 +102,9 @@ export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
{realmList.length > 5 && (
|
||||
{realms.length > 5 && (
|
||||
<ContextSelector
|
||||
data-testid="realmSelector"
|
||||
toggleText={toUpperCase(realm)}
|
||||
isOpen={open}
|
||||
screenReaderLabel={toUpperCase(realm)}
|
||||
|
@ -117,8 +116,10 @@ export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
|
|||
} else {
|
||||
element = r as ReactElement;
|
||||
}
|
||||
const value = element.props.value || "master";
|
||||
selectRealm(value);
|
||||
const value = element.props.value;
|
||||
if (value) {
|
||||
selectRealm(value);
|
||||
}
|
||||
}}
|
||||
searchInputValue={search}
|
||||
onSearchInputChange={(value) => setSearch(value)}
|
||||
|
@ -130,7 +131,7 @@ export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
|
|||
<RealmText value={realm} /> <Label>{t("recent")}</Label>
|
||||
</ContextSelectorItem>
|
||||
))}
|
||||
{filteredItems
|
||||
{(filteredItems || realms)
|
||||
.filter((r) => !recentUsed.used.includes(r.realm!))
|
||||
.map((item) => (
|
||||
<ContextSelectorItem key={item.id}>
|
||||
|
@ -142,14 +143,15 @@ export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
|
|||
</ContextSelectorItem>
|
||||
</ContextSelector>
|
||||
)}
|
||||
{realmList.length <= 5 && (
|
||||
{realms.length <= 5 && (
|
||||
<Dropdown
|
||||
id="realm-select"
|
||||
data-testid="realmSelector"
|
||||
className="keycloak__realm_selector__dropdown"
|
||||
isOpen={open}
|
||||
toggle={
|
||||
<DropdownToggle
|
||||
id="realm-select-toggle"
|
||||
data-testid="realmSelectorToggle"
|
||||
onToggle={() => setOpen(!open)}
|
||||
className="keycloak__realm_selector_dropdown__toggle"
|
||||
>
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
import React from "react";
|
||||
import { mount } from "enzyme";
|
||||
import { act } from "@testing-library/react";
|
||||
|
||||
import { RealmSelector } from "../RealmSelector";
|
||||
import { RealmContextProvider } from "../../../context/realm-context/RealmContext";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
|
||||
it("renders realm selector", async () => {
|
||||
const wrapper = mount(
|
||||
<MemoryRouter>
|
||||
<RealmContextProvider>
|
||||
<div id="realm">
|
||||
<RealmSelector realmList={[{ id: "321", realm: "another" }]} />
|
||||
</div>
|
||||
</RealmContextProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
const expandButton = wrapper.find("button");
|
||||
act(() => {
|
||||
expandButton!.simulate("click");
|
||||
});
|
||||
|
||||
expect(wrapper.find("#realm")).toMatchSnapshot();
|
||||
});
|
|
@ -1,305 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders realm selector 1`] = `
|
||||
<div
|
||||
id="realm"
|
||||
>
|
||||
<RealmSelector
|
||||
realmList={
|
||||
Array [
|
||||
Object {
|
||||
"id": "321",
|
||||
"realm": "another",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<Dropdown
|
||||
className="keycloak__realm_selector__dropdown"
|
||||
dropdownItems={
|
||||
Array [
|
||||
<DropdownItem
|
||||
onClick={[Function]}
|
||||
>
|
||||
<RealmText
|
||||
value="another"
|
||||
/>
|
||||
</DropdownItem>,
|
||||
<React.Fragment />,
|
||||
]
|
||||
}
|
||||
id="realm-select"
|
||||
isOpen={false}
|
||||
toggle={
|
||||
<DropdownToggle
|
||||
className="keycloak__realm_selector_dropdown__toggle"
|
||||
id="realm-select-toggle"
|
||||
onToggle={[Function]}
|
||||
>
|
||||
|
||||
</DropdownToggle>
|
||||
}
|
||||
>
|
||||
<DropdownWithContext
|
||||
autoFocus={true}
|
||||
className="keycloak__realm_selector__dropdown"
|
||||
direction="down"
|
||||
dropdownItems={
|
||||
Array [
|
||||
<DropdownItem
|
||||
onClick={[Function]}
|
||||
>
|
||||
<RealmText
|
||||
value="another"
|
||||
/>
|
||||
</DropdownItem>,
|
||||
<React.Fragment />,
|
||||
]
|
||||
}
|
||||
id="realm-select"
|
||||
isGrouped={false}
|
||||
isOpen={false}
|
||||
isPlain={false}
|
||||
menuAppendTo="inline"
|
||||
onSelect={[Function]}
|
||||
position="left"
|
||||
toggle={
|
||||
<DropdownToggle
|
||||
className="keycloak__realm_selector_dropdown__toggle"
|
||||
id="realm-select-toggle"
|
||||
onToggle={[Function]}
|
||||
>
|
||||
|
||||
</DropdownToggle>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="pf-c-dropdown keycloak__realm_selector__dropdown"
|
||||
data-ouia-component-id="OUIA-Generated-Dropdown-1"
|
||||
data-ouia-component-type="PF4/Dropdown"
|
||||
data-ouia-safe={true}
|
||||
id="realm-select"
|
||||
>
|
||||
<DropdownToggle
|
||||
aria-haspopup={true}
|
||||
className="keycloak__realm_selector_dropdown__toggle"
|
||||
getMenuRef={[Function]}
|
||||
id="realm-select-toggle"
|
||||
isOpen={false}
|
||||
isPlain={false}
|
||||
key=".0"
|
||||
onEnter={[Function]}
|
||||
onToggle={[Function]}
|
||||
parentRef={
|
||||
Object {
|
||||
"current": <div
|
||||
class="pf-c-dropdown pf-m-expanded keycloak__realm_selector__dropdown"
|
||||
data-ouia-component-id="OUIA-Generated-Dropdown-1"
|
||||
data-ouia-component-type="PF4/Dropdown"
|
||||
data-ouia-safe="true"
|
||||
id="realm-select"
|
||||
>
|
||||
<button
|
||||
aria-expanded="true"
|
||||
aria-haspopup="true"
|
||||
class="pf-c-dropdown__toggle keycloak__realm_selector_dropdown__toggle"
|
||||
data-ouia-component-id="OUIA-Generated-DropdownToggle-1"
|
||||
data-ouia-component-type="PF4/DropdownToggle"
|
||||
data-ouia-safe="true"
|
||||
id="realm-select-toggle"
|
||||
type="button"
|
||||
>
|
||||
|
||||
<span
|
||||
class="pf-c-dropdown__toggle-icon"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
role="img"
|
||||
style="vertical-align: -0.125em;"
|
||||
viewBox="0 0 320 512"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
<ul
|
||||
aria-labelledby="realm-select-toggle"
|
||||
class="pf-c-dropdown__menu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
role="menuitem"
|
||||
>
|
||||
<a
|
||||
aria-disabled="false"
|
||||
class="pf-c-dropdown__menu-item"
|
||||
data-ouia-component-id="OUIA-Generated-DropdownItem-1"
|
||||
data-ouia-component-type="PF4/DropdownItem"
|
||||
data-ouia-safe="true"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="pf-l-split keycloak__realm_selector__list-item-split"
|
||||
>
|
||||
<div
|
||||
class="pf-l-split__item pf-m-fill"
|
||||
>
|
||||
Another
|
||||
</div>
|
||||
<div
|
||||
class="pf-l-split__item"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Toggle
|
||||
aria-haspopup={true}
|
||||
bubbleEvent={false}
|
||||
className="keycloak__realm_selector_dropdown__toggle"
|
||||
data-ouia-component-id="OUIA-Generated-DropdownToggle-1"
|
||||
data-ouia-component-type="PF4/DropdownToggle"
|
||||
data-ouia-safe={true}
|
||||
getMenuRef={[Function]}
|
||||
id="realm-select-toggle"
|
||||
isActive={false}
|
||||
isDisabled={false}
|
||||
isOpen={false}
|
||||
isPlain={false}
|
||||
isPrimary={false}
|
||||
isSplitButton={false}
|
||||
onEnter={[Function]}
|
||||
onToggle={[Function]}
|
||||
parentRef={
|
||||
Object {
|
||||
"current": <div
|
||||
class="pf-c-dropdown pf-m-expanded keycloak__realm_selector__dropdown"
|
||||
data-ouia-component-id="OUIA-Generated-Dropdown-1"
|
||||
data-ouia-component-type="PF4/Dropdown"
|
||||
data-ouia-safe="true"
|
||||
id="realm-select"
|
||||
>
|
||||
<button
|
||||
aria-expanded="true"
|
||||
aria-haspopup="true"
|
||||
class="pf-c-dropdown__toggle keycloak__realm_selector_dropdown__toggle"
|
||||
data-ouia-component-id="OUIA-Generated-DropdownToggle-1"
|
||||
data-ouia-component-type="PF4/DropdownToggle"
|
||||
data-ouia-safe="true"
|
||||
id="realm-select-toggle"
|
||||
type="button"
|
||||
>
|
||||
|
||||
<span
|
||||
class="pf-c-dropdown__toggle-icon"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
role="img"
|
||||
style="vertical-align: -0.125em;"
|
||||
viewBox="0 0 320 512"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
<ul
|
||||
aria-labelledby="realm-select-toggle"
|
||||
class="pf-c-dropdown__menu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
role="menuitem"
|
||||
>
|
||||
<a
|
||||
aria-disabled="false"
|
||||
class="pf-c-dropdown__menu-item"
|
||||
data-ouia-component-id="OUIA-Generated-DropdownItem-1"
|
||||
data-ouia-component-type="PF4/DropdownItem"
|
||||
data-ouia-safe="true"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="pf-l-split keycloak__realm_selector__list-item-split"
|
||||
>
|
||||
<div
|
||||
class="pf-l-split__item pf-m-fill"
|
||||
>
|
||||
Another
|
||||
</div>
|
||||
<div
|
||||
class="pf-l-split__item"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>,
|
||||
}
|
||||
}
|
||||
>
|
||||
<button
|
||||
aria-expanded={false}
|
||||
aria-haspopup={true}
|
||||
className="pf-c-dropdown__toggle keycloak__realm_selector_dropdown__toggle"
|
||||
data-ouia-component-id="OUIA-Generated-DropdownToggle-1"
|
||||
data-ouia-component-type="PF4/DropdownToggle"
|
||||
data-ouia-safe={true}
|
||||
disabled={false}
|
||||
id="realm-select-toggle"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="pf-c-dropdown__toggle-icon"
|
||||
>
|
||||
<CaretDownIcon
|
||||
color="currentColor"
|
||||
noVerticalAlign={false}
|
||||
size="sm"
|
||||
>
|
||||
<svg
|
||||
aria-hidden={true}
|
||||
aria-labelledby={null}
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"verticalAlign": "-0.125em",
|
||||
}
|
||||
}
|
||||
viewBox="0 0 320 512"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"
|
||||
/>
|
||||
</svg>
|
||||
</CaretDownIcon>
|
||||
</span>
|
||||
</button>
|
||||
</Toggle>
|
||||
</DropdownToggle>
|
||||
</div>
|
||||
</DropdownWithContext>
|
||||
</Dropdown>
|
||||
</RealmSelector>
|
||||
</div>
|
||||
`;
|
|
@ -1,307 +0,0 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useErrorHandler } from "react-error-boundary";
|
||||
import _ from "lodash";
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Chip,
|
||||
ChipGroup,
|
||||
Divider,
|
||||
Modal,
|
||||
ModalVariant,
|
||||
Select,
|
||||
SelectGroup,
|
||||
SelectOption,
|
||||
SelectVariant,
|
||||
ToolbarItem,
|
||||
} from "@patternfly/react-core";
|
||||
|
||||
import { KeycloakDataTable } from "../table-toolbar/KeycloakDataTable";
|
||||
import {
|
||||
asyncStateFetch,
|
||||
useAdminClient,
|
||||
} from "../../context/auth/AdminClient";
|
||||
import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation";
|
||||
import { FilterIcon } from "@patternfly/react-icons";
|
||||
import { Row, ServiceRole } from "./RoleMapping";
|
||||
import RoleRepresentation from "keycloak-admin/lib/defs/roleRepresentation";
|
||||
|
||||
export type MappingType = "service-account" | "client-scope";
|
||||
|
||||
type AddRoleMappingModalProps = {
|
||||
id: string;
|
||||
type: MappingType;
|
||||
name: string;
|
||||
onAssign: (rows: Row[]) => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
type ClientRole = ClientRepresentation & {
|
||||
numberOfRoles: number;
|
||||
};
|
||||
|
||||
const realmRole = {
|
||||
name: "realmRoles",
|
||||
} as ClientRepresentation;
|
||||
|
||||
export const AddRoleMappingModal = ({
|
||||
id,
|
||||
name,
|
||||
type,
|
||||
onAssign,
|
||||
onClose,
|
||||
}: AddRoleMappingModalProps) => {
|
||||
const { t } = useTranslation("clients");
|
||||
const adminClient = useAdminClient();
|
||||
const errorHandler = useErrorHandler();
|
||||
|
||||
const [clients, setClients] = useState<ClientRole[]>([]);
|
||||
const [name, setName] = useState<string>();
|
||||
const [searchToggle, setSearchToggle] = useState(false);
|
||||
|
||||
const [key, setKey] = useState(0);
|
||||
const refresh = () => setKey(new Date().getTime());
|
||||
|
||||
const [selectedClients, setSelectedClients] = useState<ClientRole[]>([]);
|
||||
const [selectedRows, setSelectedRows] = useState<Row[]>([]);
|
||||
|
||||
useEffect(
|
||||
() =>
|
||||
asyncStateFetch(
|
||||
async () => {
|
||||
const clients = await adminClient.clients.find();
|
||||
setName(clients.find((client) => client.id === clientId)?.clientId);
|
||||
return (
|
||||
await Promise.all(
|
||||
clients.map(async (client) => {
|
||||
let roles: RoleRepresentation[] = [];
|
||||
if (type === "service-account") {
|
||||
roles = await adminClient.users.listAvailableClientRoleMappings(
|
||||
{
|
||||
id: id,
|
||||
clientUniqueId: client.id!,
|
||||
}
|
||||
);
|
||||
} else if (type === "client-scope") {
|
||||
roles = await adminClient.clientScopes.listAvailableClientScopeMappings(
|
||||
{
|
||||
id,
|
||||
client: client.id!,
|
||||
}
|
||||
);
|
||||
}
|
||||
return {
|
||||
roles,
|
||||
client,
|
||||
};
|
||||
})
|
||||
)
|
||||
)
|
||||
.flat()
|
||||
.filter((row) => row.roles.length !== 0)
|
||||
.map((row) => {
|
||||
return { ...row.client, numberOfRoles: row.roles.length };
|
||||
});
|
||||
},
|
||||
(clients) => {
|
||||
setClients(clients);
|
||||
},
|
||||
errorHandler
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(refresh, [searchToggle]);
|
||||
|
||||
const removeClient = (client: ClientRole) => {
|
||||
setSelectedClients(selectedClients.filter((item) => item.id !== client.id));
|
||||
};
|
||||
|
||||
const loader = async () => {
|
||||
const realmRolesSelected = _.findIndex(
|
||||
selectedClients,
|
||||
(client) => client.name === "realmRoles"
|
||||
);
|
||||
let selected = selectedClients;
|
||||
if (realmRolesSelected !== -1) {
|
||||
selected = selectedClients.filter(
|
||||
(client) => client.name !== "realmRoles"
|
||||
);
|
||||
}
|
||||
|
||||
let availableRoles: RoleRepresentation[] = [];
|
||||
if (type === "service-account") {
|
||||
availableRoles = await adminClient.users.listAvailableRealmRoleMappings({
|
||||
id,
|
||||
});
|
||||
} else if (type === "client-scope") {
|
||||
availableRoles = await adminClient.clientScopes.listAvailableRealmScopeMappings(
|
||||
{ id }
|
||||
);
|
||||
}
|
||||
const realmRoles = availableRoles.map((role) => {
|
||||
return {
|
||||
role,
|
||||
client: undefined,
|
||||
};
|
||||
});
|
||||
|
||||
const allClients =
|
||||
selectedClients.length !== 0
|
||||
? selected
|
||||
: await adminClient.clients.find();
|
||||
|
||||
const roles = (
|
||||
await Promise.all(
|
||||
allClients.map(async (client) => {
|
||||
let clientAvailableRoles: RoleRepresentation[] = [];
|
||||
if (type === "service-account") {
|
||||
clientAvailableRoles = await adminClient.users.listAvailableClientRoleMappings(
|
||||
{
|
||||
id,
|
||||
clientUniqueId: client.id!,
|
||||
}
|
||||
);
|
||||
} else if (type === "client-scope") {
|
||||
clientAvailableRoles = await adminClient.clientScopes.listAvailableClientScopeMappings(
|
||||
{ id, client: client.id! }
|
||||
);
|
||||
}
|
||||
return clientAvailableRoles.map((role) => {
|
||||
return {
|
||||
role,
|
||||
client,
|
||||
};
|
||||
});
|
||||
})
|
||||
)
|
||||
).flat();
|
||||
|
||||
return [
|
||||
...(realmRolesSelected !== -1 || selected.length === 0 ? realmRoles : []),
|
||||
...roles,
|
||||
];
|
||||
};
|
||||
|
||||
const createSelectGroup = (clients: ClientRepresentation[]) => [
|
||||
<SelectGroup key="role" label={t("realmRoles")}>
|
||||
<SelectOption key="realmRoles" value={realmRole}>
|
||||
{t("realmRoles")}
|
||||
</SelectOption>
|
||||
</SelectGroup>,
|
||||
<Divider key="divider" />,
|
||||
<SelectGroup key="group" label={t("clients")}>
|
||||
{clients.map((client) => (
|
||||
<SelectOption key={client.id} value={client}>
|
||||
{client.clientId}
|
||||
</SelectOption>
|
||||
))}
|
||||
</SelectGroup>,
|
||||
];
|
||||
|
||||
return (
|
||||
<Modal
|
||||
variant={ModalVariant.large}
|
||||
<<<<<<< HEAD:src/clients/service-account/AddServiceAccountModal.tsx
|
||||
title={t("assignRolesTo", {
|
||||
client: name,
|
||||
})}
|
||||
=======
|
||||
title={t("assignRolesTo", { client: name })}
|
||||
>>>>>>> 0f6f6ab (fixed assign):src/components/role-mapping/AddRoleMappingModal.tsx
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
actions={[
|
||||
<Button
|
||||
data-testid="assign"
|
||||
key="confirm"
|
||||
isDisabled={selectedRows?.length === 0}
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
onAssign(selectedRows);
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t("assign")}
|
||||
</Button>,
|
||||
<Button
|
||||
data-testid="cancel"
|
||||
key="cancel"
|
||||
variant="link"
|
||||
onClick={onClose}
|
||||
>
|
||||
{t("common:cancel")}
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
toggleId="role"
|
||||
onToggle={() => setSearchToggle(!searchToggle)}
|
||||
isOpen={searchToggle}
|
||||
variant={SelectVariant.checkbox}
|
||||
hasInlineFilter
|
||||
menuAppendTo="parent"
|
||||
placeholderText={
|
||||
<>
|
||||
<FilterIcon /> {t("filterByOrigin")}
|
||||
</>
|
||||
}
|
||||
isGrouped
|
||||
onFilter={(evt) => {
|
||||
const value = evt?.target.value || "";
|
||||
return createSelectGroup(
|
||||
clients.filter((client) => client.clientId?.includes(value))
|
||||
);
|
||||
}}
|
||||
selections={selectedClients}
|
||||
onClear={() => setSelectedClients([])}
|
||||
onSelect={(_, selection) => {
|
||||
const client = selection as ClientRole;
|
||||
if (selectedClients.includes(client)) {
|
||||
removeClient(client);
|
||||
} else {
|
||||
setSelectedClients([...selectedClients, client]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{createSelectGroup(clients)}
|
||||
</Select>
|
||||
<ToolbarItem variant="chip-group">
|
||||
<ChipGroup>
|
||||
{selectedClients.map((client) => (
|
||||
<Chip
|
||||
key={`chip-${client.id}`}
|
||||
onClick={() => {
|
||||
removeClient(client);
|
||||
refresh();
|
||||
}}
|
||||
>
|
||||
{client.clientId || t("realmRoles")}
|
||||
<Badge isRead={true}>{client.numberOfRoles}</Badge>
|
||||
</Chip>
|
||||
))}
|
||||
</ChipGroup>
|
||||
</ToolbarItem>
|
||||
|
||||
<KeycloakDataTable
|
||||
key={key}
|
||||
onSelect={(rows) => setSelectedRows([...rows])}
|
||||
searchPlaceholderKey="clients:searchByRoleName"
|
||||
canSelectAll={false}
|
||||
loader={loader}
|
||||
ariaLabelKey="clients:roles"
|
||||
columns={[
|
||||
{
|
||||
name: "name",
|
||||
cellRenderer: ServiceRole,
|
||||
},
|
||||
{
|
||||
name: "role.description",
|
||||
displayKey: t("description"),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
};
|
|
@ -290,7 +290,6 @@ export function KeycloakDataTable<T>({
|
|||
)
|
||||
)
|
||||
);
|
||||
setSearch;
|
||||
};
|
||||
|
||||
const convertAction = () =>
|
||||
|
|
|
@ -1,20 +1,12 @@
|
|||
import { createContext, useContext } from "react";
|
||||
import KeycloakAdminClient from "keycloak-admin";
|
||||
import { RealmContext } from "../realm-context/RealmContext";
|
||||
|
||||
export const AdminClient = createContext<KeycloakAdminClient | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
export const useAdminClient = () => {
|
||||
const adminClient = useContext(AdminClient)!;
|
||||
const { realm } = useContext(RealmContext);
|
||||
|
||||
adminClient.setConfig({
|
||||
realmName: realm,
|
||||
});
|
||||
|
||||
return adminClient;
|
||||
return useContext(AdminClient)!;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
import React, { useContext, useState } from "react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import _ from "lodash";
|
||||
|
||||
import RealmRepresentation from "keycloak-admin/lib/defs/realmRepresentation";
|
||||
import { RecentUsed } from "../../components/realm-selector/recent-used";
|
||||
import { useErrorHandler } from "react-error-boundary";
|
||||
import { asyncStateFetch, useAdminClient } from "../auth/AdminClient";
|
||||
import { WhoAmIContext } from "../whoami/WhoAmI";
|
||||
|
||||
type RealmContextType = {
|
||||
realm: string;
|
||||
setRealm: (realm: string) => void;
|
||||
realms: RealmRepresentation[];
|
||||
refresh: () => Promise<void>;
|
||||
};
|
||||
|
||||
export const RealmContext = React.createContext<RealmContextType>({
|
||||
realm: "",
|
||||
setRealm: () => {},
|
||||
realms: [],
|
||||
refresh: () => Promise.resolve(),
|
||||
});
|
||||
|
||||
type RealmContextProviderProps = { children: React.ReactNode };
|
||||
|
@ -16,16 +26,52 @@ type RealmContextProviderProps = { children: React.ReactNode };
|
|||
export const RealmContextProvider = ({
|
||||
children,
|
||||
}: RealmContextProviderProps) => {
|
||||
const [realm, setRealm] = useState("");
|
||||
const { whoAmI } = useContext(WhoAmIContext);
|
||||
const [realm, setRealm] = useState(whoAmI.getHomeRealm());
|
||||
const [realms, setRealms] = useState<RealmRepresentation[]>([]);
|
||||
const adminClient = useAdminClient();
|
||||
const errorHandler = useErrorHandler();
|
||||
const recentUsed = new RecentUsed();
|
||||
|
||||
const set = (realm: string) => {
|
||||
recentUsed.setRecentUsed(realm);
|
||||
setRealm(realm);
|
||||
const updateRealmsList = (realms: RealmRepresentation[]) => {
|
||||
setRealms(_.sortBy(realms, "realm"));
|
||||
recentUsed.clean(realms.map((r) => r.realm!));
|
||||
};
|
||||
|
||||
useEffect(
|
||||
() =>
|
||||
asyncStateFetch(
|
||||
() => adminClient.realms.find(),
|
||||
(realms) => updateRealmsList(realms),
|
||||
errorHandler
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
const set = (realm: string) => {
|
||||
if (
|
||||
realms.length === 0 ||
|
||||
realms.findIndex((r) => r.realm == realm) !== -1
|
||||
) {
|
||||
recentUsed.setRecentUsed(realm);
|
||||
setRealm(realm);
|
||||
adminClient.setConfig({
|
||||
realmName: realm,
|
||||
});
|
||||
}
|
||||
};
|
||||
return (
|
||||
<RealmContext.Provider value={{ realm, setRealm: set }}>
|
||||
<RealmContext.Provider
|
||||
value={{
|
||||
realm,
|
||||
setRealm: set,
|
||||
realms,
|
||||
refresh: async () => {
|
||||
const list = await adminClient.realms.find();
|
||||
updateRealmsList(list);
|
||||
},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</RealmContext.Provider>
|
||||
);
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import React, { useContext, useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useErrorHandler } from "react-error-boundary";
|
||||
import i18n from "../../i18n";
|
||||
|
||||
import { AdminClient, asyncStateFetch } from "../auth/AdminClient";
|
||||
import { RealmContext } from "../realm-context/RealmContext";
|
||||
import { asyncStateFetch, useAdminClient } from "../auth/AdminClient";
|
||||
import WhoAmIRepresentation, {
|
||||
AccessType,
|
||||
} from "keycloak-admin/lib/defs/whoAmIRepresentation";
|
||||
|
@ -62,23 +61,16 @@ export const WhoAmIContext = React.createContext<WhoAmIProps>({
|
|||
|
||||
type WhoAmIProviderProps = { children: React.ReactNode };
|
||||
export const WhoAmIContextProvider = ({ children }: WhoAmIProviderProps) => {
|
||||
const adminClient = useContext(AdminClient)!;
|
||||
const adminClient = useAdminClient();
|
||||
const handleError = useErrorHandler();
|
||||
const { realm, setRealm } = useContext(RealmContext);
|
||||
const [whoAmI, setWhoAmI] = useState<WhoAmI>(new WhoAmI());
|
||||
const [key, setKey] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
return asyncStateFetch(
|
||||
() =>
|
||||
adminClient.whoAmI.find({
|
||||
realm: adminClient.keycloak?.realm,
|
||||
}),
|
||||
() => adminClient.whoAmI.find({ realm: "master" }),
|
||||
(me) => {
|
||||
const whoAmI = new WhoAmI(adminClient.keycloak?.realm, me);
|
||||
if (!realm) {
|
||||
setRealm(whoAmI.getHomeRealm());
|
||||
}
|
||||
setWhoAmI(whoAmI);
|
||||
},
|
||||
handleError
|
||||
|
|
|
@ -43,7 +43,7 @@ const RealmSettingsHeader = ({
|
|||
const adminClient = useAdminClient();
|
||||
const { addAlert } = useAlerts();
|
||||
const history = useHistory();
|
||||
const { setRealm } = useRealm();
|
||||
const { refresh } = useRealm();
|
||||
const [partialImportOpen, setPartialImportOpen] = useState(false);
|
||||
|
||||
const [toggleDisableDialog, DisableConfirm] = useConfirmDialog({
|
||||
|
@ -65,8 +65,8 @@ const RealmSettingsHeader = ({
|
|||
try {
|
||||
await adminClient.realms.del({ realm: realmName });
|
||||
addAlert(t("deletedSuccess"), AlertVariant.success);
|
||||
setRealm("master");
|
||||
history.push("/master/");
|
||||
refresh();
|
||||
} catch (error) {
|
||||
addAlert(t("deleteError", { error }), AlertVariant.danger);
|
||||
}
|
||||
|
|
|
@ -19,11 +19,13 @@ import RealmRepresentation from "keycloak-admin/lib/defs/realmRepresentation";
|
|||
import { useAdminClient } from "../../context/auth/AdminClient";
|
||||
import { WhoAmIContext } from "../../context/whoami/WhoAmI";
|
||||
import { FormAccess } from "../../components/form-access/FormAccess";
|
||||
import { useRealm } from "../../context/realm-context/RealmContext";
|
||||
|
||||
export const NewRealmForm = () => {
|
||||
const { t } = useTranslation("realm");
|
||||
const history = useHistory();
|
||||
const { refresh } = useContext(WhoAmIContext);
|
||||
const { refresh: realmRefresh } = useRealm();
|
||||
const adminClient = useAdminClient();
|
||||
const { addAlert } = useAlerts();
|
||||
|
||||
|
@ -56,10 +58,12 @@ export const NewRealmForm = () => {
|
|||
try {
|
||||
await adminClient.realms.create(realm);
|
||||
addAlert(t("saveRealmSuccess"), AlertVariant.success);
|
||||
refresh();
|
||||
|
||||
//force token update
|
||||
refresh();
|
||||
await adminClient.keycloak?.updateToken(Number.MAX_VALUE);
|
||||
history.push(`/${realm.realm}/`);
|
||||
await realmRefresh();
|
||||
history.push(`/${realm.realm}`);
|
||||
} catch (error) {
|
||||
addAlert(
|
||||
t("saveRealmError", {
|
||||
|
|
|
@ -43,7 +43,12 @@ export const MockAdminClient = (props: {
|
|||
>
|
||||
<WhoAmIContextProvider>
|
||||
<RealmContext.Provider
|
||||
value={{ realm: "master", setRealm: () => {} }}
|
||||
value={{
|
||||
realm: "master",
|
||||
setRealm: () => {},
|
||||
realms: [],
|
||||
refresh: () => Promise.resolve(),
|
||||
}}
|
||||
>
|
||||
<AccessContextProvider>{props.children}</AccessContextProvider>
|
||||
</RealmContext.Provider>
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
import { Meta } from "@storybook/react";
|
||||
|
||||
import { RealmSelector } from "../components/realm-selector/RealmSelector";
|
||||
import { RealmContextProvider } from "../context/realm-context/RealmContext";
|
||||
import { RealmContext } from "../context/realm-context/RealmContext";
|
||||
import { HashRouter } from "react-router-dom";
|
||||
|
||||
export default {
|
||||
|
@ -20,20 +20,24 @@ export default {
|
|||
export const Header = () => {
|
||||
return (
|
||||
<HashRouter>
|
||||
<RealmContextProvider>
|
||||
<RealmContext.Provider
|
||||
value={{
|
||||
realm: "master",
|
||||
setRealm: () => {},
|
||||
realms: [
|
||||
{ id: "master", realm: "Master" },
|
||||
{ id: "photoz", realm: "Photoz" },
|
||||
],
|
||||
refresh: () => Promise.resolve(),
|
||||
}}
|
||||
>
|
||||
<Page
|
||||
sidebar={
|
||||
<PageSidebar
|
||||
nav={
|
||||
<Nav>
|
||||
<NavList>
|
||||
<RealmSelector
|
||||
realmList={[
|
||||
{ id: "master", realm: "Master" },
|
||||
{ id: "photoz", realm: "Photoz" },
|
||||
]}
|
||||
/>
|
||||
|
||||
<RealmSelector />
|
||||
<NavItem id="default-link1" to="#default-link1" itemId={0}>
|
||||
Link 1
|
||||
</NavItem>
|
||||
|
@ -57,7 +61,7 @@ export const Header = () => {
|
|||
/>
|
||||
}
|
||||
/>
|
||||
</RealmContextProvider>
|
||||
</RealmContext.Provider>
|
||||
</HashRouter>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue