Fix(clients -> authorization): Evaluate results screen (#2220)

This commit is contained in:
Jenny 2022-03-11 05:19:20 -05:00 committed by GitHub
parent 3a25b5e5ec
commit b64fd23559
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,5 +1,5 @@
import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation"; import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
import React, { useState, KeyboardEvent } from "react"; import React, { useState, KeyboardEvent, useMemo, useRef } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
FormGroup, FormGroup,
@ -17,6 +17,7 @@ import {
Toolbar, Toolbar,
ToolbarGroup, ToolbarGroup,
ToolbarItem, ToolbarItem,
Divider,
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import { Controller, useFormContext } from "react-hook-form"; import { Controller, useFormContext } from "react-hook-form";
@ -78,6 +79,26 @@ export type AttributeForm = Omit<
type Props = ClientSettingsProps & EvaluationResultRepresentation; type Props = ClientSettingsProps & EvaluationResultRepresentation;
enum ResultsFilter {
All = "ALL",
StatusDenied = "STATUS_DENIED",
StatusPermitted = "STATUS_PERMITTED",
}
function filterResults(
results: EvaluationResultRepresentation[],
filter: ResultsFilter
) {
switch (filter) {
case ResultsFilter.StatusPermitted:
return results.filter(({ status }) => status === "PERMIT");
case ResultsFilter.StatusDenied:
return results.filter(({ status }) => status === "DENY");
default:
return results;
}
}
export const AuthorizationEvaluate = ({ client }: Props) => { export const AuthorizationEvaluate = ({ client }: Props) => {
const form = useFormContext<EvaluateFormInputs>(); const form = useFormContext<EvaluateFormInputs>();
const { control, reset, trigger } = form; const { control, reset, trigger } = form;
@ -98,10 +119,8 @@ export const AuthorizationEvaluate = ({ client }: Props) => {
EvaluationResultRepresentation[] EvaluationResultRepresentation[]
>([]); >([]);
const [showEvaluateResults, setShowEvaluateResults] = useState(false); const [showEvaluateResults, setShowEvaluateResults] = useState(false);
const [searchVal, setSearchVal] = useState(""); const searchQueryRef = useRef("");
const [filteredResources, setFilteredResources] = useState< const [searchQuery, setSearchQuery] = useState("");
EvaluationResultRepresentation[]
>([]);
const [filterDropdownOpen, setFilterDropdownOpen] = useState(false); const [filterDropdownOpen, setFilterDropdownOpen] = useState(false);
const [key, setKey] = useState(0); const [key, setKey] = useState(0);
@ -109,18 +128,20 @@ export const AuthorizationEvaluate = ({ client }: Props) => {
setKey(key + 1); setKey(key + 1);
}; };
const FilterType = { const [filter, setFilter] = useState(ResultsFilter.All);
allResults: t("allResults"),
resultPermit: t("resultPermit"),
resultDeny: t("resultDeny"),
};
const [filterType, setFilterType] = useState(FilterType.allResults);
const [clients, setClients] = useState<ClientRepresentation[]>([]); const [clients, setClients] = useState<ClientRepresentation[]>([]);
const [clientRoles, setClientRoles] = useState<RoleRepresentation[]>([]); const [clientRoles, setClientRoles] = useState<RoleRepresentation[]>([]);
const [users, setUsers] = useState<UserRepresentation[]>([]); const [users, setUsers] = useState<UserRepresentation[]>([]);
const filteredResources = useMemo(
() =>
filterResults(evaluateResults, filter).filter(
({ resource }) => resource?.name?.includes(searchQuery) ?? false
),
[evaluateResults, filter, searchQuery]
);
useFetch( useFetch(
() => () =>
Promise.all([ Promise.all([
@ -137,7 +158,7 @@ export const AuthorizationEvaluate = ({ client }: Props) => {
); );
useFetch( useFetch(
async () => () =>
Promise.all([ Promise.all([
adminClient.clients.listResources({ adminClient.clients.listResources({
id: client.id!, id: client.id!,
@ -150,7 +171,7 @@ export const AuthorizationEvaluate = ({ client }: Props) => {
setResources(resources); setResources(resources);
setScopes(scopes); setScopes(scopes);
}, },
[key, filterType] [key, filter]
); );
const evaluate = async () => { const evaluate = async () => {
@ -184,51 +205,23 @@ export const AuthorizationEvaluate = ({ client }: Props) => {
return evaluateResults; return evaluateResults;
}; };
const onSearch = () => { const confirmSearchQuery = () => {
if (searchVal !== "") { setSearchQuery(searchQueryRef.current);
setSearchVal(searchVal);
const filtered = evaluateResults.filter((resource) =>
resource.resource?.name?.includes(searchVal)
);
setFilteredResources(filtered);
} else {
setSearchVal("");
setFilteredResources(evaluateResults);
}
}; };
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => { const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") { if (e.key === "Enter") {
onSearch(); confirmSearchQuery();
} }
}; };
const handleInputChange = (value: string) => { const handleInputChange = (value: string) => {
setSearchVal(value); searchQueryRef.current = value;
}; };
const noEvaluatedData = evaluateResults.length === 0; const noEvaluatedData = evaluateResults.length === 0;
const noFilteredData = filteredResources.length === 0; const noFilteredData = filteredResources.length === 0;
const options = [
<SelectOption
key={1}
data-testid="all-results-option"
value={FilterType.allResults}
isPlaceholder
/>,
<SelectOption
data-testid="result-permit-option"
key={2}
value={FilterType.resultPermit}
/>,
<SelectOption
data-testid="result-deny-option"
key={3}
value={FilterType.resultDeny}
/>,
];
return showEvaluateResults ? ( return showEvaluateResults ? (
<PageSection> <PageSection>
<Toolbar> <Toolbar>
@ -247,7 +240,7 @@ export const AuthorizationEvaluate = ({ client }: Props) => {
<Button <Button
variant={ButtonVariant.control} variant={ButtonVariant.control}
aria-label={t("common:search")} aria-label={t("common:search")}
onClick={() => onSearch()} onClick={() => confirmSearchQuery()}
> >
<SearchIcon /> <SearchIcon />
</Button> </Button>
@ -262,34 +255,36 @@ export const AuthorizationEvaluate = ({ client }: Props) => {
variant={SelectVariant.single} variant={SelectVariant.single}
onToggle={() => setFilterDropdownOpen(!filterDropdownOpen)} onToggle={() => setFilterDropdownOpen(!filterDropdownOpen)}
onSelect={(_, value) => { onSelect={(_, value) => {
if (value === FilterType.allResults) { setFilter(value as ResultsFilter);
setFilterType(FilterType.allResults);
} else if (value === FilterType.resultPermit) {
const filterPermit = evaluateResults.filter(
(resource) => resource.status === "PERMIT"
);
setFilteredResources(filterPermit);
setFilterType(FilterType.resultPermit);
refresh();
} else if (value === FilterType.resultDeny) {
const filterDeny = evaluateResults.filter(
(resource) => resource.status === "DENY"
);
setFilterType(FilterType.resultDeny);
setFilteredResources(filterDeny);
refresh();
}
setFilterDropdownOpen(false); setFilterDropdownOpen(false);
refresh();
}} }}
selections={filterType} selections={filter}
> >
{options} <SelectOption
data-testid="all-results-option"
value={ResultsFilter.All}
isPlaceholder
>
{t("allResults")}
</SelectOption>
<SelectOption
data-testid="result-permit-option"
value={ResultsFilter.StatusPermitted}
>
{t("resultPermit")}
</SelectOption>
<SelectOption
data-testid="result-deny-option"
value={ResultsFilter.StatusDenied}
>
{t("resultDeny")}
</SelectOption>
</Select> </Select>
</ToolbarItem> </ToolbarItem>
</ToolbarGroup> </ToolbarGroup>
</Toolbar> </Toolbar>
{!noEvaluatedData && !noFilteredData && ( {!noFilteredData && (
<TableComposable aria-label={t("evaluationResults")}> <TableComposable aria-label={t("evaluationResults")}>
<Thead> <Thead>
<Tr> <Tr>
@ -300,10 +295,7 @@ export const AuthorizationEvaluate = ({ client }: Props) => {
<Th /> <Th />
</Tr> </Tr>
</Thead> </Thead>
{(filterType == FilterType.allResults {filteredResources.map((resource, rowIndex) => (
? evaluateResults
: filteredResources
).map((resource, rowIndex) => (
<AuthorizationEvaluateResource <AuthorizationEvaluateResource
key={rowIndex} key={rowIndex}
rowIndex={rowIndex} rowIndex={rowIndex}
@ -313,14 +305,16 @@ export const AuthorizationEvaluate = ({ client }: Props) => {
))} ))}
</TableComposable> </TableComposable>
)} )}
{noEvaluatedData || {(noFilteredData || noEvaluatedData) && (
(noFilteredData && ( <>
<Divider />
<ListEmptyState <ListEmptyState
isSearchVariant isSearchVariant
message={t("common:noSearchResults")} message={t("common:noSearchResults")}
instructions={t("common:noSearchResultsInstructions")} instructions={t("common:noSearchResultsInstructions")}
/> />
))} </>
)}
<ActionGroup className="kc-evaluated-options"> <ActionGroup className="kc-evaluated-options">
<Button <Button
data-testid="authorization-eval" data-testid="authorization-eval"