From e7c0216b25d77eda0e298caab41dabc87a5437a6 Mon Sep 17 00:00:00 2001 From: Ram Narayan Balaji <81347100+yan-3005@users.noreply.github.com> Date: Thu, 19 Jun 2025 15:13:20 +0530 Subject: [PATCH] Fix:#21647 Inherited Tags from Glossary Term should be shown in put column response (#21832) * Fix: Inherited Tags from Glossary Term should be shown in put/patch column response * Placement of the statement fixed --------- Co-authored-by: sonika-shah <58761340+sonika-shah@users.noreply.github.com> --- .../service/jdbi3/ColumnRepository.java | 9 +- .../resources/columns/ColumnResourceTest.java | 121 +++++++++++++++++- 2 files changed, 126 insertions(+), 4 deletions(-) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ColumnRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ColumnRepository.java index cb47b68d61d..f9bd5c34a72 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ColumnRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ColumnRepository.java @@ -15,6 +15,7 @@ package org.openmetadata.service.jdbi3; import static org.openmetadata.service.Entity.DASHBOARD_DATA_MODEL; import static org.openmetadata.service.Entity.TABLE; +import static org.openmetadata.service.resources.tags.TagLabelUtil.addDerivedTags; import jakarta.json.JsonPatch; import jakarta.ws.rs.core.SecurityContext; @@ -123,7 +124,9 @@ public class ColumnRepository { } } if (updateColumn.getTags() != null) { - column.setTags(updateColumn.getTags()); // Empty array = remove all tags + column.setTags( + addDerivedTags( + updateColumn.getTags())); // Include Derived Tags, Empty array = remove all tags } // Handle constraint updates and removal if (updateColumn.getRemoveConstraint() != null && updateColumn.getRemoveConstraint()) { @@ -191,7 +194,9 @@ public class ColumnRepository { } } if (updateColumn.getTags() != null) { - column.setTags(updateColumn.getTags()); // Empty array = remove all tags + column.setTags( + addDerivedTags( + updateColumn.getTags())); // Include Derived Tags, Empty array = remove all tags } JsonPatch jsonPatch = JsonUtils.getJsonPatch(originalDataModel, updatedDataModel); diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/columns/ColumnResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/columns/ColumnResourceTest.java index b445c46b6ed..ea9a231524f 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/columns/ColumnResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/columns/ColumnResourceTest.java @@ -467,12 +467,13 @@ class ColumnResourceTest extends OpenMetadataApplicationTest { assertNotNull(updatedColumn.getTags()); assertEquals(2, updatedColumn.getTags().size()); + // Sorted order of glossary terms assertEquals( - businessTermsGlossaryTerm.getFullyQualifiedName(), + technicalTermsGlossaryTerm.getFullyQualifiedName(), updatedColumn.getTags().get(0).getTagFQN()); assertEquals(TagLabel.TagSource.GLOSSARY, updatedColumn.getTags().get(0).getSource()); assertEquals( - technicalTermsGlossaryTerm.getFullyQualifiedName(), + businessTermsGlossaryTerm.getFullyQualifiedName(), updatedColumn.getTags().get(1).getTagFQN()); assertEquals(TagLabel.TagSource.GLOSSARY, updatedColumn.getTags().get(1).getSource()); } @@ -1033,6 +1034,122 @@ class ColumnResourceTest extends OpenMetadataApplicationTest { assertNull(dimension1ColumnAfterDelete.getDescription()); } + @Test + void test_updateColumnWithDerivedTags(TestInfo test) throws IOException { + // Create a classification and tag + ClassificationResourceTest classificationTest = new ClassificationResourceTest(); + CreateClassification createClassification = + new CreateClassification() + .withName("Tier" + test.getDisplayName().replaceAll("[^A-Za-z0-9]", "")) + .withDescription("Tier classification for data quality"); + Classification tierClassification = + classificationTest.createEntity(createClassification, ADMIN_AUTH_HEADERS); + + TagResourceTest tagTest = new TagResourceTest(); + CreateTag createTag = + new CreateTag() + .withName("Tier1") + .withDescription("Tier 1 data") + .withClassification(tierClassification.getFullyQualifiedName()); + Tag tierTag = tagTest.createEntity(createTag, ADMIN_AUTH_HEADERS); + + // Create a glossary + GlossaryResourceTest glossaryTest = new GlossaryResourceTest(); + CreateGlossary createGlossary = + new CreateGlossary() + .withName("TestGlossaryTag" + test.getDisplayName().replaceAll("[^A-Za-z0-9]", "")) + .withDescription("Test glossary for derived tags"); + Glossary glossary = glossaryTest.createEntity(createGlossary, ADMIN_AUTH_HEADERS); + + // Create a glossary term with the tier tag + GlossaryTermResourceTest glossaryTermTest = new GlossaryTermResourceTest(); + CreateGlossaryTerm createGlossaryTerm = + new CreateGlossaryTerm() + .withName("CustomerData" + test.getDisplayName().replaceAll("[^A-Za-z0-9]", "")) + .withDescription("Customer data term") + .withGlossary(glossary.getFullyQualifiedName()) + .withTags( + List.of( + new TagLabel() + .withTagFQN(tierTag.getFullyQualifiedName()) + .withName(tierTag.getName()) + .withDisplayName(tierTag.getDisplayName()) + .withSource(TagLabel.TagSource.CLASSIFICATION) + .withState(TagLabel.State.CONFIRMED))); + GlossaryTerm glossaryTerm = + glossaryTermTest.createEntity(createGlossaryTerm, ADMIN_AUTH_HEADERS); + String columnFQN = FullyQualifiedName.add(table.getFullyQualifiedName(), "id"); + + // Update column with the glossary term tag + UpdateColumn updateColumnWithTags = + new UpdateColumn() + .withTags( + List.of( + new TagLabel() + .withTagFQN(glossaryTerm.getFullyQualifiedName()) + .withName(glossaryTerm.getName()) + .withDisplayName(glossaryTerm.getDisplayName()) + .withSource(TagLabel.TagSource.GLOSSARY) + .withState(TagLabel.State.CONFIRMED))); + + Column updatedColumnWithTags = updateColumnByFQN(columnFQN, updateColumnWithTags); + + // Verify that both the glossary term tag and its associated tier tag are present + assertNotNull(updatedColumnWithTags.getTags()); + assertEquals(2, updatedColumnWithTags.getTags().size()); + + // Verify the glossary term tag + TagLabel glossaryTag = + updatedColumnWithTags.getTags().stream() + .filter(tag -> tag.getSource() == TagLabel.TagSource.GLOSSARY) + .findFirst() + .orElse(null); + assertNotNull(glossaryTag); + assertEquals(glossaryTerm.getFullyQualifiedName(), glossaryTag.getTagFQN()); + assertEquals(TagLabel.LabelType.MANUAL, glossaryTag.getLabelType()); + + // Verify the derived tier tag + TagLabel derivedTag = + updatedColumnWithTags.getTags().stream() + .filter(tag -> tag.getSource() == TagLabel.TagSource.CLASSIFICATION) + .findFirst() + .orElse(null); + assertNotNull(derivedTag); + assertEquals(tierTag.getFullyQualifiedName(), derivedTag.getTagFQN()); + assertEquals(TagLabel.LabelType.DERIVED, derivedTag.getLabelType()); + + // Verify persistence by getting the table again + Table persistedTable = + tableResourceTest.getEntity(table.getId(), "columns,tags", ADMIN_AUTH_HEADERS); + Column persistedColumn = + persistedTable.getColumns().stream() + .filter(c -> c.getName().equals("id")) + .findFirst() + .orElseThrow(); + + // Verify tags are still present in persisted data + assertNotNull(persistedColumn.getTags()); + assertEquals(2, persistedColumn.getTags().size()); + + // Verify both manual and derived tags are present in persisted data + boolean hasGlossaryTag = + persistedColumn.getTags().stream() + .anyMatch( + tag -> + tag.getSource() == TagLabel.TagSource.GLOSSARY + && tag.getTagFQN().equals(glossaryTerm.getFullyQualifiedName())); + boolean hasDerivedTag = + persistedColumn.getTags().stream() + .anyMatch( + tag -> + tag.getSource() == TagLabel.TagSource.CLASSIFICATION + && tag.getTagFQN().equals(tierTag.getFullyQualifiedName()) + && tag.getLabelType() == TagLabel.LabelType.DERIVED); + + assertTrue(hasGlossaryTag, "Glossary tag should be present in persisted data"); + assertTrue(hasDerivedTag, "Derived tag should be present in persisted data"); + } + private Column updateColumnByFQN(String columnFQN, UpdateColumn updateColumn, String entityType) throws IOException { WebTarget target =