Issue-19197: Deleting Data Product should delete the data asset relationships (#19208)

* Issue-19197: Deleting Data Product should delete the data asset relationships

* Issue-19197: Deleting Data Product should delete the data asset relationships

* Minor: Fix the deleteByName

* add tests for data product delete case

* fix flaky tests

* update data products fqn keyword mapping

* Update table_index_mapping.json

* fix flaky test

* fix tests

* fix flaky tests

* fix flakiness

---------

Co-authored-by: karanh37 <karanh37@gmail.com>
Co-authored-by: Karan Hotchandani <33024356+karanh37@users.noreply.github.com>
This commit is contained in:
Sriharsha Chintalapani 2025-01-08 01:38:08 -08:00 committed by GitHub
parent 8fdaea805f
commit 490ebdebc3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
76 changed files with 309 additions and 106 deletions

View File

@ -1154,6 +1154,7 @@ public abstract class EntityRepository<T extends EntityInterface> {
String updatedBy, UUID id, boolean recursive, boolean hardDelete) {
DeleteResponse<T> response = deleteInternal(updatedBy, id, recursive, hardDelete);
postDelete(response.entity());
deleteFromSearch(response.entity(), hardDelete);
return response;
}
@ -1178,6 +1179,7 @@ public abstract class EntityRepository<T extends EntityInterface> {
name = quoteFqn ? quoteName(name) : name;
DeleteResponse<T> response = deleteInternalByName(updatedBy, name, recursive, hardDelete);
postDelete(response.entity());
deleteFromSearch(response.entity(), hardDelete);
return response;
}
@ -1188,12 +1190,12 @@ public abstract class EntityRepository<T extends EntityInterface> {
protected void postDelete(T entity) {}
public final void deleteFromSearch(T entity, EventType changeType) {
public final void deleteFromSearch(T entity, boolean hardDelete) {
if (supportsSearch) {
if (changeType.equals(ENTITY_SOFT_DELETED)) {
searchRepository.softDeleteOrRestoreEntity(entity, true);
} else {
if (hardDelete) {
searchRepository.deleteEntity(entity);
} else {
searchRepository.softDeleteOrRestoreEntity(entity, true);
}
}
}

View File

@ -361,7 +361,6 @@ public abstract class EntityResource<T extends EntityInterface, K extends Entity
authorizer.authorize(securityContext, operationContext, getResourceContextById(id));
DeleteResponse<T> response =
repository.delete(securityContext.getUserPrincipal().getName(), id, recursive, hardDelete);
repository.deleteFromSearch(response.entity(), response.changeType());
if (hardDelete) {
limits.invalidateCache(entityType);
}
@ -380,7 +379,6 @@ public abstract class EntityResource<T extends EntityInterface, K extends Entity
DeleteResponse<T> response =
repository.deleteByName(
securityContext.getUserPrincipal().getName(), name, recursive, hardDelete);
repository.deleteFromSearch(response.entity(), response.changeType());
addHref(uriInfo, response.entity());
return response.toResponse();
}

View File

@ -762,7 +762,7 @@ public class TestSuiteResource extends EntityResource<TestSuite, TestSuiteReposi
}
RestUtil.DeleteResponse<TestSuite> response =
repository.deleteLogicalTestSuite(securityContext, testSuite, hardDelete);
repository.deleteFromSearch(response.entity(), response.changeType());
repository.deleteFromSearch(response.entity(), hardDelete);
addHref(uriInfo, response.entity());
return response.toResponse();
}

View File

@ -42,6 +42,7 @@ public interface SearchClient {
String DELETE = "delete";
String GLOBAL_SEARCH_ALIAS = "all";
String DATA_ASSET_SEARCH_ALIAS = "dataAsset";
String GLOSSARY_TERM_SEARCH_INDEX = "glossary_term_search_index";
String TAG_SEARCH_INDEX = "tag_search_index";
String DEFAULT_UPDATE_SCRIPT = "for (k in params.keySet()) { ctx._source.put(k, params.get(k)) }";
@ -74,6 +75,8 @@ public interface SearchClient {
String REMOVE_TAGS_CHILDREN_SCRIPT =
"for (int i = 0; i < ctx._source.tags.length; i++) { if (ctx._source.tags[i].tagFQN == params.fqn) { ctx._source.tags.remove(i) }}";
String REMOVE_DATA_PRODUCTS_CHILDREN_SCRIPT =
"for (int i = 0; i < ctx._source.dataProducts.length; i++) { if (ctx._source.dataProducts[i].fullyQualifiedName == params.fqn) { ctx._source.dataProducts.remove(i) }}";
String UPDATE_CERTIFICATION_SCRIPT =
"if (ctx._source.certification != null && ctx._source.certification.tagLabel != null) {ctx._source.certification.tagLabel.style = params.style; ctx._source.certification.tagLabel.description = params.description; ctx._source.certification.tagLabel.tagFQN = params.tagFQN; ctx._source.certification.tagLabel.name = params.name; }";

View File

@ -16,6 +16,7 @@ import static org.openmetadata.service.search.SearchClient.GLOBAL_SEARCH_ALIAS;
import static org.openmetadata.service.search.SearchClient.PROPAGATE_ENTITY_REFERENCE_FIELD_SCRIPT;
import static org.openmetadata.service.search.SearchClient.PROPAGATE_FIELD_SCRIPT;
import static org.openmetadata.service.search.SearchClient.PROPAGATE_TEST_SUITES_SCRIPT;
import static org.openmetadata.service.search.SearchClient.REMOVE_DATA_PRODUCTS_CHILDREN_SCRIPT;
import static org.openmetadata.service.search.SearchClient.REMOVE_DOMAINS_CHILDREN_SCRIPT;
import static org.openmetadata.service.search.SearchClient.REMOVE_OWNERS_SCRIPT;
import static org.openmetadata.service.search.SearchClient.REMOVE_PROPAGATED_ENTITY_REFERENCE_FIELD_SCRIPT;
@ -727,6 +728,13 @@ public class SearchRepository {
indexMapping.getChildAliases(clusterAlias),
List.of(new ImmutablePair<>(entityType + ".id", docId)));
}
case Entity.DATA_PRODUCT -> searchClient.updateChildren(
GLOBAL_SEARCH_ALIAS,
new ImmutablePair<>("dataProducts.id", docId),
new ImmutablePair<>(
REMOVE_DATA_PRODUCTS_CHILDREN_SCRIPT,
Collections.singletonMap("fqn", entity.getFullyQualifiedName())));
case Entity.TAG, Entity.GLOSSARY_TERM -> searchClient.updateChildren(
GLOBAL_SEARCH_ALIAS,
new ImmutablePair<>("tags.tagFQN", entity.getFullyQualifiedName()),

View File

@ -2212,7 +2212,7 @@ public class ElasticSearchClient implements SearchClient {
private void updateElasticSearchByQuery(UpdateByQueryRequest updateByQueryRequest) {
if (updateByQueryRequest != null && isClientAvailable) {
updateByQueryRequest.setRefresh(true);
LOG.debug(SENDING_REQUEST_TO_ELASTIC_SEARCH, updateByQueryRequest);
LOG.info(SENDING_REQUEST_TO_ELASTIC_SEARCH, updateByQueryRequest);
client.updateByQuery(updateByQueryRequest, RequestOptions.DEFAULT);
}
}

View File

@ -152,7 +152,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -236,7 +236,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -244,7 +244,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -323,7 +323,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -245,7 +245,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -129,7 +129,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -301,7 +301,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -243,7 +243,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -152,7 +152,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -129,7 +129,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -244,7 +244,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -243,7 +243,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -145,7 +145,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -244,7 +244,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -128,7 +128,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -242,7 +242,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -438,7 +438,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -244,7 +244,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -239,7 +239,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -139,7 +139,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -451,7 +451,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -192,7 +192,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -201,7 +201,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -228,7 +228,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -244,7 +244,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -348,7 +348,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -180,7 +180,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -130,7 +130,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -182,7 +182,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -234,7 +234,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -201,7 +201,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -128,7 +128,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -244,7 +244,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -239,7 +239,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -137,7 +137,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -244,7 +244,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -116,7 +116,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -234,7 +234,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -435,7 +435,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -244,7 +244,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -231,7 +231,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -131,7 +131,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -446,7 +446,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -362,7 +362,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -140,7 +140,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -226,7 +226,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -228,7 +228,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -344,7 +344,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -230,7 +230,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -117,7 +117,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -236,7 +236,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -224,7 +224,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -140,7 +140,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -117,7 +117,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -228,7 +228,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -226,7 +226,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -131,7 +131,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -228,7 +228,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -117,7 +117,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -224,7 +224,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -426,7 +426,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -228,7 +228,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -221,7 +221,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -127,7 +127,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -442,7 +442,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -180,7 +180,7 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "keyword"
},
"description": {
"type": "text"

View File

@ -114,13 +114,13 @@ test.describe('Ingestion Bot ', () => {
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.DOMAIN);
await selectDomain(page, domain1.data);
await addAssetsToDomain(page, domain1.data, domainAsset1);
await addAssetsToDomain(page, domain1, domainAsset1);
// Add assets to domain 2
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.DOMAIN);
await selectDomain(page, domain2.data);
await addAssetsToDomain(page, domain2.data, domainAsset2);
await addAssetsToDomain(page, domain2, domainAsset2);
});
await test.step(

View File

@ -34,6 +34,7 @@ import {
selectDomain,
selectSubDomain,
setupAssetsForDomain,
verifyDataProductAssetsAfterDelete,
verifyDomain,
} from '../../utils/domain';
import { sidebarClick } from '../../utils/sidebar';
@ -61,7 +62,7 @@ test.describe('Domains', () => {
await test.step('Add assets to domain', async () => {
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.DOMAIN);
await addAssetsToDomain(page, domain.data, assets);
await addAssetsToDomain(page, domain, assets);
});
await test.step('Delete domain using delete modal', async () => {
@ -100,7 +101,7 @@ test.describe('Domains', () => {
await domain.create(apiContext);
await sidebarClick(page, SidebarItem.DOMAIN);
await page.reload();
await addAssetsToDomain(page, domain.data, assets);
await addAssetsToDomain(page, domain, assets);
await test.step('Create DataProducts', async () => {
await selectDomain(page, domain.data);
@ -115,7 +116,11 @@ test.describe('Domains', () => {
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.DOMAIN);
await selectDataProduct(page, domain.data, dataProduct1.data);
await addAssetsToDataProduct(page, dataProduct1.data, assets);
await addAssetsToDataProduct(
page,
dataProduct1.data.fullyQualifiedName ?? '',
assets
);
});
await test.step('Remove assets from DataProducts', async () => {
@ -220,7 +225,7 @@ test.describe('Domains', () => {
await domain.create(apiContext);
await page.reload();
await sidebarClick(page, SidebarItem.DOMAIN);
await addAssetsToDomain(page, domain.data, assets);
await addAssetsToDomain(page, domain, assets);
await page.getByTestId('documentation').click();
const updatedDomainName = 'PW Domain Updated';
@ -264,6 +269,71 @@ test.describe('Domains', () => {
await domain.delete(apiContext);
await afterAction();
});
test('Should clear assets from data products after deletion of data product in Domain', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const { assets, assetCleanup } = await setupAssetsForDomain(page);
const domain = new Domain({
name: 'PW_Domain_Delete_Testing',
displayName: 'PW_Domain_Delete_Testing',
description: 'playwright domain description',
domainType: 'Aggregate',
fullyQualifiedName: 'PW_Domain_Delete_Testing',
});
const dataProduct1 = new DataProduct(domain, 'PW_DataProduct_Sales');
const dataProduct2 = new DataProduct(domain, 'PW_DataProduct_Finance');
const domain1 = new Domain({
name: 'PW_Domain_Delete_Testing',
displayName: 'PW_Domain_Delete_Testing',
description: 'playwright domain description',
domainType: 'Aggregate',
fullyQualifiedName: 'PW_Domain_Delete_Testing',
});
const newDomainDP1 = new DataProduct(domain1, 'PW_DataProduct_Sales');
const newDomainDP2 = new DataProduct(domain1, 'PW_DataProduct_Finance');
try {
await domain.create(apiContext);
await dataProduct1.create(apiContext);
await dataProduct2.create(apiContext);
await sidebarClick(page, SidebarItem.DOMAIN);
await page.reload();
await addAssetsToDomain(page, domain, assets);
await verifyDataProductAssetsAfterDelete(page, {
domain,
dataProduct1,
dataProduct2,
assets,
});
await test.step(
'Delete domain & recreate the same domain and data product',
async () => {
await domain.delete(apiContext);
await domain1.create(apiContext);
await newDomainDP1.create(apiContext);
await newDomainDP2.create(apiContext);
await page.reload();
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.DOMAIN);
await selectDataProduct(page, domain1.data, newDomainDP1.data);
await checkAssetsCount(page, 0);
await sidebarClick(page, SidebarItem.DOMAIN);
await selectDataProduct(page, domain1.data, newDomainDP2.data);
await checkAssetsCount(page, 0);
}
);
} finally {
await newDomainDP1.delete(apiContext);
await newDomainDP2.delete(apiContext);
await domain1.delete(apiContext);
await assetCleanup();
await afterAction();
}
});
});
test.describe('Domains Rbac', () => {
@ -343,13 +413,13 @@ test.describe('Domains Rbac', () => {
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.DOMAIN);
await selectDomain(page, domain1.data);
await addAssetsToDomain(page, domain1.data, domainAssset1);
await addAssetsToDomain(page, domain1, domainAssset1);
// Add assets to domain 2
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.DOMAIN);
await selectDomain(page, domain2.data);
await addAssetsToDomain(page, domain2.data, domainAssset2);
await addAssetsToDomain(page, domain2, domainAssset2);
});
await test.step('User with access to multiple domains', async () => {

View File

@ -15,6 +15,7 @@ import { uuid } from '../../utils/common';
import { EntityTypeEndpoint } from '../entity/Entity.interface';
import { EntityClass } from '../entity/EntityClass';
import { Domain } from './Domain';
import { SubDomain } from './SubDomain';
type UserTeamRef = {
name: string;
@ -45,9 +46,10 @@ export class DataProduct extends EntityClass {
responseData: ResponseDataType = {} as ResponseDataType;
constructor(domain: Domain, name?: string) {
constructor(domain: Domain, name?: string, subDomain?: SubDomain) {
super(EntityTypeEndpoint.DATA_PRODUCT);
this.data.domain = domain.data.name;
this.data.domain =
domain.data.name + (subDomain ? `.${subDomain?.data.name}` : ''); // fqn
this.data.name = name ?? this.data.name;
// eslint-disable-next-line no-useless-escape
this.data.fullyQualifiedName = `\"${this.data.name}\"`;

View File

@ -10,8 +10,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { expect, Page } from '@playwright/test';
import test, { expect, Page } from '@playwright/test';
import { get, isEmpty, isUndefined } from 'lodash';
import { SidebarItem } from '../constant/sidebar';
import { DataProduct } from '../support/domain/DataProduct';
import { Domain } from '../support/domain/Domain';
import { SubDomain } from '../support/domain/SubDomain';
@ -27,8 +28,10 @@ import {
INVALID_NAMES,
NAME_MAX_LENGTH_VALIDATION_ERROR,
NAME_VALIDATION_ERROR,
redirectToHomePage,
} from './common';
import { addOwner } from './entity';
import { sidebarClick } from './sidebar';
export const assignDomain = async (page: Page, domain: Domain['data']) => {
await page.getByTestId('add-domain').click();
@ -113,13 +116,45 @@ export const selectSubDomain = async (
domain: Domain['data'],
subDomain: SubDomain['data']
) => {
await page
.getByRole('menuitem', { name: domain.displayName })
.locator('span')
.click();
const menuItem = page.getByRole('menuitem', { name: domain.displayName });
const isSelected = await menuItem.evaluate((element) => {
return element.classList.contains('ant-menu-item-selected');
});
if (!isSelected) {
const subDomainRes = page.waitForResponse(
'/api/v1/search/query?*&from=0&size=50&index=domain_search_index'
);
await menuItem.click();
await subDomainRes;
}
await page.getByTestId('subdomains').getByText('Sub Domains').click();
const res = page.waitForResponse(
'/api/v1/search/query?*&index=data_product_search_index'
);
await page.getByTestId(subDomain.name).click();
await res;
};
export const selectDataProductFromTab = async (
page: Page,
dataProduct: DataProduct['data']
) => {
const dpRes = page.waitForResponse(
'/api/v1/search/query?*&from=0&size=50&index=data_product_search_index'
);
await page.getByText('Data Products').click();
await dpRes;
const dpDataRes = page.waitForResponse('/api/v1/dataProducts/name/*');
await page
.getByTestId(`explore-card-${dataProduct.name}`)
.getByTestId('entity-link')
.click();
await dpDataRes;
};
export const selectDataProduct = async (
@ -132,11 +167,7 @@ export const selectDataProduct = async (
.locator('span')
.click();
await page.getByText('Data Products').click();
await page
.getByTestId(`explore-card-${dataProduct.name}`)
.getByTestId('entity-link')
.click();
await selectDataProductFromTab(page, dataProduct);
};
const goToAssetsTab = async (page: Page, domain: Domain['data']) => {
@ -280,10 +311,13 @@ export const createSubDomain = async (
export const addAssetsToDomain = async (
page: Page,
domain: Domain['data'],
assets: EntityClass[]
domain: Domain,
assets: EntityClass[],
navigateToAssetsTab = true
) => {
await goToAssetsTab(page, domain);
if (navigateToAssetsTab) {
await goToAssetsTab(page, domain.data);
}
await checkAssetsCount(page, 0);
await expect(page.getByTestId('no-data-placeholder')).toContainText(
@ -309,14 +343,16 @@ export const addAssetsToDomain = async (
await page.locator(`[data-testid="table-data-card_${fqn}"] input`).check();
}
const assetsAddRes = page.waitForResponse(
`/api/v1/domains/${encodeURIComponent(
domain.fullyQualifiedName ?? ''
)}/assets/add`
);
const assetsAddRes = page.waitForResponse(`/api/v1/domains/*/assets/add`);
await page.getByTestId('save-btn').click();
await assetsAddRes;
const countRes = page.waitForResponse(
'/api/v1/search/query?q=*&index=all&from=0&size=15'
);
await page.reload();
await countRes;
await checkAssetsCount(page, assets.length);
};
@ -357,7 +393,7 @@ export const addServicesToDomain = async (
export const addAssetsToDataProduct = async (
page: Page,
dataProduct: DataProduct['data'],
dataProductFqn: string,
assets: EntityClass[]
) => {
await page.getByTestId('assets').click();
@ -383,9 +419,7 @@ export const addAssetsToDataProduct = async (
}
const assetsAddRes = page.waitForResponse(
`/api/v1/dataProducts/${encodeURIComponent(
dataProduct.fullyQualifiedName ?? ''
)}/assets/add`
`/api/v1/dataProducts/*/assets/add`
);
await page.getByTestId('save-btn').click();
await assetsAddRes;
@ -458,3 +492,89 @@ export const createDataProduct = async (
await page.getByTestId('save-data-product').click();
await saveRes;
};
export const verifyDataProductAssetsAfterDelete = async (
page: Page,
{
domain,
dataProduct1,
dataProduct2,
assets,
subDomain,
}: {
domain: Domain;
dataProduct1: DataProduct;
dataProduct2: DataProduct;
assets: EntityClass[];
subDomain?: SubDomain;
}
) => {
const { apiContext } = await getApiContext(page);
const newDataProduct1 = new DataProduct(domain, 'PW_DataProduct_Sales');
await test.step('Add assets to DataProduct Sales', async () => {
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.DOMAIN);
if (subDomain) {
await selectSubDomain(page, domain.data, subDomain.data);
await selectDataProductFromTab(page, dataProduct1.data);
} else {
await selectDataProduct(page, domain.data, dataProduct1.data);
}
await addAssetsToDataProduct(
page,
dataProduct1.responseData.fullyQualifiedName ?? '',
assets.slice(0, 2)
);
});
await test.step('Add assets to DataProduct Finance', async () => {
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.DOMAIN);
if (subDomain) {
await selectSubDomain(page, domain.data, subDomain.data);
await selectDataProductFromTab(page, dataProduct2.data);
} else {
await selectDataProduct(page, domain.data, dataProduct2.data);
}
await addAssetsToDataProduct(
page,
dataProduct2.responseData.fullyQualifiedName ?? '',
[assets[2]]
);
});
await test.step(
'Remove Data Product Sales and Create the same again',
async () => {
// Remove sales data product
await dataProduct1.delete(apiContext);
// Create sales data product again
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.DOMAIN);
if (subDomain) {
await selectSubDomain(page, domain.data, subDomain.data);
} else {
await selectDomain(page, domain.data);
}
await createDataProduct(page, newDataProduct1.data);
}
);
await test.step(
'Verify assets are not present in the newly created data product',
async () => {
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.DOMAIN);
if (subDomain) {
await selectSubDomain(page, domain.data, subDomain.data);
await selectDataProductFromTab(page, newDataProduct1.data);
} else {
await selectDataProduct(page, domain.data, newDataProduct1.data);
}
await checkAssetsCount(page, 0);
}
);
};