minor: add encoding and decoding for policy rule name (#17344)

* minor: add encoding and decoding for policy rule name

* move encoding logic to util method

* refactor: move the constant to separate file

* chore: Remove unnecessary wait for timeout in Policies.spec.ts
This commit is contained in:
Sachin Chaurasiya 2024-08-10 10:13:24 +05:30 committed by GitHub
parent 81e467a225
commit c6ee207837
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 396 additions and 380 deletions

View File

@ -1,374 +0,0 @@
/*
* Copyright 2022 Collate.
* Licensed 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 {
descriptionBox,
interceptURL,
uuid,
verifyResponseStatusCode,
} from '../../common/common';
import { validateFormNameFieldInput } from '../../common/Utils/Form';
import { BASE_URL } from '../../constants/constants';
import { GlobalSettingOptions } from '../../constants/settings.constant';
const roles = {
dataConsumer: 'Data Consumer',
dataSteward: 'Data Steward',
};
const policies = {
dataConsumerPolicy: 'Data Consumer Policy',
dataStewardPolicy: 'Data Steward Policy',
organizationPolicy: 'Organization Policy',
teamOnlyAccessPolicy: 'Team only access Policy',
};
const ruleDetails = {
resources: 'All',
operations: 'All',
effect: 'Allow',
condition: 'isOwner()',
inValidCondition: 'isOwner(',
};
const errorMessageValidation = {
lastPolicyCannotBeRemoved: 'At least one policy is required in a role',
lastRuleCannotBeRemoved: 'At least one rule is required in a policy',
};
const policyName = `Policy-test-${uuid()}`;
const description = `This is ${policyName} description`;
const ruleName = `Rule-test-${uuid()}`;
const ruleDescription = `This is ${ruleName} description`;
const updatedDescription = 'This is updated description';
const newRuleName = `New-Rule-test-${uuid()}`;
const newRuledescription = `This is ${newRuleName} description`;
const updatedRuleName = `New-Rule-test-${uuid()}-updated`;
const addRule = (rulename, ruleDescription, descriptionIndex) => {
validateFormNameFieldInput({
value: rulename,
fieldName: 'Name',
fieldSelector: '[data-testid="rule-name"]',
errorDivSelector: '#ruleName_help',
});
// Enter rule description
cy.get('.toastui-editor-md-container > .toastui-editor > .ProseMirror')
.eq(descriptionIndex)
.scrollIntoView()
.type(ruleDescription);
// Select resource dropdown
cy.get('[data-testid="resources"]')
.scrollIntoView()
.should('be.visible')
.click();
// Select All
cy.get('.ant-select-tree-checkbox-inner').should('be.visible').click();
// Click on operations dropdown
cy.get('[data-testid="operations"]').should('be.visible').click();
cy.get('.ant-select-tree-checkbox-inner').eq(1).should('be.visible').click();
// Click on condition combobox
cy.get('[data-testid="condition"]')
.scrollIntoView()
.should('be.visible')
.click();
cy.get(`[title="${ruleDetails.condition}"]`).should('be.visible').click();
cy.get('[data-testid="condition-success"]').contains('✅ Valid condition');
cy.wait(500);
// Submit
cy.get('[data-testid="submit-btn"]')
.scrollIntoView()
.should('be.visible')
.click();
};
describe('Policy page should work properly', { tags: 'Settings' }, () => {
beforeEach(() => {
cy.login();
cy.intercept('GET', '*api/v1/policies*').as('getPolicies');
cy.settingClick(GlobalSettingOptions.POLICIES);
cy.wait('@getPolicies', { timeout: 15000 })
.its('response.statusCode')
.should('equal', 200);
cy.url().should('eq', `${BASE_URL}/settings/access/policies`);
});
it('Default Policies and Roles should be displayed', () => {
// Verifying the default roles and policies are present
Object.values(policies).forEach((policy) => {
cy.get('[data-testid="policy-name"]')
.should('contain', policy)
.should('be.visible');
});
});
it('Add new policy', () => {
// Click on add new policy
cy.get('[data-testid="add-policy"]').should('be.visible').click();
cy.get('[data-testid="inactive-link"]');
// Enter policy name
cy.get('[data-testid="policy-name"]').should('be.visible').type(policyName);
validateFormNameFieldInput({
value: policyName,
fieldName: 'Name',
fieldSelector: '[data-testid="policy-name"]',
errorDivSelector: '#name_help',
});
// Enter description
cy.get(descriptionBox).eq(0).type(description);
// Enter rule name
addRule(ruleName, ruleDescription, 1);
// Validate the added policy
cy.get('[data-testid="inactive-link"]')
.should('be.visible')
.should('have.text', policyName);
cy.get('[data-testid="rule-name"]')
.should('be.visible')
.should('contain', ruleName);
// Verify policy description
cy.get(
'[data-testid="asset-description-container"] [data-testid="viewer-container"]'
)
.eq(0)
.should('be.visible')
.should('contain', description);
// verify rule description
cy.get('[data-testid="viewer-container"] > [data-testid="markdown-parser"]')
.should('be.visible')
.should('contain', ruleDescription);
// Verify other details
cy.get('[data-testid="rule-name"]').should('be.visible').click();
cy.get('[data-testid="resources"]')
.should('be.visible')
.should('contain', ruleDetails.resources);
cy.get('[data-testid="operations"]')
.should('be.visible')
.should('contain', ruleDetails.operations);
cy.get('[data-testid="effect"]')
.should('be.visible')
.should('contain', ruleDetails.effect);
cy.get('[data-testid="condition"]')
.should('be.visible')
.should('contain', ruleDetails.condition);
});
it('Edit policy description', () => {
interceptURL(
'GET',
`/api/v1/policies/name/${policyName}*`,
'getSelectedPolicy'
);
// Click on created policy name
cy.get('[data-testid="policy-name"]').contains(policyName).click();
verifyResponseStatusCode('@getSelectedPolicy', 200);
cy.get('[data-testid="edit-description"]').should('be.visible').click();
// Enter updated description
cy.get(descriptionBox)
.should('be.visible')
.clear()
.type(`${updatedDescription}-${policyName}`);
// Click on save
cy.get('[data-testid="save"]').should('be.visible').click();
// Validate added description
cy.get(
'[data-testid="asset-description-container"] [data-testid="viewer-container"]'
)
.should('be.visible')
.should('contain', `${updatedDescription}-${policyName}`);
});
it('Add new rule', () => {
interceptURL(
'GET',
`/api/v1/policies/name/${policyName}*`,
'getSelectedPolicy'
);
// Click on created policy name
cy.get('[data-testid="policy-name"]').contains(policyName).click();
verifyResponseStatusCode('@getSelectedPolicy', 200);
interceptURL('GET', '/api/v1/policies/*', 'addRulepage');
// Click on add rule button
cy.get('[data-testid="add-rule"]').should('be.visible').click();
verifyResponseStatusCode('@addRulepage', 200);
addRule(newRuleName, newRuledescription, 0);
// Validate added rule
cy.get('[data-testid="rule-name"]')
.should('be.visible')
.should('contain', ruleName);
// Verify other details
cy.get('[data-testid="rule-name"]')
.last()
.scrollIntoView()
.contains(ruleName)
.should('be.visible')
.click();
cy.get('[data-testid="resources"]')
.last()
.scrollIntoView()
.should('exist')
.should('contain', ruleDetails.resources);
cy.get('[data-testid="operations"]')
.last()
.scrollIntoView()
.should('exist')
.should('contain', ruleDetails.operations);
cy.get('[data-testid="effect"]')
.last()
.scrollIntoView()
.should('exist')
.should('contain', ruleDetails.effect);
cy.get('[data-testid="condition"]')
.last()
.scrollIntoView()
.should('exist')
.should('contain', ruleDetails.condition);
});
it('Edit rule name for created Rule', () => {
interceptURL(
'GET',
`/api/v1/policies/name/${policyName}*`,
'getSelectedPolicy'
);
// Click on created policy name
cy.get('[data-testid="policy-name"]').contains(policyName).click();
verifyResponseStatusCode('@getSelectedPolicy', 200);
// Click on new rule manage button
cy.get(`[data-testid="manage-button-${newRuleName}"]`)
.should('be.visible')
.click();
interceptURL('GET', '/api/v1/policies/*', 'editRulePage');
cy.get('[data-testid="edit-rule"]').should('be.visible').click();
verifyResponseStatusCode('@editRulePage', 200);
verifyResponseStatusCode('@getSelectedPolicy', 200);
// Enter new name
cy.get('[data-testid="rule-name"]').clear().type(updatedRuleName);
interceptURL('PATCH', '/api/v1/policies/*', 'updateRule');
cy.get('[data-testid="submit-btn"]')
.scrollIntoView()
.should('be.visible')
.click();
verifyResponseStatusCode('@updateRule', 200);
verifyResponseStatusCode('@getSelectedPolicy', 200);
cy.url().should('include', policyName);
cy.get('[data-testid="rule-name"]').should('contain', updatedRuleName);
});
it('Delete new rule', () => {
interceptURL(
'GET',
`/api/v1/policies/name/${policyName}*`,
'getSelectedPolicy'
);
// Click on created policy name
cy.get('[data-testid="policy-name"]').contains(policyName).click();
verifyResponseStatusCode('@getSelectedPolicy', 200);
// Click on new rule manage button
cy.get(`[data-testid="manage-button-${updatedRuleName}"]`)
.should('be.visible')
.click();
cy.get('[data-testid="delete-rule"]').should('be.visible').click();
// Validate the deleted rule
cy.get('[data-testid="rule-name"]')
.should('be.visible')
.should('not.contain', updatedRuleName);
});
it('Delete last rule and validate', () => {
interceptURL(
'GET',
`/api/v1/policies/name/${policyName}*`,
'getSelectedPolicy'
);
// Click on created policy name
cy.get('[data-testid="policy-name"]').contains(policyName).click();
verifyResponseStatusCode('@getSelectedPolicy', 200);
// Click on new rule manage button
cy.get(`[data-testid="manage-button-${ruleName}"]`)
.should('be.visible')
.click();
interceptURL('PATCH', '/api/v1/policies/*', 'deletelastPolicy');
cy.get('[data-testid="delete-rule"]').should('be.visible').click();
verifyResponseStatusCode('@deletelastPolicy', 400);
cy.get('.Toastify__toast-body')
.should('be.visible')
.should('contain', errorMessageValidation.lastRuleCannotBeRemoved);
});
it('Delete created policy', () => {
cy.get(`[data-testid="delete-action-${policyName}"]`).click({
force: true,
});
cy.get('[data-testid="confirmation-text-input"]').type('DELETE');
cy.get('[data-testid="confirm-button"]').click();
// Validate deleted policy
cy.get('[data-testid="policy-name"]').should('not.contain', policyName);
});
});

View File

@ -0,0 +1,45 @@
/*
* Copyright 2024 Collate.
* Licensed 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 { uuid } from '../utils/common';
export const DEFAULT_POLICIES = {
dataConsumerPolicy: 'Data Consumer Policy',
dataStewardPolicy: 'Data Steward Policy',
organizationPolicy: 'Organization Policy',
teamOnlyAccessPolicy: 'Team only access Policy',
};
export const RULE_DETAILS = {
resources: 'All',
operations: 'All',
effect: 'Allow',
condition: 'isOwner()',
inValidCondition: 'isOwner(',
};
export const ERROR_MESSAGE_VALIDATION = {
lastPolicyCannotBeRemoved: 'At least one policy is required in a role',
lastRuleCannotBeRemoved: 'At least one rule is required in a policy',
};
export const POLICY_NAME = `Policy-test-${uuid()}`;
export const DESCRIPTION = `This is ${POLICY_NAME} description`;
export const RULE_NAME = `Rule / test-${uuid()}`;
export const RULE_DESCRIPTION = `This is ${RULE_NAME} description`;
export const UPDATED_DESCRIPTION = 'This is updated description';
export const NEW_RULE_NAME = `New / Rule-test-${uuid()}`;
export const NEW_RULE_DESCRIPTION = `This is ${NEW_RULE_NAME} description`;
export const UPDATED_RULE_NAME = `New-Rule-test-${uuid()}-updated`;

View File

@ -0,0 +1,290 @@
/*
* Copyright 2024 Collate.
* Licensed 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 test, { expect, Page } from '@playwright/test';
import {
DEFAULT_POLICIES,
DESCRIPTION,
ERROR_MESSAGE_VALIDATION,
NEW_RULE_DESCRIPTION,
NEW_RULE_NAME,
POLICY_NAME,
RULE_DESCRIPTION,
RULE_DETAILS,
RULE_NAME,
UPDATED_DESCRIPTION,
UPDATED_RULE_NAME,
} from '../../constant/permission';
import { GlobalSettingOptions } from '../../constant/settings';
import { descriptionBox, redirectToHomePage } from '../../utils/common';
import { validateFormNameFieldInput } from '../../utils/form';
import { settingClick } from '../../utils/sidebar';
// use the admin user to login
test.use({ storageState: 'playwright/.auth/admin.json' });
const addRule = async (
page: Page,
rulename: string,
ruleDescription: string,
descriptionIndex: number
) => {
// Validate form name field input
await page.fill('[data-testid="rule-name"]', rulename);
// Enter rule description
await page
.locator(descriptionBox)
.nth(descriptionIndex)
.fill(ruleDescription);
// Select resource dropdown
await page.locator('[data-testid="resources"]').click();
// Select All
await page.locator('.ant-select-tree-checkbox-inner').first().click();
// Click on operations dropdown
await page.locator('[data-testid="operations"]').click();
// Select operation
await page.locator('.ant-select-tree-checkbox-inner').nth(1).click();
// Click on condition combobox
await page.locator('[data-testid="condition"]').click();
// Select condition
await page.locator(`[title="${RULE_DETAILS.condition}"]`).click();
// Verify condition success
await expect(page.locator('[data-testid="condition-success"]')).toContainText(
'✅ Valid condition'
);
// Submit
await page.locator('[data-testid="submit-btn"]').click();
};
test.describe('Policy page should work properly', () => {
test.beforeEach('Visit entity details page', async ({ page }) => {
await redirectToHomePage(page);
await settingClick(page, GlobalSettingOptions.POLICIES);
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
});
test('Add new policy with invalid condition', async ({ page }) => {
await test.step(
'Default Policies and Roles should be displayed',
async () => {
// Verifying the default roles and policies are present
for (const policy of Object.values(DEFAULT_POLICIES)) {
const policyElement = page.locator('[data-testid="policy-name"]', {
hasText: policy,
});
await expect(policyElement).toBeVisible();
}
}
);
await test.step('Add new policy', async () => {
// Click on add new policy
await page.locator('[data-testid="add-policy"]').click();
await expect(page.locator('[data-testid="inactive-link"]')).toBeVisible();
// Enter policy name
await page.locator('[data-testid="policy-name"]').fill(POLICY_NAME);
await validateFormNameFieldInput({
page,
value: POLICY_NAME,
fieldName: 'Name',
fieldSelector: '[data-testid="policy-name"]',
errorDivSelector: '#name_help',
});
// Enter description
await page.locator(descriptionBox).nth(0).fill(DESCRIPTION);
// Enter rule name
await addRule(page, RULE_NAME, RULE_DESCRIPTION, 1);
// Validate the added policy
await expect(page.locator('[data-testid="inactive-link"]')).toHaveText(
POLICY_NAME
);
await page.getByText(RULE_NAME, { exact: true }).isVisible();
// Verify policy description
await expect(
page
.locator(
'[data-testid="asset-description-container"] [data-testid="viewer-container"]'
)
.nth(0)
).toContainText(DESCRIPTION);
// Verify rule description
await expect(
page
.locator(
'[data-testid="viewer-container"] > [data-testid="markdown-parser"]'
)
.nth(1)
).toContainText(RULE_DESCRIPTION);
// Verify other details
await page.locator('[data-testid="rule-name"]').click();
await expect(page.locator('[data-testid="resources"]')).toContainText(
RULE_DETAILS.resources
);
await expect(page.locator('[data-testid="operations"]')).toContainText(
RULE_DETAILS.operations
);
await expect(page.locator('[data-testid="effect"]')).toContainText(
RULE_DETAILS.effect
);
await expect(page.locator('[data-testid="condition"]')).toContainText(
RULE_DETAILS.condition
);
});
await test.step('Edit policy description', async () => {
// Click on edit description
await page.locator('[data-testid="edit-description"]').click();
await page
.locator(descriptionBox)
.fill(`${UPDATED_DESCRIPTION}-${POLICY_NAME}`);
// Click on save
await page.locator('[data-testid="save"]').click();
// Validate added description
await expect(
page
.locator(
'[data-testid="asset-description-container"] [data-testid="viewer-container"]'
)
.nth(0)
).toContainText(`${UPDATED_DESCRIPTION}-${POLICY_NAME}`);
});
await test.step('Add new rule', async () => {
// Click on add rule button
await page.locator('[data-testid="add-rule"]').click();
// Add rule (assuming addRule is a function you have defined elsewhere)
await addRule(page, NEW_RULE_NAME, NEW_RULE_DESCRIPTION, 0);
// Validate added rule
await page.getByText(RULE_NAME, { exact: true }).isVisible();
// Verify other details
await page.getByText(RULE_NAME, { exact: true }).click();
await expect(
page.locator('[data-testid="resources"]').last()
).toContainText(RULE_DETAILS.resources);
await expect(
page.locator('[data-testid="operations"]').last()
).toContainText(RULE_DETAILS.operations);
await expect(page.locator('[data-testid="effect"]').last()).toContainText(
RULE_DETAILS.effect
);
await expect(
page.locator('[data-testid="condition"]').last()
).toContainText(RULE_DETAILS.condition);
});
await test.step('Edit rule name for created Rule', async () => {
// Click on new rule manage button
await page
.locator(`[data-testid="manage-button-${NEW_RULE_NAME}"]`)
.click();
// Click on edit rule button
await page.locator('[data-testid="edit-rule"]').click();
// Enter new name
await page.locator('[data-testid="rule-name"]').fill(UPDATED_RULE_NAME);
// Click on submit button
await page.locator('[data-testid="submit-btn"]').click();
// Verify the URL contains the policy name
await expect(page).toHaveURL(new RegExp(POLICY_NAME));
// Verify the rule name is updated
await page.getByText(UPDATED_RULE_NAME, { exact: true }).isVisible();
});
await test.step('Delete new rule', async () => {
// Click on new rule manage button
await page
.locator(`[data-testid="manage-button-${UPDATED_RULE_NAME}"]`)
.click();
// Click on delete rule button
await page.locator('[data-testid="delete-rule"]').click();
await expect(
page.getByText(UPDATED_RULE_NAME, { exact: true })
).not.toBeVisible();
});
await test.step('Delete last rule and validate', async () => {
// Click on new rule manage button
await page.locator(`[data-testid="manage-button-${RULE_NAME}"]`).click();
// Click on delete rule button
await page.locator('[data-testid="delete-rule"]').click();
// Validate the error message
await expect(page.locator('.Toastify__toast-body')).toContainText(
ERROR_MESSAGE_VALIDATION.lastRuleCannotBeRemoved
);
});
await test.step('Delete created policy', async () => {
await settingClick(page, GlobalSettingOptions.POLICIES);
await page.waitForSelector('[data-testid="loader"]', {
state: 'detached',
});
// Click on delete action button
await page
.locator(`[data-testid="delete-action-${POLICY_NAME}"]`)
.click({ force: true });
// Type 'DELETE' in the confirmation text input
await page
.locator('[data-testid="confirmation-text-input"]')
.fill('DELETE');
// Click on confirm button
await page.locator('[data-testid="confirm-button"]').click();
// Validate deleted policy
await expect(
page.getByText(POLICY_NAME, { exact: true })
).not.toBeVisible();
});
});
});

View File

@ -0,0 +1,51 @@
/*
* Copyright 2024 Collate.
* Licensed 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 { Page } from '@playwright/test';
export const validateFormNameFieldInput = async ({
page,
fieldSelector = '#name',
checkEmpty = true,
checkLong = true,
fieldName,
value,
}: {
page: Page;
value: string;
fieldName: string;
errorDivSelector: string;
fieldSelector?: string;
checkEmpty?: boolean;
checkLong?: boolean;
}) => {
if (checkEmpty) {
// Check empty name field message
await page.fill(fieldSelector, 'test');
await page.locator(fieldSelector).clear();
await page.getByText(`${fieldName} is required`).isVisible();
}
if (checkLong) {
// Check long name field message
await page.fill(fieldSelector, 'name'.repeat(33));
await page
.getByText(`${fieldName} size must be between 1 and 128`)
.isVisible();
await page.locator(fieldSelector).clear();
}
await page.fill(fieldSelector, value);
};

View File

@ -27,6 +27,7 @@ describe('useFqn', () => {
(useParams as jest.Mock).mockReturnValue({
fqn: 'sample_data.db_sample.schema_sample.dim%2Fclient.',
ingestionFQN: 'sample_data.db_sample.schema_sample.dim%2Fclient.',
ruleName: 'testing%20%2F%20policy%20rule%20do%20not%20use',
});
const { result } = renderHook(() => useFqn());
@ -34,6 +35,7 @@ describe('useFqn', () => {
expect(result.current).toEqual({
fqn: 'sample_data.db_sample.schema_sample.dim/client.',
ingestionFQN: 'sample_data.db_sample.schema_sample.dim/client.',
ruleName: 'testing / policy rule do not use',
});
});
@ -45,6 +47,7 @@ describe('useFqn', () => {
expect(result.current).toEqual({
fqn: '',
ingestionFQN: '',
ruleName: '',
});
});
});

View File

@ -13,17 +13,18 @@
import { useParams } from 'react-router-dom';
import { getDecodedFqn } from '../utils/StringsUtils';
type Fqn = { fqn: string; ingestionFQN: string };
type Fqn = { fqn: string; ingestionFQN: string; ruleName: string };
/**
* @description Hook to get the decoded fqn and ingestionFQN from the url
* @returns {fqn: string, ingestionFQN: string} - fqn and ingestionFQN from the url
*/
export const useFqn = (): Fqn => {
const { fqn, ingestionFQN } = useParams<Fqn>();
const { fqn, ingestionFQN, ruleName } = useParams<Fqn>();
return {
fqn: fqn ? getDecodedFqn(fqn) : '',
ingestionFQN: ingestionFQN ? getDecodedFqn(ingestionFQN) : '',
ruleName: ruleName ? getDecodedFqn(ruleName) : '',
};
};

View File

@ -17,7 +17,7 @@ import { compare } from 'fast-json-patch';
import { trim } from 'lodash';
import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory, useParams } from 'react-router-dom';
import { useHistory } from 'react-router-dom';
import Loader from '../../../components/common/Loader/Loader';
import TitleBreadcrumb from '../../../components/common/TitleBreadcrumb/TitleBreadcrumb.component';
import PageLayoutV1 from '../../../components/PageLayoutV1/PageLayoutV1';
@ -51,8 +51,7 @@ const InitialData: Rule = {
const EditRulePage = () => {
const { t } = useTranslation();
const history = useHistory();
const { ruleName } = useParams<{ ruleName: string }>();
const { fqn } = useFqn();
const { fqn, ruleName } = useFqn();
const [isLoading, setLoading] = useState<boolean>(false);
const [policy, setPolicy] = useState<Policy>({} as Policy);
const [ruleData, setRuleData] = useState<Rule>(InitialData);

View File

@ -323,7 +323,8 @@ export const getEditPolicyRulePath = (fqn: string, ruleName: string) => {
path = path
.replace(PLACEHOLDER_ROUTE_FQN, getEncodedFqn(fqn))
.replace(PLACEHOLDER_RULE_NAME, ruleName);
// rule name is same as entity fqn so we need to encode it to pass it as a param
.replace(PLACEHOLDER_RULE_NAME, getEncodedFqn(ruleName));
return path;
};