Skip to content

Commit

Permalink
Merge pull request #7454 from jeradrutnam/rules-feature
Browse files Browse the repository at this point in the history
Update rules feature with readonly support and missing resource alert messaging
  • Loading branch information
jeradrutnam authored Jan 31, 2025
2 parents efeb584 + 35dddfb commit a172e42
Show file tree
Hide file tree
Showing 19 changed files with 749 additions and 536 deletions.
8 changes: 8 additions & 0 deletions .changeset/smart-grapes-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@wso2is/admin.rules.v1": minor
"@wso2is/admin.core.v1": minor
"@wso2is/i18n": minor
"@wso2is/admin.actions.v1": patch
---

Add readonly view and missing resource alert support to the rules component
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ const PreIssueAccessTokenActionConfigForm: FunctionComponent<PreIssueAccessToken

const {
data: actionData,
mutate: mutateAction
isLoading: isActionLoading,
mutate: mutateAction,
error: actionError
} = useGetActionById(actionTypeApiPath, initialValues?.id);

const {
Expand Down Expand Up @@ -270,6 +272,7 @@ const PreIssueAccessTokenActionConfigForm: FunctionComponent<PreIssueAccessToken
} }/>
{ (RuleExpressionsMetaData && showRuleComponent) && (
<RuleConfigForm
readonly={ getFieldDisabledStatus() }
rule={ rule }
setRule={ setRule }
isHasRule={ isHasRule }
Expand All @@ -281,113 +284,117 @@ const PreIssueAccessTokenActionConfigForm: FunctionComponent<PreIssueAccessToken
};

return (
<RulesProvider
conditionExpressionsMetaData={ RuleExpressionsMetaData }
initialData={ actionData?.rule }
>
<FinalForm
onSubmit={ (values: ActionConfigFormPropertyInterface, form: any) => {
handleSubmit(values, form.getState().dirtyFields); }
}
validate={ validateForm }
initialValues={ initialValues }
render={ ({ handleSubmit, form }: FormRenderProps) => (
<form onSubmit={ handleSubmit }>
<EmphasizedSegment
className="form-wrapper"
padded={ "very" }
data-componentid={ `${ _componentId }-section` }
>
<div className="form-container with-max-width">
{ renderFormFields() }
{ !isLoading && (
<Button
size="medium"
variant="contained"
onClick={ handleSubmit }
className={ "button-container" }
data-componentid={ `${ _componentId }-primary-button` }
loading={ isSubmitting }
disabled={ getFieldDisabledStatus() }
>
{
isCreateFormState
? t("actions:buttons.create")
: t("actions:buttons.update")
<>
{ !isActionLoading && !actionError && actionData && (
<RulesProvider
conditionExpressionsMetaData={ RuleExpressionsMetaData }
initialData={ actionData?.rule }
>
<FinalForm
onSubmit={ (values: ActionConfigFormPropertyInterface, form: any) => {
handleSubmit(values, form.getState().dirtyFields); }
}
validate={ validateForm }
initialValues={ initialValues }
render={ ({ handleSubmit, form }: FormRenderProps) => (
<form onSubmit={ handleSubmit }>
<EmphasizedSegment
className="form-wrapper"
padded="very"
data-componentid={ `${ _componentId }-section` }
>
<div className="form-container with-max-width">
{ renderFormFields() }
{ !isLoading && (
<Button
size="medium"
variant="contained"
onClick={ handleSubmit }
className={ "button-container" }
data-componentid={ `${ _componentId }-primary-button` }
loading={ isSubmitting }
disabled={ getFieldDisabledStatus() }
>
{
isCreateFormState
? t("actions:buttons.create")
: t("actions:buttons.update")
}
</Button>
) }
</div>
</EmphasizedSegment>
<FormSpy
subscription={ { values: true } }
>
{ ({ 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;
}
}
</Button>
) }
</div>
</EmphasizedSegment>
<FormSpy
subscription={ { values: true } }
>
{ ({ 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;
} }
</FormSpy>
</form>
) }
>
</FinalForm>
</RulesProvider>

// 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;
} }
</FormSpy>
</form>
) }
>
</FinalForm>
</RulesProvider>
) }
</>
);
};

Expand Down
34 changes: 19 additions & 15 deletions features/admin.actions.v1/components/rule-config-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ 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;
setRule: Dispatch<React.SetStateAction<RuleWithoutIdInterface>>;
}

const RuleConfigForm: FunctionComponent<RuleConfigFormInterface> = ({
readonly,
rule,
isHasRule,
setIsHasRule,
Expand Down Expand Up @@ -69,37 +71,39 @@ const RuleConfigForm: FunctionComponent<RuleConfigFormInterface> = ({
<Divider className="divider-container" />
<Heading className="heading-container" as="h5">
<Trans i18nKey={ t("actions:fields.rules.label") }>
Execution Rule
Execution Rule
</Trans>
</Heading>
{ isHasRule ? (
<Rules disableLastRuleDelete={ false } />
<Rules disableLastRuleDelete={ false } readonly={ readonly } />
) : (
<Alert className="alert-nutral" icon={ false }>
<AlertTitle
className="alert-title"
data-componentid={ `${ _componentId }-rule-info-box-title` }
>
<Trans i18nKey={ t("actions:fields.rules.info.title") }>
No execution rule is configured.
No execution rule is configured.
</Trans>
</AlertTitle>
<Trans
i18nKey={ t("actions:fields.authentication.info.message") }
>
This action will be executed without any conditions.
This action will be executed without any conditions.
</Trans>
<div>
<Button
onClick={ () => setIsHasRule(true) }
variant="outlined"
size="small"
className={ "secondary-button" }
data-componentid={ `${ _componentId }-configure-rule-button` }
>
{ t("actions:fields.rules.button") }
</Button>
</div>
{ !readonly && (
<div>
<Button
onClick={ () => setIsHasRule(true) }
variant="outlined"
size="small"
className={ "secondary-button" }
data-componentid={ `${ _componentId }-configure-rule-button` }
>
{ t("actions:fields.rules.button") }
</Button>
</div>
) }
</Alert>
) }
</>
Expand Down
1 change: 1 addition & 0 deletions features/admin.core.v1/store/reducers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export const commonConfigReducerInitialState: CommonConfigReducerStateInterface<
accountRecovery: "",
actions: "",
adminAdvisoryBanner: "",
apiRoot: "",
applicationTemplate: "",
applicationTemplateMetadata: "",
applications: "",
Expand Down
3 changes: 2 additions & 1 deletion features/admin.rules.v1/__tests__/__mocks__/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/

import { ConditionExpressionsMetaDataInterface, RuleExecutionMetaDataInterface } from "../../models/meta";
import { ResourceListInterface } from "../../models/resource";
import { RuleExecuteCollectionInterface, RuleInterface } from "../../models/rules";

export const sampleRuleExecuteInstance: RuleInterface = {
Expand Down Expand Up @@ -83,7 +84,7 @@ export const sampleRuleExecuteInstances: RuleExecuteCollectionInterface = {
]
};

export const sampleApplicationList: any = {
export const sampleApplicationList: ResourceListInterface = {
applications: [
{
access: "READ",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
* under the License.
*/

import { Config } from "@wso2is/admin.core.v1/configs/app";
import useRequest, {
RequestConfigInterface,
RequestErrorInterface,
RequestResultInterface
} from "@wso2is/admin.core.v1/hooks/use-request";
import { store } from "@wso2is/admin.core.v1/store";
import { HttpMethods } from "@wso2is/core/models";

/**
Expand All @@ -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 useGetResourcesList = <Data = any, Error = RequestErrorInterface>(
const useGetResourceListOrResourceDetails = <Data = any, Error = RequestErrorInterface>(
endpointPath: string,
shouldFetch: boolean = true
): RequestResultInterface<Data, Error> => {
Expand All @@ -44,7 +44,7 @@ const useGetResourcesList = <Data = any, Error = RequestErrorInterface>(
"Content-Type": "application/json"
},
method: HttpMethods.GET,
url: Config.resolveServerHost() + `/api/server/v1${endpointPath}`
url: store.getState().config.endpoints.apiRoot + endpointPath
};

const { data, error, isLoading, isValidating, mutate } = useRequest<Data, Error>(
Expand All @@ -60,4 +60,4 @@ const useGetResourcesList = <Data = any, Error = RequestErrorInterface>(
};
};

export default useGetResourcesList;
export default useGetResourceListOrResourceDetails;
Loading

0 comments on commit a172e42

Please sign in to comment.