From 86c3ae30f12992ad9d77e9afa428cb150294ee83 Mon Sep 17 00:00:00 2001 From: Suresh Srinivas Date: Thu, 10 Nov 2022 16:47:21 -0800 Subject: [PATCH] Fixes #8596 Introduce mutually exclusive tags and glossary terms (#8597) --- .../v006__create_db_connection_info.sql | 18 +++++ .../v006__create_db_connection_info.sql | 20 ++++- .../ingestion/source/dashboard/tableau.py | 1 - .../ingestion/source/database/bigquery.py | 1 - .../source/database/database_service.py | 1 - .../ingestion/source/database/snowflake.py | 1 - .../ingestion/source/metadata/amundsen.py | 3 - .../ingestion/source/pipeline/dagster.py | 1 - .../ometa/test_ometa_tags_mixin.py | 4 +- .../schemas/api/tags/createTagCategory.md | 1 - .../schemas/entity/tags/tagCategory.md | 2 - .../service/jdbi3/TagCategoryRepository.java | 3 +- .../service/jdbi3/TagRepository.java | 3 + .../service/resources/tags/TagResource.java | 5 +- .../elasticsearch/tag_index_mapping.json | 3 - .../json/data/tags/personalDataTags.json | 2 +- .../resources/json/data/tags/piiTags.json | 2 +- .../resources/json/data/tags/tierTags.json | 2 +- .../resources/tags/TagResourceTest.java | 75 +++---------------- .../json/schema/api/tags/createTag.json | 5 ++ .../schema/api/tags/createTagCategory.json | 8 +- .../json/schema/entity/data/glossary.json | 7 +- .../json/schema/entity/data/glossaryTerm.json | 5 ++ .../json/schema/entity/tags/tagCategory.json | 30 +++----- .../DashboardDetails.test.tsx | 2 - .../EntityTable/EntityTable.test.tsx | 2 - .../common/TierCard/TierCard.test.tsx | 1 - .../entityPageInfo/EntityPageInfo.test.tsx | 2 - .../resources/ui/src/pages/tags/Form.test.tsx | 3 - .../main/resources/ui/src/pages/tags/Form.tsx | 21 ------ .../ui/src/pages/tags/index.test.tsx | 4 - .../resources/ui/src/pages/tags/index.tsx | 8 +- .../resources/ui/src/pages/tags/tagsTypes.ts | 1 - 33 files changed, 95 insertions(+), 152 deletions(-) diff --git a/bootstrap/sql/com.mysql.cj.jdbc.Driver/v006__create_db_connection_info.sql b/bootstrap/sql/com.mysql.cj.jdbc.Driver/v006__create_db_connection_info.sql index c261eb5853f..b2e959c056a 100644 --- a/bootstrap/sql/com.mysql.cj.jdbc.Driver/v006__create_db_connection_info.sql +++ b/bootstrap/sql/com.mysql.cj.jdbc.Driver/v006__create_db_connection_info.sql @@ -1,3 +1,6 @@ +-- +-- Upgrade changes for 0.13 +-- CREATE TABLE IF NOT EXISTS web_analytic_event ( id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') NOT NULL, name VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.name') NOT NULL, @@ -53,6 +56,21 @@ UPDATE pipeline_service_entity SET json = JSON_REMOVE(json ,'$.connection.config.hostPort', '$.connection.config.numberOfStatus') WHERE serviceType = 'Dagster'; +-- Remove categoryType +UPDATE tag_category +SET json = JSON_REMOVE(json ,'$.categoryType'); + +-- Set mutuallyExclusive flag +UPDATE tag_category +SET json = JSON_INSERT(json ,'$.mutuallyExclusive', 'false'); + +UPDATE tag_category +SET json = JSON_INSERT(json ,'$.mutuallyExclusive', 'true') +WHERE name in ('PersonalData', 'PII', 'Tier'); + +UPDATE tag +SET json = JSON_INSERT(json ,'$.mutuallyExclusive', 'false'); + CREATE TABLE IF NOT EXISTS kpi_entity ( id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL, name VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.name') NOT NULL, diff --git a/bootstrap/sql/org.postgresql.Driver/v006__create_db_connection_info.sql b/bootstrap/sql/org.postgresql.Driver/v006__create_db_connection_info.sql index ab82d5b77a7..70754c9c985 100644 --- a/bootstrap/sql/org.postgresql.Driver/v006__create_db_connection_info.sql +++ b/bootstrap/sql/org.postgresql.Driver/v006__create_db_connection_info.sql @@ -1,3 +1,6 @@ +-- +-- Upgrade changes for 0.13 +-- CREATE TABLE IF NOT EXISTS web_analytic_event ( id VARCHAR(36) GENERATED ALWAYS AS (json ->> 'id') STORED NOT NULL, name VARCHAR(256) GENERATED ALWAYS AS (json ->> 'name') STORED NOT NULL, @@ -51,13 +54,28 @@ WHERE fullyQualifiedName in ('PersonalData.Personal', 'PersonalData.SpecialCateg 'Tier.Tier1', 'Tier.Tier2', 'Tier.Tier3', 'Tier.Tier4', 'Tier.Tier5'); UPDATE pipeline_service_entity -SET json = jsonb_set(json::jsonb,'{connection,config}',json::jsonb #>'{connection,config}' || jsonb_build_object('configSource',jsonb_build_object('hostPort',json #>'{connection,config,hostPort}')), true) +SET json = JSONB_SET(json::jsonb,'{connection,config}',json::jsonb #>'{connection,config}' || jsonb_build_object('configSource',jsonb_build_object('hostPort',json #>'{connection,config,hostPort}')), true) where servicetype = 'Dagster'; UPDATE pipeline_service_entity SET json = json::jsonb #- '{connection,config,hostPort}' #- '{connection,config,numberOfStatus}' where servicetype = 'Dagster'; +-- Remove categoryType +UPDATE tag_category +SET json = json::jsonb #- '{categoryType}'; + +-- set mutuallyExclusive flag +UPDATE tag_category +SET json = jsonb_set(json, '{mutuallyExclusive}', 'false'::jsonb, true); + +UPDATE tag_category +SET json = jsonb_set(json, '{mutuallyExclusive}', 'true'::jsonb, true) +WHERE name in ('PersonalData', 'PII', 'Tier'); + +UPDATE tag +SET json = jsonb_set(json, '{mutuallyExclusive}', 'false'::jsonb, true); + CREATE TABLE IF NOT EXISTS kpi_entity ( id VARCHAR(36) GENERATED ALWAYS AS (json ->> 'id') STORED NOT NULL, name VARCHAR(256) GENERATED ALWAYS AS (json ->> 'name') STORED NOT NULL, diff --git a/ingestion/src/metadata/ingestion/source/dashboard/tableau.py b/ingestion/src/metadata/ingestion/source/dashboard/tableau.py index fd011b37c7e..1dba041df36 100644 --- a/ingestion/src/metadata/ingestion/source/dashboard/tableau.py +++ b/ingestion/src/metadata/ingestion/source/dashboard/tableau.py @@ -194,7 +194,6 @@ class TableauSource(DashboardServiceSource): category_name=CreateTagCategoryRequest( name=TABLEAU_TAG_CATEGORY, description="Tags associates with tableau entities", - categoryType="Descriptive", ), category_details=CreateTagRequest(name=tag, description="Tableau Tag"), ) diff --git a/ingestion/src/metadata/ingestion/source/database/bigquery.py b/ingestion/src/metadata/ingestion/source/database/bigquery.py index c73abd38f8e..8d8e18bf8e7 100644 --- a/ingestion/src/metadata/ingestion/source/database/bigquery.py +++ b/ingestion/src/metadata/ingestion/source/database/bigquery.py @@ -159,7 +159,6 @@ class BigquerySource(CommonDbSourceService): category_name=CreateTagCategoryRequest( name=self.service_connection.tagCategoryName, description="", - categoryType="Classification", ), category_details=CreateTagRequest( name=tag.display_name, description="Bigquery Policy Tag" diff --git a/ingestion/src/metadata/ingestion/source/database/database_service.py b/ingestion/src/metadata/ingestion/source/database/database_service.py index d4536280030..d9a5fb7a94f 100644 --- a/ingestion/src/metadata/ingestion/source/database/database_service.py +++ b/ingestion/src/metadata/ingestion/source/database/database_service.py @@ -407,7 +407,6 @@ class DatabaseServiceSource( category_name=CreateTagCategoryRequest( name="DBTTags", description="", - categoryType="Classification", ), category_details=CreateTagRequest( name=tag_label.tagFQN.__root__.split(".")[1], diff --git a/ingestion/src/metadata/ingestion/source/database/snowflake.py b/ingestion/src/metadata/ingestion/source/database/snowflake.py index c9e5434c919..eb91d50500d 100644 --- a/ingestion/src/metadata/ingestion/source/database/snowflake.py +++ b/ingestion/src/metadata/ingestion/source/database/snowflake.py @@ -308,7 +308,6 @@ class SnowflakeSource(CommonDbSourceService): category_name=CreateTagCategoryRequest( name=row[0], description="SNOWFLAKE TAG NAME", - categoryType="Descriptive", ), category_details=CreateTagRequest( name=row[1], description="SNOWFLAKE TAG VALUE" diff --git a/ingestion/src/metadata/ingestion/source/metadata/amundsen.py b/ingestion/src/metadata/ingestion/source/metadata/amundsen.py index 248f89db93e..6974ee13914 100644 --- a/ingestion/src/metadata/ingestion/source/metadata/amundsen.py +++ b/ingestion/src/metadata/ingestion/source/metadata/amundsen.py @@ -251,7 +251,6 @@ class AmundsenSource(Source[Entity]): category_name=CreateTagCategoryRequest( name=AMUNDSEN_TAG_CATEGORY, description="Tags associates with amundsen entities", - categoryType="Descriptive", ), category_details=CreateTagRequest( name=tag, description="Amundsen Table Tag" @@ -351,7 +350,6 @@ class AmundsenSource(Source[Entity]): category_name=CreateTagCategoryRequest( name=AMUNDSEN_TAG_CATEGORY, description="Tags associates with amundsen entities", - categoryType="Descriptive", ), category_details=CreateTagRequest( name=AMUNDSEN_TABLE_TAG, description="Amundsen Table Tag" @@ -362,7 +360,6 @@ class AmundsenSource(Source[Entity]): category_name=CreateTagCategoryRequest( name=AMUNDSEN_TAG_CATEGORY, description="Tags associates with amundsen entities", - categoryType="Descriptive", ), category_details=CreateTagRequest( name=table["cluster"], description="Amundsen Cluster Tag" diff --git a/ingestion/src/metadata/ingestion/source/pipeline/dagster.py b/ingestion/src/metadata/ingestion/source/pipeline/dagster.py index 319a075d0fb..1a54ad9184b 100644 --- a/ingestion/src/metadata/ingestion/source/pipeline/dagster.py +++ b/ingestion/src/metadata/ingestion/source/pipeline/dagster.py @@ -172,7 +172,6 @@ class DagsterSource(PipelineServiceSource): category_name=CreateTagCategoryRequest( name="DagsterTags", description="Tags associated with dagster", - categoryType="Descriptive", ), category_details=CreateTagRequest( name=self.context.repository_name, description="Dagster Tag" diff --git a/ingestion/tests/integration/ometa/test_ometa_tags_mixin.py b/ingestion/tests/integration/ometa/test_ometa_tags_mixin.py index 1b9aab8309b..2e6537e0a1b 100644 --- a/ingestion/tests/integration/ometa/test_ometa_tags_mixin.py +++ b/ingestion/tests/integration/ometa/test_ometa_tags_mixin.py @@ -42,7 +42,7 @@ class OMetaTagMixinPost(TestCase): """Test POST category Mixin method""" tag_category = CreateTagCategoryRequest( - categoryType="Descriptive", description="test tag", name=CATEGORY_NAME + description="test tag", name=CATEGORY_NAME ) self.metadata.create_tag_category(tag_category) @@ -144,7 +144,7 @@ class OMetaTagMixinPut(TestCase): rand_name = random.getrandbits(64) updated_tag_category = CreateTagCategoryRequest( - categoryType="Descriptive", description="test tag", name=f"{rand_name}" + description="test tag", name=f"{rand_name}" ) self.metadata.create_or_update_tag_category(CATEGORY_NAME, updated_tag_category) diff --git a/openmetadata-docs/content/main-concepts/metadata-standard/schemas/api/tags/createTagCategory.md b/openmetadata-docs/content/main-concepts/metadata-standard/schemas/api/tags/createTagCategory.md index 96cb77b3712..9db44f264b2 100644 --- a/openmetadata-docs/content/main-concepts/metadata-standard/schemas/api/tags/createTagCategory.md +++ b/openmetadata-docs/content/main-concepts/metadata-standard/schemas/api/tags/createTagCategory.md @@ -12,7 +12,6 @@ slug: /main-concepts/metadata-standard/schemas/api/tags/createtagcategory - **`name`**: Refer to *../../entity/tags/tagCategory.json#/definitions/tagName*. - **`displayName`** *(string)*: Display Name that identifies this tag category. - **`description`**: Description of the tag category. Refer to *../../type/basic.json#/definitions/markdown*. -- **`categoryType`**: Refer to *../../entity/tags/tagCategory.json#/definitions/tagCategoryType*. Documentation file automatically generated at 2022-07-14 10:51:34.749986. diff --git a/openmetadata-docs/content/main-concepts/metadata-standard/schemas/entity/tags/tagCategory.md b/openmetadata-docs/content/main-concepts/metadata-standard/schemas/entity/tags/tagCategory.md index cd5fa9b0e05..dc7c10a3e86 100644 --- a/openmetadata-docs/content/main-concepts/metadata-standard/schemas/entity/tags/tagCategory.md +++ b/openmetadata-docs/content/main-concepts/metadata-standard/schemas/entity/tags/tagCategory.md @@ -17,7 +17,6 @@ slug: /main-concepts/metadata-standard/schemas/entity/tags/tagcategory - **`version`**: Metadata version of the entity. Refer to *../../type/entityHistory.json#/definitions/entityVersion*. - **`updatedAt`**: Last update time corresponding to the new version of the entity in Unix epoch time milliseconds. Refer to *../../type/basic.json#/definitions/timestamp*. - **`updatedBy`** *(string)*: User who made the update. -- **`categoryType`**: Refer to *#/definitions/tagCategoryType*. - **`href`**: Link to the resource corresponding to the tag category. Refer to *../../type/basic.json#/definitions/href*. - **`usageCount`** *(integer)*: Count of how many times the tags from this tag category are used. - **`children`** *(array)*: Tags under this category. @@ -27,7 +26,6 @@ slug: /main-concepts/metadata-standard/schemas/entity/tags/tagcategory ## Definitions - **`tagName`** *(string)*: Name of the tag. -- **`tagCategoryType`** *(string)*: Type of tag category. Must be one of: `['Descriptive', 'Classification']`. - **`tag`**: Cannot contain additional properties. - **`id`**: Unique identifier of this entity instance. Refer to *../../type/basic.json#/definitions/uuid*. - **`name`**: Name of the tag. Refer to *#/definitions/tagName*. diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TagCategoryRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TagCategoryRepository.java index 6e115657e44..fd60f02e405 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TagCategoryRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TagCategoryRepository.java @@ -135,7 +135,8 @@ public class TagCategoryRepository extends EntityRepository { @Override public void entitySpecificUpdate() throws IOException { // TODO handle name change - recordChange("categoryType", original.getCategoryType(), updated.getCategoryType()); + // TODO mutuallyExclusive from false to true? + recordChange("mutuallyExclusive", original.getMutuallyExclusive(), updated.getMutuallyExclusive()); updateName(original, updated); } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TagRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TagRepository.java index 983bc5e5cc7..70d6e964b59 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TagRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TagRepository.java @@ -144,6 +144,9 @@ public class TagRepository extends EntityRepository { @Override public void entitySpecificUpdate() throws IOException { + // TODO mutuallyExclusive from false to true? + recordChange("mutuallyExclusive", original.getMutuallyExclusive(), updated.getMutuallyExclusive()); + // TODO check the below updateName(original, updated); } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java index 36c594c0d5b..f3e74808415 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java @@ -551,7 +551,7 @@ public class TagResource { .withId(UUID.randomUUID()) .withName(create.getName()) .withFullyQualifiedName(create.getName()) - .withCategoryType(create.getCategoryType()) + .withMutuallyExclusive(create.getMutuallyExclusive()) .withDescription(create.getDescription()) .withUpdatedBy(securityContext.getUserPrincipal().getName()) .withUpdatedAt(System.currentTimeMillis()); @@ -564,6 +564,7 @@ public class TagResource { .withFullyQualifiedName(FullyQualifiedName.add(parentFQN, create.getName())) .withDescription(create.getDescription()) .withUpdatedBy(securityContext.getUserPrincipal().getName()) - .withUpdatedAt(System.currentTimeMillis()); + .withUpdatedAt(System.currentTimeMillis()) + .withMutuallyExclusive(create.getMutuallyExclusive()); } } diff --git a/openmetadata-service/src/main/resources/elasticsearch/tag_index_mapping.json b/openmetadata-service/src/main/resources/elasticsearch/tag_index_mapping.json index de780699996..55fa67a9539 100644 --- a/openmetadata-service/src/main/resources/elasticsearch/tag_index_mapping.json +++ b/openmetadata-service/src/main/resources/elasticsearch/tag_index_mapping.json @@ -55,9 +55,6 @@ "href": { "type": "text" }, - "categoryType": { - "type": "text" - }, "deleted": { "type": "text" }, diff --git a/openmetadata-service/src/main/resources/json/data/tags/personalDataTags.json b/openmetadata-service/src/main/resources/json/data/tags/personalDataTags.json index 545e370f73f..fe4d19d3b57 100644 --- a/openmetadata-service/src/main/resources/json/data/tags/personalDataTags.json +++ b/openmetadata-service/src/main/resources/json/data/tags/personalDataTags.json @@ -1,8 +1,8 @@ { "name": "PersonalData", - "categoryType": "Classification", "description": "Tags related classifying **Personal data** as defined by **GDPR.**

_Note to Legal - This tag category is provided as a starting point. Please review and update the tags based on your company policy. Also, add a reference to your GDPR policy document in this description._", "provider": "system", + "mutuallyExclusive": "true", "children": [ { "name": "Personal", diff --git a/openmetadata-service/src/main/resources/json/data/tags/piiTags.json b/openmetadata-service/src/main/resources/json/data/tags/piiTags.json index c1e45ec290b..9d1b953427e 100644 --- a/openmetadata-service/src/main/resources/json/data/tags/piiTags.json +++ b/openmetadata-service/src/main/resources/json/data/tags/piiTags.json @@ -1,8 +1,8 @@ { "name": "PII", - "categoryType": "Classification", "description": "Personally Identifiable Information information that, when used alone or with other relevant data, can identify an individual.

_Note to Legal - This tag category is provided as a starting point. Please review and update the tags based on your company policy. Also, add a reference to your PII policy document in this description._", "provider": "system", + "mutuallyExclusive": "true", "children": [ { "name": "None", diff --git a/openmetadata-service/src/main/resources/json/data/tags/tierTags.json b/openmetadata-service/src/main/resources/json/data/tags/tierTags.json index 0ef0f420a5a..fcc58ce8a13 100644 --- a/openmetadata-service/src/main/resources/json/data/tags/tierTags.json +++ b/openmetadata-service/src/main/resources/json/data/tags/tierTags.json @@ -1,8 +1,8 @@ { "name": "Tier", - "categoryType": "Descriptive", "description": "Tags related to tiering of the data. Tiers capture the business importance of data. When a data asset is tagged with `Tier` tag, all the upstream data assets used for producing it will also be labeled with the same tag. This will help upstream data asset owners to understand the critical purposes their data is being used.", "provider": "system", + "mutuallyExclusive": "true", "children": [ { "name": "Tier1", diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/tags/TagResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/tags/TagResourceTest.java index d2c2b944152..3d22df3d045 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/tags/TagResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/tags/TagResourceTest.java @@ -45,7 +45,6 @@ import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.TestMethodOrder; import org.openmetadata.schema.api.tags.CreateTag; import org.openmetadata.schema.api.tags.CreateTagCategory; -import org.openmetadata.schema.api.tags.CreateTagCategory.TagCategoryType; import org.openmetadata.schema.entity.tags.Tag; import org.openmetadata.schema.type.TagCategory; import org.openmetadata.schema.type.TagLabel; @@ -80,11 +79,7 @@ public class TagResourceTest extends OpenMetadataApplicationTest { EntityResourceTest.TIER1_TAG_LABEL = getTagLabel(FullyQualifiedName.add("Tier", "Tier1")); EntityResourceTest.TIER2_TAG_LABEL = getTagLabel(FullyQualifiedName.add("Tier", "Tier2")); - CreateTagCategory create = - new CreateTagCategory() - .withName("User") - .withDescription("description") - .withCategoryType(TagCategoryType.Descriptive); + CreateTagCategory create = new CreateTagCategory().withName("User").withDescription("description"); USER_TAG_CATEGORY = tagResourceTest.createAndCheckCategory(create, ADMIN_AUTH_HEADERS); List associatedTags = new ArrayList<>(); @@ -156,10 +151,7 @@ public class TagResourceTest extends OpenMetadataApplicationTest { void post_alreadyExistingTagCategory_4xx() { // POST .../tags/{allReadyExistingCategory} returns 4xx CreateTagCategory create = - new CreateTagCategory() - .withName(USER_TAG_CATEGORY.getName()) - .withDescription("description") - .withCategoryType(TagCategoryType.Descriptive); + new CreateTagCategory().withName(USER_TAG_CATEGORY.getName()).withDescription("description"); assertResponse(() -> createAndCheckCategory(create, ADMIN_AUTH_HEADERS), CONFLICT, "Entity already exists"); } @@ -167,11 +159,7 @@ public class TagResourceTest extends OpenMetadataApplicationTest { void post_delete_validTagCategory_as_admin_201(TestInfo test) throws HttpResponseException { // POST .../tags/{newCategory} returns 201 String categoryName = test.getDisplayName().substring(0, 20); // Form a unique category name based on the test name - CreateTagCategory create = - new CreateTagCategory() - .withName(categoryName) - .withDescription("description") - .withCategoryType(TagCategoryType.Descriptive); + CreateTagCategory create = new CreateTagCategory().withName(categoryName).withDescription("description"); TagCategory category = createAndCheckCategory(create, ADMIN_AUTH_HEADERS); assertEquals(0, category.getChildren().size()); @@ -198,11 +186,7 @@ public class TagResourceTest extends OpenMetadataApplicationTest { void post_delete_validTags_as_admin_201(TestInfo test) throws HttpResponseException { // Create tag category String categoryName = test.getDisplayName().substring(0, 20); - CreateTagCategory create = - new CreateTagCategory() - .withName(categoryName) - .withDescription("description") - .withCategoryType(TagCategoryType.Descriptive); + CreateTagCategory create = new CreateTagCategory().withName(categoryName).withDescription("description"); TagCategory category = createAndCheckCategory(create, ADMIN_AUTH_HEADERS); assertEquals(0, category.getChildren().size()); @@ -272,21 +256,12 @@ public class TagResourceTest extends OpenMetadataApplicationTest { String categoryName = test.getDisplayName().substring(0, 10); // Form a unique category name based on the test name // Missing description - CreateTagCategory create = - new CreateTagCategory() - .withName(categoryName) - .withDescription(null) - .withCategoryType(TagCategoryType.Descriptive); + CreateTagCategory create = new CreateTagCategory().withName(categoryName).withDescription(null); assertResponseContains( () -> createAndCheckCategory(create, ADMIN_AUTH_HEADERS), BAD_REQUEST, "description must not be null"); - // Missing category - create.withDescription("description").withCategoryType(null); - assertResponseContains( - () -> createAndCheckCategory(create, ADMIN_AUTH_HEADERS), BAD_REQUEST, "categoryType must not be null"); - // Long name - create.withName(TestUtils.LONG_ENTITY_NAME).withCategoryType(TagCategoryType.Descriptive); + create.withName(TestUtils.LONG_ENTITY_NAME); assertResponseContains( () -> createAndCheckCategory(create, ADMIN_AUTH_HEADERS), BAD_REQUEST, "name size must be between 2 and 64"); } @@ -354,31 +329,11 @@ public class TagResourceTest extends OpenMetadataApplicationTest { entityNotFound(Entity.TAG, FullyQualifiedName.build(USER_TAG_CATEGORY.getName(), nonExistent))); } - @Test - void put_tagCategory_200(TestInfo test) { - // Update an existing tag category - String newCategoryName = test.getDisplayName().substring(0, 10); - CreateTagCategory create = - new CreateTagCategory() - .withName(newCategoryName) - .withDescription("updatedDescription") - .withCategoryType(TagCategoryType.Descriptive); - updateCategory(USER_TAG_CATEGORY.getName(), create, ADMIN_AUTH_HEADERS); - - // Revert tag category back - create.withName(USER_TAG_CATEGORY.getName()).withCategoryType(TagCategoryType.Classification); - updateCategory(newCategoryName, create, ADMIN_AUTH_HEADERS); - } - @Test void put_tagCategoryInvalidRequest_400(TestInfo test) { // Primary tag with missing description String newCategoryName = test.getDisplayName().substring(0, 10); - CreateTagCategory create = - new CreateTagCategory() - .withName(newCategoryName) - .withDescription(null) - .withCategoryType(TagCategoryType.Descriptive); + CreateTagCategory create = new CreateTagCategory().withName(newCategoryName).withDescription(null); assertResponseContains( () -> updateCategory(USER_TAG_CATEGORY.getName(), create, ADMIN_AUTH_HEADERS), BAD_REQUEST, @@ -448,12 +403,11 @@ public class TagResourceTest extends OpenMetadataApplicationTest { String updatedBy = getPrincipalName(authHeaders); WebTarget target = getResource("tags"); TagCategory tagCategory = TestUtils.post(target, create, TagCategory.class, authHeaders); - TagCategory category = - validate(tagCategory, create.getCategoryType(), create.getName(), create.getDescription(), updatedBy); + TagCategory category = validate(tagCategory, create.getName(), create.getDescription(), updatedBy); assertEquals(0.1, category.getVersion()); TagCategory getCategory = getCategory(create.getName(), authHeaders); - validate(getCategory, create.getCategoryType(), create.getName(), create.getDescription(), updatedBy); + validate(getCategory, create.getName(), create.getDescription(), updatedBy); return category; } @@ -504,11 +458,11 @@ public class TagResourceTest extends OpenMetadataApplicationTest { // Ensure PUT returns the updated tag category TagCategory tagCategory = TestUtils.put(target, update, TagCategory.class, Status.OK, authHeaders); - validate(tagCategory, update.getCategoryType(), update.getName(), update.getDescription(), updatedBy); + validate(tagCategory, update.getName(), update.getDescription(), updatedBy); // Ensure GET returns the updated tag category TagCategory getCategory = getCategory(update.getName(), authHeaders); - validate(getCategory, update.getCategoryType(), update.getName(), update.getDescription(), updatedBy); + validate(getCategory, update.getName(), update.getDescription(), updatedBy); } private void updatePrimaryTag(String category, String primaryTag, CreateTag update, Map authHeaders) @@ -587,14 +541,9 @@ public class TagResourceTest extends OpenMetadataApplicationTest { } private TagCategory validate( - TagCategory actual, - TagCategoryType expectedCategoryType, - String expectedName, - String expectedDescription, - String expectedUpdatedBy) { + TagCategory actual, String expectedName, String expectedDescription, String expectedUpdatedBy) { validate(actual); assertEquals(expectedName, actual.getName()); - assertEquals(expectedCategoryType, actual.getCategoryType()); assertEquals(expectedDescription, actual.getDescription()); assertEquals(expectedUpdatedBy, actual.getUpdatedBy()); return actual; diff --git a/openmetadata-spec/src/main/resources/json/schema/api/tags/createTag.json b/openmetadata-spec/src/main/resources/json/schema/api/tags/createTag.json index 6987089b2a5..f26c121e944 100644 --- a/openmetadata-spec/src/main/resources/json/schema/api/tags/createTag.json +++ b/openmetadata-spec/src/main/resources/json/schema/api/tags/createTag.json @@ -25,6 +25,11 @@ "items": { "type": "string" } + }, + "mutuallyExclusive" : { + "description" : "Tags under this category are mutually exclusive. When mutually exclusive is `true` the tags from this category are used to **classify** an entity. An entity can only be in one class - example, it can only be either `tier1` or `tier2` and not both. When mutually exclusive is `false`, the tags from this category are used to **categorize** an entity. An entity can be in multiple categories simultaneously - example a customer can be `newCustomer` and `atRisk` simultaneously. ** Note when a tag category is marked mutually exclusive, all the tag groups under it are also mutually exclusive.", + "type" : "boolean", + "default" : "false" } }, "required": ["name", "description"], diff --git a/openmetadata-spec/src/main/resources/json/schema/api/tags/createTagCategory.json b/openmetadata-spec/src/main/resources/json/schema/api/tags/createTagCategory.json index 8880786c1a7..a74c92d5f79 100644 --- a/openmetadata-spec/src/main/resources/json/schema/api/tags/createTagCategory.json +++ b/openmetadata-spec/src/main/resources/json/schema/api/tags/createTagCategory.json @@ -19,10 +19,12 @@ "description": "Description of the tag category", "$ref": "../../type/basic.json#/definitions/markdown" }, - "categoryType": { - "$ref": "../../entity/tags/tagCategory.json#/definitions/tagCategoryType" + "mutuallyExclusive" : { + "description" : "Tags under this category are mutually exclusive. When mutually exclusive is `true` the tags from this category are used to **classify** an entity. An entity can only be in one class - example, it can only be either `tier1` or `tier2` and not both. When mutually exclusive is `false`, the tags from this category are used to **categorize** an entity. An entity can be in multiple categories simultaneously - example a customer can be `newCustomer` and `atRisk` simultaneously. ** Note when a tag category is marked mutually exclusive, all the tag groups under it are also mutually exclusive.", + "type" : "boolean", + "default" : "false" } }, - "required": ["name", "description", "categoryType"], + "required": ["name", "description"], "additionalProperties": false } diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/glossary.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/glossary.json index 5a93f7830ad..587383d90e7 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/glossary.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/glossary.json @@ -18,7 +18,7 @@ "$ref": "../../type/basic.json#/definitions/uuid" }, "name": { - "description": "Preferred name for the glossary term.", + "description": "Name of the glossary", "type": "string", "$ref": "#/definitions/name" }, @@ -88,6 +88,11 @@ "disabled" : { "description": "System glossary can't be deleted. Use this flag to disable them.", "type": "boolean" + }, + "mutuallyExclusive" : { + "description" : "Glossary terms under this glossary are mutually exclusive. When mutually exclusive is `true` only one term can be used to label an entity. When mutually exclusive is `false`, multiple terms from this group can be used to label an entity. When Glossary is mutually exclusive, all the glossary term groups under it are also mutually exclusive.", + "type" : "boolean", + "default" : "false" } }, "required": ["id", "name", "description"], diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/glossaryTerm.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/glossaryTerm.json index 34f77d23d74..afadd71d158 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/glossaryTerm.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/glossaryTerm.json @@ -134,6 +134,11 @@ "disabled" : { "description": "System glossary can't be deleted. Use this flag to disable them.", "type": "boolean" + }, + "mutuallyExclusive" : { + "description" : "Glossary terms under this group mutually exclusive. When mutually exclusive is `true` only one term can be used to label an entity from this group. When mutually exclusive is `false`, multiple terms from this group can be used to label an entity. When a group is mutually exclusive, all the glossary term groups under it are also mutually exclusive.", + "type" : "boolean", + "default" : "false" } }, "required": ["id", "name", "description", "glossary"], diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/tags/tagCategory.json b/openmetadata-spec/src/main/resources/json/schema/entity/tags/tagCategory.json index fc86884238e..002e4500365 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/tags/tagCategory.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/tags/tagCategory.json @@ -13,21 +13,6 @@ "minLength": 2, "maxLength": 64 }, - "tagCategoryType": { - "description": "Type of tag category.", - "type": "string", - "enum": ["Descriptive", "Classification"], - "javaEnums": [ - { - "name": "Descriptive", - "description": "Tag category used for describing an entity. Example - column is of of type User.Address." - }, - { - "name": "Classification", - "description": "Tag category used for classifying an entity. Example - column is of of type PII.sensitive." - } - ] - }, "tag": { "javaType": "org.openmetadata.schema.entity.tags.Tag", "javaInterfaces": ["org.openmetadata.schema.EntityInterface"], @@ -99,6 +84,11 @@ "disabled" : { "description": "System tags can't be deleted. Use this flag to disable them.", "type": "boolean" + }, + "mutuallyExclusive" : { + "description" : "Children tags under this group are mutually exclusive. When mutually exclusive is `true` the tags from this group are used to **classify** an entity. An entity can only be in one class - example, it can only be either `tier1` or `tier2` and not both. When mutually exclusive is `false`, the tags from this group are used to **categorize** an entity. An entity can be in multiple categories simultaneously - example a customer can be `newCustomer` and `atRisk` simultaneously.", + "type" : "boolean", + "default" : "false" } }, "required": ["name", "description"], @@ -137,9 +127,6 @@ "description": "User who made the update.", "type": "string" }, - "categoryType": { - "$ref": "#/definitions/tagCategoryType" - }, "href": { "description": "Link to the resource corresponding to the tag category.", "$ref": "../../type/basic.json#/definitions/href" @@ -170,8 +157,13 @@ "disabled" : { "description": "System tag categories can't be deleted. Use this flag to disable them.", "type": "boolean" + }, + "mutuallyExclusive" : { + "description" : "Tags under this category are mutually exclusive. When mutually exclusive is `true` the tags from this category are used to **classify** an entity. An entity can only be in one class - example, it can only be either `tier1` or `tier2` and not both. When mutually exclusive is `false`, the tags from this category are used to **categorize** an entity. An entity can be in multiple categories simultaneously - example a customer can be `newCustomer` and `atRisk` simultaneously. ** Note when a tag category is marked mutually exclusive, all the tag groups under it are also mutually exclusive.", + "type" : "boolean", + "default" : "false" } }, - "required": ["name", "description", "categoryType"], + "required": ["name", "description"], "additionalProperties": false } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.test.tsx index 522ac0881b8..28e99d0d1ea 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.test.tsx @@ -131,7 +131,6 @@ const mockTagList = [ id: 'tagCatId1', name: 'TagCat1', description: '', - categoryType: 'Classification', children: [ { id: 'tagId1', @@ -147,7 +146,6 @@ const mockTagList = [ id: 'tagCatId2', name: 'TagCat2', description: '', - categoryType: 'Classification', children: [ { id: 'tagId2', diff --git a/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.test.tsx index c4bb45c21c3..d4584a8acc8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.test.tsx @@ -122,7 +122,6 @@ const mockTagList = [ id: 'tagCatId1', name: 'TagCat1', description: '', - categoryType: 'Classification', children: [ { id: 'tagId1', @@ -138,7 +137,6 @@ const mockTagList = [ id: 'tagCatId2', name: 'TagCat2', description: '', - categoryType: 'Classification', children: [ { id: 'tagId2', diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/TierCard/TierCard.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/TierCard/TierCard.test.tsx index d82f0697785..f7b451692e7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/TierCard/TierCard.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/TierCard/TierCard.test.tsx @@ -23,7 +23,6 @@ const mockTierData = { version: 0.1, updatedAt: 1665646906357, updatedBy: 'admin', - categoryType: 'Descriptive', href: 'http://localhost:8585/api/v1/tags/Tier', children: [ { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/entityPageInfo/EntityPageInfo.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/entityPageInfo/EntityPageInfo.test.tsx index 38818cef96d..81cdf0e2bac 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/entityPageInfo/EntityPageInfo.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/entityPageInfo/EntityPageInfo.test.tsx @@ -127,7 +127,6 @@ const mockTagList = [ id: 'tagCatId1', name: 'TagCat1', description: '', - categoryType: 'Classification', children: [ { id: 'tagId1', @@ -143,7 +142,6 @@ const mockTagList = [ id: 'tagCatId2', name: 'TagCat2', description: '', - categoryType: 'Classification', children: [ { id: 'tagId2', diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/tags/Form.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/tags/Form.test.tsx index f9f856e7721..339f5ad3518 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/tags/Form.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/tags/Form.test.tsx @@ -5,7 +5,6 @@ import Form from './Form'; const mockFunction = jest.fn(); const mockInitialData = { - categoryType: 'Descriptive', description: '', name: '', }; @@ -27,10 +26,8 @@ describe('Test TagsPage form component', () => { } ); - const categoryType = await findByTestId(container, 'category-type'); const name = await findByTestId(container, 'name'); - expect(categoryType).toBeInTheDocument(); expect(name).toBeInTheDocument(); expect( await findByText(container, /MarkdownWithPreview component/i) diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/tags/Form.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/tags/Form.tsx index 512abd90723..132a343b55b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/tags/Form.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/tags/Form.tsx @@ -24,7 +24,6 @@ import { CreateTagCategory } from '../../generated/api/tags/createTagCategory'; import { errorMsg } from '../../utils/CommonUtils'; type CustomTagCategory = { - categoryType: string; description: CreateTagCategory['description']; name: CreateTagCategory['name']; }; @@ -42,7 +41,6 @@ const Form: React.FC = forwardRef( const [data, setData] = useState({ name: initialData.name, description: initialData.description, - categoryType: initialData.categoryType, }); const isMounting = useRef(true); @@ -83,25 +81,6 @@ const Form: React.FC = forwardRef(
- {initialData.categoryType && ( -
- - -
- )}
({ const mockTagsCategory = [ { - categoryType: 'Classification', id: 'test', children: [ { @@ -78,7 +77,6 @@ const mockTagsCategory = [ usageCount: 3, }, { - categoryType: 'Classification', id: 'test2', children: [], description: 'description', @@ -96,7 +94,6 @@ const mockCategory = [ version: 0.1, updatedAt: 1649665563400, updatedBy: 'admin', - categoryType: 'Classification', href: 'http://localhost:8585/api/v1/tags/PersonalData', usageCount: 0, children: [ @@ -139,7 +136,6 @@ const mockCategory = [ version: 0.1, updatedAt: 1649665563410, updatedBy: 'admin', - categoryType: 'Classification', href: 'http://localhost:8585/api/v1/tags/PII', usageCount: 0, children: [ diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/tags/index.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/tags/index.tsx index d57799e4792..1fd5fc88640 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/tags/index.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/tags/index.tsx @@ -46,10 +46,7 @@ import { import { TIER_CATEGORY } from '../../constants/constants'; import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil'; import { delimiterRegex } from '../../constants/regex.constants'; -import { - CreateTagCategory, - TagCategoryType, -} from '../../generated/api/tags/createTagCategory'; +import { CreateTagCategory } from '../../generated/api/tags/createTagCategory'; import { Operation } from '../../generated/entity/policies/accessControl/rule'; import { TagCategory, TagClass } from '../../generated/entity/tags/tagCategory'; import { EntityReference } from '../../generated/type/entityReference'; @@ -334,7 +331,6 @@ const TagsPage = () => { const response = await updateTagCategory(currentCategory?.name ?? '', { name: currentCategory?.name ?? '', description: updatedHTML, - categoryType: currentCategory?.categoryType, }); if (response) { await fetchCurrentCategory(currentCategory?.name as string, true); @@ -722,7 +718,6 @@ const TagsPage = () => { initialData={{ name: '', description: '', - categoryType: TagCategoryType.Descriptive, }} isSaveButtonDisabled={!isEmpty(errorDataCategory)} onCancel={() => setIsAddingCategory(false)} @@ -743,7 +738,6 @@ const TagsPage = () => { initialData={{ name: '', description: '', - categoryType: '', }} isSaveButtonDisabled={!isEmpty(errorDataTag)} onCancel={() => setIsAddingTag(false)} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/tags/tagsTypes.ts b/openmetadata-ui/src/main/resources/ui/src/pages/tags/tagsTypes.ts index ea9af7be6ae..d986868d91a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/tags/tagsTypes.ts +++ b/openmetadata-ui/src/main/resources/ui/src/pages/tags/tagsTypes.ts @@ -22,7 +22,6 @@ export type Tag = { export type TagsCategory = { name: string; description: string; - categoryType?: string; children?: Array; href?: string; usageCount?: number;