@keycloakify/keycloak-admin-ui
Version:
Repackaged Keycloak Admin UI
253 lines • 13.8 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { KeycloakSelect } from "../ui-shared";
import { Alert, Button, ButtonVariant, Checkbox, DataList, DataListCell, DataListItem, DataListItemCells, DataListItemRow, Divider, Label, Modal, ModalVariant, SelectOption, Stack, StackItem, Text, TextContent, } from "@patternfly/react-core";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useAdminClient } from "../admin-client";
import { useAlerts } from "../ui-shared";
import { JsonFileUpload } from "../components/json-file-upload/JsonFileUpload";
import { KeycloakDataTable } from "../ui-shared";
import { useRealm } from "../context/realm-context/RealmContext";
const INITIAL_RESOURCES = {
users: false,
clients: false,
groups: false,
identityProviders: false,
realmRoles: false,
clientRoles: false,
};
export const PartialImportDialog = (props) => {
const { adminClient } = useAdminClient();
const { t } = useTranslation();
const { realm } = useRealm();
const [importedFile, setImportedFile] = useState();
const isFileSelected = !!importedFile;
const [isRealmSelectOpen, setIsRealmSelectOpen] = useState(false);
const [isCollisionSelectOpen, setIsCollisionSelectOpen] = useState(false);
const [importInProgress, setImportInProgress] = useState(false);
const [collisionOption, setCollisionOption] = useState("FAIL");
const [targetRealm, setTargetRealm] = useState({});
const [importResponse, setImportResponse] = useState();
const { addError } = useAlerts();
const [resourcesToImport, setResourcesToImport] = useState(INITIAL_RESOURCES);
const isAnyResourceChecked = Object.values(resourcesToImport).some((checked) => checked);
const resetResourcesToImport = () => {
setResourcesToImport(INITIAL_RESOURCES);
};
const resetInputState = () => {
setImportedFile(undefined);
setTargetRealm({});
setCollisionOption("FAIL");
resetResourcesToImport();
};
// when dialog opens or closes, clear state
useEffect(() => {
setImportInProgress(false);
setImportResponse(undefined);
resetInputState();
}, [props.open]);
const handleFileChange = (value) => {
resetInputState();
setImportedFile(value);
if (!Array.isArray(value)) {
setTargetRealm(value);
}
else if (value.length > 0) {
setTargetRealm(value[0]);
}
};
const handleRealmSelect = (realm) => {
setTargetRealm(realm);
setIsRealmSelectOpen(false);
resetResourcesToImport();
};
const handleResourceCheckBox = (checked, event) => {
const resource = event.currentTarget.name;
setResourcesToImport({
...resourcesToImport,
[resource]: checked,
});
};
const realmSelectOptions = (realms) => realms.map((realm) => (_jsx(SelectOption, { value: realm, "data-testid": realm.id + "-select-option", children: realm.realm || realm.id }, realm.id)));
const handleCollisionSelect = (option) => {
setCollisionOption(option);
setIsCollisionSelectOpen(false);
};
const collisionOptions = () => {
return [
_jsx(SelectOption, { value: "FAIL", children: t("FAIL") }, "fail"),
_jsx(SelectOption, { value: "SKIP", children: t("SKIP") }, "skip"),
_jsx(SelectOption, { value: "OVERWRITE", children: t("OVERWRITE") }, "overwrite"),
];
};
const targetHasResources = () => {
return (targetHasResource("users") ||
targetHasResource("groups") ||
targetHasResource("clients") ||
targetHasResource("identityProviders") ||
targetHasRealmRoles() ||
targetHasClientRoles());
};
const targetHasResource = (resource) => {
const value = targetRealm[resource];
return value !== undefined && value.length > 0;
};
const targetHasRealmRoles = () => {
var _a;
const value = (_a = targetRealm.roles) === null || _a === void 0 ? void 0 : _a.realm;
return value !== undefined && value.length > 0;
};
const targetHasClientRoles = () => {
var _a;
const value = (_a = targetRealm.roles) === null || _a === void 0 ? void 0 : _a.client;
return value !== undefined && Object.keys(value).length > 0;
};
const itemCount = (resource) => {
var _a, _b, _c, _d, _e;
if (!isFileSelected)
return 0;
if (resource === "realmRoles") {
return (_c = (_b = (_a = targetRealm.roles) === null || _a === void 0 ? void 0 : _a.realm) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0;
}
if (resource === "clientRoles") {
return targetHasClientRoles()
? clientRolesCount(targetRealm.roles.client)
: 0;
}
return (_e = (_d = targetRealm[resource]) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0;
};
const clientRolesCount = (clientRoles) => Object.values(clientRoles).reduce((total, role) => total + role.length, 0);
const resourceDataListItem = (resource, resourceDisplayName) => {
return (_jsx(DataListItem, { "aria-labelledby": `${resource}-list-item`, children: _jsx(DataListItemRow, { children: _jsx(DataListItemCells, { dataListCells: [
_jsx(DataListCell, { children: _jsx(Checkbox, { id: `${resource}-checkbox`, label: `${itemCount(resource)} ${resourceDisplayName}`, "aria-labelledby": `${resource}-checkbox`, name: resource, isChecked: resourcesToImport[resource], onChange: (event, checked) => handleResourceCheckBox(checked, event), "data-testid": resource + "-checkbox" }) }, resource),
] }) }) }));
};
const jsonForImport = () => {
var _a, _b;
const jsonToImport = {
ifResourceExists: collisionOption,
id: targetRealm.id,
realm: targetRealm.realm,
};
if (resourcesToImport["users"])
jsonToImport.users = targetRealm.users;
if (resourcesToImport["groups"])
jsonToImport.groups = targetRealm.groups;
if (resourcesToImport["identityProviders"])
jsonToImport.identityProviders = targetRealm.identityProviders;
if (resourcesToImport["clients"])
jsonToImport.clients = targetRealm.clients;
if (resourcesToImport["realmRoles"] || resourcesToImport["clientRoles"]) {
jsonToImport.roles = targetRealm.roles;
if (!resourcesToImport["realmRoles"])
(_a = jsonToImport.roles) === null || _a === void 0 ? true : delete _a.realm;
if (!resourcesToImport["clientRoles"])
(_b = jsonToImport.roles) === null || _b === void 0 ? true : delete _b.client;
}
return jsonToImport;
};
async function doImport() {
if (importInProgress)
return;
setImportInProgress(true);
try {
const importResults = await adminClient.realms.partialImport({
realm,
rep: jsonForImport(),
});
setImportResponse(importResults);
}
catch (error) {
addError("importFail", error);
}
setImportInProgress(false);
}
const importModal = () => {
return (_jsx(Modal, { variant: ModalVariant.medium, title: t("partialImport"), isOpen: props.open, onClose: props.toggleDialog, actions: [
_jsx(Button, { id: "modal-import", "data-testid": "import-button", isDisabled: !isAnyResourceChecked, onClick: () => {
doImport();
}, children: t("import") }, "import"),
_jsx(Button, { id: "modal-cancel", "data-testid": "cancel-button", variant: ButtonVariant.link, onClick: () => {
props.toggleDialog();
}, children: t("cancel") }, "cancel"),
], children: _jsxs(Stack, { hasGutter: true, children: [_jsx(StackItem, { children: _jsx(TextContent, { children: _jsx(Text, { children: t("partialImportHeaderText") }) }) }), _jsx(StackItem, { children: _jsx(JsonFileUpload, { id: "partial-import-file", allowEditingUploadedText: true, onChange: handleFileChange }) }), isFileSelected && targetHasResources() && (_jsxs(_Fragment, { children: [_jsx(StackItem, { children: _jsx(Divider, {}) }), Array.isArray(importedFile) && importedFile.length > 1 && (_jsxs(StackItem, { children: [_jsxs(Text, { children: [t("selectRealm"), ":"] }), _jsx(KeycloakSelect, { toggleId: "realm-selector", isOpen: isRealmSelectOpen, typeAheadAriaLabel: t("realmSelector"), "aria-label": t("realmSelector"), onToggle: () => setIsRealmSelectOpen(!isRealmSelectOpen), selections: targetRealm.id, onSelect: (value) => handleRealmSelect(value), placeholderText: targetRealm.realm || targetRealm.id, children: realmSelectOptions(importedFile) })] })), _jsxs(StackItem, { children: [_jsxs(Text, { children: [t("chooseResources"), ":"] }), _jsxs(DataList, { "aria-label": t("resourcesToImport"), isCompact: true, children: [targetHasResource("users") &&
resourceDataListItem("users", t("users")), targetHasResource("groups") &&
resourceDataListItem("groups", t("groups")), targetHasResource("clients") &&
resourceDataListItem("clients", t("clients")), targetHasResource("identityProviders") &&
resourceDataListItem("identityProviders", t("identityProviders")), targetHasRealmRoles() &&
resourceDataListItem("realmRoles", t("realmRoles")), targetHasClientRoles() &&
resourceDataListItem("clientRoles", t("clientRoles"))] })] }), _jsxs(StackItem, { children: [_jsxs(Text, { children: [t("selectIfResourceExists"), ":"] }), _jsx(KeycloakSelect, { isOpen: isCollisionSelectOpen, direction: "up", onToggle: () => {
setIsCollisionSelectOpen(!isCollisionSelectOpen);
}, selections: collisionOption, onSelect: handleCollisionSelect, placeholderText: t(collisionOption), children: collisionOptions() })] })] }))] }) }));
};
const importCompleteMessage = () => {
return `${t("importAdded", {
count: importResponse === null || importResponse === void 0 ? void 0 : importResponse.added,
})} ${t("importSkipped", {
count: importResponse === null || importResponse === void 0 ? void 0 : importResponse.skipped,
})} ${t("importOverwritten", {
count: importResponse === null || importResponse === void 0 ? void 0 : importResponse.overwritten,
})}`;
};
const loader = async (first = 0, max = 15) => {
if (!importResponse) {
return [];
}
const last = Math.min(first + max, importResponse.results.length);
return importResponse.results.slice(first, last);
};
const ActionLabel = (importRecord) => {
switch (importRecord.action) {
case "ADDED":
return (_jsx(Label, { color: "green", children: t("added") }, importRecord.id));
case "SKIPPED":
return (_jsx(Label, { color: "orange", children: t("skipped") }, importRecord.id));
case "OVERWRITTEN":
return (_jsx(Label, { color: "purple", children: t("overwritten") }, importRecord.id));
default:
return "";
}
};
const TypeRenderer = (importRecord) => {
const typeMap = new Map([
["CLIENT", t("clients")],
["REALM_ROLE", t("realmRoles")],
["USER", t("users")],
["CLIENT_ROLE", t("clientRoles")],
["IDP", t("identityProviders")],
["GROUP", t("groups")],
]);
return _jsx("span", { children: typeMap.get(importRecord.resourceType) });
};
const importCompletedModal = () => {
return (_jsxs(Modal, { variant: ModalVariant.medium, title: t("partialImport"), isOpen: props.open, onClose: props.toggleDialog, actions: [
_jsx(Button, { id: "modal-close", "data-testid": "close-button", variant: ButtonVariant.primary, onClick: () => {
props.toggleDialog();
}, children: t("close") }, "close"),
], children: [_jsx(Alert, { variant: "success", component: "p", isInline: true, title: importCompleteMessage() }), _jsx(KeycloakDataTable, { loader: loader, isPaginated: true, ariaLabelKey: "partialImport", columns: [
{
name: "action",
displayKey: "action",
cellRenderer: ActionLabel,
},
{
name: "resourceType",
displayKey: "type",
cellRenderer: TypeRenderer,
},
{
name: "resourceName",
displayKey: "name",
},
{
name: "id",
displayKey: "id",
},
] })] }));
};
if (!importResponse) {
return importModal();
}
return importCompletedModal();
};
//# sourceMappingURL=PartialImport.js.map