From 269371a00e1cebc7450bdfa197c7364e5c3a8da5 Mon Sep 17 00:00:00 2001 From: Jerad Rutnam Date: Thu, 30 Jan 2025 13:28:35 +0530 Subject: [PATCH 01/19] Improve rule component with readonly support + reource missing check alert along with some refactoring --- ...-issue-access-token-action-config-form.tsx | 218 +++++++++--------- .../components/rule-config-form.tsx | 4 +- ...-get-resource-list-or-resource-details.ts} | 6 +- .../admin.rules.v1/api/use-get-resource.ts | 6 +- .../components/rule-conditions.tsx | 49 +++- features/admin.rules.v1/components/rules.tsx | 19 +- features/admin.rules.v1/configs/endpoints.ts | 1 + features/admin.rules.v1/models/endpoints.ts | 1 + .../providers/rules-provider.tsx | 18 +- .../utils/{add-remove-ids.ts => add-ids.ts} | 16 +- .../utils/{clean.ts => clean-instance.ts} | 4 +- features/admin.rules.v1/utils/remove-ids.ts | 33 +++ .../i18n/src/models/namespaces/rules-ns.ts | 8 +- .../src/translations/en-US/portals/rules.ts | 6 + 14 files changed, 239 insertions(+), 150 deletions(-) rename features/admin.rules.v1/api/{use-get-resource-details.ts => use-get-resource-list-or-resource-details.ts} (89%) rename features/admin.rules.v1/utils/{add-remove-ids.ts => add-ids.ts} (74%) rename features/admin.rules.v1/utils/{clean.ts => clean-instance.ts} (96%) create mode 100644 features/admin.rules.v1/utils/remove-ids.ts diff --git a/features/admin.actions.v1/components/pre-issue-access-token-action-config-form.tsx b/features/admin.actions.v1/components/pre-issue-access-token-action-config-form.tsx index bad2edeea8c..cd7ab49ca2c 100644 --- a/features/admin.actions.v1/components/pre-issue-access-token-action-config-form.tsx +++ b/features/admin.actions.v1/components/pre-issue-access-token-action-config-form.tsx @@ -102,6 +102,7 @@ const PreIssueAccessTokenActionConfigForm: FunctionComponent { (RuleExpressionsMetaData && showRuleComponent) && ( - { - handleSubmit(values, form.getState().dirtyFields); } - } - validate={ validateForm } - initialValues={ initialValues } - render={ ({ handleSubmit, form }: FormRenderProps) => ( -
- -
- { renderFormFields() } - { !isLoading && ( - + ) } +
+
+ + { ({ values }: { values: ActionConfigFormPropertyInterface }) => { + if (!isAuthenticationUpdateFormState) { + form.change("authenticationType", + initialValues?.authenticationType); + switch (authenticationType) { + case AuthenticationType.BASIC: + delete values.usernameAuthProperty; + delete values.passwordAuthProperty; + + break; + case AuthenticationType.BEARER: + delete values.accessTokenAuthProperty; + + break; + case AuthenticationType.API_KEY: + delete values.headerAuthProperty; + delete values.valueAuthProperty; + + break; + default: + break; + } } - - ) } - - - - { ({ values }: { values: ActionConfigFormPropertyInterface }) => { - if (!isAuthenticationUpdateFormState) { - form.change("authenticationType", - initialValues?.authenticationType); - switch (authenticationType) { - case AuthenticationType.BASIC: - delete values.usernameAuthProperty; - delete values.passwordAuthProperty; - - break; - case AuthenticationType.BEARER: - delete values.accessTokenAuthProperty; - - break; - case AuthenticationType.API_KEY: - delete values.headerAuthProperty; - delete values.valueAuthProperty; - - break; - default: - break; - } - } - - // Clear inputs of property field values of other authentication types. - switch (authenticationType) { - case AuthenticationType.BASIC: - delete values.accessTokenAuthProperty; - delete values.headerAuthProperty; - delete values.valueAuthProperty; - - break; - case AuthenticationType.BEARER: - delete values.usernameAuthProperty; - delete values.passwordAuthProperty; - delete values.headerAuthProperty; - delete values.valueAuthProperty; - - break; - case AuthenticationType.API_KEY: - delete values.usernameAuthProperty; - delete values.passwordAuthProperty; - delete values.accessTokenAuthProperty; - - break; - case AuthenticationType.NONE: - delete values.usernameAuthProperty; - delete values.passwordAuthProperty; - delete values.headerAuthProperty; - delete values.valueAuthProperty; - delete values.accessTokenAuthProperty; - - break; - default: - - break; - } - - return null; - } } - - - ) } - > -
- + + // Clear inputs of property field values of other authentication types. + switch (authenticationType) { + case AuthenticationType.BASIC: + delete values.accessTokenAuthProperty; + delete values.headerAuthProperty; + delete values.valueAuthProperty; + + break; + case AuthenticationType.BEARER: + delete values.usernameAuthProperty; + delete values.passwordAuthProperty; + delete values.headerAuthProperty; + delete values.valueAuthProperty; + + break; + case AuthenticationType.API_KEY: + delete values.usernameAuthProperty; + delete values.passwordAuthProperty; + delete values.accessTokenAuthProperty; + + break; + case AuthenticationType.NONE: + delete values.usernameAuthProperty; + delete values.passwordAuthProperty; + delete values.headerAuthProperty; + delete values.valueAuthProperty; + delete values.accessTokenAuthProperty; + + break; + default: + + break; + } + + return null; + } } + + + ) } + > + + + ) } + ); }; diff --git a/features/admin.actions.v1/components/rule-config-form.tsx b/features/admin.actions.v1/components/rule-config-form.tsx index 499964ade92..6662aeb920d 100644 --- a/features/admin.actions.v1/components/rule-config-form.tsx +++ b/features/admin.actions.v1/components/rule-config-form.tsx @@ -30,6 +30,7 @@ import React, { Dispatch, FunctionComponent, ReactElement, useEffect } from "rea import { Trans, useTranslation } from "react-i18next"; interface RuleConfigFormInterface extends IdentifiableComponentInterface { + readonly: boolean; rule: RuleWithoutIdInterface; isHasRule : boolean; setIsHasRule: (value: boolean) => void; @@ -37,6 +38,7 @@ interface RuleConfigFormInterface extends IdentifiableComponentInterface { } const RuleConfigForm: FunctionComponent = ({ + readonly, rule, isHasRule, setIsHasRule, @@ -73,7 +75,7 @@ const RuleConfigForm: FunctionComponent = ({ { isHasRule ? ( - + ) : ( ( +const useGetResourceListOrResourceDetails = ( endpointPath: string, shouldFetch: boolean = true ): RequestResultInterface => { @@ -44,7 +44,7 @@ const useGetResourcesList = ( "Content-Type": "application/json" }, method: HttpMethods.GET, - url: store.getState().config.deployment.idpConfigs.serverOrigin + `/api/server/v1${endpointPath}` + url: store.getState().config.endpoints.apiRoot + endpointPath }; const { data, error, isLoading, isValidating, mutate } = useRequest( @@ -60,4 +60,4 @@ const useGetResourcesList = ( }; }; -export default useGetResourcesList; +export default useGetResourceListOrResourceDetails; diff --git a/features/admin.rules.v1/api/use-get-resource.ts b/features/admin.rules.v1/api/use-get-resource.ts index 83d71d4a61e..8627ae4bb05 100644 --- a/features/admin.rules.v1/api/use-get-resource.ts +++ b/features/admin.rules.v1/api/use-get-resource.ts @@ -34,7 +34,7 @@ import { HttpMethods } from "@wso2is/core/models"; * @param shouldFetch - Should fetch the data. * @returns SWR response object containing the data, error, isLoading, isValidating, mutate. */ -const useGetResourceDetails = ( +const useGetResourceListOrResourceDetails = ( endpointPath: string, shouldFetch: boolean = true ): RequestResultInterface => { @@ -44,7 +44,7 @@ const useGetResourceDetails = ( "Content-Type": "application/json" }, method: HttpMethods.GET, - url: store.getState().config.deployment.idpConfigs.serverOrigin + `/api/server/v1${endpointPath}` + url: store.getState().config.endpoints.apiEndpoint + endpointPath }; const { data, error, isLoading, isValidating, mutate } = useRequest( @@ -60,4 +60,4 @@ const useGetResourceDetails = ( }; }; -export default useGetResourceDetails; +export default useGetResourceListOrResourceDetails; diff --git a/features/admin.rules.v1/components/rule-conditions.tsx b/features/admin.rules.v1/components/rule-conditions.tsx index 97e7acf2870..ea0f639d34c 100644 --- a/features/admin.rules.v1/components/rule-conditions.tsx +++ b/features/admin.rules.v1/components/rule-conditions.tsx @@ -16,6 +16,8 @@ * under the License. */ +import Alert from "@oxygen-ui/react/Alert"; +import AlertTitle from "@oxygen-ui/react/AlertTitle"; import Autocomplete from "@oxygen-ui/react/Autocomplete"; import Box from "@oxygen-ui/react/Box"; import Button from "@oxygen-ui/react/Button"; @@ -30,8 +32,8 @@ import { MinusIcon, PlusIcon } from "@oxygen-ui/react-icons"; import { IdentifiableComponentInterface } from "@wso2is/core/models"; import debounce from "lodash-es/debounce"; import React, { ChangeEvent, Fragment, FunctionComponent, ReactElement, useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; -import useGetResourceDetails from "../api/use-get-resource-details"; +import { Trans, useTranslation } from "react-i18next"; +import useGetResourceListOrResourceDetails from "../api/use-get-resource-list-or-resource-details"; import { useRulesContext } from "../hooks/use-rules-context"; import { ConditionExpressionMetaInterface, @@ -83,6 +85,7 @@ interface ResourceListSelectProps { * Props interface of {@link RulesComponent} */ export interface RulesComponentPropsInterface extends IdentifiableComponentInterface { + readonly?: boolean; rule: RuleInterface; } @@ -94,6 +97,7 @@ export interface RulesComponentPropsInterface extends IdentifiableComponentInter */ const RuleConditions: FunctionComponent = ({ ["data-componentid"]: componentId = "rules-component", + readonly, rule: ruleInstance }: RulesComponentPropsInterface): ReactElement => { @@ -162,6 +166,8 @@ const RuleConditions: FunctionComponent = ({ (expressionMeta: ConditionExpressionMetaInterface) => expressionMeta.field.name === expression.field ); + const [ isResourceMissing, setIsResourceMissing ] = useState(false); + /** * Value input autocomplete component. * @@ -186,10 +192,11 @@ const RuleConditions: FunctionComponent = ({ : initialResourcesLoadUrl; const { data: initialResources = [], isLoading: isInitialLoading } = - useGetResourceDetails(initialResourcesLoadUrl); - const { data: filteredResources = [], isLoading: isFiltering } = useGetResourceDetails(filterUrl); + useGetResourceListOrResourceDetails(initialResourcesLoadUrl); + const { data: filteredResources = [], isLoading: isFiltering } = + useGetResourceListOrResourceDetails(filterUrl); const { data: resourceDetails, isLoading: isResourceDetailsLoading } = - useGetResourceDetails(`/${resourceType}/${expression.value}`); + useGetResourceListOrResourceDetails(`/${resourceType}/${expression.value}`); useEffect(() => { if (resourceDetails) { @@ -224,6 +231,7 @@ const RuleConditions: FunctionComponent = ({ return ( setOpen(true) } onClose={ () => setOpen(false) } @@ -324,7 +332,7 @@ const RuleConditions: FunctionComponent = ({ const valueReferenceAttribute: string = findMetaValuesAgainst?.value?.valueReferenceAttribute || "id"; const valueDisplayAttribute: string = findMetaValuesAgainst?.value?.valueDisplayAttribute || "name"; - const { data: fetchedResourcesList } = useGetResourceDetails(initialResourcesLoadUrl); + const { data: fetchedResourcesList } = useGetResourceListOrResourceDetails(initialResourcesLoadUrl); let resourcesList: any = null; let resourceType: string = ""; @@ -352,6 +360,10 @@ const RuleConditions: FunctionComponent = ({ ); } + if (!resourcesList[resourceType].find((resource: any) => resource.id === expression.value)) { + setIsResourceMissing(true); + } + if (resourcesList.totalResults > resourcesList.count) { return ( = ({ return ( { updateConditionExpression( @@ -486,8 +501,25 @@ const RuleConditions: FunctionComponent = ({ className="box-container" data-componentid={ componentId } > + { isResourceMissing && ( + + + + Previous configured resource cannot be found. + + + + Please make sure to update it to a resource that is available. + + + ) } { updateConditionExpression( @@ -541,6 +574,7 @@ const RuleConditions: FunctionComponent = ({ - { isConditionExpressionRemovable && ( + { isConditionExpressionRemovable && !readonly && ( = ({ { condition.expressions?.length > 0 && ( - + { !readonly && ( +
+ +
+ ) }
) } diff --git a/features/admin.rules.v1/components/rule-conditions.tsx b/features/admin.rules.v1/components/rule-conditions.tsx index 505c60635de..b3d93c33f62 100644 --- a/features/admin.rules.v1/components/rule-conditions.tsx +++ b/features/admin.rules.v1/components/rule-conditions.tsx @@ -112,6 +112,7 @@ interface RuleExpressionComponentProps extends IdentifiableComponentInterface { ruleId: string; conditionId: string; index: number; + isConditionLast: boolean; isConditionExpressionRemovable: boolean; } @@ -561,6 +562,7 @@ const RuleConditions: FunctionComponent = ({ ruleId, conditionId, index, + isConditionLast, isConditionExpressionRemovable }: RuleExpressionComponentProps) => { @@ -664,26 +666,28 @@ const RuleConditions: FunctionComponent = ({ setIsResourceMissing={ setIsResourceMissing } /> - - - + { ((!readonly) || (readonly && !isConditionLast)) && ( + + + + ) } { isConditionExpressionRemovable && !readonly && ( = ({ { condition.expressions?.map( (expression: ConditionExpressionInterface, exprIndex: number) => ( + { (condition.expressions.length === (exprIndex + 1)) } 1 || ruleInstance.rules.length > 1 @@ -726,7 +732,8 @@ const RuleConditions: FunctionComponent = ({ ) } ) } - { condition.expressions?.length > 0 && ( + { ((!readonly && (condition.expressions?.length > 0)) || + (readonly && (condition.expressions?.length !== index))) && (