added by configuration dialog into the flow (#221)
* updated the detail screen to support new mapping * fix merge error * added by configuration dialog
This commit is contained in:
parent
8e33519df1
commit
ba761c0526
4 changed files with 138 additions and 65 deletions
|
@ -1,6 +1,6 @@
|
|||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Link, useHistory } from "react-router-dom";
|
||||
import {
|
||||
AlertVariant,
|
||||
ButtonVariant,
|
||||
|
@ -43,6 +43,7 @@ export const MapperList = ({ clientScope, refresh }: MapperListProps) => {
|
|||
const { t } = useTranslation("client-scopes");
|
||||
const adminClient = useAdminClient();
|
||||
const { addAlert } = useAlerts();
|
||||
const history = useHistory();
|
||||
|
||||
const [filteredData, setFilteredData] = useState<
|
||||
{ mapper: ProtocolMapperRepresentation; cells: Row }[]
|
||||
|
@ -53,21 +54,34 @@ export const MapperList = ({ clientScope, refresh }: MapperListProps) => {
|
|||
clientScope.protocol!
|
||||
];
|
||||
|
||||
const [builtInDialogOpen, setBuiltInDialogOpen] = useState(false);
|
||||
const toggleBuiltInMapperDialog = () =>
|
||||
setBuiltInDialogOpen(!builtInDialogOpen);
|
||||
const [addMapperDialogOpen, setAddMapperDialogOpen] = useState(false);
|
||||
const [filter, setFilter] = useState(clientScope.protocolMappers);
|
||||
const toggleAddMapperDialog = (buildIn: boolean) => {
|
||||
if (buildIn) {
|
||||
setFilter(mapperList);
|
||||
} else {
|
||||
setFilter(undefined);
|
||||
}
|
||||
setAddMapperDialogOpen(!addMapperDialogOpen);
|
||||
};
|
||||
|
||||
const addMappers = async (
|
||||
mappers: ProtocolMapperTypeRepresentation | ProtocolMapperRepresentation[]
|
||||
) => {
|
||||
try {
|
||||
await adminClient.clientScopes.addMultipleProtocolMappers(
|
||||
{ id: clientScope.id! },
|
||||
mappers as ProtocolMapperRepresentation[]
|
||||
);
|
||||
refresh();
|
||||
addAlert(t("mappingCreatedSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addAlert(t("mappingCreatedError", { error }), AlertVariant.danger);
|
||||
): Promise<void> => {
|
||||
if (filter === undefined) {
|
||||
const mapper = mappers as ProtocolMapperTypeRepresentation;
|
||||
history.push(`/client-scopes/${clientScope.id}/${mapper.id}`);
|
||||
} else {
|
||||
try {
|
||||
await adminClient.clientScopes.addMultipleProtocolMappers(
|
||||
{ id: clientScope.id! },
|
||||
mappers as ProtocolMapperRepresentation[]
|
||||
);
|
||||
refresh();
|
||||
addAlert(t("mappingCreatedSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addAlert(t("mappingCreatedError", { error }), AlertVariant.danger);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -76,20 +90,20 @@ export const MapperList = ({ clientScope, refresh }: MapperListProps) => {
|
|||
<>
|
||||
<AddMapperDialog
|
||||
protocol={clientScope.protocol!}
|
||||
filter={mapperList || []}
|
||||
filter={filter}
|
||||
onConfirm={addMappers}
|
||||
open={builtInDialogOpen}
|
||||
toggleDialog={toggleBuiltInMapperDialog}
|
||||
open={addMapperDialogOpen}
|
||||
toggleDialog={() => setAddMapperDialogOpen(!addMapperDialogOpen)}
|
||||
/>
|
||||
<ListEmptyState
|
||||
message={t("emptyMappers")}
|
||||
instructions={t("emptyMappersInstructions")}
|
||||
primaryActionText={t("emptyPrimaryAction")}
|
||||
onPrimaryAction={toggleBuiltInMapperDialog}
|
||||
onPrimaryAction={() => toggleAddMapperDialog(true)}
|
||||
secondaryActions={[
|
||||
{
|
||||
text: t("emptySecondaryAction"),
|
||||
onClick: () => {},
|
||||
onClick: () => toggleAddMapperDialog(false),
|
||||
type: ButtonVariant.secondary,
|
||||
},
|
||||
]}
|
||||
|
@ -149,10 +163,16 @@ export const MapperList = ({ clientScope, refresh }: MapperListProps) => {
|
|||
}
|
||||
isOpen={mapperAction}
|
||||
dropdownItems={[
|
||||
<DropdownItem key="predefined" onClick={toggleBuiltInMapperDialog}>
|
||||
<DropdownItem
|
||||
key="predefined"
|
||||
onClick={() => toggleAddMapperDialog(true)}
|
||||
>
|
||||
{t("fromPredefinedMapper")}
|
||||
</DropdownItem>,
|
||||
<DropdownItem key="byConfiguration">
|
||||
<DropdownItem
|
||||
key="byConfiguration"
|
||||
onClick={() => toggleAddMapperDialog(false)}
|
||||
>
|
||||
{t("byConfiguration")}
|
||||
</DropdownItem>,
|
||||
]}
|
||||
|
@ -161,10 +181,10 @@ export const MapperList = ({ clientScope, refresh }: MapperListProps) => {
|
|||
>
|
||||
<AddMapperDialog
|
||||
protocol={clientScope.protocol!}
|
||||
filter={mapperList || []}
|
||||
filter={filter}
|
||||
onConfirm={addMappers}
|
||||
open={builtInDialogOpen}
|
||||
toggleDialog={toggleBuiltInMapperDialog}
|
||||
open={addMapperDialogOpen}
|
||||
toggleDialog={() => setAddMapperDialogOpen(!addMapperDialogOpen)}
|
||||
/>
|
||||
<Table
|
||||
variant={TableVariant.compact}
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
SelectVariant,
|
||||
Switch,
|
||||
TextInput,
|
||||
ValidatedOptions,
|
||||
} from "@patternfly/react-core";
|
||||
import { ConfigPropertyRepresentation } from "keycloak-admin/lib/defs/configPropertyRepresentation";
|
||||
|
||||
|
@ -31,43 +32,54 @@ import { HelpItem } from "../../components/help-enabler/HelpItem";
|
|||
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
|
||||
import { convertFormValuesToObject, convertToFormValues } from "../../util";
|
||||
|
||||
type Params = {
|
||||
scopeId: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const MappingDetails = () => {
|
||||
const { t } = useTranslation("client-scopes");
|
||||
const adminClient = useAdminClient();
|
||||
const { addAlert } = useAlerts();
|
||||
|
||||
const { scopeId, id } = useParams<{ scopeId: string; id: string }>();
|
||||
const { register, setValue, control, handleSubmit } = useForm();
|
||||
const { scopeId, id } = useParams<Params>();
|
||||
const { register, errors, setValue, control, handleSubmit } = useForm();
|
||||
const [mapping, setMapping] = useState<ProtocolMapperRepresentation>();
|
||||
const [typeOpen, setTypeOpen] = useState(false);
|
||||
const [configProperties, setConfigProperties] = useState<
|
||||
ConfigPropertyRepresentation[]
|
||||
>();
|
||||
|
||||
const serverInfo = useServerInfo();
|
||||
const history = useHistory();
|
||||
const serverInfo = useServerInfo();
|
||||
const isGuid = /^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$/;
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const data = await adminClient.clientScopes.findProtocolMapper({
|
||||
id: scopeId,
|
||||
mapperId: id,
|
||||
});
|
||||
if (data) {
|
||||
Object.entries(data).map((entry) => {
|
||||
if (entry[0] === "config") {
|
||||
convertToFormValues(entry[1], "config", setValue);
|
||||
}
|
||||
setValue(entry[0], entry[1]);
|
||||
if (id.match(isGuid)) {
|
||||
(async () => {
|
||||
const data = await adminClient.clientScopes.findProtocolMapper({
|
||||
id: scopeId,
|
||||
mapperId: id,
|
||||
});
|
||||
}
|
||||
setMapping(data);
|
||||
const mapperTypes = serverInfo.protocolMapperTypes![data!.protocol!];
|
||||
const properties = mapperTypes.find(
|
||||
(type) => type.id === data.protocolMapper
|
||||
)?.properties!;
|
||||
setConfigProperties(properties);
|
||||
})();
|
||||
if (data) {
|
||||
Object.entries(data).map((entry) => {
|
||||
convertToFormValues(entry[1], "config", setValue);
|
||||
});
|
||||
}
|
||||
const mapperTypes = serverInfo.protocolMapperTypes![data!.protocol!];
|
||||
const properties = mapperTypes.find(
|
||||
(type) => type.id === data!.protocolMapper
|
||||
)?.properties!;
|
||||
setConfigProperties(properties);
|
||||
|
||||
setMapping(data);
|
||||
})();
|
||||
} else {
|
||||
(async () => {
|
||||
const scope = await adminClient.clientScopes.findOne({ id: scopeId });
|
||||
setMapping({ protocol: scope.protocol, protocolMapper: id });
|
||||
})();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||
|
@ -91,15 +103,20 @@ export const MappingDetails = () => {
|
|||
|
||||
const save = async (formMapping: ProtocolMapperRepresentation) => {
|
||||
const config = convertFormValuesToObject(formMapping.config);
|
||||
const map = { ...mapping, config };
|
||||
const map = { ...mapping, ...formMapping, config };
|
||||
const key = id.match(isGuid) ? "Updated" : "Created";
|
||||
try {
|
||||
await adminClient.clientScopes.updateProtocolMapper(
|
||||
{ id: scopeId, mapperId: id },
|
||||
map
|
||||
);
|
||||
addAlert(t("mappingUpdatedSuccess"), AlertVariant.success);
|
||||
if (id.match(isGuid)) {
|
||||
await adminClient.clientScopes.updateProtocolMapper(
|
||||
{ id: scopeId, mapperId: id },
|
||||
map
|
||||
);
|
||||
} else {
|
||||
await adminClient.clientScopes.addProtocolMapper({ id: scopeId }, map);
|
||||
}
|
||||
addAlert(t(`mapping${key}Success`), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addAlert(t("mappingUpdatedError", { error }), AlertVariant.danger);
|
||||
addAlert(t(`mapping${key}Error`, { error }), AlertVariant.danger);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -107,21 +124,55 @@ export const MappingDetails = () => {
|
|||
<>
|
||||
<DeleteConfirm />
|
||||
<ViewHeader
|
||||
titleKey={mapping ? mapping.name! : ""}
|
||||
subKey={id}
|
||||
titleKey={mapping ? mapping.name! : t("addMapper")}
|
||||
subKey={id.match(isGuid) ? id : ""}
|
||||
badge={mapping?.protocol}
|
||||
dropdownItems={[
|
||||
<DropdownItem
|
||||
key="delete"
|
||||
value="delete"
|
||||
onClick={toggleDeleteDialog}
|
||||
>
|
||||
{t("common:delete")}
|
||||
</DropdownItem>,
|
||||
]}
|
||||
dropdownItems={
|
||||
id.match(isGuid)
|
||||
? [
|
||||
<DropdownItem
|
||||
key="delete"
|
||||
value="delete"
|
||||
onClick={toggleDeleteDialog}
|
||||
>
|
||||
{t("common:delete")}
|
||||
</DropdownItem>,
|
||||
]
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
<PageSection variant="light">
|
||||
<Form isHorizontal onSubmit={handleSubmit(save)}>
|
||||
{!id.match(isGuid) && (
|
||||
<FormGroup
|
||||
label={t("name")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="client-scopes-help:mapperName"
|
||||
forLabel={t("name")}
|
||||
forID="name"
|
||||
/>
|
||||
}
|
||||
fieldId="name"
|
||||
isRequired
|
||||
validated={
|
||||
errors.name ? ValidatedOptions.error : ValidatedOptions.default
|
||||
}
|
||||
helperTextInvalid={t("common:required")}
|
||||
>
|
||||
<TextInput
|
||||
ref={register({ required: true })}
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
<FormGroup
|
||||
label={t("realmRolePrefix")}
|
||||
labelIcon={
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
"deleteMappingConfirm": "Are you sure you want to delete this mapping?",
|
||||
"mappingUpdatedSuccess": "Mapping successfully updated",
|
||||
"mappingUpdatedError": "Could not update mapping: '{{error}}'",
|
||||
"mappingCreatedSuccess": "Mapping successfully created",
|
||||
"mappingCreatedError": "Could not create mapping: '{{error}}'",
|
||||
"realmRolePrefix": "Realm role prefix",
|
||||
"multiValued": "Multivalued",
|
||||
"tokenClaimName": "Token claim name",
|
||||
|
|
|
@ -8,7 +8,7 @@ import { routes } from "../../route-config";
|
|||
|
||||
export const PageBreadCrumbs = () => {
|
||||
const { t } = useTranslation();
|
||||
const crumbs = useBreadcrumbs(routes(t)).slice(1);
|
||||
const crumbs = useBreadcrumbs(routes(t), { excludePaths: ["/"] });
|
||||
return (
|
||||
<>
|
||||
{crumbs.length > 1 && (
|
||||
|
|
Loading…
Reference in a new issue