Fix roles pagination+filter out duplicates from associated roles list (#460)

* realm roles UX review progress wip

* filter realm roles on Enter key press, add filter functionality

* clean up

* filterChips logic now in table toolbar

* fix lint and format

* save with erik

* remove filter chips functionality

* fix check-types

* fix realm roles cypress test

* format

* wip pagination

* rebase

* fix roles pagination

* format

* add back save

* remove duplicates in associated roles table, can now paginate modal

* remove logs

* PR feedback from Erik

* rebase and fix pagination/search

* remove slice

* pagination in modal and associated roles tab

* show client roles

* lint and format

* remove unused variable

* address tbody console warning, clean up log stmts

* clean up log stmts

* fix ts error in AliasRenderer

* fix lint

* lint

* PR feedback from Erik

* resolve conflicts and format

* comment
This commit is contained in:
Eugenia 2021-04-01 09:41:21 -04:00 committed by GitHub
parent c503663e4e
commit a0faba0f97
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 48 additions and 37 deletions

View file

@ -159,6 +159,8 @@ export const AttributesForm = ({
)} )}
</Tr> </Tr>
))} ))}
<Tr>
<Td>
<Button <Button
aria-label={t("roles:addAttributeText")} aria-label={t("roles:addAttributeText")}
id="plus-icon" id="plus-icon"
@ -170,6 +172,8 @@ export const AttributesForm = ({
> >
{t("roles:addAttributeText")} {t("roles:addAttributeText")}
</Button> </Button>
</Td>
</Tr>
</Tbody> </Tbody>
</TableComposable> </TableComposable>
<ActionGroup className="kc-attributes__action-group"> <ActionGroup className="kc-attributes__action-group">

View file

@ -16,7 +16,6 @@ import { Spinner } from "@patternfly/react-core";
import _ from "lodash"; import _ from "lodash";
import { PaginatingTableToolbar } from "./PaginatingTableToolbar"; import { PaginatingTableToolbar } from "./PaginatingTableToolbar";
import { TableToolbar } from "./TableToolbar";
import { asyncStateFetch } from "../../context/auth/AdminClient"; import { asyncStateFetch } from "../../context/auth/AdminClient";
import { ListEmptyState } from "../list-empty-state/ListEmptyState"; import { ListEmptyState } from "../list-empty-state/ListEmptyState";
@ -56,11 +55,7 @@ function DataTable<T>({
} }
canSelectAll={canSelectAll} canSelectAll={canSelectAll}
cells={columns.map((column) => { cells={columns.map((column) => {
return { return { ...column, title: t(column.displayKey || column.name) };
...column,
title: t(column.displayKey || column.name),
transforms: column.transforms,
};
})} })}
rows={rows} rows={rows}
actions={actions} actions={actions}

View file

@ -20,9 +20,11 @@ export const AliasRendererComponent = ({
const [containerName, setContainerName] = useState<string>(""); const [containerName, setContainerName] = useState<string>("");
useEffect(() => { useEffect(() => {
if (filterType === "clients") {
adminClient.clients adminClient.clients
.findOne({ id: containerId! }) .findOne({ id: containerId! })
.then((client) => setContainerName(client.clientId! as string)); .then((client) => setContainerName(client.clientId! as string));
}
}, [containerId]); }, [containerId]);
if (filterType === "roles" || !containerName) { if (filterType === "roles" || !containerName) {

View file

@ -44,14 +44,20 @@ export const AssociatedRolesModal = (props: AssociatedRolesModalProps) => {
}; };
const loader = async () => { const loader = async () => {
const allRoles = await adminClient.roles.find(); const roles = await adminClient.roles.find();
const existingAdditionalRoles = await adminClient.roles.getCompositeRoles({ const existingAdditionalRoles = await adminClient.roles.getCompositeRoles({
id, id,
}); });
const allRoles = [...roles, ...existingAdditionalRoles];
return alphabetize(allRoles).filter((role: RoleRepresentation) => { const filterDupes = allRoles.filter(
(thing, index, self) =>
index === self.findIndex((t) => t.name === thing.name)
);
return alphabetize(filterDupes).filter((role: RoleRepresentation) => {
return ( return (
existingAdditionalRoles.find( props.existingCompositeRoles.find(
(existing: RoleRepresentation) => existing.name === role.name (existing: RoleRepresentation) => existing.name === role.name
) === undefined && role.name !== name ) === undefined && role.name !== name
); );

View file

@ -22,7 +22,6 @@ import { RoleFormType } from "./RealmRoleTabs";
import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation"; import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation";
import { AliasRendererComponent } from "./AliasRendererComponent"; import { AliasRendererComponent } from "./AliasRendererComponent";
import _ from "lodash"; import _ from "lodash";
import { cellWidth } from "@patternfly/react-table";
type AssociatedRolesTabProps = { type AssociatedRolesTabProps = {
additionalRoles: RoleRepresentation[]; additionalRoles: RoleRepresentation[];
@ -47,6 +46,7 @@ export const AssociatedRolesTab = ({
const [selectedRows, setSelectedRows] = useState<RoleRepresentation[]>([]); const [selectedRows, setSelectedRows] = useState<RoleRepresentation[]>([]);
const [isInheritedHidden, setIsInheritedHidden] = useState(false); const [isInheritedHidden, setIsInheritedHidden] = useState(false);
const [allRoles, setAllRoles] = useState<RoleRepresentation[]>([]);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@ -85,12 +85,12 @@ export const AssociatedRolesTab = ({
return newRoles; return newRoles;
}; };
const loader = async () => {
const alphabetize = (rolesList: RoleRepresentation[]) => { const alphabetize = (rolesList: RoleRepresentation[]) => {
return _.sortBy(rolesList, (role) => role.name?.toUpperCase()); return _.sortBy(rolesList, (role) => role.name?.toUpperCase());
}; };
const loader = async () => {
if (isInheritedHidden) { if (isInheritedHidden) {
setAllRoles(additionalRoles);
return alphabetize(additionalRoles); return alphabetize(additionalRoles);
} }
@ -106,7 +106,13 @@ export const AssociatedRolesTab = ({
); );
return fetchedRoles.then((results: RoleRepresentation[]) => { return fetchedRoles.then((results: RoleRepresentation[]) => {
return alphabetize(results); const filterDupes = results.filter(
(thing, index, self) =>
index === self.findIndex((t) => t.name === thing.name)
);
setAllRoles(filterDupes);
return alphabetize(filterDupes);
}); });
}; };
@ -136,11 +142,12 @@ export const AssociatedRolesTab = ({
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
titleKey: "roles:roleRemoveAssociatedRoleConfirm", titleKey: "roles:roleRemoveAssociatedRoleConfirm",
messageKey: t("roles:roleRemoveAssociatedText"), messageKey: t("roles:roleRemoveAssociatedText"),
continueButtonLabel: t("common:remove"), continueButtonLabel: "common:delete",
continueButtonVariant: ButtonVariant.danger, continueButtonVariant: ButtonVariant.danger,
onConfirm: async () => { onConfirm: async () => {
try { try {
await adminClient.roles.delCompositeRoles({ id }, selectedRows); await adminClient.roles.delCompositeRoles({ id }, selectedRows);
onRemove(selectedRows);
setSelectedRows([]); setSelectedRows([]);
addAlert(t("associatedRolesRemoved"), AlertVariant.success); addAlert(t("associatedRolesRemoved"), AlertVariant.success);
@ -158,11 +165,11 @@ export const AssociatedRolesTab = ({
messageKey: t("roles:removeAllAssociatedRolesConfirmDialog", { messageKey: t("roles:removeAllAssociatedRolesConfirmDialog", {
name: parentRole?.name || t("createRole"), name: parentRole?.name || t("createRole"),
}), }),
continueButtonLabel: "common:remove", continueButtonLabel: "common:delete",
continueButtonVariant: ButtonVariant.danger, continueButtonVariant: ButtonVariant.danger,
onConfirm: async () => { onConfirm: async () => {
try { try {
if (selectedRows.length === additionalRoles.length) { if (selectedRows.length === allRoles.length) {
onRemove(selectedRows); onRemove(selectedRows);
const loc = url.replace(/\/AssociatedRoles/g, "/details"); const loc = url.replace(/\/AssociatedRoles/g, "/details");
history.push(loc); history.push(loc);
@ -170,6 +177,7 @@ export const AssociatedRolesTab = ({
onRemove(selectedRows); onRemove(selectedRows);
await adminClient.roles.delCompositeRoles({ id }, selectedRows); await adminClient.roles.delCompositeRoles({ id }, selectedRows);
addAlert(t("associatedRolesRemoved"), AlertVariant.success); addAlert(t("associatedRolesRemoved"), AlertVariant.success);
refresh();
} catch (error) { } catch (error) {
addAlert(`${t("roleDeleteError")} ${error}`, AlertVariant.danger); addAlert(`${t("roleDeleteError")} ${error}`, AlertVariant.danger);
} }
@ -246,20 +254,17 @@ export const AssociatedRolesTab = ({
displayKey: "roles:roleName", displayKey: "roles:roleName",
cellRenderer: AliasRenderer, cellRenderer: AliasRenderer,
cellFormatters: [formattedLinkTableCell(), emptyFormatter()], cellFormatters: [formattedLinkTableCell(), emptyFormatter()],
transforms: [cellWidth(40)],
}, },
{ {
name: "containerId", name: "containerId",
displayKey: "roles:inheritedFrom", displayKey: "roles:inheritedFrom",
cellRenderer: InheritedRoleName, cellRenderer: InheritedRoleName,
cellFormatters: [emptyFormatter()], cellFormatters: [emptyFormatter()],
transforms: [cellWidth(30)],
}, },
{ {
name: "description", name: "description",
displayKey: "common:description", displayKey: "common:description",
cellFormatters: [emptyFormatter()], cellFormatters: [emptyFormatter()],
transforms: [cellWidth(30)],
}, },
]} ]}
emptyState={ emptyState={

View file

@ -57,6 +57,7 @@ export const RealmRoleTabs = () => {
const [additionalRoles, setAdditionalRoles] = useState<RoleRepresentation[]>( const [additionalRoles, setAdditionalRoles] = useState<RoleRepresentation[]>(
[] []
); );
const { addAlert } = useAlerts(); const { addAlert } = useAlerts();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@ -321,7 +322,6 @@ export const RealmRoleTabs = () => {
addComposites={addComposites} addComposites={addComposites}
parentRole={role!} parentRole={role!}
onRemove={() => refresh()} onRemove={() => refresh()}
// client={client!}
/> />
</Tab> </Tab>
) : null} ) : null}

View file

@ -61,7 +61,6 @@ export const UserForm = ({
const setupForm = (user: UserRepresentation) => { const setupForm = (user: UserRepresentation) => {
reset(); reset();
Object.entries(user).map((entry) => { Object.entries(user).map((entry) => {
console.log(entry[0], entry[1]);
if (entry[0] == "createdTimestamp") { if (entry[0] == "createdTimestamp") {
setTimestamp(entry[1]); setTimestamp(entry[1]);
} else { } else {