Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update rules feature with readonly support and missing resource alert messaging #7454

Merged
merged 20 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
269371a
Improve rule component with readonly support + reource missing check …
jeradrutnam Jan 30, 2025
75a5ede
Remove duplicate use-get-resource api hook
jeradrutnam Jan 30, 2025
2dd73d5
Merge branch 'master' of https://github.com/wso2/identity-apps into r…
jeradrutnam Jan 30, 2025
3205746
Add apiRoot to store.reducer.endpoints
jeradrutnam Jan 30, 2025
5f74a67
🦋 Add changeset
jeradrutnam Jan 30, 2025
cb261d6
Fix alert message not showing properly issue in rules component
jeradrutnam Jan 30, 2025
2c43412
Fix use-get-resource-list module import in rule component
jeradrutnam Jan 30, 2025
93c635f
Update resource not found messaging in rule component
jeradrutnam Jan 30, 2025
28f4754
Fix deleted resource detail caching issue in rules component
jeradrutnam Jan 31, 2025
33a0fd0
Add missing data-componentid rule conditions component
jeradrutnam Jan 31, 2025
8f997df
Add some missing doc comments in rules component utils
jeradrutnam Jan 31, 2025
f2a355b
Fix some type declarions in rules conditions component
jeradrutnam Jan 31, 2025
67013b5
Add null check for findMetaValuesAgainst in rules condition component
jeradrutnam Jan 31, 2025
aa40b36
Fix alert flicker issue on saving in rule component
jeradrutnam Jan 31, 2025
27c67a6
Fix wrong module version bump in changeset
jeradrutnam Jan 31, 2025
45d4d42
Address some comments in pre-issue-access-token component
jeradrutnam Jan 31, 2025
319b75f
Change useGetResourceListOrResourceDetails hook file namein rules com…
jeradrutnam Jan 31, 2025
f09ee88
Delete use-get-resource-list file in rules component
jeradrutnam Jan 31, 2025
e0fb4e7
Fix an issue in rules component
jeradrutnam Jan 31, 2025
35dddfb
Improve readonly view in rules component
jeradrutnam Jan 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/core": minor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we remove this as this PR doesn't contain changes to core module. Let's include admin.core.1 instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad. I have bump the wrong module. I fixed it now.

"@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,6 +102,7 @@ const PreIssueAccessTokenActionConfigForm: FunctionComponent<PreIssueAccessToken

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

Expand Down Expand Up @@ -270,6 +271,7 @@ const PreIssueAccessTokenActionConfigForm: FunctionComponent<PreIssueAccessToken
} }/>
{ (RuleExpressionsMetaData && showRuleComponent) && (
<RuleConfigForm
readonly={ getFieldDisabledStatus() }
rule={ rule }
setRule={ setRule }
isHasRule={ isHasRule }
Expand All @@ -281,113 +283,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 && actionData && (
jeradrutnam marked this conversation as resolved.
Show resolved Hide resolved
<RulesProvider
conditionExpressionsMetaData={ RuleExpressionsMetaData }
initialData={ actionData?.rule }
>
<FinalForm
onSubmit={ (values: ActionConfigFormPropertyInterface, form: any) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import type { FormApi } from "final-form";
Suggested change
onSubmit={ (values: ActionConfigFormPropertyInterface, form: any) => {
onSubmit={ (values: ActionConfigFormPropertyInterface, form: FormApi<ActionConfigFormPropertyInterface>) => {

handleSubmit(values, form.getState().dirtyFields); }
}
validate={ validateForm }
initialValues={ initialValues }
render={ ({ handleSubmit, form }: FormRenderProps) => (
<form onSubmit={ handleSubmit }>
<EmphasizedSegment
className="form-wrapper"
padded={ "very" }
jeradrutnam marked this conversation as resolved.
Show resolved Hide resolved
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
10 changes: 6 additions & 4 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,25 +71,25 @@ 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
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
52 changes: 52 additions & 0 deletions features/admin.rules.v1/api/get-resource-details.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { AsgardeoSPAClient, HttpClientInstance } from "@asgardeo/auth-react";
import { store } from "@wso2is/admin.core.v1/store";
import { HttpMethods } from "@wso2is/core/models";
import { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";

/**
* Initialize an axios Http client.
*/
const httpClient: HttpClientInstance = AsgardeoSPAClient.getInstance()
.httpRequest.bind(AsgardeoSPAClient.getInstance())
.bind(AsgardeoSPAClient.getInstance());
jeradrutnam marked this conversation as resolved.
Show resolved Hide resolved

/**
* Retrieve resource details for a given endpoint path.
jeradrutnam marked this conversation as resolved.
Show resolved Hide resolved
*/
const getResourceDetails = (endpointPath: string): Promise<any> => {
const requestConfig: AxiosRequestConfig = {
headers: {
"Access-Control-Allow-Origin": store.getState().config.deployment.clientHost,
jeradrutnam marked this conversation as resolved.
Show resolved Hide resolved
"Content-Type": "application/json"
},
method: HttpMethods.GET,
url: store.getState().config.endpoints.apiRoot + endpointPath
};

return httpClient(requestConfig)
jeradrutnam marked this conversation as resolved.
Show resolved Hide resolved
.then((response: AxiosResponse) => {
return Promise.resolve(response);
}).catch((error: AxiosError) => {
return Promise.reject(error);
});
};

export default getResourceDetails;
Loading
Loading