migrated discovery field (#27762)

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Co-authored-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
Erik Jan de Wit 2024-03-12 16:28:08 +01:00 committed by GitHub
parent 401e1bd636
commit c0731ac502
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 80 additions and 76 deletions

View file

@ -78,6 +78,7 @@
"keycloak-js": "workspace:*", "keycloak-js": "workspace:*",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"monaco-editor": "^0.47.0", "monaco-editor": "^0.47.0",
"p-debounce": "^4.0.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",

View file

@ -33,6 +33,7 @@ export default function AddOpenIdConnect() {
const form = useForm<IdentityProviderRepresentation>({ const form = useForm<IdentityProviderRepresentation>({
defaultValues: { alias: id }, defaultValues: { alias: id },
mode: "onChange",
}); });
const { const {
handleSubmit, handleSubmit,

View file

@ -30,6 +30,7 @@ export default function AddSamlConnect() {
const form = useForm<DiscoveryIdentityProvider>({ const form = useForm<DiscoveryIdentityProvider>({
defaultValues: { alias: id, config: { allowCreate: "true" } }, defaultValues: { alias: id, config: { allowCreate: "true" } },
mode: "onChange",
}); });
const { const {
handleSubmit, handleSubmit,

View file

@ -1,11 +1,11 @@
import { FormGroup, Switch } from "@patternfly/react-core"; import { FormGroup, Switch } from "@patternfly/react-core";
import { ReactNode, useEffect, useState } from "react"; import debouncePromise from "p-debounce";
import { ReactNode, useMemo, useState } from "react";
import { useFormContext } from "react-hook-form"; import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { HelpItem } from "ui-shared"; import { HelpItem, TextControl } from "ui-shared";
import { adminClient } from "../../admin-client"; import { adminClient } from "../../admin-client";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
import environment from "../../environment"; import environment from "../../environment";
type DiscoveryEndpointFieldProps = { type DiscoveryEndpointFieldProps = {
@ -22,14 +22,9 @@ export const DiscoveryEndpointField = ({
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
setValue, setValue,
register,
setError,
watch,
clearErrors, clearErrors,
formState: { errors }, formState: { errors },
} = useFormContext(); } = useFormContext();
const discoveryUrl = watch("discoveryEndpoint");
const [discovery, setDiscovery] = useState(true); const [discovery, setDiscovery] = useState(true);
const [discovering, setDiscovering] = useState(false); const [discovering, setDiscovering] = useState(false);
const [discoveryResult, setDiscoveryResult] = const [discoveryResult, setDiscoveryResult] =
@ -39,31 +34,23 @@ export const DiscoveryEndpointField = ({
Object.keys(result).map((k) => setValue(`config.${k}`, result[k])); Object.keys(result).map((k) => setValue(`config.${k}`, result[k]));
}; };
useEffect(() => { const discover = async (fromUrl: string) => {
if (!discoveryUrl) { setDiscovering(true);
try {
const result = await adminClient.identityProviders.importFromUrl({
providerId: id,
fromUrl,
});
setupForm(result);
setDiscoveryResult(result);
} catch (error) {
return (error as Error).message;
} finally {
setDiscovering(false); setDiscovering(false);
return;
} }
};
(async () => { const discoverDebounced = useMemo(() => debouncePromise(discover, 1000), []);
clearErrors("discoveryError");
try {
const result = await adminClient.identityProviders.importFromUrl({
providerId: id,
fromUrl: discoveryUrl,
});
setupForm(result);
setDiscoveryResult(result);
} catch (error) {
setError("discoveryError", {
type: "manual",
message: (error as Error).message,
});
}
setDiscovering(false);
})();
}, [discovering]);
return ( return (
<> <>
@ -98,20 +85,21 @@ export const DiscoveryEndpointField = ({
/> />
</FormGroup> </FormGroup>
{discovery && ( {discovery && (
<FormGroup <TextControl
name="discoveryEndpoint"
label={t( label={t(
id === "oidc" ? "discoveryEndpoint" : "samlEntityDescriptor", id === "oidc" ? "discoveryEndpoint" : "samlEntityDescriptor",
)} )}
fieldId="kc-discovery-endpoint" labelIcon={t(
labelIcon={ id === "oidc"
<HelpItem ? "discoveryEndpointHelp"
helpText={t( : "samlEntityDescriptorHelp",
id === "oidc" )}
? "discoveryEndpointHelp" type="url"
: "samlEntityDescriptorHelp", placeholder={
)} id === "oidc"
fieldLabelId="discoveryEndpoint" ? "https://hostname/auth/realms/master/.well-known/openid-configuration"
/> : ""
} }
validated={ validated={
errors.discoveryError || errors.discoveryEndpoint errors.discoveryError || errors.discoveryEndpoint
@ -120,42 +108,16 @@ export const DiscoveryEndpointField = ({
? "default" ? "default"
: "success" : "success"
} }
helperTextInvalid={ customIconUrl={
errors.discoveryEndpoint discovering
? t("required") ? environment.resourceUrl + "/discovery-load-indicator.svg"
: t("noValidMetaDataFound", { : ""
error: errors.discoveryError?.message,
})
} }
isRequired rules={{
> required: t("required"),
<KeycloakTextInput validate: (value: string) => discoverDebounced(value),
type="url" }}
data-testid="discoveryEndpoint" />
id="kc-discovery-endpoint"
placeholder={
id === "oidc"
? "https://hostname/auth/realms/master/.well-known/openid-configuration"
: ""
}
validated={
errors.discoveryError || errors.discoveryEndpoint
? "error"
: !discoveryResult
? "default"
: "success"
}
customIconUrl={
discovering
? environment.resourceUrl + "/discovery-load-indicator.svg"
: ""
}
{...register("discoveryEndpoint", {
required: true,
onBlur: () => setDiscovering(true),
})}
/>
</FormGroup>
)} )}
{!discovery && fileUpload} {!discovery && fileUpload}
{discovery && !errors.discoveryError && children(true)} {discovery && !errors.discoveryError && children(true)}

View file

@ -198,6 +198,9 @@ importers:
monaco-editor: monaco-editor:
specifier: ^0.47.0 specifier: ^0.47.0
version: 0.47.0 version: 0.47.0
p-debounce:
specifier: ^4.0.0
version: 4.0.0
react: react:
specifier: ^18.2.0 specifier: ^18.2.0
version: 18.2.0 version: 18.2.0
@ -2183,6 +2186,10 @@ packages:
resolution: {integrity: sha512-XKJdy+OClLk3hketHi9Qg6gTfe1F3y+UFnHxKA2rn9Dw+oXa4Gb378Ztz9HlMgZKSxpPmn4BNVh9wgkpvrK1uw==} resolution: {integrity: sha512-XKJdy+OClLk3hketHi9Qg6gTfe1F3y+UFnHxKA2rn9Dw+oXa4Gb378Ztz9HlMgZKSxpPmn4BNVh9wgkpvrK1uw==}
dev: true dev: true
/@types/debounce-promise@3.1.9:
resolution: {integrity: sha512-awNxydYSU+E2vL7EiOAMtiSOfL5gUM5X4YSE2A92qpxDJQ/rXz6oMPYBFDcDywlUmvIDI6zsqgq17cGm5CITQw==}
dev: false
/@types/estree@1.0.5: /@types/estree@1.0.5:
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
dev: true dev: true
@ -2954,6 +2961,28 @@ packages:
possible-typed-array-names: 1.0.0 possible-typed-array-names: 1.0.0
dev: true dev: true
/awesome-debounce-promise@2.1.0:
resolution: {integrity: sha512-0Dv4j2wKk5BrNZh4jgV2HUdznaeVgEK/WTvcHhZWUElhmQ1RR+iURRoLEwICFyR0S/5VtxfcvY6gR+qSe95jNg==}
engines: {node: '>=8', npm: '>=5'}
dependencies:
'@types/debounce-promise': 3.1.9
awesome-imperative-promise: 1.0.1
awesome-only-resolves-last-promise: 1.0.3
debounce-promise: 3.1.2
dev: false
/awesome-imperative-promise@1.0.1:
resolution: {integrity: sha512-EmPr3FqbQGqlNh+WxMNcF9pO9uDQJnOC4/3rLBQNH9m4E9qI+8lbfHCmHpVAsmGqPJPKhCjJLHUQzQW/RBHRdQ==}
engines: {node: '>=8', npm: '>=5'}
dev: false
/awesome-only-resolves-last-promise@1.0.3:
resolution: {integrity: sha512-7q4WPsYiD8Omvi/yHL314DkvsD/lM//Z2/KcU1vWk0xJotiV0GMJTgHTpWl3n90HJqpXKg7qX+VVNs5YbQyPRQ==}
engines: {node: '>=8', npm: '>=5'}
dependencies:
awesome-imperative-promise: 1.0.1
dev: false
/aws-sign2@0.7.0: /aws-sign2@0.7.0:
resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==}
dev: true dev: true
@ -3633,6 +3662,10 @@ packages:
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
dev: true dev: true
/debounce-promise@3.1.2:
resolution: {integrity: sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg==}
dev: false
/debug@3.2.7(supports-color@8.1.1): /debug@3.2.7(supports-color@8.1.1):
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies: peerDependencies:
@ -6463,6 +6496,11 @@ packages:
resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==} resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==}
dev: true dev: true
/p-debounce@4.0.0:
resolution: {integrity: sha512-4Ispi9I9qYGO4lueiLDhe4q4iK5ERK8reLsuzH6BPaXn53EGaua8H66PXIFGrW897hwjXp+pVLrm/DLxN0RF0A==}
engines: {node: '>=12'}
dev: false
/p-limit@3.1.0: /p-limit@3.1.0:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -8709,6 +8747,7 @@ packages:
'@patternfly/react-icons': 4.93.7(react-dom@18.2.0)(react@18.2.0) '@patternfly/react-icons': 4.93.7(react-dom@18.2.0)(react@18.2.0)
'@patternfly/react-styles': 4.92.8 '@patternfly/react-styles': 4.92.8
'@patternfly/react-table': 4.113.6(react-dom@18.2.0)(react@18.2.0) '@patternfly/react-table': 4.113.6(react-dom@18.2.0)(react@18.2.0)
awesome-debounce-promise: 2.1.0
dagre: 0.8.5 dagre: 0.8.5
file-saver: 2.0.5 file-saver: 2.0.5
file-selector: 0.6.0 file-selector: 0.6.0