From d5989b304ae552462b17ed0b4adf98ae2acfe320 Mon Sep 17 00:00:00 2001 From: Ram Narayan Balaji <81347100+yan-3005@users.noreply.github.com> Date: Thu, 4 Sep 2025 22:39:20 +0530 Subject: [PATCH] Restore deleted entities will take inherited fields into consideration (#23255) * Restore deleted entities will take inherited fields into consideration * dummy commit (cherry picked from commit be5887eb009a99aec1ec9aca43268f88a8ffc046) --- .../service/jdbi3/EntityRepository.java | 1 + .../databases/TableResourceTest.java | 111 ++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java index eef21cd67e7..9aeae5e87eb 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java @@ -2578,6 +2578,7 @@ public abstract class EntityRepository { try { T original = find(id, DELETED); setFieldsInternal(original, putFields); + setInheritedFields(original, putFields); T updated = JsonUtils.readValue(JsonUtils.pojoToJson(original), entityClass); updated.setUpdatedBy(updatedBy); updated.setUpdatedAt(System.currentTimeMillis()); diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/databases/TableResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/databases/TableResourceTest.java index 35bdeb4f9aa..556e5c4239b 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/databases/TableResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/databases/TableResourceTest.java @@ -6126,4 +6126,115 @@ public class TableResourceTest extends EntityResourceTest { schemaTest.deleteEntity(schema.getId(), false, true, ADMIN_AUTH_HEADERS); dbTest.deleteEntity(db.getId(), false, true, ADMIN_AUTH_HEADERS); } + + @Test + void testTableSoftDeleteAndRestoreWithDataProducts(TestInfo test) throws IOException { + // This test verifies the fix for the issue where restoring a soft-deleted table + // with dataProducts fails with "Domain cannot be empty when data products are provided" + + // Step 1: Create a domain + DomainResourceTest domainTest = new DomainResourceTest(); + CreateDomain createDomain = domainTest.createRequest("test_domain_" + test.getDisplayName()); + Domain domain = domainTest.createEntity(createDomain, ADMIN_AUTH_HEADERS); + + // Step 2: Create a data product in that domain + DataProductResourceTest dataProductTest = new DataProductResourceTest(); + CreateDataProduct createDataProduct = + new CreateDataProduct() + .withName("test_dataproduct_" + test.getDisplayName()) + .withDescription("Test data product for soft delete/restore") + .withDomains(List.of(domain.getFullyQualifiedName())); + DataProduct dataProduct = dataProductTest.createEntity(createDataProduct, ADMIN_AUTH_HEADERS); + + // Step 3: Create a database + DatabaseResourceTest dbTest = new DatabaseResourceTest(); + Database db = + dbTest.createEntity( + dbTest.createRequest("test_restore_db_" + test.getDisplayName()), ADMIN_AUTH_HEADERS); + + // Step 4: Create a schema with the domain (schema inherits domain) + DatabaseSchemaResourceTest schemaTest = new DatabaseSchemaResourceTest(); + CreateDatabaseSchema createSchema = + schemaTest + .createRequest("test_restore_schema_" + test.getDisplayName()) + .withDatabase(db.getFullyQualifiedName()) + .withDomains(List.of(domain.getFullyQualifiedName())); + DatabaseSchema schema = schemaTest.createEntity(createSchema, ADMIN_AUTH_HEADERS); + + // Step 5: Create a table (inherits domain from schema) + // Use minimal table configuration - we only care about domain inheritance + CreateTable createTable = + createRequest(test).withDatabaseSchema(schema.getFullyQualifiedName()); + Table table = createEntity(createTable, ADMIN_AUTH_HEADERS); + + // Verify table inherited the domain from schema + Table tableWithDomain = getEntity(table.getId(), "domains,dataProducts", ADMIN_AUTH_HEADERS); + assertNotNull(tableWithDomain.getDomains()); + assertEquals(1, tableWithDomain.getDomains().size()); + assertEquals(domain.getId(), tableWithDomain.getDomains().getFirst().getId()); + + // Step 6: Add the table to the dataProduct + String originalJson = JsonUtils.pojoToJson(dataProduct); + dataProduct.setAssets(List.of(table.getEntityReference())); + ChangeDescription change = getChangeDescription(dataProduct, MINOR_UPDATE); + fieldAdded(change, "assets", List.of(table.getEntityReference())); + dataProduct = + dataProductTest.patchEntityAndCheck( + dataProduct, originalJson, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); + + // Verify table has the dataProduct + Table tableWithDataProduct = + getEntity(table.getId(), "domains,dataProducts", ADMIN_AUTH_HEADERS); + assertNotNull(tableWithDataProduct.getDataProducts()); + assertEquals(1, tableWithDataProduct.getDataProducts().size()); + assertEquals(dataProduct.getId(), tableWithDataProduct.getDataProducts().getFirst().getId()); + + // Step 7: Soft delete the table + deleteEntity(table.getId(), false, false, ADMIN_AUTH_HEADERS); + + // Verify table is soft deleted + assertResponse( + () -> getEntity(table.getId(), "domains,dataProducts", ADMIN_AUTH_HEADERS), + NOT_FOUND, + entityNotFound(entityType, table.getId())); + + // Step 8: Restore the table + // The issue was that during restore, the table has dataProducts but domains are not loaded + // (since domains are inherited from schema), causing validation to fail with: + // "Domain cannot be empty when data products are provided" + RestoreEntity restoreRequest = new RestoreEntity().withId(table.getId()); + Table restoredTable = restoreEntity(restoreRequest, Status.OK, ADMIN_AUTH_HEADERS); + + // Step 9: Verify the table is successfully restored with both domain and dataProducts + assertNotNull(restoredTable); + assertFalse(restoredTable.getDeleted()); + + Table fullyRestoredTable = getEntity(table.getId(), "domains,dataProducts", ADMIN_AUTH_HEADERS); + + // Verify domain is still present (inherited from schema) + assertNotNull(fullyRestoredTable.getDomains()); + assertEquals(1, fullyRestoredTable.getDomains().size()); + assertEquals(domain.getId(), fullyRestoredTable.getDomains().getFirst().getId()); + + // Verify dataProduct relationship is preserved + assertNotNull(fullyRestoredTable.getDataProducts()); + assertEquals(1, fullyRestoredTable.getDataProducts().size()); + assertEquals(dataProduct.getId(), fullyRestoredTable.getDataProducts().getFirst().getId()); + + // Additional verification: Check that the dataProduct still lists the table as an asset + DataProduct verifyDataProduct = + dataProductTest.getEntity(dataProduct.getId(), "assets", ADMIN_AUTH_HEADERS); + assertNotNull(verifyDataProduct.getAssets()); + assertTrue( + verifyDataProduct.getAssets().stream() + .anyMatch(asset -> asset.getId().equals(table.getId())), + "Table should still be an asset of the data product after restore"); + + // Cleanup: Hard delete all test entities + deleteEntity(table.getId(), false, true, ADMIN_AUTH_HEADERS); + schemaTest.deleteEntity(schema.getId(), false, true, ADMIN_AUTH_HEADERS); + dbTest.deleteEntity(db.getId(), false, true, ADMIN_AUTH_HEADERS); + dataProductTest.deleteEntity(dataProduct.getId(), false, true, ADMIN_AUTH_HEADERS); + domainTest.deleteEntity(domain.getId(), false, true, ADMIN_AUTH_HEADERS); + } }