diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ClassificationRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ClassificationRepository.java index 67b2c5027cc..fef2dc44d73 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ClassificationRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ClassificationRepository.java @@ -51,6 +51,7 @@ public class ClassificationRepository extends EntityRepository { @Override public Classification setFields(Classification category, Fields fields) { + category.withTermCount(fields.contains("termCount") ? getTermCount(category) : null); return category.withUsageCount(fields.contains("usageCount") ? getUsageCount(category) : null); } @@ -67,6 +68,11 @@ public class ClassificationRepository extends EntityRepository { @Override public void storeRelationships(Classification entity) {} + private int getTermCount(Classification category) { + ListFilter filter = new ListFilter(Include.NON_DELETED).addQueryParam("parent", category.getName()); + return daoCollection.tagDAO().listCount(filter); + } + private Integer getUsageCount(Classification category) { return daoCollection.tagUsageDAO().getTagCount(TagSource.TAG.ordinal(), category.getName()); } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/GlossaryRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/GlossaryRepository.java index 0d473635ede..853286e082f 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/GlossaryRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/GlossaryRepository.java @@ -72,6 +72,7 @@ public class GlossaryRepository extends EntityRepository { @Override public Glossary setFields(Glossary glossary, Fields fields) throws IOException { + glossary.setTermCount(fields.contains("termCount") ? getTermCount(glossary) : null); glossary.setReviewers(fields.contains("reviewers") ? getReviewers(glossary) : null); return glossary.withUsageCount(fields.contains("usageCount") ? getUsageCount(glossary) : null); } @@ -110,6 +111,11 @@ public class GlossaryRepository extends EntityRepository { return daoCollection.tagUsageDAO().getTagCount(TagSource.GLOSSARY.ordinal(), glossary.getName()); } + private Integer getTermCount(Glossary glossary) { + ListFilter filter = new ListFilter(Include.NON_DELETED).addQueryParam("parent", glossary.getName()); + return daoCollection.glossaryTermDAO().listCount(filter); + } + @Override public EntityUpdater getUpdater(Glossary original, Glossary updated, Operation operation) { return new GlossaryUpdater(original, updated, operation); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java index b29b72af72e..4dbe47643a5 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java @@ -90,7 +90,7 @@ public class GlossaryResource extends EntityResource authHeaders) throws IOException { + public T createAndCheckEntity(K create, Map authHeaders) throws IOException { // Validate an entity that is created has all the information set in create request String updatedBy = SecurityUtil.getPrincipalName(authHeaders); T entity = createEntity(create, authHeaders); @@ -1762,7 +1762,7 @@ public abstract class EntityResourceTest authHeaders, @@ -2331,7 +2331,7 @@ public abstract class EntityResourceTest { + private final GlossaryResourceTest glossaryResourceTest = new GlossaryResourceTest(); + public GlossaryTermResourceTest() { super( Entity.GLOSSARY_TERM, @@ -99,9 +102,7 @@ public class GlossaryTermResourceTest extends EntityResourceTest aa -> aaa; GlossaryTerm a = createTerm(glossary, null, "a", null); @@ -284,9 +278,7 @@ public class GlossaryTermResourceTest extends EntityResourceTest authHeaders) + throws IOException { + int termCount = getGlossary(create.getGlossary().getName()).getTermCount(); + GlossaryTerm term = super.createAndCheckEntity(create, authHeaders); + assertEquals(termCount + 1, getGlossary(create.getGlossary().getName()).getTermCount()); + return term; + } + + @Override + public GlossaryTerm updateAndCheckEntity( + CreateGlossaryTerm request, + Response.Status status, + Map authHeaders, + UpdateType updateType, + ChangeDescription changeDescription) + throws IOException { + int termCount = getGlossary(request.getGlossary().getName()).getTermCount(); + GlossaryTerm term = super.updateAndCheckEntity(request, status, authHeaders, updateType, changeDescription); + if (status == Response.Status.CREATED) { + assertEquals(termCount + 1, getGlossary(request.getGlossary().getName()).getTermCount()); + } + return term; + } + public void renameGlossaryTermAndCheck(GlossaryTerm term, String newName) throws IOException { String oldName = term.getName(); String json = JsonUtils.pojoToJson(term); @@ -572,4 +589,17 @@ public class GlossaryTermResourceTest extends EntityResourceTest { TIER1_TAG_LABEL = getTagLabel(FullyQualifiedName.add("Tier", "Tier1")); TIER2_TAG_LABEL = getTagLabel(FullyQualifiedName.add("Tier", "Tier2")); - CreateClassification create = classificationResourceTest.createRequest("User"); - USER_TAG_CATEGORY = classificationResourceTest.createAndCheckEntity(create, ADMIN_AUTH_HEADERS); + USER_TAG_CATEGORY = createClassification("User"); - List associatedTags = new ArrayList<>(); - associatedTags.add(PERSONAL_DATA_TAG_LABEL.getTagFQN()); - associatedTags.add(PII_SENSITIVE_TAG_LABEL.getTagFQN()); - - CreateTag createTag = - createRequest("Address").withClassification(USER_TAG_CATEGORY.getName()).withAssociatedTags(associatedTags); - ADDRESS_TAG = createEntity(createTag, ADMIN_AUTH_HEADERS); + ADDRESS_TAG = + createTag( + "Address", + USER_TAG_CATEGORY.getName(), + null, + PERSONAL_DATA_TAG_LABEL.getTagFQN(), + PII_SENSITIVE_TAG_LABEL.getTagFQN()); USER_ADDRESS_TAG_LABEL = getTagLabel(FullyQualifiedName.add("User", "Address")); } @@ -94,31 +95,27 @@ public class TagResourceTest extends EntityResourceTest { @Order(1) @Test - void post_validTags_200() throws HttpResponseException { - Classification category = - classificationResourceTest.getEntityByName(USER_TAG_CATEGORY.getName(), authHeaders("test@open-meatadata.org")); + void post_validTags_200() throws IOException { + Classification classification = getClassification(USER_TAG_CATEGORY.getName()); Map queryParams = new HashMap<>(); - queryParams.put("parent", category.getFullyQualifiedName()); + queryParams.put("parent", classification.getFullyQualifiedName()); List childrenBefore = listEntities(queryParams, ADMIN_AUTH_HEADERS).getData(); - CreateTag create = createRequest("tag1").withClassification(category.getName()); - Tag tag1 = createEntity(create, ADMIN_AUTH_HEADERS); + Tag tag1 = createTag("tag1", classification.getName(), null); List childrenAfter = listEntities(queryParams, ADMIN_AUTH_HEADERS).getData(); assertEquals(childrenBefore.size() + 1, childrenAfter.size()); // POST .../tags/{category}/{primaryTag}/{secondaryTag} to create secondary tag - create = createRequest("SecondaryTag").withParent(tag1.getFullyQualifiedName()); - createEntity(create, ADMIN_AUTH_HEADERS); + createTag("SecondaryTag", classification.getName(), tag1.getFullyQualifiedName()); } @Test void post_newTagsOnNonExistentParents_404() { // POST .../tags/{nonExistent}/{primaryTag} where category does not exist String nonExistent = "nonExistent"; - CreateTag create = createRequest("primary").withClassification(nonExistent); assertResponse( - () -> createEntity(create, ADMIN_AUTH_HEADERS), NOT_FOUND, entityNotFound(Entity.CLASSIFICATION, nonExistent)); + () -> createTag("primary", nonExistent, null), NOT_FOUND, entityNotFound(Entity.CLASSIFICATION, nonExistent)); // POST .../tags/{user}/{nonExistent}/tag where primaryTag does not exist String parentFqn = FullyQualifiedName.build(USER_TAG_CATEGORY.getName(), nonExistent); @@ -133,17 +130,15 @@ public class TagResourceTest extends EntityResourceTest { // Create under tag1 secondary tags t11 t12 // Create under tag2 secondary tags t21 t22 // - String categoryName = test.getDisplayName().substring(0, 10); - CreateClassification createCategory = classificationResourceTest.createRequest(categoryName); - Classification category = - classificationResourceTest.updateEntity(createCategory, Status.CREATED, ADMIN_AUTH_HEADERS); + String classificationName = test.getDisplayName().substring(0, 10); + Classification classification = createClassification(classificationName); - Tag t1 = createOrUpdate(categoryName, null, "t1", CREATED); - createOrUpdate(categoryName, t1, "t11", CREATED); - createOrUpdate(categoryName, t1, "t12", CREATED); - Tag t2 = createOrUpdate(categoryName, null, "t2", CREATED); - createOrUpdate(categoryName, t2, "t21", CREATED); - Tag t22 = createOrUpdate(categoryName, t2, "t22", CREATED); + Tag t1 = createOrUpdate(classificationName, null, "t1", CREATED); + createOrUpdate(classificationName, t1, "t11", CREATED); + createOrUpdate(classificationName, t1, "t12", CREATED); + Tag t2 = createOrUpdate(classificationName, null, "t2", CREATED); + createOrUpdate(classificationName, t2, "t21", CREATED); + Tag t22 = createOrUpdate(classificationName, t2, "t22", CREATED); // Rename leaf node t22 to newt22 renameTagAndCheck(t22, "newt22"); @@ -152,8 +147,8 @@ public class TagResourceTest extends EntityResourceTest { renameTagAndCheck(t2, "newt2"); // Change classification name and ensure all the tags have the new names - String newCategoryName = "new" + categoryName; - classificationResourceTest.renameClassificationAndCheck(category, newCategoryName); + String newclassificationName = "new" + classificationName; + classificationResourceTest.renameClassificationAndCheck(classification, newclassificationName); } @Test @@ -165,11 +160,12 @@ public class TagResourceTest extends EntityResourceTest { CatalogExceptionMessage.systemEntityDeleteNotAllowed(tag.getName(), Entity.TAG)); } - private Tag createOrUpdate(String categoryName, Tag parent, String name, Status status) throws HttpResponseException { + private Tag createOrUpdate(String classificationName, Tag parent, String name, Status status) throws IOException { String parentFqn = parent != null ? parent.getFullyQualifiedName() : null; CreateTag createTag = - createRequest(name).withParent(parentFqn).withClassification(categoryName).withDescription("description"); - return updateEntity(createTag, status, ADMIN_AUTH_HEADERS); // Change to updateAndCheck + createRequest(name).withParent(parentFqn).withClassification(classificationName).withDescription("description"); + Tag tag = updateAndCheckEntity(createTag, status, ADMIN_AUTH_HEADERS, NO_CHANGE, null); + return tag; } public void renameTagAndCheck(Tag tag, String newName) throws IOException { @@ -250,4 +246,49 @@ public class TagResourceTest extends EntityResourceTest { } assertCommonFieldChange(fieldName, expected, actual); } + + @Override + public Tag createAndCheckEntity(CreateTag create, Map authHeaders) throws IOException { + int termCount = getClassification(create.getClassification()).getTermCount(); + Tag tag = super.createAndCheckEntity(create, authHeaders); + assertEquals(termCount + 1, getClassification(create.getClassification()).getTermCount()); + return tag; + } + + @Override + public Tag updateAndCheckEntity( + CreateTag request, + Status status, + Map authHeaders, + UpdateType updateType, + ChangeDescription changeDescription) + throws IOException { + int termCount = getClassification(request.getClassification()).getTermCount(); + Tag tag = super.updateAndCheckEntity(request, status, authHeaders, updateType, changeDescription); + if (status == Response.Status.CREATED) { + assertEquals(termCount + 1, getClassification(request.getClassification()).getTermCount()); + } + return tag; + } + + public Tag createTag(String name, String classification, String parentFqn, String... associatedTags) + throws IOException { + List associatedTagList = associatedTags.length == 0 ? null : listOf(associatedTags); + CreateTag createTag = + createRequest(name) + .withParent(parentFqn) + .withClassification(classification) + .withAssociatedTags(associatedTagList); + return createEntity(createTag, ADMIN_AUTH_HEADERS); + } + + public Classification createClassification(String name) throws IOException { + CreateClassification create = classificationResourceTest.createRequest(name); + return classificationResourceTest.createAndCheckEntity(create, ADMIN_AUTH_HEADERS); + } + + public Classification getClassification(String name) throws IOException { + return classificationResourceTest.getEntityByName( + name, classificationResourceTest.getAllowedFields(), ADMIN_AUTH_HEADERS); + } } diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/teams/UserResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/teams/UserResourceTest.java index f065e43e017..c9b81b0b6ec 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/teams/UserResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/teams/UserResourceTest.java @@ -1089,7 +1089,7 @@ public class UserResourceTest extends EntityResourceTest { } @Override - protected String getAllowedFields() { + public String getAllowedFields() { List allowedFields = Entity.getAllowedFields(entityClass); allowedFields.removeAll(of(USER_PROTECTED_FIELDS.split(","))); return String.join(",", allowedFields); diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/classification/classification.json b/openmetadata-spec/src/main/resources/json/schema/entity/classification/classification.json index deedcc13a15..c8605e2dc28 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/classification/classification.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/classification/classification.json @@ -30,6 +30,11 @@ "description": "Metadata version of the entity.", "$ref": "../../type/entityHistory.json#/definitions/entityVersion" }, + "termCount" : { + "description": "Total number of children tag terms under this classification. This includes all the children in the hierarchy.", + "type" : "integer", + "minimum": 0 + }, "updatedAt": { "description": "Last update time corresponding to the new version of the entity in Unix epoch time milliseconds.", "$ref": "../../type/basic.json#/definitions/timestamp" 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 62b30e06e1a..b21604a275e 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 @@ -73,6 +73,11 @@ }, "default": null }, + "termCount" : { + "description": "Total number of terms in the glossary. This includes all the children in the hierarchy.", + "type" : "integer", + "minimum": 0 + }, "changeDescription": { "description": "Change that lead to this version of the entity.", "$ref": "../../type/entityHistory.json#/definitions/changeDescription"