diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/IngestionPipelineOwnerInheritanceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/IngestionPipelineOwnerInheritanceIT.java index 7b2536041827..3e91817342c9 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/IngestionPipelineOwnerInheritanceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/IngestionPipelineOwnerInheritanceIT.java @@ -41,6 +41,7 @@ import org.openmetadata.sdk.client.OpenMetadataClient; import org.openmetadata.sdk.exceptions.ForbiddenException; import org.openmetadata.sdk.exceptions.InvalidRequestException; +import org.openmetadata.sdk.models.ListParams; import org.openmetadata.sdk.network.HttpMethod; /** @@ -120,6 +121,67 @@ void test_inheritedOwners_fromService(TestNamespace ns) { } } + @Test + void test_listedPipelinesInheritServiceOwner(TestNamespace ns) { + OpenMetadataClient adminClient = SdkClients.adminClient(); + String unique = UUID.randomUUID().toString().substring(0, 8); + String userName = "iplistowner_" + unique; + User serviceOwner = + adminClient + .users() + .create( + new CreateUser().withName(userName).withEmail(userName + "@test.openmetadata.org")); + + try { + DashboardService service = DashboardServiceTestFactory.createMetabase(ns); + DashboardService fetchedService = + adminClient.dashboardServices().get(service.getId().toString()); + fetchedService.setOwners(List.of(serviceOwner.getEntityReference())); + adminClient.dashboardServices().update(service.getId().toString(), fetchedService); + + try { + IngestionPipeline pipeline = + adminClient + .ingestionPipelines() + .create( + new CreateIngestionPipeline() + .withName(ns.prefix("iplistPipeline")) + .withPipelineType(PipelineType.METADATA) + .withService(service.getEntityReference()) + .withSourceConfig( + new SourceConfig().withConfig(new DashboardServiceMetadataPipeline())) + .withAirflowConfig(new AirflowConfig().withStartDate(START_DATE))); + + try { + ListParams params = + new ListParams().setFields("owners").setService(service.getFullyQualifiedName()); + IngestionPipeline listed = + adminClient.ingestionPipelines().list(params).getData().stream() + .filter(candidate -> candidate.getId().equals(pipeline.getId())) + .findFirst() + .orElseThrow(() -> new AssertionError("Pipeline missing from its service list")); + + List owners = listed.getOwners(); + assertNotNull(owners, "Listed pipeline should carry its service's owners"); + assertTrue( + owners.stream().anyMatch(owner -> serviceOwner.getId().equals(owner.getId())), + "Service owner missing from listed pipeline owners " + owners); + assertTrue( + owners.stream().allMatch(owner -> Boolean.TRUE.equals(owner.getInherited())), + "Owners inherited from the service must be marked inherited=true: " + owners); + } finally { + adminClient.ingestionPipelines().delete(pipeline.getId().toString()); + } + } finally { + adminClient + .dashboardServices() + .delete(service.getId().toString(), Map.of("hardDelete", "true", "recursive", "true")); + } + } finally { + adminClient.users().delete(serviceOwner.getId()); + } + } + @Test void test_isOwnerPolicy_appliesToEditTriggerDeployAndToggle(TestNamespace ns) { OpenMetadataClient adminClient = SdkClients.adminClient(); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestSuiteResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestSuiteResourceIT.java index 3010295fd143..b8cb824b54bd 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestSuiteResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestSuiteResourceIT.java @@ -613,6 +613,25 @@ void test_inheritOwnerFromTable(TestNamespace ns) { assertEquals(testUser1().getId(), fetchedSuite.getOwners().get(0).getId()); } + @Test + void list_basicSuiteInheritsTableOwner(TestNamespace ns) { + TestSuite testSuite = createBasicTestSuiteForOwnedTable(ns, "list_owner_table"); + + assertInheritedTableOwner(findInBasicSuiteList(testSuite)); + } + + @Test + void searchList_basicSuiteInheritsTableOwner(TestNamespace ns) { + OpenMetadataClient client = SdkClients.adminClient(); + TestSuite testSuite = createBasicTestSuiteForOwnedTable(ns, "search_list_owner_table"); + + Awaitility.await("search list returns the basic suite with its table's owner") + .atMost(SEARCH_CONVERGENCE_TIMEOUT) + .pollInterval(Duration.ofSeconds(2)) + .untilAsserted( + () -> assertInheritedTableOwner(searchBasicTestSuiteWithOwners(client, testSuite))); + } + @Test void test_inheritDomainFromTable(TestNamespace ns) { OpenMetadataClient client = SdkClients.adminClient(); @@ -1409,6 +1428,72 @@ private ListResponse listTestSuites(Map params) { } } + private TestSuite createBasicTestSuiteForOwnedTable(TestNamespace ns, String tableName) { + Table table = createTableForBasicTestSuite(ns, tableName); + table.setOwners(List.of(testUser1Ref())); + Table ownedTable = SdkClients.adminClient().tables().update(table.getId().toString(), table); + return createBasicTestSuiteForTable(ownedTable); + } + + /** The DB list has no name filter, so page through basic suites until this one shows up. */ + private TestSuite findInBasicSuiteList(TestSuite testSuite) { + ListParams params = + new ListParams() + .setFields("owners") + .setLimit(100) + .addFilter("testSuiteType", "basic") + .addFilter("includeEmptyTestSuites", "true"); + while (true) { + ListResponse page = SdkClients.adminClient().testSuites().list(params); + for (TestSuite suite : page.getData()) { + if (suite.getId().equals(testSuite.getId())) { + return suite; + } + } + String after = page.getPaging() == null ? null : page.getPaging().getAfter(); + if (after == null) { + throw new AssertionError("Basic suite missing from list: " + testSuite.getId()); + } + params.setAfter(after); + } + } + + private TestSuite searchBasicTestSuiteWithOwners(OpenMetadataClient client, TestSuite testSuite) + throws Exception { + String encodedFqn = + java.net.URLEncoder.encode(testSuite.getFullyQualifiedName(), StandardCharsets.UTF_8); + TestSuiteResource.TestSuiteList result = + client + .getHttpClient() + .execute( + HttpMethod.GET, + "/v1/dataQuality/testSuites/search/list" + + "?fields=owners&limit=10&offset=0&includeEmptyTestSuites=true" + + "&testSuiteType=basic&fullyQualifiedName=" + + encodedFqn, + null, + TestSuiteResource.TestSuiteList.class, + RequestOptions.builder().build()); + return result.getData().stream() + .filter(suite -> suite.getId().equals(testSuite.getId())) + .findFirst() + .orElseThrow(); + } + + private void assertInheritedTableOwner(TestSuite listedSuite) { + List owners = listedSuite.getOwners(); + assertNotNull(owners, "basic suite should carry its table's owners"); + assertAll( + () -> + assertTrue( + owners.stream().anyMatch(owner -> testUser1().getId().equals(owner.getId())), + "table owner missing from " + owners), + () -> + assertTrue( + owners.stream().allMatch(owner -> Boolean.TRUE.equals(owner.getInherited())), + "owners should be marked inherited: " + owners)); + } + private TestSuite createBasicTestSuiteForTable(Table table) { CreateTestSuite request = new CreateTestSuite(); request.setName(table.getFullyQualifiedName()); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/IngestionPipelineRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/IngestionPipelineRepository.java index 72ae514f81d0..60967d1e366d 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/IngestionPipelineRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/IngestionPipelineRepository.java @@ -896,6 +896,18 @@ protected EntityReference getParentReference(IngestionPipeline entity) { return entity.getService(); } + @Override + protected String getInheritableFields() { + return "owners,domains"; + } + + @Override + protected void applyInheritance( + IngestionPipeline entity, Fields fields, EntityInterface parent) { + inheritOwners(entity, fields, parent); + super.applyInheritance(entity, fields, parent); + } + @Override public EntityInterface getParentEntity(IngestionPipeline entity, String fields) { if (entity.getService() == null) { diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TestSuiteRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TestSuiteRepository.java index c3800cced610..7e64a69c829f 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TestSuiteRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TestSuiteRepository.java @@ -338,6 +338,18 @@ protected EntityReference getParentReference(TestSuite entity) { return entity.getBasic() ? entity.getBasicEntityReference() : null; } + @Override + protected String getInheritableFields() { + return "owners,domains"; + } + + @Override + protected void applyInheritance( + TestSuite entity, EntityUtil.Fields fields, EntityInterface parent) { + inheritOwners(entity, fields, parent); + super.applyInheritance(entity, fields, parent); + } + @Override public EntityInterface getParentEntity(TestSuite entity, String fields) { if (entity.getBasic() && entity.getBasicEntityReference() != null) {