From e313b144315c14d5df7cdfa7fc636953e2b48053 Mon Sep 17 00:00:00 2001 From: sonikashah Date: Tue, 14 Jan 2025 12:25:18 +0530 Subject: [PATCH 1/2] dataProduct : get inherited fields from corresponding Domain --- .../service/jdbi3/DataProductRepository.java | 19 +++++ .../domains/DataProductResourceTest.java | 79 +++++++++++++++++++ .../resources/domains/DomainResourceTest.java | 8 ++ 3 files changed, 106 insertions(+) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/DataProductRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/DataProductRepository.java index ff9f463d98a1..5af70aa6e4e5 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/DataProductRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/DataProductRepository.java @@ -14,7 +14,9 @@ package org.openmetadata.service.jdbi3; import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; +import static org.openmetadata.schema.type.Include.ALL; import static org.openmetadata.service.Entity.DATA_PRODUCT; +import static org.openmetadata.service.Entity.DOMAIN; import static org.openmetadata.service.Entity.FIELD_ASSETS; import static org.openmetadata.service.util.EntityUtil.entityReferenceMatch; @@ -26,6 +28,7 @@ import org.jdbi.v3.sqlobject.transaction.Transaction; import org.openmetadata.schema.EntityInterface; import org.openmetadata.schema.entity.domains.DataProduct; +import org.openmetadata.schema.entity.domains.Domain; import org.openmetadata.schema.type.EntityReference; import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.Relationship; @@ -93,6 +96,22 @@ public void storeRelationships(DataProduct entity) { } } + public final EntityReference getDomain(Domain domain) { + return getFromEntityRef(domain.getId(), Relationship.CONTAINS, DOMAIN, false); + } + + @Override + public void setInheritedFields(DataProduct dataProduct, Fields fields) { + // If dataProduct does not have owners and experts, inherit them from its domain + EntityReference parentRef = + dataProduct.getDomain() != null ? dataProduct.getDomain() : getDomain(dataProduct); + if (parentRef != null) { + Domain parent = Entity.getEntity(DOMAIN, parentRef.getId(), "owners,experts", ALL); + inheritOwners(dataProduct, fields, parent); + inheritExperts(dataProduct, fields, parent); + } + } + @Override public EntityUpdater getUpdater(DataProduct original, DataProduct updated, Operation operation) { return new DataProductUpdater(original, updated, operation); diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/domains/DataProductResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/domains/DataProductResourceTest.java index 4069fbc5a291..a83a5bf224c3 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/domains/DataProductResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/domains/DataProductResourceTest.java @@ -23,8 +23,10 @@ import org.junit.jupiter.api.TestInfo; import org.openmetadata.schema.EntityInterface; import org.openmetadata.schema.api.domains.CreateDataProduct; +import org.openmetadata.schema.api.domains.CreateDomain; import org.openmetadata.schema.entity.data.Topic; import org.openmetadata.schema.entity.domains.DataProduct; +import org.openmetadata.schema.entity.domains.Domain; import org.openmetadata.schema.entity.type.Style; import org.openmetadata.schema.type.ChangeDescription; import org.openmetadata.schema.type.EntityReference; @@ -177,6 +179,74 @@ void testValidateDataProducts() { .hasMessage(String.format("dataProduct instance for %s not found", rdnUUID)); } + @Test + void test_inheritOwnerExpertsFromDomain(TestInfo test) throws IOException { + DomainResourceTest domainResourceTest = new DomainResourceTest(); + + // Create parent domain + CreateDomain parentDomainReq = + domainResourceTest + .createRequest(test, 1) + .withOwners(List.of(USER1_REF)) + .withExperts(List.of(USER2.getFullyQualifiedName())); + Domain parentDomain = domainResourceTest.createEntity(parentDomainReq, ADMIN_AUTH_HEADERS); + parentDomain = domainResourceTest.getEntity(parentDomain.getId(), "*", ADMIN_AUTH_HEADERS); + + // Create data product corresponding to parent domain + CreateDataProduct create = + createRequestWithoutExpertsOwners(getEntityName(test, 1)) + .withDomain(parentDomain.getFullyQualifiedName()); + DataProduct dataProduct = createAndCheckEntity(create, ADMIN_AUTH_HEADERS); + assertOwners(dataProduct.getOwners(), parentDomain.getOwners()); + assertEntityReferences(dataProduct.getExperts(), parentDomain.getExperts()); + + // Create subdomain with no owners and experts + CreateDomain subDomainReq = + domainResourceTest + .createRequestWithoutOwnersExperts(getEntityName(test, 2)) + .withDomain(parentDomain.getFullyQualifiedName()); + Domain subDomain = domainResourceTest.createEntity(subDomainReq, ADMIN_AUTH_HEADERS); + subDomain = domainResourceTest.getEntity(subDomain.getId(), "*", ADMIN_AUTH_HEADERS); + + // Create data product corresponding to subdomain + CreateDataProduct subDomainDataProductCreate = + createRequestWithoutExpertsOwners(getEntityName(test, 2)) + .withDomain(subDomain.getFullyQualifiedName()); + DataProduct subDomainDataProduct = + createAndCheckEntity(subDomainDataProductCreate, ADMIN_AUTH_HEADERS); + + // Subdomain and its data product should inherit owners and experts from parent domain + assertOwners(subDomain.getOwners(), parentDomain.getOwners()); + assertEntityReferences(subDomain.getExperts(), parentDomain.getExperts()); + assertOwners(subDomainDataProduct.getOwners(), parentDomain.getOwners()); + assertEntityReferences(subDomainDataProduct.getExperts(), parentDomain.getExperts()); + + // Add owner and expert to subdomain + Domain updateSubDomainOwner = + JsonUtils.readValue(JsonUtils.pojoToJson(subDomain), Domain.class); + updateSubDomainOwner.setOwners(List.of(TEAM11_REF)); + domainResourceTest.patchEntity( + subDomain.getId(), + JsonUtils.pojoToJson(subDomain), + updateSubDomainOwner, + ADMIN_AUTH_HEADERS); + subDomain = domainResourceTest.getEntity(subDomain.getId(), "*", ADMIN_AUTH_HEADERS); + + Domain updateSubDomainExpert = + JsonUtils.readValue(JsonUtils.pojoToJson(subDomain), Domain.class); + updateSubDomainExpert.setExperts(List.of(USER1_REF)); + domainResourceTest.patchEntity( + subDomain.getId(), + JsonUtils.pojoToJson(subDomain), + updateSubDomainExpert, + ADMIN_AUTH_HEADERS); + subDomain = domainResourceTest.getEntity(subDomain.getId(), "*", ADMIN_AUTH_HEADERS); + + // Data product of subdomain should also have the same changes as its corresponding domain + assertOwners(subDomainDataProduct.getOwners(), subDomain.getOwners()); + assertEntityReferences(subDomainDataProduct.getExperts(), subDomain.getExperts()); + } + private void entityInDataProduct( EntityInterface entity, EntityInterface product, boolean inDataProduct) throws HttpResponseException { @@ -200,6 +270,15 @@ public CreateDataProduct createRequest(String name) { .withAssets(TEST_TABLE1 != null ? listOf(TEST_TABLE1.getEntityReference()) : null); } + public CreateDataProduct createRequestWithoutExpertsOwners(String name) { + return new CreateDataProduct() + .withName(name) + .withDescription(name) + .withDomain(DOMAIN.getFullyQualifiedName()) + .withStyle(new Style().withColor("#40E0D0").withIconURL("https://dataProductIcon")) + .withAssets(TEST_TABLE1 != null ? listOf(TEST_TABLE1.getEntityReference()) : null); + } + @Override public void validateCreatedEntity( DataProduct createdEntity, CreateDataProduct request, Map authHeaders) { diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/domains/DomainResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/domains/DomainResourceTest.java index 7054353f2f6c..f2d1e8540309 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/domains/DomainResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/domains/DomainResourceTest.java @@ -153,6 +153,14 @@ public CreateDomain createRequest(String name) { .withExperts(listOf(USER1.getFullyQualifiedName())); } + public CreateDomain createRequestWithoutOwnersExperts(String name) { + return new CreateDomain() + .withName(name) + .withDomainType(DomainType.AGGREGATE) + .withDescription("name") + .withStyle(new Style().withColor("#FFA07A").withIconURL("https://domainIcon")); + } + @Override public void validateCreatedEntity( Domain createdEntity, CreateDomain request, Map authHeaders) { From d8282502e4004f5200a9ffce8ca77f3bf4b81b9f Mon Sep 17 00:00:00 2001 From: karanh37 Date: Tue, 14 Jan 2025 15:59:52 +0530 Subject: [PATCH 2/2] add tests --- .../ui/playwright/e2e/Pages/Domains.spec.ts | 55 +++++++++++++++++++ .../ui/playwright/support/domain/Domain.ts | 4 +- .../ui/playwright/support/domain/SubDomain.ts | 4 +- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts index 528eda7b740c..c53c42e60b83 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts @@ -334,6 +334,61 @@ test.describe('Domains', () => { await afterAction(); } }); + + test('Should inherit owners and experts from parent domain', async ({ + page, + }) => { + const { afterAction, apiContext } = await getApiContext(page); + const user1 = new UserClass(); + const user2 = new UserClass(); + let domain; + let dataProduct; + try { + await user1.create(apiContext); + await user2.create(apiContext); + + domain = new Domain({ + name: 'PW_Domain_Inherit_Testing', + displayName: 'PW_Domain_Inherit_Testing', + description: 'playwright domain description', + domainType: 'Aggregate', + fullyQualifiedName: 'PW_Domain_Inherit_Testing', + owners: [ + { + name: user1.responseData.name, + type: 'user', + fullyQualifiedName: user1.responseData.fullyQualifiedName ?? '', + id: user1.responseData.id, + }, + ], + experts: [user2.responseData.name], + }); + dataProduct = new DataProduct(domain); + await domain.create(apiContext); + await dataProduct.create(apiContext); + + await page.reload(); + await redirectToHomePage(page); + + await sidebarClick(page, SidebarItem.DOMAIN); + await selectDomain(page, domain.data); + await selectDataProduct(page, domain.data, dataProduct.data); + + await expect( + page.getByTestId('domain-owner-name').getByTestId('owner-label') + ).toContainText(user1.responseData.displayName); + + await expect( + page.getByTestId('domain-expert-name').getByTestId('owner-label') + ).toContainText(user2.responseData.displayName); + } finally { + await dataProduct?.delete(apiContext); + await domain?.delete(apiContext); + await user1.delete(apiContext); + await user2.delete(apiContext); + await afterAction(); + } + }); }); test.describe('Domains Rbac', () => { diff --git a/openmetadata-ui/src/main/resources/ui/playwright/support/domain/Domain.ts b/openmetadata-ui/src/main/resources/ui/playwright/support/domain/Domain.ts index d5e760a47a85..e3cbee7ed962 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/support/domain/Domain.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/support/domain/Domain.ts @@ -16,6 +16,8 @@ import { uuid } from '../../utils/common'; type UserTeamRef = { name: string; type: string; + fullyQualifiedName?: string; + id?: string; }; type ResponseDataType = { @@ -26,7 +28,7 @@ type ResponseDataType = { id?: string; fullyQualifiedName?: string; owners?: UserTeamRef[]; - experts?: UserTeamRef[]; + experts?: string[]; }; export class Domain { diff --git a/openmetadata-ui/src/main/resources/ui/playwright/support/domain/SubDomain.ts b/openmetadata-ui/src/main/resources/ui/playwright/support/domain/SubDomain.ts index fb7c3c1fbd55..dabe1cafb841 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/support/domain/SubDomain.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/support/domain/SubDomain.ts @@ -17,6 +17,8 @@ import { Domain } from './Domain'; type UserTeamRef = { name: string; type: string; + fullyQualifiedName?: string; + id?: string; }; type ResponseDataType = { @@ -27,7 +29,7 @@ type ResponseDataType = { id?: string; fullyQualifiedName?: string; owners?: UserTeamRef[]; - experts?: UserTeamRef[]; + experts?: string[]; parent?: string; };