mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-26 09:55:52 +00:00
playwright: migrate permission spec to playwright (#17795)
* playwright: remove organisation policy and role before staring the playwright test * added default role as data consumer * fixed failing test * keeping org policies as it is. * migrate permission spec --------- Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
This commit is contained in:
parent
75588cf364
commit
0e75a9cceb
@ -1,439 +0,0 @@
|
||||
/*
|
||||
* 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 {
|
||||
interceptURL,
|
||||
uuid,
|
||||
verifyResponseStatusCode,
|
||||
} from '../../common/common';
|
||||
import UsersTestClass from '../../common/Entities/UserClass';
|
||||
import { hardDeleteService } from '../../common/EntityUtils';
|
||||
import {
|
||||
createEntityTableViaREST,
|
||||
visitEntityDetailsPage,
|
||||
} from '../../common/Utils/Entity';
|
||||
import { getToken } from '../../common/Utils/LocalStorage';
|
||||
import { EntityType } from '../../constants/Entity.interface';
|
||||
import { DATABASE_SERVICE, USER_DETAILS } from '../../constants/EntityConstant';
|
||||
import { SERVICE_CATEGORIES } from '../../constants/service.constants';
|
||||
|
||||
type RoleType = {
|
||||
name: string;
|
||||
policies: string[];
|
||||
id?: string;
|
||||
};
|
||||
type PolicyType = {
|
||||
name: string;
|
||||
rules: {
|
||||
name: string;
|
||||
resources: string[];
|
||||
operations: string[];
|
||||
effect: string;
|
||||
}[];
|
||||
id?: string;
|
||||
};
|
||||
type OrganizationTeamType = {
|
||||
id: string;
|
||||
policies: {
|
||||
id: string;
|
||||
type: string;
|
||||
}[];
|
||||
defaultRoles: {
|
||||
id: string;
|
||||
type: string;
|
||||
}[];
|
||||
};
|
||||
const entity = new UsersTestClass();
|
||||
const policy: PolicyType = {
|
||||
name: `cy-permission-policy-${uuid()}`,
|
||||
rules: [
|
||||
{
|
||||
name: `cy-permission-rule-${uuid()}`,
|
||||
resources: ['All'],
|
||||
operations: ['ViewBasic'],
|
||||
effect: 'allow',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const role: RoleType = {
|
||||
name: `cy-permission-role-${uuid()}`,
|
||||
policies: [policy.name],
|
||||
};
|
||||
const tableFqn = `${DATABASE_SERVICE.entity.databaseSchema}.${DATABASE_SERVICE.entity.name}`;
|
||||
const testSuite = {
|
||||
name: `${tableFqn}.testSuite`,
|
||||
executableEntityReference: tableFqn,
|
||||
};
|
||||
const testCase = {
|
||||
name: `user_tokens_table_column_name_to_exist_${uuid()}`,
|
||||
entityLink: `<#E::table::${testSuite.executableEntityReference}>`,
|
||||
parameterValues: [{ name: 'columnName', value: 'id' }],
|
||||
testDefinition: 'tableColumnNameToExist',
|
||||
description: 'test case description',
|
||||
testSuite: testSuite.name,
|
||||
};
|
||||
|
||||
let organizationTeam = {} as OrganizationTeamType;
|
||||
let userId = '';
|
||||
let teamId = '';
|
||||
|
||||
const viewPermissions = [
|
||||
{
|
||||
title: 'ViewBasic, ViewSampleData & ViewQueries permission',
|
||||
data: {
|
||||
patch: [
|
||||
{ op: 'add', path: '/rules/0/operations/1', value: 'ViewSampleData' },
|
||||
{ op: 'add', path: '/rules/0/operations/2', value: 'ViewQueries' },
|
||||
],
|
||||
permission: { viewSampleData: true, viewQueries: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'ViewBasic, ViewSampleData, ViewQueries & ViewTests permission',
|
||||
data: {
|
||||
patch: [{ op: 'add', path: '/rules/0/operations/3', value: 'ViewTests' }],
|
||||
permission: {
|
||||
viewSampleData: true,
|
||||
viewQueries: true,
|
||||
viewTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'EditDisplayName permission',
|
||||
data: {
|
||||
patch: [
|
||||
{ op: 'add', path: '/rules/0/operations/4', value: 'EditDisplayName' },
|
||||
],
|
||||
permission: {
|
||||
viewSampleData: true,
|
||||
viewQueries: true,
|
||||
viewTests: true,
|
||||
editDisplayName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const createViewBasicRoleViaREST = ({ token }) => {
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `/api/v1/policies`,
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
body: policy,
|
||||
}).then((policyResponse) => {
|
||||
policy.id = policyResponse.body.id;
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `/api/v1/roles`,
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
body: role,
|
||||
}).then((roleResponse) => {
|
||||
role.id = roleResponse.body.id;
|
||||
cy.request({
|
||||
method: 'GET',
|
||||
url: `/api/v1/teams/name/Organization?fields=defaultRoles,policies`,
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
}).then((orgResponse) => {
|
||||
organizationTeam = orgResponse.body;
|
||||
cy.request({
|
||||
method: 'PATCH',
|
||||
url: `/api/v1/teams/${orgResponse.body.id}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json-patch+json',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/policies',
|
||||
value: [
|
||||
{
|
||||
id: policyResponse.body.id,
|
||||
type: 'policy',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/defaultRoles',
|
||||
value: [
|
||||
{
|
||||
id: roleResponse.body.id,
|
||||
type: 'role',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `/api/v1/users/signup`,
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
body: USER_DETAILS,
|
||||
}).then((userResponse) => {
|
||||
userId = userResponse.body.id;
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `/api/v1/teams`,
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
body: {
|
||||
name: `teamBasic-${uuid()}`,
|
||||
description: 'teamBasic',
|
||||
teamType: 'Group',
|
||||
defaultRoles: [roleResponse.body.id],
|
||||
policies: [policyResponse.body.id],
|
||||
users: [userResponse.body.id],
|
||||
},
|
||||
}).then((teamResponse) => {
|
||||
teamId = teamResponse.body.id;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const preRequisite = () => {
|
||||
cy.login();
|
||||
cy.getAllLocalStorage().then((data) => {
|
||||
const token = getToken(data);
|
||||
createViewBasicRoleViaREST({
|
||||
token,
|
||||
});
|
||||
|
||||
createEntityTableViaREST({
|
||||
token,
|
||||
...DATABASE_SERVICE,
|
||||
tables: [],
|
||||
});
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `/api/v1/tables`,
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
body: DATABASE_SERVICE.entity,
|
||||
}).then((response) => {
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `/api/v1/queries`,
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
body: {
|
||||
query: `select * from dim_address_${uuid()}`,
|
||||
queryUsedIn: [{ id: response.body.id, type: 'table' }],
|
||||
queryDate: Date.now(),
|
||||
service: 'sample_data',
|
||||
},
|
||||
});
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `/api/v1/dataQuality/testSuites/executable`,
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
body: testSuite,
|
||||
}).then(() => {
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `/api/v1/dataQuality/testCases`,
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
body: testCase,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
cy.logout();
|
||||
};
|
||||
|
||||
const cleanUp = () => {
|
||||
cy.login();
|
||||
cy.getAllLocalStorage().then((data) => {
|
||||
const token = getToken(data);
|
||||
hardDeleteService({
|
||||
token,
|
||||
serviceFqn: DATABASE_SERVICE.service.name,
|
||||
serviceType: SERVICE_CATEGORIES.DATABASE_SERVICES,
|
||||
});
|
||||
cy.request({
|
||||
method: 'DELETE',
|
||||
url: `/api/v1/roles/${role.id}?hardDelete=true&recursive=false`,
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
cy.request({
|
||||
method: 'DELETE',
|
||||
url: `/api/v1/policies/${policy.id}?hardDelete=true&recursive=false`,
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
cy.request({
|
||||
method: 'PATCH',
|
||||
url: `/api/v1/teams/${organizationTeam.id}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json-patch+json',
|
||||
},
|
||||
body: [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/policies',
|
||||
value: organizationTeam.policies,
|
||||
},
|
||||
|
||||
{
|
||||
op: 'add',
|
||||
path: '/defaultRoles',
|
||||
value: organizationTeam.defaultRoles,
|
||||
},
|
||||
],
|
||||
});
|
||||
// Delete created user
|
||||
cy.request({
|
||||
method: 'DELETE',
|
||||
url: `/api/v1/users/${userId}?hardDelete=true&recursive=false`,
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
// Delete created team
|
||||
cy.request({
|
||||
method: 'DELETE',
|
||||
url: `/api/v1/teams/${teamId}?hardDelete=true&recursive=false`,
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const checkPermission = (permission?: {
|
||||
viewSampleData?: boolean;
|
||||
viewQueries?: boolean;
|
||||
viewTests?: boolean;
|
||||
editDisplayName?: boolean;
|
||||
}) => {
|
||||
cy.login(USER_DETAILS.email, USER_DETAILS.password);
|
||||
visitEntityDetailsPage({
|
||||
term: DATABASE_SERVICE.entity.name,
|
||||
serviceName: DATABASE_SERVICE.service.name,
|
||||
entity: EntityType.Table,
|
||||
});
|
||||
entity.viewPermissions(permission);
|
||||
cy.logout();
|
||||
};
|
||||
const updatePolicy = (
|
||||
patch: { op: string; path: string; value: unknown }[]
|
||||
) => {
|
||||
cy.login();
|
||||
cy.getAllLocalStorage().then((data) => {
|
||||
const token = getToken(data);
|
||||
cy.request({
|
||||
method: 'PATCH',
|
||||
url: `/api/v1/policies/${policy.id}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json-patch+json',
|
||||
},
|
||||
body: patch,
|
||||
});
|
||||
});
|
||||
cy.logout();
|
||||
cy.reload();
|
||||
};
|
||||
|
||||
describe('Permissions', { tags: 'Settings' }, () => {
|
||||
before(preRequisite);
|
||||
after(cleanUp);
|
||||
|
||||
it('ViewBasic permission', () => {
|
||||
checkPermission();
|
||||
});
|
||||
|
||||
viewPermissions.forEach((permissionData) => {
|
||||
it(`check ${permissionData.title}`, () => {
|
||||
updatePolicy(permissionData.data.patch);
|
||||
checkPermission(permissionData.data.permission);
|
||||
});
|
||||
});
|
||||
|
||||
it('EditQuery permission', () => {
|
||||
updatePolicy([
|
||||
{
|
||||
op: 'add',
|
||||
path: '/rules/1',
|
||||
value: {
|
||||
name: `cy-edit-query-rule-${uuid()}`,
|
||||
resources: ['query'],
|
||||
operations: ['ViewAll', 'EditAll'],
|
||||
effect: 'allow',
|
||||
},
|
||||
},
|
||||
{ op: 'add', path: '/rules/0/operations/5', value: 'EditQueries' },
|
||||
]);
|
||||
|
||||
cy.login(USER_DETAILS.email, USER_DETAILS.password);
|
||||
visitEntityDetailsPage({
|
||||
term: DATABASE_SERVICE.entity.name,
|
||||
serviceName: DATABASE_SERVICE.service.name,
|
||||
entity: EntityType.Table,
|
||||
});
|
||||
interceptURL(
|
||||
'GET',
|
||||
'/api/v1/search/query?q=*&index=query_search_index*',
|
||||
'getQueries'
|
||||
);
|
||||
cy.get('[data-testid="table_queries"]').click();
|
||||
verifyResponseStatusCode('@getQueries', 200);
|
||||
cy.get('[data-testid="query-btn"]').click();
|
||||
cy.get('[data-menu-id*="edit-query"]').click();
|
||||
interceptURL('PATCH', '/api/v1/queries/*', 'updateQuery');
|
||||
cy.get('.CodeMirror-line').click().type('updated');
|
||||
cy.get('[data-testid="save-query-btn"]').click();
|
||||
verifyResponseStatusCode('@updateQuery', 200);
|
||||
cy.logout();
|
||||
});
|
||||
|
||||
it('EditTest permission', () => {
|
||||
updatePolicy([
|
||||
{ op: 'add', path: '/rules/1/operations/6', value: 'EditTests' },
|
||||
{
|
||||
op: 'add',
|
||||
path: '/rules/2',
|
||||
value: {
|
||||
name: `cy-edit-test-case-rule-${uuid()}`,
|
||||
resources: ['testCase'],
|
||||
operations: ['ViewAll', 'EditAll'],
|
||||
effect: 'allow',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
cy.login(USER_DETAILS.email, USER_DETAILS.password);
|
||||
visitEntityDetailsPage({
|
||||
term: DATABASE_SERVICE.entity.name,
|
||||
serviceName: DATABASE_SERVICE.service.name,
|
||||
entity: EntityType.Table,
|
||||
});
|
||||
interceptURL(
|
||||
'GET',
|
||||
'/api/v1/dataQuality/testCases/search/list?fields=*',
|
||||
'testCase'
|
||||
);
|
||||
cy.get('[data-testid="profiler"]').click();
|
||||
cy.get('[data-testid="profiler-tab-left-panel"]')
|
||||
.contains('Data Quality')
|
||||
.click();
|
||||
verifyResponseStatusCode('@testCase', 200);
|
||||
cy.get(`[data-testid="edit-${testCase.name}"]`).click();
|
||||
cy.get('#tableTestForm_params_columnName')
|
||||
.scrollIntoView()
|
||||
.clear()
|
||||
.type('test');
|
||||
interceptURL('PATCH', '/api/v1/dataQuality/testCases/*', 'updateTest');
|
||||
cy.get('.ant-modal-footer').contains('Submit').click();
|
||||
verifyResponseStatusCode('@updateTest', 200);
|
||||
});
|
||||
});
|
@ -0,0 +1,271 @@
|
||||
/*
|
||||
* 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, test as base } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { PolicyClass } from '../../support/access-control/PoliciesClass';
|
||||
import { RolesClass } from '../../support/access-control/RolesClass';
|
||||
import { TableClass } from '../../support/entity/TableClass';
|
||||
import { UserClass } from '../../support/user/UserClass';
|
||||
import { performAdminLogin } from '../../utils/admin';
|
||||
import { getApiContext, redirectToHomePage, uuid } from '../../utils/common';
|
||||
import { validateViewPermissions } from '../../utils/permission';
|
||||
|
||||
const policy = new PolicyClass();
|
||||
const policy2 = new PolicyClass();
|
||||
const role = new RolesClass();
|
||||
const role2 = new RolesClass();
|
||||
const user = new UserClass();
|
||||
const table = new TableClass();
|
||||
|
||||
const viewPermissionsData = [
|
||||
{
|
||||
title: 'ViewBasic, ViewSampleData & ViewQueries permission',
|
||||
data: {
|
||||
patch: [
|
||||
{ op: 'add', path: '/rules/0/operations/1', value: 'ViewSampleData' },
|
||||
{ op: 'add', path: '/rules/0/operations/2', value: 'ViewQueries' },
|
||||
],
|
||||
permission: { viewSampleData: true, viewQueries: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'ViewBasic, ViewSampleData, ViewQueries & ViewTests permission',
|
||||
data: {
|
||||
patch: [{ op: 'add', path: '/rules/0/operations/3', value: 'ViewTests' }],
|
||||
permission: {
|
||||
viewSampleData: true,
|
||||
viewQueries: true,
|
||||
viewTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'EditDisplayName permission',
|
||||
data: {
|
||||
patch: [
|
||||
{ op: 'add', path: '/rules/0/operations/4', value: 'EditDisplayName' },
|
||||
],
|
||||
permission: {
|
||||
viewSampleData: true,
|
||||
viewQueries: true,
|
||||
viewTests: true,
|
||||
editDisplayName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const test = base.extend<{
|
||||
adminPage: Page;
|
||||
userPage: Page;
|
||||
}>({
|
||||
adminPage: async ({ browser }, use) => {
|
||||
const { page } = await performAdminLogin(browser);
|
||||
await use(page);
|
||||
await page.close();
|
||||
},
|
||||
userPage: async ({ browser }, use) => {
|
||||
const page = await browser.newPage();
|
||||
await user.login(page);
|
||||
await use(page);
|
||||
await page.close();
|
||||
},
|
||||
});
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||
await user.create(apiContext);
|
||||
const policyResponse = await policy.create(apiContext, [
|
||||
{
|
||||
name: `pw-permission-rule-${uuid()}`,
|
||||
resources: ['All'],
|
||||
operations: ['ViewBasic'],
|
||||
effect: 'allow',
|
||||
},
|
||||
]);
|
||||
const policyResponse2 = await policy2.create(apiContext, [
|
||||
{
|
||||
name: `pw-permission-rule-${uuid()}`,
|
||||
resources: ['All'],
|
||||
operations: ['EditOwners'],
|
||||
effect: 'deny',
|
||||
},
|
||||
]);
|
||||
await table.create(apiContext);
|
||||
await table.createTestCase(apiContext);
|
||||
await table.createQuery(apiContext);
|
||||
const roleResponse = await role.create(apiContext, [
|
||||
policyResponse.fullyQualifiedName,
|
||||
]);
|
||||
const roleResponse2 = await role2.create(apiContext, [
|
||||
policyResponse2.fullyQualifiedName,
|
||||
]);
|
||||
await user.patch({
|
||||
apiContext,
|
||||
patchData: [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/roles',
|
||||
value: [
|
||||
{
|
||||
id: roleResponse.id,
|
||||
type: 'role',
|
||||
name: roleResponse.name,
|
||||
},
|
||||
{
|
||||
id: roleResponse2.id,
|
||||
type: 'role',
|
||||
name: roleResponse2.name,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test.afterAll(async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||
await user.delete(apiContext);
|
||||
await role.delete(apiContext);
|
||||
await policy.delete(apiContext);
|
||||
await table.delete(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test('Permissions', async ({ userPage, adminPage }) => {
|
||||
test.slow();
|
||||
|
||||
await redirectToHomePage(userPage);
|
||||
|
||||
await test.step('ViewBasic permission', async () => {
|
||||
await table.visitEntityPage(userPage);
|
||||
await userPage.waitForSelector('[data-testid="loader"]', {
|
||||
state: 'detached',
|
||||
});
|
||||
await validateViewPermissions(userPage);
|
||||
});
|
||||
|
||||
for (const viewPermission of viewPermissionsData) {
|
||||
await test.step(viewPermission.title, async () => {
|
||||
const { apiContext, afterAction } = await getApiContext(adminPage);
|
||||
await policy.patch(apiContext, viewPermission.data.patch as Operation[]);
|
||||
await afterAction();
|
||||
await redirectToHomePage(userPage);
|
||||
await userPage.reload();
|
||||
const permissionResponse = userPage.waitForResponse(
|
||||
`/api/v1/permissions/table/name/${encodeURIComponent(
|
||||
table.entityResponseData?.['fullyQualifiedName']
|
||||
)}`
|
||||
);
|
||||
await table.visitEntityPage(userPage);
|
||||
await permissionResponse;
|
||||
await userPage.waitForSelector('[data-testid="loader"]', {
|
||||
state: 'detached',
|
||||
});
|
||||
await validateViewPermissions(userPage, viewPermission.data.permission);
|
||||
});
|
||||
}
|
||||
|
||||
await test.step('EditQuery permission', async () => {
|
||||
const { apiContext, afterAction } = await getApiContext(adminPage);
|
||||
await policy.patch(apiContext, [
|
||||
{
|
||||
op: 'add',
|
||||
path: '/rules/1',
|
||||
value: {
|
||||
name: `pw-edit-query-rule-${uuid()}`,
|
||||
resources: ['query'],
|
||||
operations: ['ViewAll', 'EditAll'],
|
||||
effect: 'allow',
|
||||
},
|
||||
},
|
||||
{ op: 'add', path: '/rules/0/operations/5', value: 'EditQueries' },
|
||||
]);
|
||||
await afterAction();
|
||||
await redirectToHomePage(userPage);
|
||||
await userPage.reload();
|
||||
const permissionResponse = userPage.waitForResponse(
|
||||
`/api/v1/permissions/table/name/${encodeURIComponent(
|
||||
table.entityResponseData?.['fullyQualifiedName']
|
||||
)}`
|
||||
);
|
||||
await table.visitEntityPage(userPage);
|
||||
await permissionResponse;
|
||||
await userPage.waitForSelector('[data-testid="loader"]', {
|
||||
state: 'detached',
|
||||
});
|
||||
const queryListResponse = userPage.waitForResponse(
|
||||
'/api/v1/search/query?q=*&index=query_search_index*'
|
||||
);
|
||||
await userPage.click('[data-testid="table_queries"]');
|
||||
await queryListResponse;
|
||||
await userPage.click('[data-testid="query-btn"]');
|
||||
await userPage.click('[data-menu-id*="edit-query"]');
|
||||
await userPage.locator('.CodeMirror-line').click();
|
||||
await userPage.keyboard.type('updated');
|
||||
const saveQueryResponse = userPage.waitForResponse('/api/v1/queries/*');
|
||||
await userPage.click('[data-testid="save-query-btn"]');
|
||||
await saveQueryResponse;
|
||||
});
|
||||
|
||||
await test.step('EditTest permission', async () => {
|
||||
const testCaseName = table.testCasesResponseData[0]?.['name'];
|
||||
const { apiContext, afterAction } = await getApiContext(adminPage);
|
||||
await policy.patch(apiContext, [
|
||||
{ op: 'add', path: '/rules/1/operations/6', value: 'EditTests' },
|
||||
{
|
||||
op: 'add',
|
||||
path: '/rules/2',
|
||||
value: {
|
||||
name: `cy-edit-test-case-rule-${uuid()}`,
|
||||
resources: ['testCase'],
|
||||
operations: ['ViewAll', 'EditAll'],
|
||||
effect: 'allow',
|
||||
},
|
||||
},
|
||||
]);
|
||||
await afterAction();
|
||||
await redirectToHomePage(userPage);
|
||||
await userPage.reload();
|
||||
const permissionResponse = userPage.waitForResponse(
|
||||
`/api/v1/permissions/table/name/${encodeURIComponent(
|
||||
table.entityResponseData?.['fullyQualifiedName']
|
||||
)}`
|
||||
);
|
||||
await table.visitEntityPage(userPage);
|
||||
await permissionResponse;
|
||||
await userPage.waitForSelector('[data-testid="loader"]', {
|
||||
state: 'detached',
|
||||
});
|
||||
|
||||
await userPage.getByTestId('profiler').click();
|
||||
const testCaseResponse = userPage.waitForResponse(
|
||||
'/api/v1/dataQuality/testCases/search/list?fields=*'
|
||||
);
|
||||
await userPage
|
||||
.getByTestId('profiler-tab-left-panel')
|
||||
.getByText('Data Quality')
|
||||
.click();
|
||||
await testCaseResponse;
|
||||
|
||||
await userPage.getByTestId(`edit-${testCaseName}`).click();
|
||||
await userPage.locator('#tableTestForm_displayName').clear();
|
||||
await userPage.fill('#tableTestForm_displayName', 'Update_display_name');
|
||||
const saveTestResponse = userPage.waitForResponse(
|
||||
'/api/v1/dataQuality/testCases/*'
|
||||
);
|
||||
await userPage.locator('.ant-modal-footer').getByText('Submit').click();
|
||||
await saveTestResponse;
|
||||
});
|
||||
});
|
@ -63,27 +63,6 @@ entities.forEach((EntityClass) => {
|
||||
|
||||
await user.create(apiContext);
|
||||
|
||||
const dataConsumerRoleResponse = await apiContext.get(
|
||||
'/api/v1/roles/name/DataConsumer'
|
||||
);
|
||||
|
||||
const dataConsumerRole = await dataConsumerRoleResponse.json();
|
||||
|
||||
await user.patch({
|
||||
apiContext,
|
||||
patchData: [
|
||||
{
|
||||
op: 'add',
|
||||
path: '/roles/0',
|
||||
value: {
|
||||
id: dataConsumerRole.id,
|
||||
type: 'role',
|
||||
name: dataConsumerRole.name,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await EntityDataClass.preRequisitesForTests(apiContext);
|
||||
await entity.create(apiContext);
|
||||
await afterAction();
|
||||
|
@ -15,6 +15,7 @@ import { JWT_EXPIRY_TIME_MAP } from '../constant/login';
|
||||
import { AdminClass } from '../support/user/AdminClass';
|
||||
import { getApiContext } from '../utils/common';
|
||||
import { updateJWTTokenExpiryTime } from '../utils/login';
|
||||
import { removeOrganizationPolicyAndRole } from '../utils/team';
|
||||
const adminFile = 'playwright/.auth/admin.json';
|
||||
|
||||
setup('authenticate as admin', async ({ page }) => {
|
||||
@ -25,6 +26,7 @@ setup('authenticate as admin', async ({ page }) => {
|
||||
await page.waitForURL('**/my-data');
|
||||
const { apiContext, afterAction } = await getApiContext(page);
|
||||
await updateJWTTokenExpiryTime(apiContext, JWT_EXPIRY_TIME_MAP['4 hours']);
|
||||
await removeOrganizationPolicyAndRole(apiContext);
|
||||
await afterAction();
|
||||
await admin.logout(page);
|
||||
await page.waitForURL('**/signin');
|
||||
|
@ -11,6 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { APIRequestContext } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { uuid } from '../../utils/common';
|
||||
|
||||
type ResponseDataType = {
|
||||
@ -55,6 +56,22 @@ export class PolicyClass {
|
||||
return data;
|
||||
}
|
||||
|
||||
async patch(apiContext: APIRequestContext, patchData: Operation[]) {
|
||||
const response = await apiContext.patch(
|
||||
`/api/v1/policies/${this.responseData.id}`,
|
||||
{
|
||||
data: patchData,
|
||||
headers: {
|
||||
'Content-Type': 'application/json-patch+json',
|
||||
},
|
||||
}
|
||||
);
|
||||
const data = await response.json();
|
||||
this.responseData = data;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
async delete(apiContext: APIRequestContext) {
|
||||
const response = await apiContext.delete(
|
||||
`/api/v1/policies/${this.responseData.id}?hardDelete=true&recursive=true`
|
||||
|
@ -110,6 +110,7 @@ export class TableClass extends EntityClass {
|
||||
testSuiteResponseData: unknown;
|
||||
testSuitePipelineResponseData: unknown[] = [];
|
||||
testCasesResponseData: unknown[] = [];
|
||||
queryResponseData: unknown[] = [];
|
||||
|
||||
constructor(name?: string) {
|
||||
super(EntityTypeEndpoint.Table);
|
||||
@ -172,6 +173,25 @@ export class TableClass extends EntityClass {
|
||||
});
|
||||
}
|
||||
|
||||
async createQuery(apiContext: APIRequestContext, queryText?: string) {
|
||||
const queryResponse = await apiContext.post('/api/v1/queries', {
|
||||
data: {
|
||||
query:
|
||||
queryText ??
|
||||
`select * from ${this.entityResponseData?.['fullyQualifiedName']}`,
|
||||
queryUsedIn: [{ id: this.entityResponseData?.['id'], type: 'table' }],
|
||||
queryDate: Date.now(),
|
||||
service: this.serviceResponseData?.['name'],
|
||||
},
|
||||
});
|
||||
|
||||
const query = await queryResponse.json();
|
||||
|
||||
this.queryResponseData.push(query);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
async createTestSuiteAndPipelines(
|
||||
apiContext: APIRequestContext,
|
||||
testSuite?: TestSuiteData
|
||||
|
@ -48,13 +48,33 @@ export class UserClass {
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
const dataConsumerRoleResponse = await apiContext.get(
|
||||
'/api/v1/roles/name/DataConsumer'
|
||||
);
|
||||
|
||||
const dataConsumerRole = await dataConsumerRoleResponse.json();
|
||||
|
||||
const response = await apiContext.post('/api/v1/users/signup', {
|
||||
data: this.data,
|
||||
});
|
||||
|
||||
this.responseData = await response.json();
|
||||
const { entity } = await this.patch({
|
||||
apiContext,
|
||||
patchData: [
|
||||
{
|
||||
op: 'add',
|
||||
path: '/roles/0',
|
||||
value: {
|
||||
id: dataConsumerRole.id,
|
||||
type: 'role',
|
||||
name: dataConsumerRole.name,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return response.body;
|
||||
return entity;
|
||||
}
|
||||
|
||||
async patch({
|
||||
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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 { expect, Page } from '@playwright/test';
|
||||
|
||||
export const checkNoPermissionPlaceholder = async (
|
||||
page: Page,
|
||||
label: string | RegExp,
|
||||
permission = false
|
||||
) => {
|
||||
const placeholder = page
|
||||
.getByLabel(label)
|
||||
.locator('[data-testid="permission-error-placeholder"]');
|
||||
|
||||
if (permission) {
|
||||
await expect(placeholder).not.toBeVisible();
|
||||
} else {
|
||||
await expect(placeholder).toBeVisible();
|
||||
await expect(placeholder).toContainText(
|
||||
'You don’t have access, please check with the admin to get permissions'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const validateViewPermissions = async (
|
||||
page: Page,
|
||||
permission?: {
|
||||
viewSampleData?: boolean;
|
||||
viewQueries?: boolean;
|
||||
viewTests?: boolean;
|
||||
editDisplayName?: boolean;
|
||||
}
|
||||
) => {
|
||||
// check Add domain permission
|
||||
await expect(page.locator('[data-testid="add-domain"]')).not.toBeVisible();
|
||||
|
||||
await expect(
|
||||
page.locator('[data-testid="edit-displayName-button"]')
|
||||
).toHaveCount(permission?.editDisplayName ? 6 : 0);
|
||||
|
||||
// check edit owner permission
|
||||
await expect(page.locator('[data-testid="edit-owner"]')).not.toBeVisible();
|
||||
// check edit description permission
|
||||
await expect(
|
||||
page.locator('[data-testid="edit-description"]')
|
||||
).not.toBeVisible();
|
||||
// check edit tier permission
|
||||
await expect(page.locator('[data-testid="edit-tier"]')).not.toBeVisible();
|
||||
|
||||
// check add tags button
|
||||
await expect(
|
||||
page.locator(
|
||||
'[data-testid="tags-container"] > [data-testid="entity-tags"] .ant-tag'
|
||||
)
|
||||
).not.toBeVisible();
|
||||
// check add glossary term button
|
||||
await expect(
|
||||
page.locator(
|
||||
'[data-testid="glossary-container"] > [data-testid="entity-tags"] .ant-tag'
|
||||
)
|
||||
).not.toBeVisible();
|
||||
|
||||
// check manage button
|
||||
await expect(page.locator('[data-testid="manage-button"]')).toHaveCount(
|
||||
permission?.editDisplayName ? 1 : 0
|
||||
);
|
||||
|
||||
if (permission?.editDisplayName) {
|
||||
await page.click('[data-testid="manage-button"]');
|
||||
await page.click('[data-testid="rename-button"]');
|
||||
await page.fill('#displayName', 'updated-table-name');
|
||||
const updateDisplayNameResponse = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('api/v1/tables/') && response.status() === 200
|
||||
);
|
||||
await page.click('[data-testid="save-button"]');
|
||||
|
||||
await updateDisplayNameResponse;
|
||||
|
||||
await expect(
|
||||
page.locator('[data-testid="entity-header-display-name"]')
|
||||
).toContainText('updated-table-name');
|
||||
}
|
||||
|
||||
await page.click('[data-testid="sample_data"]');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await checkNoPermissionPlaceholder(
|
||||
page,
|
||||
/Sample Data/,
|
||||
permission?.viewSampleData
|
||||
);
|
||||
await page.click('[data-testid="table_queries"]');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await checkNoPermissionPlaceholder(page, /Queries/, permission?.viewQueries);
|
||||
await page.click('[data-testid="profiler"]');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await checkNoPermissionPlaceholder(
|
||||
page,
|
||||
/Profiler & Data Quality/,
|
||||
permission?.viewTests
|
||||
);
|
||||
await page.click('[data-testid="lineage"]');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
await expect(page.locator('[data-testid="edit-lineage"]')).toBeDisabled();
|
||||
|
||||
await page.click('[data-testid="custom_properties"]');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await checkNoPermissionPlaceholder(page, /Custom Properties/);
|
||||
};
|
@ -10,7 +10,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { expect, Page } from '@playwright/test';
|
||||
import { APIRequestContext, expect, Page } from '@playwright/test';
|
||||
import { descriptionBox, toastNotification, uuid } from './common';
|
||||
import { validateFormNameFieldInput } from './form';
|
||||
|
||||
@ -204,3 +204,24 @@ export const addTeamHierarchy = async (
|
||||
await page.click('[form="add-team-form"]');
|
||||
await saveTeamResponse;
|
||||
};
|
||||
|
||||
export const removeOrganizationPolicyAndRole = async (
|
||||
apiContext: APIRequestContext
|
||||
) => {
|
||||
const organizationTeamResponse = await apiContext
|
||||
.get(`/api/v1/teams/name/Organization`)
|
||||
.then((res) => res.json());
|
||||
|
||||
await apiContext.patch(`/api/v1/teams/${organizationTeamResponse.id}`, {
|
||||
data: [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/defaultRoles',
|
||||
value: [],
|
||||
},
|
||||
],
|
||||
headers: {
|
||||
'Content-Type': 'application/json-patch+json',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user