diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchClient.java b/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchClient.java index 2e185c03290e..3b6e251702f5 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchClient.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchClient.java @@ -179,6 +179,18 @@ public interface SearchClient } """; + // Cascade variant: full-object replace (handles add/update) plus removal on + // null params, so child docs stay in sync when a parent's cert is added, + // changed, or removed. + String CASCADE_CERTIFICATION_SCRIPT = + """ + if (params.certification == null) { + ctx._source.remove('certification'); + } else { + ctx._source.certification = params.certification; + } + """; + String UPDATE_GLOSSARY_TERM_TAG_FQN_BY_PREFIX_SCRIPT = """ if (ctx._source.containsKey('tags')) { diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java index 083cddf6e6a3..f64ce93fe83b 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java @@ -18,6 +18,7 @@ import static org.openmetadata.service.Entity.WEB_ANALYTIC_ENTITY_VIEW_REPORT_DATA; import static org.openmetadata.service.Entity.WEB_ANALYTIC_USER_ACTIVITY_REPORT_DATA; import static org.openmetadata.service.search.SearchClient.ADD_FOLLOWERS_SCRIPT; +import static org.openmetadata.service.search.SearchClient.CASCADE_CERTIFICATION_SCRIPT; import static org.openmetadata.service.search.SearchClient.DATA_ASSET_SEARCH_ALIAS; import static org.openmetadata.service.search.SearchClient.DEFAULT_UPDATE_SCRIPT; import static org.openmetadata.service.search.SearchClient.GLOBAL_SEARCH_ALIAS; @@ -1816,6 +1817,47 @@ private void handleEntityCertificationUpdate(EntityInterface entity, ChangeDescr AssetCertification certification = getCertificationFromEntity(entity); updateEntityCertificationInSearch(entity, certification); + cascadeCertificationToChildren(entity, certification); + } + + // Pushes the cert change onto every child search doc denormalized from this + // entity. Without this the cert filter on the DQ dashboard (which queries + // children like test_case/test_case_result/test_case_resolution_status by + // `certification.tagLabel.tagFQN`) would silently use the stale cert until a + // reindex. RAW_REPLACE in PropagationDescriptor can't be used because it + // restores the old value on delete; we drive a dedicated script instead. + private void cascadeCertificationToChildren( + EntityInterface entity, AssetCertification certification) { + String type = entity.getEntityReference().getType(); + if (!Entity.TABLE.equalsIgnoreCase(type)) { + // Scope: Table only. Dashboard/ApiCollection children also have cert in + // their mappings; extend here when those denormalization paths are added. + return; + } + IndexMapping indexMapping = entityIndexMap.get(Entity.TABLE); + if (indexMapping == null) { + return; + } + List childAliases = indexMapping.getChildAliases(clusterAlias); + if (nullOrEmpty(childAliases)) { + return; + } + + Map params = new HashMap<>(); + params.put("certification", certification); // null when cert was removed + + Pair parentMatch = new ImmutablePair<>("table.id", entity.getId().toString()); + + try { + searchClient.updateChildren( + childAliases, parentMatch, new ImmutablePair<>(CASCADE_CERTIFICATION_SCRIPT, params)); + } catch (Exception e) { + LOG.error( + "Failed to cascade certification for table [{}]: {}", + entity.getFullyQualifiedName(), + e.getMessage(), + e); + } } private boolean isCertificationUpdated(ChangeDescription change) { diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/ColumnSearchIndex.java b/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/ColumnSearchIndex.java index 1b9a76525611..6ab6deee288d 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/ColumnSearchIndex.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/ColumnSearchIndex.java @@ -135,6 +135,10 @@ public Map buildSearchIndexDocInternal(Map doc) } } + if (parentTable.getCertification() != null) { + doc.put("certification", parentTable.getCertification()); + } + if (column.getExtension() != null) { doc.put("extension", column.getExtension()); doc.put( diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestCaseIndex.java b/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestCaseIndex.java index e19a891024a0..522e226269b6 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestCaseIndex.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestCaseIndex.java @@ -73,8 +73,8 @@ public Map buildSearchIndexDocInternal(Map doc) } private void setParentRelationships(Map doc, TestCase testCase) { - // Denormalize parent relationships and inherit domains from the linked table. - // addTestSuiteParentEntityRelations already fetches the Table with "domains", + // Denormalize parent relationships and inherit domains/certification from the linked table. + // addTestSuiteParentEntityRelations already fetches the Table with these fields, // so we reuse it to avoid an extra DB query per test case. EntityInterface linkedTable = denormalizeTestSuiteParents(doc, testCase); @@ -83,6 +83,12 @@ private void setParentRelationships(Map doc, TestCase testCase) && !nullOrEmpty(linkedTable.getDomains())) { doc.put("domains", getEntitiesWithDisplayName(linkedTable.getDomains())); } + + if (testCase.getCertification() == null + && linkedTable != null + && linkedTable.getCertification() != null) { + doc.put("certification", linkedTable.getCertification()); + } } private EntityInterface denormalizeTestSuiteParents(Map doc, TestCase testCase) { diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestCaseResolutionStatusIndex.java b/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestCaseResolutionStatusIndex.java index b149e6b161ac..a7429b6e25ef 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestCaseResolutionStatusIndex.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestCaseResolutionStatusIndex.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.Map; +import org.openmetadata.schema.entity.data.Table; import org.openmetadata.schema.tests.TestCase; import org.openmetadata.schema.tests.TestSuite; import org.openmetadata.schema.tests.type.TestCaseResolutionStatus; @@ -62,7 +63,12 @@ private void setParentRelationships(Map doc) { if (testSuite == null) return; doc.put("testSuite", testSuite.getEntityReference()); if (testSuite.getBasicEntityReference() != null) { - TestSuiteIndex.addTestSuiteParentEntityRelations(testSuite.getBasicEntityReference(), doc); + Table linkedTable = + TestSuiteIndex.addTestSuiteParentEntityRelations( + testSuite.getBasicEntityReference(), doc); + if (linkedTable != null && linkedTable.getCertification() != null) { + doc.put("certification", linkedTable.getCertification()); + } } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestCaseResultIndex.java b/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestCaseResultIndex.java index edd8bdd1428a..2fe3b1a8ff90 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestCaseResultIndex.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestCaseResultIndex.java @@ -118,7 +118,7 @@ private void setTableEntityParentRelations( Entity.getEntityByName( Entity.TABLE, entityLink.getEntityFQN(), - "database,databaseSchema,service", + "database,databaseSchema,service,certification", Include.ALL); esDoc.put("database", table.getDatabase()); esDoc.put("databaseSchema", table.getDatabaseSchema()); @@ -127,6 +127,9 @@ private void setTableEntityParentRelations( esDoc.put("serviceType", table.getServiceType()); } esDoc.put("table", table.getEntityReference()); + if (table.getCertification() != null) { + esDoc.put("certification", table.getCertification()); + } } catch (EntityNotFoundException ex) { LOG.warn( "Table [{}] not found during search indexing: {}", diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestSuiteIndex.java b/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestSuiteIndex.java index 6cbfa4109c7f..c6f623ebc96a 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestSuiteIndex.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestSuiteIndex.java @@ -47,14 +47,17 @@ public Map buildSearchIndexDocInternal(Map doc) private void setParentRelationships(Map doc, TestSuite testSuite) { EntityReference entityReference = testSuite.getBasicEntityReference(); if (entityReference == null) return; - addTestSuiteParentEntityRelations(entityReference, doc); + Table linkedTable = addTestSuiteParentEntityRelations(entityReference, doc); + if (linkedTable != null && linkedTable.getCertification() != null) { + doc.put("certification", linkedTable.getCertification()); + } } static Table addTestSuiteParentEntityRelations( EntityReference testSuiteRef, Map doc) { if (testSuiteRef.getType().equals(Entity.TABLE)) { try { - Table table = Entity.getEntity(testSuiteRef, "domains", Include.ALL); + Table table = Entity.getEntity(testSuiteRef, "domains,certification", Include.ALL); doc.put("table", table.getEntityReference()); doc.put("database", table.getDatabase()); doc.put("databaseSchema", table.getDatabaseSchema()); diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/search/SearchRepositoryBehaviorTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/search/SearchRepositoryBehaviorTest.java index 0bc8e1bc61ba..41a76648a687 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/search/SearchRepositoryBehaviorTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/search/SearchRepositoryBehaviorTest.java @@ -849,6 +849,101 @@ void propagateCertificationTagsUsesOldFqnWhenTagRenamed() { assertEquals("Certification.Gold", keyCaptor.getValue().getRight()); } + @Test + void propagateCertificationTagsCascadesToTableChildrenOnAdd() throws IOException { + Table table = mock(Table.class); + UUID entityId = UUID.randomUUID(); + when(table.getId()).thenReturn(entityId); + when(table.getEntityReference()) + .thenReturn(new EntityReference().withId(entityId).withType(Entity.TABLE)); + AssetCertification cert = + new AssetCertification() + .withTagLabel( + new TagLabel() + .withName("Gold") + .withDescription("Certified") + .withTagFQN("Certification.Gold")); + when(table.getCertification()).thenReturn(cert); + + ChangeDescription changeDescription = + changeDescription( + List.of(), + List.of( + new FieldChange().withName("certification").withOldValue("{}").withNewValue("{}")), + List.of()); + + repository.propagateCertificationTags(Entity.TABLE, table, changeDescription); + + @SuppressWarnings("unchecked") + ArgumentCaptor>> updatesCaptor = + ArgumentCaptor.forClass(Pair.class); + @SuppressWarnings("unchecked") + ArgumentCaptor> matchCaptor = ArgumentCaptor.forClass(Pair.class); + verify(searchClient) + .updateChildren( + eq(List.of("cluster_column_search_index")), + matchCaptor.capture(), + updatesCaptor.capture()); + assertEquals("table.id", matchCaptor.getValue().getLeft()); + assertEquals(entityId.toString(), matchCaptor.getValue().getRight()); + assertEquals(SearchClient.CASCADE_CERTIFICATION_SCRIPT, updatesCaptor.getValue().getLeft()); + assertSame(cert, updatesCaptor.getValue().getRight().get("certification")); + } + + @Test + void propagateCertificationTagsCascadesNullToTableChildrenOnRemove() throws IOException { + Table table = mock(Table.class); + UUID entityId = UUID.randomUUID(); + when(table.getId()).thenReturn(entityId); + when(table.getEntityReference()) + .thenReturn(new EntityReference().withId(entityId).withType(Entity.TABLE)); + when(table.getCertification()).thenReturn(null); + + ChangeDescription changeDescription = + changeDescription( + List.of(), + List.of(), + List.of(new FieldChange().withName("certification").withOldValue("{}"))); + + repository.propagateCertificationTags(Entity.TABLE, table, changeDescription); + + @SuppressWarnings("unchecked") + ArgumentCaptor>> updatesCaptor = + ArgumentCaptor.forClass(Pair.class); + verify(searchClient) + .updateChildren( + eq(List.of("cluster_column_search_index")), any(Pair.class), updatesCaptor.capture()); + assertEquals(SearchClient.CASCADE_CERTIFICATION_SCRIPT, updatesCaptor.getValue().getLeft()); + assertNull(updatesCaptor.getValue().getRight().get("certification")); + } + + @Test + void propagateCertificationTagsDoesNotCascadeForNonTableEntities() throws IOException { + // Pipelines carry a native certification but DQ dashboard cascade is + // scoped to Table — children of Pipeline aren't part of the test_case + // family. Verify we don't blast an updateByQuery against unrelated + // child indices. + Pipeline pipeline = mock(Pipeline.class); + UUID entityId = UUID.randomUUID(); + when(pipeline.getId()).thenReturn(entityId); + when(pipeline.getEntityReference()) + .thenReturn(new EntityReference().withId(entityId).withType(Entity.PIPELINE)); + when(pipeline.getCertification()) + .thenReturn( + new AssetCertification().withTagLabel(new TagLabel().withTagFQN("Certification.Gold"))); + + ChangeDescription changeDescription = + changeDescription( + List.of(), + List.of( + new FieldChange().withName("certification").withOldValue("{}").withNewValue("{}")), + List.of()); + + repository.propagateCertificationTags(Entity.PIPELINE, pipeline, changeDescription); + + verify(searchClient, never()).updateChildren(any(List.class), any(Pair.class), any(Pair.class)); + } + @Test void propagateCertificationTagsUsesQuotedOldNameWhenTagHasNoParentFqn() { Tag tag = mock(Tag.class); diff --git a/openmetadata-spec/src/main/resources/elasticsearch/en/column_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/en/column_index_mapping.json index c8e6f2d55f30..51b337389a2a 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/en/column_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/en/column_index_mapping.json @@ -535,6 +535,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "classificationTags": { "type": "keyword", "normalizer": "lowercase_normalizer" diff --git a/openmetadata-spec/src/main/resources/elasticsearch/en/test_case_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/en/test_case_index_mapping.json index 1549ba155ae9..f79a581c4f11 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/en/test_case_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/en/test_case_index_mapping.json @@ -577,6 +577,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "classificationTags": { "type": "keyword", "normalizer": "lowercase_normalizer" diff --git a/openmetadata-spec/src/main/resources/elasticsearch/en/test_case_resolution_status_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/en/test_case_resolution_status_index_mapping.json index 98acac92da1a..1956e1220b29 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/en/test_case_resolution_status_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/en/test_case_resolution_status_index_mapping.json @@ -606,6 +606,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "testSuite": { "properties": { "id": { diff --git a/openmetadata-spec/src/main/resources/elasticsearch/en/test_case_result_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/en/test_case_result_index_mapping.json index 968c364dc536..d525c4a406c3 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/en/test_case_result_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/en/test_case_result_index_mapping.json @@ -471,6 +471,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "service": { "properties": { "id": { diff --git a/openmetadata-spec/src/main/resources/elasticsearch/en/test_suite_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/en/test_suite_index_mapping.json index 17eb61a01b04..a6c39d644076 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/en/test_suite_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/en/test_suite_index_mapping.json @@ -269,6 +269,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "classificationTags": { "type": "keyword", "normalizer": "lowercase_normalizer" diff --git a/openmetadata-spec/src/main/resources/elasticsearch/jp/column_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/jp/column_index_mapping.json index a9e41ccf43b7..ff1354c69910 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/jp/column_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/jp/column_index_mapping.json @@ -540,6 +540,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "classificationTags": { "type": "keyword", "normalizer": "lowercase_normalizer" diff --git a/openmetadata-spec/src/main/resources/elasticsearch/jp/test_case_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/jp/test_case_index_mapping.json index 13fdc4ad0a34..163b13e3390a 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/jp/test_case_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/jp/test_case_index_mapping.json @@ -300,6 +300,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "classificationTags": { "type": "keyword", "normalizer": "lowercase_normalizer" diff --git a/openmetadata-spec/src/main/resources/elasticsearch/jp/test_case_resolution_status_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/jp/test_case_resolution_status_index_mapping.json index 4a4e0f000291..c338987dc985 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/jp/test_case_resolution_status_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/jp/test_case_resolution_status_index_mapping.json @@ -430,6 +430,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "database": { "properties": { "id": { diff --git a/openmetadata-spec/src/main/resources/elasticsearch/jp/test_case_result_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/jp/test_case_result_index_mapping.json index 80f9b7eb2d0d..c3c4796fa655 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/jp/test_case_result_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/jp/test_case_result_index_mapping.json @@ -346,6 +346,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "testDefinition": { "properties": { "id": { diff --git a/openmetadata-spec/src/main/resources/elasticsearch/jp/test_suite_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/jp/test_suite_index_mapping.json index 260b00a787b5..669844135eaf 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/jp/test_suite_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/jp/test_suite_index_mapping.json @@ -248,6 +248,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "classificationTags": { "type": "keyword", "normalizer": "lowercase_normalizer" diff --git a/openmetadata-spec/src/main/resources/elasticsearch/ru/column_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/ru/column_index_mapping.json index 0230c20515a1..e9c81bc47c31 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/ru/column_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/ru/column_index_mapping.json @@ -554,6 +554,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "classificationTags": { "type": "keyword", "normalizer": "lowercase_normalizer" diff --git a/openmetadata-spec/src/main/resources/elasticsearch/ru/test_case_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/ru/test_case_index_mapping.json index 79d7c3cbfa78..9c3f656b5633 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/ru/test_case_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/ru/test_case_index_mapping.json @@ -593,6 +593,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "classificationTags": { "type": "keyword", "normalizer": "lowercase_normalizer" diff --git a/openmetadata-spec/src/main/resources/elasticsearch/ru/test_case_resolution_status_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/ru/test_case_resolution_status_index_mapping.json index b3636861b800..38eadd24e585 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/ru/test_case_resolution_status_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/ru/test_case_resolution_status_index_mapping.json @@ -625,6 +625,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "testSuite": { "properties": { "id": { diff --git a/openmetadata-spec/src/main/resources/elasticsearch/ru/test_case_result_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/ru/test_case_result_index_mapping.json index 227e716afa84..e667feb63890 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/ru/test_case_result_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/ru/test_case_result_index_mapping.json @@ -489,6 +489,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "service": { "properties": { "id": { diff --git a/openmetadata-spec/src/main/resources/elasticsearch/ru/test_suite_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/ru/test_suite_index_mapping.json index 2ac5a264ed1c..9b46935e7c16 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/ru/test_suite_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/ru/test_suite_index_mapping.json @@ -287,6 +287,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "classificationTags": { "type": "keyword", "normalizer": "lowercase_normalizer" diff --git a/openmetadata-spec/src/main/resources/elasticsearch/zh/column_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/zh/column_index_mapping.json index af6274af661b..9dcf5d175991 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/zh/column_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/zh/column_index_mapping.json @@ -532,6 +532,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "classificationTags": { "type": "keyword", "normalizer": "lowercase_normalizer" diff --git a/openmetadata-spec/src/main/resources/elasticsearch/zh/test_case_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/zh/test_case_index_mapping.json index 1bd7d33a4264..da7a503e9f66 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/zh/test_case_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/zh/test_case_index_mapping.json @@ -209,6 +209,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "classificationTags": { "type": "keyword", "normalizer": "lowercase_normalizer" diff --git a/openmetadata-spec/src/main/resources/elasticsearch/zh/test_case_resolution_status_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/zh/test_case_resolution_status_index_mapping.json index b7bba43008f4..b1d220eb110a 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/zh/test_case_resolution_status_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/zh/test_case_resolution_status_index_mapping.json @@ -420,6 +420,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "database": { "properties": { "id": { diff --git a/openmetadata-spec/src/main/resources/elasticsearch/zh/test_case_result_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/zh/test_case_result_index_mapping.json index 023d490e8387..905273b2debd 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/zh/test_case_result_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/zh/test_case_result_index_mapping.json @@ -345,6 +345,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "testDefinition": { "properties": { "id": { diff --git a/openmetadata-spec/src/main/resources/elasticsearch/zh/test_suite_index_mapping.json b/openmetadata-spec/src/main/resources/elasticsearch/zh/test_suite_index_mapping.json index 3aa7ad33dbbf..eb62699cc8e5 100644 --- a/openmetadata-spec/src/main/resources/elasticsearch/zh/test_suite_index_mapping.json +++ b/openmetadata-spec/src/main/resources/elasticsearch/zh/test_suite_index_mapping.json @@ -223,6 +223,46 @@ } } }, + "certification": { + "type": "object", + "properties": { + "tagLabel": { + "type": "object", + "properties": { + "tagFQN": { + "type": "keyword", + "normalizer": "lowercase_normalizer", + "fields": { + "text": { + "type": "text", + "analyzer": "om_analyzer" + } + } + }, + "labelType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "source": { + "type": "keyword" + }, + "state": { + "type": "keyword" + } + } + }, + "appliedDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "expiryDate": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + } + } + }, "classificationTags": { "type": "keyword", "normalizer": "lowercase_normalizer" diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DataQuality/DataQualityPage.interface.ts b/openmetadata-ui/src/main/resources/ui/src/pages/DataQuality/DataQualityPage.interface.ts index a8c46d4e718b..b994145a317f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DataQuality/DataQualityPage.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DataQuality/DataQualityPage.interface.ts @@ -36,6 +36,7 @@ export type DataQualityDashboardChartFilters = { ownerFqn?: string; tags?: string[]; tier?: string[]; + certification?: string[]; dataProductFqns?: string[]; startTs?: number; endTs?: number; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/DataQualityUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/DataQualityUtils.tsx index 7857aac46821..0f2c26dfd46f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/DataQualityUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/DataQualityUtils.tsx @@ -282,6 +282,16 @@ export const buildDataQualityDashboardFilters = (data: { }); } + if (filters?.certification) { + mustFilter.push({ + bool: { + should: filters.certification.map((fqn) => ({ + term: { 'certification.tagLabel.tagFQN': fqn }, + })), + }, + }); + } + if ((filters?.tags || filters?.tier) && !isTableApi) { mustFilter.push( buildMustEsFilterForTags([