mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-23 16:38:17 +00:00
Tests #19066: Playwright coverage for ViewAll rule with matchAnyTag() and isOwner() condition (#19374)
* Modify the setup for the tests and add teardown to reset the organization policies * Fix the loader shown in case of no permission * Add playwright tests to cover the viewAll permission with conditions * Add description for the commented code
This commit is contained in:
parent
d8e6219200
commit
7fea955338
@ -63,6 +63,11 @@ export default defineConfig({
|
||||
{
|
||||
name: 'setup',
|
||||
testMatch: '**/*.setup.ts',
|
||||
teardown: 'restore-policies',
|
||||
},
|
||||
{
|
||||
name: 'restore-policies',
|
||||
testMatch: '**/auth.teardown.ts',
|
||||
},
|
||||
{
|
||||
name: 'chromium',
|
||||
|
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2025 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 { PolicyClass } from '../support/access-control/PoliciesClass';
|
||||
import { RolesClass } from '../support/access-control/RolesClass';
|
||||
import { ApiCollectionClass } from '../support/entity/ApiCollectionClass';
|
||||
import { ContainerClass } from '../support/entity/ContainerClass';
|
||||
import { DashboardClass } from '../support/entity/DashboardClass';
|
||||
import { MlModelClass } from '../support/entity/MlModelClass';
|
||||
import { PipelineClass } from '../support/entity/PipelineClass';
|
||||
import { SearchIndexClass } from '../support/entity/SearchIndexClass';
|
||||
import { TableClass } from '../support/entity/TableClass';
|
||||
import { TopicClass } from '../support/entity/TopicClass';
|
||||
import { EntityData } from '../support/interfaces/ConditionalPermissions.interface';
|
||||
import { UserClass } from '../support/user/UserClass';
|
||||
import { ServiceTypes } from './settings';
|
||||
|
||||
export const isOwnerPolicy = new PolicyClass();
|
||||
export const matchAnyTagPolicy = new PolicyClass();
|
||||
export const isOwnerRole = new RolesClass();
|
||||
export const matchAnyTagRole = new RolesClass();
|
||||
export const userWithOwnerPermission = new UserClass();
|
||||
export const userWithTagPermission = new UserClass();
|
||||
export const apiCollectionWithOwner = new ApiCollectionClass();
|
||||
export const apiCollectionWithTag = new ApiCollectionClass();
|
||||
export const containerWithOwner = new ContainerClass();
|
||||
export const containerWithTag = new ContainerClass();
|
||||
export const dashboardWithOwner = new DashboardClass();
|
||||
export const dashboardWithTag = new DashboardClass();
|
||||
export const mlModelWithOwner = new MlModelClass();
|
||||
export const mlModelWithTag = new MlModelClass();
|
||||
export const pipelineWithOwner = new PipelineClass();
|
||||
export const pipelineWithTag = new PipelineClass();
|
||||
export const searchIndexWithOwner = new SearchIndexClass();
|
||||
export const searchIndexWithTag = new SearchIndexClass();
|
||||
export const tableWithOwner = new TableClass();
|
||||
export const tableWithTag = new TableClass();
|
||||
export const topicWithOwner = new TopicClass();
|
||||
export const topicWithTag = new TopicClass();
|
||||
|
||||
const withOwner = {
|
||||
apiCollectionWithOwner,
|
||||
containerWithOwner,
|
||||
dashboardWithOwner,
|
||||
mlModelWithOwner,
|
||||
pipelineWithOwner,
|
||||
searchIndexWithOwner,
|
||||
tableWithOwner,
|
||||
topicWithOwner,
|
||||
};
|
||||
|
||||
const withTag = {
|
||||
apiCollectionWithTag,
|
||||
containerWithTag,
|
||||
dashboardWithTag,
|
||||
mlModelWithTag,
|
||||
pipelineWithTag,
|
||||
searchIndexWithTag,
|
||||
tableWithTag,
|
||||
topicWithTag,
|
||||
};
|
||||
|
||||
export const assetsData = [
|
||||
{
|
||||
asset: ServiceTypes.API_SERVICES,
|
||||
withOwner: apiCollectionWithOwner,
|
||||
withTag: apiCollectionWithTag,
|
||||
childTabId: 'collections',
|
||||
assetOwnerUrl: `/service/${apiCollectionWithOwner.serviceType}`,
|
||||
assetTagUrl: `/service/${apiCollectionWithTag.serviceType}`,
|
||||
},
|
||||
{
|
||||
asset: ServiceTypes.STORAGE_SERVICES,
|
||||
withOwner: containerWithOwner,
|
||||
withTag: containerWithTag,
|
||||
childTabId: 'containers',
|
||||
assetOwnerUrl: `/service/${containerWithOwner.serviceType}`,
|
||||
assetTagUrl: `/service/${containerWithTag.serviceType}`,
|
||||
},
|
||||
{
|
||||
asset: ServiceTypes.DASHBOARD_SERVICES,
|
||||
withOwner: dashboardWithOwner,
|
||||
withTag: dashboardWithTag,
|
||||
childTabId: 'dashboards',
|
||||
childTabId2: 'data-model',
|
||||
childTableId2: 'data-models-table',
|
||||
assetOwnerUrl: `/service/${dashboardWithOwner.serviceType}`,
|
||||
assetTagUrl: `/service/${dashboardWithTag.serviceType}`,
|
||||
},
|
||||
{
|
||||
asset: ServiceTypes.ML_MODEL_SERVICES,
|
||||
withOwner: mlModelWithOwner,
|
||||
withTag: mlModelWithTag,
|
||||
childTabId: 'ml models',
|
||||
assetOwnerUrl: `/service/${mlModelWithOwner.serviceType}`,
|
||||
assetTagUrl: `/service/${mlModelWithTag.serviceType}`,
|
||||
},
|
||||
{
|
||||
asset: ServiceTypes.PIPELINE_SERVICES,
|
||||
withOwner: pipelineWithOwner,
|
||||
withTag: pipelineWithTag,
|
||||
childTabId: 'pipelines',
|
||||
assetOwnerUrl: `/service/${pipelineWithOwner.serviceType}`,
|
||||
assetTagUrl: `/service/${pipelineWithTag.serviceType}`,
|
||||
},
|
||||
// TODO: Uncomment when search index permission issue is fixed
|
||||
// {
|
||||
// asset: ServiceTypes.SEARCH_SERVICES,
|
||||
// withOwner: searchIndexWithOwner,
|
||||
// withTag: searchIndexWithTag,
|
||||
// childTabId: 'search indexes',
|
||||
// assetOwnerUrl: `/service/${searchIndexWithOwner.serviceType}`,
|
||||
// assetTagUrl: `/service/${searchIndexWithTag.serviceType}`,
|
||||
// },
|
||||
{
|
||||
asset: ServiceTypes.DATABASE_SERVICES,
|
||||
withOwner: tableWithOwner,
|
||||
withTag: tableWithTag,
|
||||
childTabId: 'databases',
|
||||
assetOwnerUrl: `/service/${tableWithOwner.serviceType}`,
|
||||
assetTagUrl: `/service/${tableWithTag.serviceType}`,
|
||||
},
|
||||
{
|
||||
asset: ServiceTypes.MESSAGING_SERVICES,
|
||||
withOwner: topicWithOwner,
|
||||
withTag: topicWithTag,
|
||||
childTabId: 'topics',
|
||||
assetOwnerUrl: `/service/${topicWithOwner.serviceType}`,
|
||||
assetTagUrl: `/service/${topicWithTag.serviceType}`,
|
||||
},
|
||||
{
|
||||
asset: 'database',
|
||||
withOwner: tableWithOwner,
|
||||
withTag: tableWithTag,
|
||||
childTabId: 'schema',
|
||||
assetOwnerUrl: `/database`,
|
||||
assetTagUrl: `/database`,
|
||||
},
|
||||
{
|
||||
asset: 'databaseSchema',
|
||||
withOwner: tableWithOwner,
|
||||
withTag: tableWithTag,
|
||||
childTabId: 'table',
|
||||
assetOwnerUrl: `/databaseSchema`,
|
||||
assetTagUrl: `/databaseSchema`,
|
||||
},
|
||||
{
|
||||
asset: 'container',
|
||||
withOwner: containerWithOwner,
|
||||
withTag: containerWithTag,
|
||||
childTabId: 'children',
|
||||
assetOwnerUrl: `/container`,
|
||||
assetTagUrl: `/container`,
|
||||
},
|
||||
];
|
||||
|
||||
export const conditionalPermissionsEntityData: EntityData = {
|
||||
isOwnerPolicy,
|
||||
matchAnyTagPolicy,
|
||||
isOwnerRole,
|
||||
matchAnyTagRole,
|
||||
userWithOwnerPermission,
|
||||
userWithTagPermission,
|
||||
withOwner,
|
||||
withTag,
|
||||
};
|
@ -77,6 +77,36 @@ export const DATA_CONSUMER_RULES: PolicyRulesType[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export const VIEW_ALL_RULE: PolicyRulesType[] = [
|
||||
{
|
||||
name: 'OrganizationPolicy-ViewAll-Rule',
|
||||
description: 'Allow all users to view all metadata',
|
||||
resources: ['All'],
|
||||
operations: ['ViewAll'],
|
||||
effect: 'allow',
|
||||
},
|
||||
];
|
||||
|
||||
export const VIEW_ALL_WITH_IS_OWNER: PolicyRulesType[] = [
|
||||
{
|
||||
name: 'viewAll-IsOwner',
|
||||
resources: ['All'],
|
||||
operations: ['ViewAll'],
|
||||
effect: 'allow',
|
||||
condition: 'isOwner()',
|
||||
},
|
||||
];
|
||||
|
||||
export const VIEW_ALL_WITH_MATCH_TAG_CONDITION: PolicyRulesType[] = [
|
||||
{
|
||||
name: 'viewAll-MatchTag',
|
||||
resources: ['All'],
|
||||
operations: ['ViewAll'],
|
||||
effect: 'allow',
|
||||
condition: "matchAnyTag('PersonalData.Personal')",
|
||||
},
|
||||
];
|
||||
|
||||
export const EDIT_USER_FOR_TEAM_RULES: PolicyRulesType[] = [
|
||||
{
|
||||
name: 'EditUserTeams-EditRule',
|
||||
@ -104,13 +134,6 @@ export const ORGANIZATION_POLICY_RULES: PolicyRulesType[] = [
|
||||
resources: ['All'],
|
||||
condition: 'isOwner()',
|
||||
},
|
||||
{
|
||||
name: 'OrganizationPolicy-ViewAll-Rule',
|
||||
description: 'Allow all users to discover data assets.',
|
||||
effect: 'allow',
|
||||
operations: ['ViewAll'],
|
||||
resources: ['All'],
|
||||
},
|
||||
];
|
||||
|
||||
export const GLOBAL_SETTING_PERMISSIONS: Record<
|
||||
|
@ -11,7 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { uuid } from '../utils/common';
|
||||
import { GlobalSettingOptions } from './settings';
|
||||
import { GlobalSettingOptions, ServiceTypes } from './settings';
|
||||
|
||||
export const SERVICE_TYPE = {
|
||||
Database: GlobalSettingOptions.DATABASES,
|
||||
@ -26,49 +26,38 @@ export const SERVICE_TYPE = {
|
||||
ApiService: GlobalSettingOptions.APIS,
|
||||
};
|
||||
|
||||
export const SERVICE_CATEGORIES = {
|
||||
DATABASE_SERVICES: 'databaseServices',
|
||||
MESSAGING_SERVICES: 'messagingServices',
|
||||
PIPELINE_SERVICES: 'pipelineServices',
|
||||
DASHBOARD_SERVICES: 'dashboardServices',
|
||||
ML_MODEL_SERVICES: 'mlmodelServices',
|
||||
STORAGE_SERVICES: 'storageServices',
|
||||
METADATA_SERVICES: 'metadataServices',
|
||||
SEARCH_SERVICES: 'searchServices',
|
||||
};
|
||||
|
||||
export const VISIT_SERVICE_PAGE_DETAILS = {
|
||||
[SERVICE_TYPE.Database]: {
|
||||
settingsMenuId: GlobalSettingOptions.DATABASES,
|
||||
serviceCategory: SERVICE_CATEGORIES.DATABASE_SERVICES,
|
||||
serviceCategory: ServiceTypes.DATABASE_SERVICES,
|
||||
},
|
||||
[SERVICE_TYPE.Messaging]: {
|
||||
settingsMenuId: GlobalSettingOptions.MESSAGING,
|
||||
serviceCategory: SERVICE_CATEGORIES.MESSAGING_SERVICES,
|
||||
serviceCategory: ServiceTypes.MESSAGING_SERVICES,
|
||||
},
|
||||
[SERVICE_TYPE.Dashboard]: {
|
||||
settingsMenuId: GlobalSettingOptions.DASHBOARDS,
|
||||
serviceCategory: SERVICE_CATEGORIES.DASHBOARD_SERVICES,
|
||||
serviceCategory: ServiceTypes.DASHBOARD_SERVICES,
|
||||
},
|
||||
[SERVICE_TYPE.Pipeline]: {
|
||||
settingsMenuId: GlobalSettingOptions.PIPELINES,
|
||||
serviceCategory: SERVICE_CATEGORIES.PIPELINE_SERVICES,
|
||||
serviceCategory: ServiceTypes.PIPELINE_SERVICES,
|
||||
},
|
||||
[SERVICE_TYPE.MLModels]: {
|
||||
settingsMenuId: GlobalSettingOptions.MLMODELS,
|
||||
serviceCategory: SERVICE_CATEGORIES.ML_MODEL_SERVICES,
|
||||
serviceCategory: ServiceTypes.ML_MODEL_SERVICES,
|
||||
},
|
||||
[SERVICE_TYPE.Storage]: {
|
||||
settingsMenuId: GlobalSettingOptions.STORAGES,
|
||||
serviceCategory: SERVICE_CATEGORIES.STORAGE_SERVICES,
|
||||
serviceCategory: ServiceTypes.STORAGE_SERVICES,
|
||||
},
|
||||
[SERVICE_TYPE.Search]: {
|
||||
settingsMenuId: GlobalSettingOptions.SEARCH,
|
||||
serviceCategory: SERVICE_CATEGORIES.SEARCH_SERVICES,
|
||||
serviceCategory: ServiceTypes.SEARCH_SERVICES,
|
||||
},
|
||||
[SERVICE_TYPE.Metadata]: {
|
||||
settingsMenuId: GlobalSettingOptions.METADATA,
|
||||
serviceCategory: SERVICE_CATEGORIES.METADATA_SERVICES,
|
||||
serviceCategory: ServiceTypes.METADATA_SERVICES,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -21,6 +21,18 @@ export enum GlobalSettingsMenuCategory {
|
||||
APPLICATIONS = 'apps',
|
||||
}
|
||||
|
||||
export enum ServiceTypes {
|
||||
API_SERVICES = 'apiServices',
|
||||
DATABASE_SERVICES = 'databaseServices',
|
||||
MESSAGING_SERVICES = 'messagingServices',
|
||||
PIPELINE_SERVICES = 'pipelineServices',
|
||||
DASHBOARD_SERVICES = 'dashboardServices',
|
||||
ML_MODEL_SERVICES = 'mlmodelServices',
|
||||
STORAGE_SERVICES = 'storageServices',
|
||||
METADATA_SERVICES = 'metadataServices',
|
||||
SEARCH_SERVICES = 'searchServices',
|
||||
}
|
||||
|
||||
export enum GlobalSettingOptions {
|
||||
USERS = 'users',
|
||||
ADMINS = 'admins',
|
||||
@ -80,6 +92,10 @@ export enum GlobalSettingOptions {
|
||||
export const SETTINGS_OPTIONS_PATH = {
|
||||
// Services
|
||||
|
||||
[GlobalSettingOptions.API_COLLECTIONS]: [
|
||||
GlobalSettingsMenuCategory.SERVICES,
|
||||
`${GlobalSettingsMenuCategory.SERVICES}.${GlobalSettingOptions.API_COLLECTIONS}`,
|
||||
],
|
||||
[GlobalSettingOptions.DATABASES]: [
|
||||
GlobalSettingsMenuCategory.SERVICES,
|
||||
`${GlobalSettingsMenuCategory.SERVICES}.${GlobalSettingOptions.DATABASES}`,
|
||||
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2025 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 { startCase } from 'lodash';
|
||||
import {
|
||||
assetsData,
|
||||
userWithOwnerPermission,
|
||||
userWithTagPermission,
|
||||
} from '../../constant/conditionalPermissions';
|
||||
import { performAdminLogin } from '../../utils/admin';
|
||||
import {
|
||||
checkViewAllPermission,
|
||||
conditionalPermissionsCleanup,
|
||||
conditionalPermissionsPrerequisites,
|
||||
getEntityFQN,
|
||||
} from '../../utils/conditionalPermissions';
|
||||
|
||||
const test = base.extend<{
|
||||
user1Page: Page;
|
||||
user2Page: Page;
|
||||
}>({
|
||||
user1Page: async ({ browser }, use) => {
|
||||
const page = await browser.newPage();
|
||||
await userWithOwnerPermission.login(page);
|
||||
await use(page);
|
||||
await page.close();
|
||||
},
|
||||
user2Page: async ({ browser }, use) => {
|
||||
const page = await browser.newPage();
|
||||
await userWithTagPermission.login(page);
|
||||
await use(page);
|
||||
await page.close();
|
||||
},
|
||||
});
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
test.slow();
|
||||
|
||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||
await conditionalPermissionsPrerequisites(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test.afterAll(async ({ browser }) => {
|
||||
test.slow();
|
||||
|
||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||
await conditionalPermissionsCleanup(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
for (const serviceData of assetsData) {
|
||||
const {
|
||||
asset,
|
||||
withOwner,
|
||||
withTag,
|
||||
assetOwnerUrl,
|
||||
assetTagUrl,
|
||||
childTabId,
|
||||
childTabId2,
|
||||
childTableId2,
|
||||
} = serviceData;
|
||||
|
||||
test(`User with owner permission can only view owned ${startCase(
|
||||
asset
|
||||
)}`, async ({ user1Page: page }) => {
|
||||
// Get the FQNs of both assets
|
||||
const ownerAssetName = getEntityFQN(asset, withOwner);
|
||||
const tagAssetName = getEntityFQN(asset, withTag);
|
||||
|
||||
const ownerAssetURL = `${assetOwnerUrl}/${ownerAssetName}`;
|
||||
const tagAssetURL = `${assetTagUrl}/${tagAssetName}`;
|
||||
|
||||
await checkViewAllPermission({
|
||||
page,
|
||||
url1: ownerAssetURL,
|
||||
url2: tagAssetURL,
|
||||
childTabId,
|
||||
childTabId2,
|
||||
childTableId2,
|
||||
});
|
||||
});
|
||||
|
||||
test(`User with matchAnyTag permission can only view ${startCase(
|
||||
asset
|
||||
)} with the tag`, async ({ user2Page: page }) => {
|
||||
// Get the FQNs of both assets
|
||||
const ownerAssetName = getEntityFQN(asset, withOwner);
|
||||
const tagAssetName = getEntityFQN(asset, withTag);
|
||||
|
||||
const ownerAssetURL = `${assetOwnerUrl}/${ownerAssetName}`;
|
||||
const tagAssetURL = `${assetTagUrl}/${tagAssetName}`;
|
||||
|
||||
await checkViewAllPermission({
|
||||
page,
|
||||
url1: tagAssetURL,
|
||||
url2: ownerAssetURL,
|
||||
childTabId,
|
||||
childTabId2,
|
||||
childTableId2,
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2025 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 as teardown } from '@playwright/test';
|
||||
import { AdminClass } from '../support/user/AdminClass';
|
||||
import { resetPolicyChanges } from '../utils/authTeardown';
|
||||
|
||||
teardown('restore the organization roles and policies', async ({ page }) => {
|
||||
const admin = new AdminClass();
|
||||
|
||||
// Reset the default organization roles and policies
|
||||
await resetPolicyChanges(page, admin);
|
||||
});
|
@ -13,6 +13,7 @@
|
||||
import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { ServiceTypes } from '../../constant/settings';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { visitEntityPage } from '../../utils/entity';
|
||||
import { visitServiceDetailsPage } from '../../utils/service';
|
||||
@ -149,6 +150,8 @@ export class ApiCollectionClass extends EntityClass {
|
||||
|
||||
constructor(name?: string) {
|
||||
super(EntityTypeEndpoint.API_COLLECTION);
|
||||
this.serviceCategory = SERVICE_TYPE.ApiService;
|
||||
this.serviceType = ServiceTypes.API_SERVICES;
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.type = 'Api Collection';
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { ServiceTypes } from '../../constant/settings';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { visitEntityPage } from '../../utils/entity';
|
||||
import {
|
||||
@ -153,6 +154,7 @@ export class ApiEndpointClass extends EntityClass {
|
||||
super(EntityTypeEndpoint.API_ENDPOINT);
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.serviceCategory = SERVICE_TYPE.ApiService;
|
||||
this.serviceType = ServiceTypes.API_SERVICES;
|
||||
this.type = 'ApiEndpoint';
|
||||
this.childrenTabId = 'schema';
|
||||
this.childrenSelectorId = this.children[0].name;
|
||||
|
@ -13,6 +13,7 @@
|
||||
import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { ServiceTypes } from '../../constant/settings';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { visitEntityPage } from '../../utils/entity';
|
||||
import {
|
||||
@ -24,6 +25,7 @@ import { EntityClass } from './EntityClass';
|
||||
|
||||
export class ContainerClass extends EntityClass {
|
||||
private containerName = `pw-container-${uuid()}`;
|
||||
private childContainerName = `pw-container-${uuid()}`;
|
||||
service = {
|
||||
name: `pw-storage-service-${uuid()}`,
|
||||
serviceType: 'S3',
|
||||
@ -45,14 +47,21 @@ export class ContainerClass extends EntityClass {
|
||||
displayName: this.containerName,
|
||||
service: this.service.name,
|
||||
};
|
||||
childContainer = {
|
||||
name: this.childContainerName,
|
||||
displayName: this.childContainerName,
|
||||
service: this.service.name,
|
||||
};
|
||||
|
||||
serviceResponseData: ResponseDataType = {} as ResponseDataType;
|
||||
entityResponseData: ResponseDataWithServiceType =
|
||||
{} as ResponseDataWithServiceType;
|
||||
childResponseData: ResponseDataType = {} as ResponseDataType;
|
||||
|
||||
constructor(name?: string) {
|
||||
super(EntityTypeEndpoint.Container);
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.serviceType = ServiceTypes.STORAGE_SERVICES;
|
||||
this.type = 'Container';
|
||||
this.serviceCategory = SERVICE_TYPE.Storage;
|
||||
}
|
||||
@ -71,6 +80,20 @@ export class ContainerClass extends EntityClass {
|
||||
this.serviceResponseData = await serviceResponse.json();
|
||||
this.entityResponseData = await entityResponse.json();
|
||||
|
||||
const childContainer = {
|
||||
...this.childContainer,
|
||||
parent: {
|
||||
id: this.entityResponseData.id,
|
||||
type: 'container',
|
||||
},
|
||||
};
|
||||
|
||||
const childResponse = await apiContext.post('/api/v1/containers', {
|
||||
data: childContainer,
|
||||
});
|
||||
|
||||
this.childResponseData = await childResponse.json();
|
||||
|
||||
return {
|
||||
service: serviceResponse.body,
|
||||
entity: entityResponse.body,
|
||||
|
@ -13,6 +13,7 @@
|
||||
import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { ServiceTypes } from '../../constant/settings';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { visitEntityPage } from '../../utils/entity';
|
||||
import {
|
||||
@ -24,6 +25,7 @@ import { EntityClass } from './EntityClass';
|
||||
|
||||
export class DashboardClass extends EntityClass {
|
||||
private dashboardName = `pw-dashboard-${uuid()}`;
|
||||
private dashboardDataModelName = `pw-dashboard-data-model-${uuid()}`;
|
||||
service = {
|
||||
name: `pw-dashboard-service-${uuid()}`,
|
||||
serviceType: 'Superset',
|
||||
@ -50,10 +52,28 @@ export class DashboardClass extends EntityClass {
|
||||
displayName: this.dashboardName,
|
||||
service: this.service.name,
|
||||
};
|
||||
children = [
|
||||
{
|
||||
name: 'country_name',
|
||||
dataType: 'VARCHAR',
|
||||
dataLength: 256,
|
||||
dataTypeDisplay: 'varchar',
|
||||
description: 'Name of the country.',
|
||||
},
|
||||
];
|
||||
dataModel = {
|
||||
name: this.dashboardDataModelName,
|
||||
displayName: this.dashboardDataModelName,
|
||||
service: this.service.name,
|
||||
columns: this.children,
|
||||
dataModelType: 'SupersetDataModel',
|
||||
};
|
||||
|
||||
serviceResponseData: ResponseDataType = {} as ResponseDataType;
|
||||
entityResponseData: ResponseDataWithServiceType =
|
||||
{} as ResponseDataWithServiceType;
|
||||
dataModelResponseData: ResponseDataWithServiceType =
|
||||
{} as ResponseDataWithServiceType;
|
||||
chartsResponseData: ResponseDataType = {} as ResponseDataType;
|
||||
|
||||
constructor(name?: string) {
|
||||
@ -61,6 +81,7 @@ export class DashboardClass extends EntityClass {
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.type = 'Dashboard';
|
||||
this.serviceCategory = SERVICE_TYPE.Dashboard;
|
||||
this.serviceType = ServiceTypes.DASHBOARD_SERVICES;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
@ -80,9 +101,16 @@ export class DashboardClass extends EntityClass {
|
||||
charts: [`${this.service.name}.${this.charts.name}`],
|
||||
},
|
||||
});
|
||||
const dataModelResponse = await apiContext.post(
|
||||
'/api/v1/dashboard/datamodels',
|
||||
{
|
||||
data: this.dataModel,
|
||||
}
|
||||
);
|
||||
|
||||
this.serviceResponseData = await serviceResponse.json();
|
||||
this.chartsResponseData = await chartsResponse.json();
|
||||
this.dataModelResponseData = await dataModelResponse.json();
|
||||
this.entityResponseData = await entityResponse.json();
|
||||
|
||||
return {
|
||||
|
@ -13,6 +13,7 @@
|
||||
import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { ServiceTypes } from '../../constant/settings';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { visitEntityPage } from '../../utils/entity';
|
||||
import {
|
||||
@ -70,6 +71,7 @@ export class DashboardDataModelClass extends EntityClass {
|
||||
this.childrenTabId = 'model';
|
||||
this.childrenSelectorId = this.children[0].name;
|
||||
this.serviceCategory = SERVICE_TYPE.Dashboard;
|
||||
this.serviceType = ServiceTypes.DASHBOARD_SERVICES;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
|
@ -13,6 +13,7 @@
|
||||
import { APIRequestContext, expect, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { ServiceTypes } from '../../constant/settings';
|
||||
import {
|
||||
assignDomain,
|
||||
removeDomain,
|
||||
@ -127,6 +128,7 @@ export class DatabaseClass extends EntityClass {
|
||||
super(EntityTypeEndpoint.Database);
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.type = 'Database';
|
||||
this.serviceType = ServiceTypes.DATABASE_SERVICES;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
|
@ -13,6 +13,7 @@
|
||||
import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { ServiceTypes } from '../../constant/settings';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { visitServiceDetailsPage } from '../../utils/service';
|
||||
import {
|
||||
@ -61,6 +62,7 @@ export class DatabaseSchemaClass extends EntityClass {
|
||||
super(EntityTypeEndpoint.DatabaseSchema);
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.type = 'Database Schema';
|
||||
this.serviceType = ServiceTypes.DATABASE_SERVICES;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
|
@ -12,7 +12,7 @@
|
||||
*/
|
||||
import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { CustomPropertySupportedEntityList } from '../../constant/customProperty';
|
||||
import { GlobalSettingOptions } from '../../constant/settings';
|
||||
import { GlobalSettingOptions, ServiceTypes } from '../../constant/settings';
|
||||
import { assignDomain, removeDomain, updateDomain } from '../../utils/common';
|
||||
import {
|
||||
createCustomPropertyForEntity,
|
||||
@ -56,6 +56,7 @@ import { EntityTypeEndpoint, ENTITY_PATH } from './Entity.interface';
|
||||
export class EntityClass {
|
||||
type = '';
|
||||
serviceCategory?: GlobalSettingOptions;
|
||||
serviceType?: ServiceTypes;
|
||||
childrenTabId?: string;
|
||||
childrenSelectorId?: string;
|
||||
endpoint: EntityTypeEndpoint;
|
||||
|
@ -13,6 +13,7 @@
|
||||
import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { ServiceTypes } from '../../constant/settings';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { visitEntityPage } from '../../utils/entity';
|
||||
import {
|
||||
@ -64,6 +65,7 @@ export class MlModelClass extends EntityClass {
|
||||
this.childrenTabId = 'features';
|
||||
this.childrenSelectorId = `feature-card-${this.children[0].name}`;
|
||||
this.serviceCategory = SERVICE_TYPE.MLModels;
|
||||
this.serviceType = ServiceTypes.ML_MODEL_SERVICES;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
|
@ -13,6 +13,7 @@
|
||||
import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { ServiceTypes } from '../../constant/settings';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { visitEntityPage } from '../../utils/entity';
|
||||
import {
|
||||
@ -59,6 +60,7 @@ export class PipelineClass extends EntityClass {
|
||||
this.childrenTabId = 'tasks';
|
||||
this.childrenSelectorId = this.children[0].name;
|
||||
this.serviceCategory = SERVICE_TYPE.Pipeline;
|
||||
this.serviceType = ServiceTypes.PIPELINE_SERVICES;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
|
@ -13,6 +13,7 @@
|
||||
import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { ServiceTypes } from '../../constant/settings';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { visitEntityPage } from '../../utils/entity';
|
||||
import {
|
||||
@ -105,6 +106,7 @@ export class SearchIndexClass extends EntityClass {
|
||||
this.childrenTabId = 'fields';
|
||||
this.childrenSelectorId = this.children[0].fullyQualifiedName;
|
||||
this.serviceCategory = SERVICE_TYPE.Search;
|
||||
this.serviceType = ServiceTypes.SEARCH_SERVICES;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
|
@ -13,6 +13,7 @@
|
||||
import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { ServiceTypes } from '../../constant/settings';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { visitEntityPage } from '../../utils/entity';
|
||||
import {
|
||||
@ -71,6 +72,7 @@ export class StoredProcedureClass extends EntityClass {
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.serviceCategory = SERVICE_TYPE.Database;
|
||||
this.type = 'Store Procedure';
|
||||
this.serviceType = ServiceTypes.DATABASE_SERVICES;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
|
@ -14,6 +14,7 @@ import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { ServiceTypes } from '../../constant/settings';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { visitEntityPage } from '../../utils/entity';
|
||||
import {
|
||||
@ -125,6 +126,7 @@ export class TableClass extends EntityClass {
|
||||
super(EntityTypeEndpoint.Table);
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.serviceCategory = SERVICE_TYPE.Database;
|
||||
this.serviceType = ServiceTypes.DATABASE_SERVICES;
|
||||
this.type = 'Table';
|
||||
this.childrenTabId = 'schema';
|
||||
this.childrenSelectorId = `${this.entity.databaseSchema}.${this.entity.name}.${this.children[0].name}`;
|
||||
|
@ -13,6 +13,7 @@
|
||||
import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { ServiceTypes } from '../../constant/settings';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { visitEntityPage } from '../../utils/entity';
|
||||
import {
|
||||
@ -107,6 +108,7 @@ export class TopicClass extends EntityClass {
|
||||
this.childrenTabId = 'schema';
|
||||
this.childrenSelectorId = this.children[0].name;
|
||||
this.serviceCategory = SERVICE_TYPE.Messaging;
|
||||
this.serviceType = ServiceTypes.MESSAGING_SERVICES;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2025 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 { PolicyClass } from '../access-control/PoliciesClass';
|
||||
import { RolesClass } from '../access-control/RolesClass';
|
||||
import { ApiCollectionClass } from '../entity/ApiCollectionClass';
|
||||
import { ContainerClass } from '../entity/ContainerClass';
|
||||
import { DashboardClass } from '../entity/DashboardClass';
|
||||
import { MlModelClass } from '../entity/MlModelClass';
|
||||
import { PipelineClass } from '../entity/PipelineClass';
|
||||
import { SearchIndexClass } from '../entity/SearchIndexClass';
|
||||
import { TableClass } from '../entity/TableClass';
|
||||
import { TopicClass } from '../entity/TopicClass';
|
||||
import { UserClass } from '../user/UserClass';
|
||||
|
||||
export interface EntityData {
|
||||
isOwnerPolicy: PolicyClass;
|
||||
matchAnyTagPolicy: PolicyClass;
|
||||
isOwnerRole: RolesClass;
|
||||
matchAnyTagRole: RolesClass;
|
||||
userWithOwnerPermission: UserClass;
|
||||
userWithTagPermission: UserClass;
|
||||
withOwner: {
|
||||
apiCollectionWithOwner: ApiCollectionClass;
|
||||
containerWithOwner: ContainerClass;
|
||||
dashboardWithOwner: DashboardClass;
|
||||
mlModelWithOwner: MlModelClass;
|
||||
pipelineWithOwner: PipelineClass;
|
||||
searchIndexWithOwner: SearchIndexClass;
|
||||
tableWithOwner: TableClass;
|
||||
topicWithOwner: TopicClass;
|
||||
};
|
||||
withTag: {
|
||||
apiCollectionWithTag: ApiCollectionClass;
|
||||
containerWithTag: ContainerClass;
|
||||
dashboardWithTag: DashboardClass;
|
||||
mlModelWithTag: MlModelClass;
|
||||
pipelineWithTag: PipelineClass;
|
||||
searchIndexWithTag: SearchIndexClass;
|
||||
tableWithTag: TableClass;
|
||||
topicWithTag: TopicClass;
|
||||
};
|
||||
}
|
||||
|
||||
export type AssetTypes =
|
||||
| ApiCollectionClass
|
||||
| ContainerClass
|
||||
| DashboardClass
|
||||
| MlModelClass
|
||||
| PipelineClass
|
||||
| TableClass
|
||||
| TopicClass;
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2025 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 { APIRequestContext, Page } from '@playwright/test';
|
||||
import {
|
||||
ORGANIZATION_POLICY_RULES,
|
||||
VIEW_ALL_RULE,
|
||||
} from '../constant/permission';
|
||||
import { AdminClass } from '../support/user/AdminClass';
|
||||
import { getApiContext } from './common';
|
||||
|
||||
export const restoreOrganizationDefaultRole = async (
|
||||
apiContext: APIRequestContext
|
||||
) => {
|
||||
const organizationTeamResponse = await apiContext
|
||||
.get(`/api/v1/teams/name/Organization`)
|
||||
.then((res) => res.json());
|
||||
|
||||
const dataConsumerRoleResponse = await apiContext
|
||||
.get('/api/v1/roles/name/DataConsumer')
|
||||
.then((res) => res.json());
|
||||
|
||||
await apiContext.patch(`/api/v1/teams/${organizationTeamResponse.id}`, {
|
||||
data: [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/defaultRoles',
|
||||
value: [
|
||||
{
|
||||
id: dataConsumerRoleResponse.id,
|
||||
type: 'role',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
headers: {
|
||||
'Content-Type': 'application/json-patch+json',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const updateDefaultOrganizationPolicy = async (
|
||||
apiContext: APIRequestContext
|
||||
) => {
|
||||
const orgPolicyResponse = await apiContext
|
||||
.get('/api/v1/policies/name/OrganizationPolicy')
|
||||
.then((response) => response.json());
|
||||
|
||||
await apiContext.patch(`/api/v1/policies/${orgPolicyResponse.id}`, {
|
||||
data: [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/rules',
|
||||
value: [...ORGANIZATION_POLICY_RULES, ...VIEW_ALL_RULE],
|
||||
},
|
||||
],
|
||||
headers: {
|
||||
'Content-Type': 'application/json-patch+json',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const restoreRolesAndPolicies = async (page: Page) => {
|
||||
const { apiContext, afterAction } = await getApiContext(page);
|
||||
// Remove organization policy and role
|
||||
await restoreOrganizationDefaultRole(apiContext);
|
||||
// update default Organization policy
|
||||
await updateDefaultOrganizationPolicy(apiContext);
|
||||
|
||||
await afterAction();
|
||||
};
|
||||
|
||||
export const resetPolicyChanges = async (page: Page, admin: AdminClass) => {
|
||||
await admin.login(page);
|
||||
await page.waitForURL('**/my-data');
|
||||
await restoreRolesAndPolicies(page);
|
||||
await admin.logout(page);
|
||||
await page.waitForURL('**/signin');
|
||||
};
|
@ -0,0 +1,304 @@
|
||||
/*
|
||||
* Copyright 2025 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 { APIRequestContext, expect, Page } from '@playwright/test';
|
||||
import { conditionalPermissionsEntityData } from '../constant/conditionalPermissions';
|
||||
import {
|
||||
VIEW_ALL_WITH_IS_OWNER,
|
||||
VIEW_ALL_WITH_MATCH_TAG_CONDITION,
|
||||
} from '../constant/permission';
|
||||
import { TableClass } from '../support/entity/TableClass';
|
||||
import { AssetTypes } from '../support/interfaces/ConditionalPermissions.interface';
|
||||
import { redirectToHomePage } from './common';
|
||||
|
||||
export const conditionalPermissionsPrerequisites = async (
|
||||
apiContext: APIRequestContext
|
||||
) => {
|
||||
const {
|
||||
isOwnerPolicy,
|
||||
matchAnyTagPolicy,
|
||||
isOwnerRole,
|
||||
matchAnyTagRole,
|
||||
userWithOwnerPermission,
|
||||
userWithTagPermission,
|
||||
withOwner,
|
||||
withTag,
|
||||
} = conditionalPermissionsEntityData;
|
||||
await userWithOwnerPermission.create(apiContext);
|
||||
await userWithTagPermission.create(apiContext);
|
||||
await withOwner.apiCollectionWithOwner.create(apiContext);
|
||||
await withTag.apiCollectionWithTag.create(apiContext);
|
||||
await withOwner.containerWithOwner.create(apiContext);
|
||||
await withTag.containerWithTag.create(apiContext);
|
||||
await withOwner.dashboardWithOwner.create(apiContext);
|
||||
await withTag.dashboardWithTag.create(apiContext);
|
||||
await withOwner.mlModelWithOwner.create(apiContext);
|
||||
await withTag.mlModelWithTag.create(apiContext);
|
||||
await withOwner.pipelineWithOwner.create(apiContext);
|
||||
await withTag.pipelineWithTag.create(apiContext);
|
||||
await withOwner.searchIndexWithOwner.create(apiContext);
|
||||
await withTag.searchIndexWithTag.create(apiContext);
|
||||
await withOwner.tableWithOwner.create(apiContext);
|
||||
await withTag.tableWithTag.create(apiContext);
|
||||
await withOwner.topicWithOwner.create(apiContext);
|
||||
await withTag.topicWithTag.create(apiContext);
|
||||
|
||||
const isOwnerPolicyResponse = await isOwnerPolicy.create(
|
||||
apiContext,
|
||||
VIEW_ALL_WITH_IS_OWNER
|
||||
);
|
||||
const matchAnyTagPolicyResponse = await matchAnyTagPolicy.create(
|
||||
apiContext,
|
||||
VIEW_ALL_WITH_MATCH_TAG_CONDITION
|
||||
);
|
||||
|
||||
const isOwnerRoleResponse = await isOwnerRole.create(apiContext, [
|
||||
isOwnerPolicyResponse.fullyQualifiedName,
|
||||
]);
|
||||
const matchAnyTagRoleResponse = await matchAnyTagRole.create(apiContext, [
|
||||
matchAnyTagPolicyResponse.fullyQualifiedName,
|
||||
]);
|
||||
|
||||
await userWithOwnerPermission.patch({
|
||||
apiContext,
|
||||
patchData: [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/roles',
|
||||
value: [
|
||||
{
|
||||
id: isOwnerRoleResponse.id,
|
||||
type: 'role',
|
||||
name: isOwnerRoleResponse.name,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
await userWithTagPermission.patch({
|
||||
apiContext,
|
||||
patchData: [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/roles',
|
||||
value: [
|
||||
{
|
||||
id: matchAnyTagRoleResponse.id,
|
||||
type: 'role',
|
||||
name: matchAnyTagRoleResponse.name,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const ownerPatchData = {
|
||||
data: [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/owners',
|
||||
value: [
|
||||
{
|
||||
id: userWithOwnerPermission.responseData.id,
|
||||
type: 'user',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
headers: {
|
||||
'Content-Type': 'application/json-patch+json',
|
||||
},
|
||||
};
|
||||
|
||||
const tagPatchData = {
|
||||
data: [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/tags',
|
||||
value: [
|
||||
{
|
||||
tagFQN: 'PersonalData.Personal',
|
||||
source: 'Classification',
|
||||
labelType: 'Manual',
|
||||
name: 'Personal',
|
||||
state: 'Confirmed',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
headers: {
|
||||
'Content-Type': 'application/json-patch+json',
|
||||
},
|
||||
};
|
||||
|
||||
for (const entity of Object.values(withOwner)) {
|
||||
await apiContext.patch(
|
||||
`/api/v1/services/${entity.serviceType}/${entity.serviceResponseData.id}`,
|
||||
ownerPatchData
|
||||
);
|
||||
}
|
||||
|
||||
await apiContext.patch(
|
||||
`/api/v1/databases/${withOwner.tableWithOwner.databaseResponseData.id}`,
|
||||
ownerPatchData
|
||||
);
|
||||
|
||||
await apiContext.patch(
|
||||
`/api/v1/databaseSchemas/${withOwner.tableWithOwner.schemaResponseData.id}`,
|
||||
ownerPatchData
|
||||
);
|
||||
|
||||
await apiContext.patch(
|
||||
`/api/v1/containers/${withOwner.containerWithOwner.entityResponseData.id}`,
|
||||
ownerPatchData
|
||||
);
|
||||
|
||||
for (const entity of Object.values(withTag)) {
|
||||
await apiContext.patch(
|
||||
`/api/v1/services/${entity.serviceType}/${entity.serviceResponseData.id}`,
|
||||
tagPatchData
|
||||
);
|
||||
}
|
||||
|
||||
await apiContext.patch(
|
||||
`/api/v1/databases/${withTag.tableWithTag.databaseResponseData.id}`,
|
||||
tagPatchData
|
||||
);
|
||||
|
||||
await apiContext.patch(
|
||||
`/api/v1/databaseSchemas/${withTag.tableWithTag.schemaResponseData.id}`,
|
||||
tagPatchData
|
||||
);
|
||||
|
||||
await apiContext.patch(
|
||||
`/api/v1/containers/${withTag.containerWithTag.entityResponseData.id}`,
|
||||
tagPatchData
|
||||
);
|
||||
};
|
||||
|
||||
export const conditionalPermissionsCleanup = async (
|
||||
apiContext: APIRequestContext
|
||||
) => {
|
||||
const {
|
||||
isOwnerPolicy,
|
||||
matchAnyTagPolicy,
|
||||
isOwnerRole,
|
||||
matchAnyTagRole,
|
||||
userWithOwnerPermission,
|
||||
userWithTagPermission,
|
||||
withOwner: {
|
||||
apiCollectionWithOwner,
|
||||
containerWithOwner,
|
||||
dashboardWithOwner,
|
||||
mlModelWithOwner,
|
||||
pipelineWithOwner,
|
||||
searchIndexWithOwner,
|
||||
tableWithOwner,
|
||||
topicWithOwner,
|
||||
},
|
||||
withTag: {
|
||||
apiCollectionWithTag,
|
||||
containerWithTag,
|
||||
dashboardWithTag,
|
||||
mlModelWithTag,
|
||||
pipelineWithTag,
|
||||
searchIndexWithTag,
|
||||
tableWithTag,
|
||||
topicWithTag,
|
||||
},
|
||||
} = conditionalPermissionsEntityData;
|
||||
await isOwnerRole.delete(apiContext);
|
||||
await matchAnyTagRole.delete(apiContext);
|
||||
await isOwnerPolicy.delete(apiContext);
|
||||
await matchAnyTagPolicy.delete(apiContext);
|
||||
await userWithOwnerPermission.delete(apiContext);
|
||||
await userWithTagPermission.delete(apiContext);
|
||||
await apiCollectionWithOwner.delete(apiContext);
|
||||
await apiCollectionWithTag.delete(apiContext);
|
||||
await containerWithOwner.delete(apiContext);
|
||||
await containerWithTag.delete(apiContext);
|
||||
await dashboardWithOwner.delete(apiContext);
|
||||
await dashboardWithTag.delete(apiContext);
|
||||
await mlModelWithOwner.delete(apiContext);
|
||||
await mlModelWithTag.delete(apiContext);
|
||||
await pipelineWithOwner.delete(apiContext);
|
||||
await pipelineWithTag.delete(apiContext);
|
||||
await searchIndexWithOwner.delete(apiContext);
|
||||
await searchIndexWithTag.delete(apiContext);
|
||||
await tableWithOwner.delete(apiContext);
|
||||
await tableWithTag.delete(apiContext);
|
||||
await topicWithOwner.delete(apiContext);
|
||||
await topicWithTag.delete(apiContext);
|
||||
};
|
||||
|
||||
export const getEntityFQN = (assetName: string, asset: AssetTypes) => {
|
||||
if (assetName === 'database') {
|
||||
return (asset as TableClass).databaseResponseData.fullyQualifiedName;
|
||||
}
|
||||
if (assetName === 'databaseSchema') {
|
||||
return (asset as TableClass).schemaResponseData.fullyQualifiedName;
|
||||
}
|
||||
if (assetName === 'container') {
|
||||
return asset.entityResponseData.fullyQualifiedName;
|
||||
}
|
||||
|
||||
return asset.serviceResponseData.fullyQualifiedName;
|
||||
};
|
||||
|
||||
export const checkViewAllPermission = async ({
|
||||
page,
|
||||
url1,
|
||||
url2,
|
||||
childTabId,
|
||||
childTabId2,
|
||||
childTableId2,
|
||||
}: {
|
||||
page: Page;
|
||||
url1: string;
|
||||
url2: string;
|
||||
childTabId: string;
|
||||
childTabId2?: string;
|
||||
childTableId2?: string;
|
||||
}) => {
|
||||
await redirectToHomePage(page);
|
||||
|
||||
// visit the page of the asset with permission
|
||||
await page.goto(url1);
|
||||
|
||||
// Check if the details are shown properly
|
||||
await expect(page.getByTestId('data-assets-header')).toBeAttached();
|
||||
|
||||
await page.waitForSelector(`[data-testid="${childTabId}"]`);
|
||||
|
||||
await page.click(`[data-testid="${childTabId}"]`);
|
||||
|
||||
await expect(
|
||||
page
|
||||
.getByTestId('service-children-table')
|
||||
.getByTestId('no-data-placeholder')
|
||||
).not.toBeAttached();
|
||||
|
||||
if (childTabId2) {
|
||||
await page.click(`[data-testid="${childTabId2}"]`);
|
||||
|
||||
await expect(
|
||||
page.getByTestId(childTableId2 ?? '').getByTestId('no-data-placeholder')
|
||||
).not.toBeAttached();
|
||||
}
|
||||
|
||||
// visit the page of the asset without permission
|
||||
await page.goto(url2);
|
||||
|
||||
// Check if the no permissions placeholder is shown
|
||||
await expect(page.getByTestId('permission-error-placeholder')).toBeAttached();
|
||||
};
|
@ -355,6 +355,8 @@ const DatabaseDetails: FunctionComponent = () => {
|
||||
if (databasePermission.ViewAll || databasePermission.ViewBasic) {
|
||||
getDetailsByFQN();
|
||||
fetchDatabaseSchemaCount();
|
||||
} else {
|
||||
setIsDatabaseDetailsLoading(false);
|
||||
}
|
||||
}, [databasePermission, decodedDatabaseFQN]);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user