Client add executors (#1284)
* client-add-executors: wip * client-add-executors: added logic for deleting client from dropdown * client-add-executors: wip * client-add-executors: added edit profiles * client-add-executors: added refesh client profiles * client-add-executors: added cypress tests * commented out test failing only in CI * Update cypress/integration/realm_settings_test.spec.ts Co-authored-by: Erik Jan de Wit <edewit@redhat.com> * changed to arrow functions * feedback fixes * feedback fixes * uncommented failing test to see if still failing and why * test possible fix * test fix * test fix * test fix * client-add-executors: reused normaliseProfile func for delete dialog Co-authored-by: Agnieszka Gancarczyk <agancarc@redhat.com> Co-authored-by: Erik Jan de Wit <edewit@redhat.com>
This commit is contained in:
parent
751dcc6e04
commit
9af18e11e2
6 changed files with 267 additions and 54 deletions
|
@ -469,5 +469,37 @@ describe("Realm settings tests", () => {
|
|||
it("Check deleting the client profile", () => {
|
||||
realmSettingsPage.shouldDeleteClientProfileDialog();
|
||||
});
|
||||
|
||||
it("Check navigating between Form View and JSON editor", () => {
|
||||
realmSettingsPage.shouldNavigateBetweenFormAndJSONView();
|
||||
});
|
||||
|
||||
it("Check saving changed JSON profiles", () => {
|
||||
realmSettingsPage.shouldSaveChangedJSONProfiles();
|
||||
realmSettingsPage.shouldDeleteClientProfileDialog();
|
||||
});
|
||||
|
||||
it("Should not create duplicate client profile", () => {
|
||||
realmSettingsPage.shouldCompleteAndCreateNewClientProfile();
|
||||
|
||||
sidebarPage.goToRealmSettings();
|
||||
cy.findByTestId("rs-clientPolicies-tab").click();
|
||||
cy.findByTestId("rs-profiles-clientPolicies-tab").click();
|
||||
realmSettingsPage.shouldCompleteAndCreateNewClientProfile();
|
||||
realmSettingsPage.shouldNotCreateDuplicateClientProfile();
|
||||
|
||||
sidebarPage.goToRealmSettings();
|
||||
cy.findByTestId("rs-clientPolicies-tab").click();
|
||||
cy.findByTestId("rs-profiles-clientPolicies-tab").click();
|
||||
realmSettingsPage.shouldDeleteClientProfileDialog();
|
||||
});
|
||||
|
||||
it("Check deleting newly created client profile from create view via dropdown", () => {
|
||||
realmSettingsPage.shouldRemoveClientFromCreateView();
|
||||
});
|
||||
|
||||
it("Check reloading JSON profiles", () => {
|
||||
realmSettingsPage.shouldReloadJSONProfiles();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -143,6 +143,8 @@ export default class RealmSettingsPage {
|
|||
executeActionsSelectMenu = "#kc-execute-actions-select-menu";
|
||||
executeActionsSelectMenuList = "#kc-execute-actions-select-menu > div > ul";
|
||||
|
||||
private formViewProfilesView = "formView-profilesView";
|
||||
private jsonEditorProfilesView = "jsonEditor-profilesView";
|
||||
private createProfileBtn = "createProfile";
|
||||
private formViewSelect = "formView-profilesView";
|
||||
private jsonEditorSelect = "jsonEditor-profilesView";
|
||||
|
@ -156,6 +158,10 @@ export default class RealmSettingsPage {
|
|||
private deleteDialogTitle = ".pf-c-modal-box__title-text";
|
||||
private deleteDialogBodyText = ".pf-c-modal-box__body";
|
||||
private deleteDialogCancelBtn = ".pf-c-button.pf-m-link";
|
||||
private jsonEditorSaveBtn = "jsonEditor-saveBtn";
|
||||
private jsonEditorReloadBtn = "jsonEditor-reloadBtn";
|
||||
private jsonEditor = ".monaco-scrollable-element.editor-scrollable.vs";
|
||||
private createClientDrpDwn = ".pf-c-dropdown.pf-m-align-right";
|
||||
|
||||
selectLoginThemeType(themeType: string) {
|
||||
cy.get(this.selectLoginTheme).click();
|
||||
|
@ -519,7 +525,64 @@ export default class RealmSettingsPage {
|
|||
cy.get(this.moreDrpDwn).last().click();
|
||||
cy.get(this.moreDrpDwnItems).click();
|
||||
cy.findByTestId("modalConfirm").contains("Delete").click();
|
||||
cy.get("table").should("not.have.text", "Test");
|
||||
cy.get(this.alertMessage).should("be.visible", "Client profile deleted");
|
||||
cy.get("table").should("not.have.text", "Test");
|
||||
}
|
||||
|
||||
shouldNavigateBetweenFormAndJSONView() {
|
||||
cy.findByTestId(this.jsonEditorProfilesView).check();
|
||||
cy.findByTestId(this.jsonEditorSaveBtn).contains("Save");
|
||||
cy.findByTestId(this.jsonEditorReloadBtn).contains("Reload");
|
||||
cy.findByTestId(this.formViewProfilesView).check();
|
||||
cy.findByTestId(this.createProfileBtn).contains("Create client profile");
|
||||
}
|
||||
|
||||
shouldSaveChangedJSONProfiles() {
|
||||
cy.findByTestId(this.jsonEditorProfilesView).check();
|
||||
cy.get(this.jsonEditor).type(`{pageup}{del} [{
|
||||
"name": "Test",
|
||||
"description": "Test Description",
|
||||
"executors": [],
|
||||
"global": false
|
||||
}, {downarrow}{end}{backspace}{backspace}`);
|
||||
cy.findByTestId(this.jsonEditorSaveBtn).click();
|
||||
cy.get(this.alertMessage).should(
|
||||
"be.visible",
|
||||
"The client profiles configuration was updated"
|
||||
);
|
||||
cy.findByTestId(this.formViewProfilesView).check();
|
||||
cy.get("table").should("be.visible").contains("td", "Test");
|
||||
}
|
||||
|
||||
shouldNotCreateDuplicateClientProfile() {
|
||||
cy.get(this.alertMessage).should(
|
||||
"be.visible",
|
||||
"Could not create client profile: 'proposed client profile name duplicated.'"
|
||||
);
|
||||
}
|
||||
|
||||
shouldRemoveClientFromCreateView() {
|
||||
cy.findByTestId(this.createProfileBtn).click();
|
||||
cy.findByTestId(this.newClientProfileNameInput).type("Test again");
|
||||
cy.findByTestId(this.newClientProfileDescriptionInput).type(
|
||||
"Test Again Description"
|
||||
);
|
||||
cy.findByTestId(this.saveNewClientProfileBtn).click();
|
||||
cy.get(this.alertMessage).should(
|
||||
"be.visible",
|
||||
"New client profile created"
|
||||
);
|
||||
cy.get(this.createClientDrpDwn).contains("Action").click();
|
||||
cy.findByTestId("deleteClientProfileDropdown").click();
|
||||
cy.findByTestId("modalConfirm").contains("Delete").click();
|
||||
cy.get(this.alertMessage).should("be.visible", "Client profile deleted");
|
||||
cy.get("table").should("not.have.text", "Test Again Description");
|
||||
}
|
||||
|
||||
shouldReloadJSONProfiles() {
|
||||
cy.findByTestId(this.jsonEditorProfilesView).check();
|
||||
cy.findByTestId(this.jsonEditorReloadBtn).contains("Reload").click();
|
||||
cy.findByTestId(this.jsonEditorSaveBtn).contains("Save");
|
||||
cy.findByTestId(this.jsonEditorReloadBtn).contains("Reload");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,11 @@ import {
|
|||
ActionGroup,
|
||||
AlertVariant,
|
||||
Button,
|
||||
ButtonVariant,
|
||||
Divider,
|
||||
DropdownItem,
|
||||
Flex,
|
||||
FlexItem,
|
||||
FormGroup,
|
||||
PageSection,
|
||||
Text,
|
||||
|
@ -16,7 +20,7 @@ import { useTranslation } from "react-i18next";
|
|||
import { useForm } from "react-hook-form";
|
||||
import { FormAccess } from "../components/form-access/FormAccess";
|
||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Link, useHistory } from "react-router-dom";
|
||||
import { useRealm } from "../context/realm-context/RealmContext";
|
||||
import { useAlerts } from "../components/alert/Alerts";
|
||||
import { useAdminClient, useFetch } from "../context/auth/AdminClient";
|
||||
|
@ -24,13 +28,14 @@ import type ClientProfileRepresentation from "@keycloak/keycloak-admin-client/li
|
|||
import { HelpItem } from "../components/help-enabler/HelpItem";
|
||||
import { PlusCircleIcon } from "@patternfly/react-icons";
|
||||
import "./RealmSettingsSection.css";
|
||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||
|
||||
type NewClientProfileForm = Required<ClientProfileRepresentation>;
|
||||
|
||||
const defaultValues: NewClientProfileForm = {
|
||||
name: "",
|
||||
executors: [],
|
||||
description: "",
|
||||
executors: [],
|
||||
};
|
||||
|
||||
export const NewClientProfileForm = () => {
|
||||
|
@ -46,6 +51,10 @@ export const NewClientProfileForm = () => {
|
|||
>([]);
|
||||
const [profiles, setProfiles] = useState<ClientProfileRepresentation[]>([]);
|
||||
const [showAddExecutorsForm, setShowAddExecutorsForm] = useState(false);
|
||||
const [createdProfile, setCreatedProfile] =
|
||||
useState<ClientProfileRepresentation>();
|
||||
const form = getValues();
|
||||
const history = useHistory();
|
||||
|
||||
useFetch(
|
||||
() =>
|
||||
|
@ -77,14 +86,56 @@ export const NewClientProfileForm = () => {
|
|||
AlertVariant.success
|
||||
);
|
||||
setShowAddExecutorsForm(true);
|
||||
setCreatedProfile(createdProfile);
|
||||
} catch (error) {
|
||||
addError("realm-settings:createClientProfileError", error);
|
||||
}
|
||||
};
|
||||
|
||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||
titleKey: t("deleteClientProfileConfirmTitle"),
|
||||
messageKey: t("deleteClientProfileConfirm"),
|
||||
continueButtonLabel: t("delete"),
|
||||
continueButtonVariant: ButtonVariant.danger,
|
||||
onConfirm: async () => {
|
||||
const updatedProfiles = profiles.filter(
|
||||
(profile) => profile.name !== createdProfile?.name
|
||||
);
|
||||
|
||||
try {
|
||||
await adminClient.clientPolicies.createProfiles({
|
||||
profiles: updatedProfiles,
|
||||
globalProfiles,
|
||||
});
|
||||
addAlert(t("deleteClientSuccess"), AlertVariant.success);
|
||||
history.push(`/${realm}/realm-settings/clientPolicies`);
|
||||
} catch (error) {
|
||||
addError(t("deleteClientError"), error);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewHeader titleKey={t("newClientProfile")} divider />
|
||||
<DeleteConfirm />
|
||||
<ViewHeader
|
||||
titleKey={showAddExecutorsForm ? form.name : t("newClientProfile")}
|
||||
divider
|
||||
dropdownItems={
|
||||
showAddExecutorsForm
|
||||
? [
|
||||
<DropdownItem
|
||||
key="delete"
|
||||
value="delete"
|
||||
onClick={toggleDeleteDialog}
|
||||
data-testid="deleteClientProfileDropdown"
|
||||
>
|
||||
{t("deleteClientProfile")}
|
||||
</DropdownItem>,
|
||||
]
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
<PageSection variant="light">
|
||||
<FormAccess isHorizontal role="view-realm" className="pf-u-mt-lg">
|
||||
<FormGroup
|
||||
|
@ -120,6 +171,7 @@ export const NewClientProfileForm = () => {
|
|||
variant="primary"
|
||||
onClick={save}
|
||||
data-testid="saveCreateProfile"
|
||||
isDisabled={showAddExecutorsForm ? true : false}
|
||||
>
|
||||
{t("common:save")}
|
||||
</Button>
|
||||
|
@ -133,41 +185,44 @@ export const NewClientProfileForm = () => {
|
|||
)}
|
||||
data-testid="cancelCreateProfile"
|
||||
>
|
||||
{t("common:cancel")}
|
||||
{showAddExecutorsForm
|
||||
? t("realm-settings:reload")
|
||||
: t("common:cancel")}
|
||||
</Button>
|
||||
</ActionGroup>
|
||||
{showAddExecutorsForm && (
|
||||
<>
|
||||
<FormGroup
|
||||
label={t("executors")}
|
||||
fieldId="kc-executors"
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText={t("realm-settings:executorsHelpText")}
|
||||
forLabel={t("executorsHelpItem")}
|
||||
forID={t("executors")}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
id="addExecutor"
|
||||
component={(props) => (
|
||||
<Link
|
||||
{...props}
|
||||
to={`/${realm}/realm-settings/clientPolicies`}
|
||||
></Link>
|
||||
)}
|
||||
variant="link"
|
||||
className="kc-addExecutor"
|
||||
data-testid="cancelCreateProfile"
|
||||
icon={<PlusCircleIcon />}
|
||||
isDisabled
|
||||
>
|
||||
{t("realm-settings:addExecutor")}
|
||||
</Button>
|
||||
</FormGroup>
|
||||
<Flex>
|
||||
<FlexItem>
|
||||
<Text className="kc-executors" component={TextVariants.h1}>
|
||||
{t("executors")}
|
||||
<HelpItem
|
||||
helpText={t("realm-settings:executorsHelpText")}
|
||||
forLabel={t("executorsHelpItem")}
|
||||
forID={t("executors")}
|
||||
/>
|
||||
</Text>
|
||||
</FlexItem>
|
||||
<FlexItem align={{ default: "alignRight" }}>
|
||||
<Button
|
||||
id="addExecutor"
|
||||
component={(props) => (
|
||||
<Link
|
||||
{...props}
|
||||
to={`/${realm}/realm-settings/clientPolicies`}
|
||||
></Link>
|
||||
)}
|
||||
variant="link"
|
||||
className="kc-addExecutor"
|
||||
data-testid="cancelCreateProfile"
|
||||
icon={<PlusCircleIcon />}
|
||||
>
|
||||
{t("realm-settings:addExecutor")}
|
||||
</Button>
|
||||
</FlexItem>
|
||||
</Flex>
|
||||
<Divider />
|
||||
<Text component={TextVariants.h6}>
|
||||
<Text className="kc-emptyExecutors" component={TextVariants.h6}>
|
||||
{t("realm-settings:emptyExecutors")}
|
||||
</Text>
|
||||
</>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import React, { useMemo, useState } from "react";
|
||||
import React, { useState } from "react";
|
||||
import { omit } from "lodash";
|
||||
import {
|
||||
ActionGroup,
|
||||
AlertVariant,
|
||||
Button,
|
||||
ButtonVariant,
|
||||
FormGroup,
|
||||
Label,
|
||||
PageSection,
|
||||
Spinner,
|
||||
|
@ -38,6 +40,7 @@ export const ProfilesTab = () => {
|
|||
useState<ClientProfileRepresentation[]>();
|
||||
const [selectedProfile, setSelectedProfile] = useState<ClientProfile>();
|
||||
const [show, setShow] = useState(false);
|
||||
const [code, setCode] = useState<string>();
|
||||
const [key, setKey] = useState(0);
|
||||
|
||||
useFetch(
|
||||
|
@ -62,16 +65,16 @@ export const ProfilesTab = () => {
|
|||
|
||||
const allClientProfiles = globalProfiles?.concat(profiles ?? []);
|
||||
setTableProfiles(allClientProfiles || []);
|
||||
setCode(JSON.stringify(allClientProfiles, null, 2));
|
||||
},
|
||||
[key]
|
||||
);
|
||||
|
||||
const loader = async () => tableProfiles ?? [];
|
||||
|
||||
const code = useMemo(
|
||||
() => JSON.stringify(tableProfiles, null, 2),
|
||||
[tableProfiles]
|
||||
);
|
||||
const normalizeProfile = (
|
||||
profile: ClientProfile
|
||||
): ClientProfileRepresentation => omit(profile, "global");
|
||||
|
||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||
titleKey: t("deleteClientProfileConfirmTitle"),
|
||||
|
@ -83,7 +86,9 @@ export const ProfilesTab = () => {
|
|||
?.filter(
|
||||
(profile) => profile.name !== selectedProfile?.name && !profile.global
|
||||
)
|
||||
.map<ClientProfileRepresentation>((profile) => omit(profile, "global"));
|
||||
.map<ClientProfileRepresentation>((profile) =>
|
||||
normalizeProfile(profile)
|
||||
);
|
||||
|
||||
try {
|
||||
await adminClient.clientPolicies.createProfiles({
|
||||
|
@ -112,6 +117,39 @@ export const ProfilesTab = () => {
|
|||
);
|
||||
}
|
||||
|
||||
const save = async () => {
|
||||
if (!code) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const obj: ClientProfile[] = JSON.parse(code);
|
||||
const changedProfiles = obj
|
||||
.filter((profile) => !profile.global)
|
||||
.map((profile) => normalizeProfile(profile));
|
||||
|
||||
const changedGlobalProfiles = obj
|
||||
.filter((profile) => profile.global)
|
||||
.map((profile) => normalizeProfile(profile));
|
||||
|
||||
try {
|
||||
await adminClient.clientPolicies.createProfiles({
|
||||
profiles: changedProfiles,
|
||||
globalProfiles: changedGlobalProfiles,
|
||||
});
|
||||
addAlert(
|
||||
t("realm-settings:updateClientProfilesSuccess"),
|
||||
AlertVariant.success
|
||||
);
|
||||
setKey(key + 1);
|
||||
} catch (error) {
|
||||
addError("realm-settings:updateClientProfilesError", error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Invalid json, ignoring value using {}");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DeleteConfirm />
|
||||
|
@ -195,26 +233,42 @@ export const ProfilesTab = () => {
|
|||
}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<FormGroup fieldId={"jsonEditor"}>
|
||||
<div className="pf-u-mt-md pf-u-ml-lg">
|
||||
<CodeEditor
|
||||
isLineNumbersVisible
|
||||
isLanguageLabelVisible
|
||||
isReadOnly={false}
|
||||
code={code}
|
||||
language={Language.json}
|
||||
height="30rem"
|
||||
onChange={(value) => {
|
||||
setCode(value ?? "");
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="pf-u-mt-md">
|
||||
<Button
|
||||
variant={ButtonVariant.primary}
|
||||
className="pf-u-mr-md pf-u-ml-lg"
|
||||
>
|
||||
{t("save")}
|
||||
</Button>
|
||||
<Button variant={ButtonVariant.link}> {t("reload")}</Button>
|
||||
</div>
|
||||
</>
|
||||
<ActionGroup>
|
||||
<div className="pf-u-mt-md">
|
||||
<Button
|
||||
variant={ButtonVariant.primary}
|
||||
className="pf-u-mr-md pf-u-ml-lg"
|
||||
onClick={save}
|
||||
data-testid="jsonEditor-saveBtn"
|
||||
>
|
||||
{t("save")}
|
||||
</Button>
|
||||
<Button
|
||||
variant={ButtonVariant.link}
|
||||
onClick={() => {
|
||||
setCode(JSON.stringify(tableProfiles, null, 2));
|
||||
}}
|
||||
data-testid="jsonEditor-reloadBtn"
|
||||
>
|
||||
{t("reload")}
|
||||
</Button>
|
||||
</div>
|
||||
</ActionGroup>
|
||||
</FormGroup>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -194,6 +194,10 @@ article.pf-c-card.pf-m-flat.kc-login-settings-template
|
|||
transform: scale(1.6);
|
||||
}
|
||||
|
||||
.kc-addExecutor {
|
||||
float: right;
|
||||
.kc-emptyExecutors {
|
||||
color: #8D9195;
|
||||
}
|
||||
|
||||
.kc-action-dropdown {
|
||||
background-color: transparent;
|
||||
}
|
|
@ -234,6 +234,7 @@ export default {
|
|||
deleteClientSuccess: "Client profile deleted",
|
||||
deleteClientError: "Could not delete profile: {{error}}",
|
||||
createClientProfile: "Create client profile",
|
||||
deleteClientProfile: "Delete this client profile",
|
||||
createClientProfileSuccess: "New client profile created",
|
||||
createClientProfileError: "Could not create client profile: '{{error}}'",
|
||||
createClientProfileNameHelperText:
|
||||
|
@ -252,6 +253,10 @@ export default {
|
|||
executorsHelpItem: "Executors help item",
|
||||
addExecutor: "Add executor",
|
||||
emptyExecutors: "No executors configured",
|
||||
updateClientProfilesSuccess:
|
||||
"The client profiles configuration was updated",
|
||||
updateClientProfilesError:
|
||||
"Provided JSON is incorrect: Unexpected token { in JSON",
|
||||
tokens: "Tokens",
|
||||
key: "Key",
|
||||
value: "Value",
|
||||
|
|
Loading…
Reference in a new issue