Fix(clients -> authorization): Evaluate results screen (#2220)
This commit is contained in:
parent
3a25b5e5ec
commit
b64fd23559
1 changed files with 69 additions and 75 deletions
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue