From bda19964bdd97c078152f67f23e39399442dfb12 Mon Sep 17 00:00:00 2001 From: TeddyCr Date: Wed, 14 Oct 2026 16:13:33 -0700 Subject: [PATCH] Fixes #35485: Inherit owners on test suite and ingestion pipeline list reads TestSuiteRepository and IngestionPipelineRepository name their parent through getParentReference, which sends list reads down the bulk inheritance path. That path loads the parent with getInheritableFields() and applies applyInheritance(), whose defaults cover domains and tags only, so a listed basic suite lost its table's owners and a listed pipeline lost its service's owners. A GET by id still inherited them through the per-entity setInheritedFields. Load owners on the parent and inherit them in applyInheritance, as Table, DatabaseSchema and StoredProcedure already do. New ITs cover the test suite list, the test suite search/list used by the Data Quality page, and the pipeline list. --- .../IngestionPipelineOwnerInheritanceIT.java | 62 ++++++++++++++ .../it/tests/TestSuiteResourceIT.java | 85 +++++++++++++++++++ .../jdbi3/IngestionPipelineRepository.java | 12 +++ .../service/jdbi3/TestSuiteRepository.java | 12 +++ 4 files changed, 171 insertions(+) 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) {