This is an automated email from the ASF dual-hosted git repository.

jshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new e3d6dab3e [#4237] test: Add integration tests for tag system (#4257)
e3d6dab3e is described below

commit e3d6dab3e744fa9d09e35728020a4cbe89ff1a4b
Author: Jerry Shao <[email protected]>
AuthorDate: Wed Jul 31 13:52:56 2024 +0800

    [#4237] test: Add integration tests for tag system (#4257)
    
    ### What changes were proposed in this pull request?
    
    This PR proposes to add integration tests for the current tag system,
    also fixes some corner issues during test.
    
    ### Why are the changes needed?
    
    These are necessary e2e tests to make sure tag system is worked.
    
    Fix: #4237
    
    ### Does this PR introduce _any_ user-facing change?
    
    NO.
    
    ### How was this patch tested?
    
    ITs to cover.
---
 .../org/apache/gravitino/tag/SupportsTags.java     |  11 +-
 .../client/MetadataObjectTagOperations.java        |   2 +
 .../apache/gravitino/integration/test/TagIT.java   | 481 +++++++++++++++++++++
 .../gravitino/server/web/rest/TagOperations.java   |   4 +-
 4 files changed, 495 insertions(+), 3 deletions(-)

diff --git a/api/src/main/java/org/apache/gravitino/tag/SupportsTags.java 
b/api/src/main/java/org/apache/gravitino/tag/SupportsTags.java
index b4c0b3806..0a909c4c8 100644
--- a/api/src/main/java/org/apache/gravitino/tag/SupportsTags.java
+++ b/api/src/main/java/org/apache/gravitino/tag/SupportsTags.java
@@ -21,6 +21,7 @@ package org.apache.gravitino.tag;
 
 import org.apache.gravitino.annotation.Evolving;
 import org.apache.gravitino.exceptions.NoSuchTagException;
+import org.apache.gravitino.exceptions.TagAlreadyAssociatedException;
 
 /**
  * Interface for supporting getting or associate tags to objects. This 
interface will be mixed with
@@ -48,16 +49,22 @@ public interface SupportsTags {
    *
    * @param name The name of the tag.
    * @return The tag.
+   * @throws NoSuchTagException If the tag does not associate with the object.
    */
   Tag getTag(String name) throws NoSuchTagException;
 
   /**
    * Associate tags to the specific object. The tagsToAdd will be added to the 
object, and the
-   * tagsToRemove will be removed from the object.
+   * tagsToRemove will be removed from the object. Note that: 1) Adding or 
removing tags that are
+   * not existed will be ignored. 2) If the same name tag is in both tagsToAdd 
and tagsToRemove, it
+   * will be ignored. 3) If the tag is already associated with the object, it 
will throw {@link
+   * TagAlreadyAssociatedException}
    *
    * @param tagsToAdd The arrays of tag name to be added to the object.
    * @param tagsToRemove The array of tag name to be removed from the object.
    * @return The array of tag names that are associated with the object.
+   * @throws TagAlreadyAssociatedException If the tag is already associated 
with the object.
    */
-  String[] associateTags(String[] tagsToAdd, String[] tagsToRemove);
+  String[] associateTags(String[] tagsToAdd, String[] tagsToRemove)
+      throws TagAlreadyAssociatedException;
 }
diff --git 
a/clients/client-java/src/main/java/org/apache/gravitino/client/MetadataObjectTagOperations.java
 
b/clients/client-java/src/main/java/org/apache/gravitino/client/MetadataObjectTagOperations.java
index a61e272ca..1aba1c888 100644
--- 
a/clients/client-java/src/main/java/org/apache/gravitino/client/MetadataObjectTagOperations.java
+++ 
b/clients/client-java/src/main/java/org/apache/gravitino/client/MetadataObjectTagOperations.java
@@ -19,6 +19,7 @@
 package org.apache.gravitino.client;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Locale;
@@ -75,6 +76,7 @@ class MetadataObjectTagOperations implements SupportsTags {
     TagListResponse resp =
         restClient.get(
             tagRequestPath,
+            ImmutableMap.of("details", "true"),
             TagListResponse.class,
             Collections.emptyMap(),
             ErrorHandlers.tagErrorHandler());
diff --git 
a/integration-test/src/test/java/org/apache/gravitino/integration/test/TagIT.java
 
b/integration-test/src/test/java/org/apache/gravitino/integration/test/TagIT.java
new file mode 100644
index 000000000..bb650b3e7
--- /dev/null
+++ 
b/integration-test/src/test/java/org/apache/gravitino/integration/test/TagIT.java
@@ -0,0 +1,481 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.integration.test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Schema;
+import org.apache.gravitino.client.GravitinoMetalake;
+import org.apache.gravitino.dto.tag.MetadataObjectDTO;
+import org.apache.gravitino.exceptions.NoSuchTagException;
+import org.apache.gravitino.exceptions.TagAlreadyAssociatedException;
+import org.apache.gravitino.exceptions.TagAlreadyExistsException;
+import org.apache.gravitino.integration.test.container.ContainerSuite;
+import org.apache.gravitino.integration.test.container.HiveContainer;
+import org.apache.gravitino.integration.test.util.AbstractIT;
+import org.apache.gravitino.integration.test.util.GravitinoITUtils;
+import org.apache.gravitino.rel.Column;
+import org.apache.gravitino.rel.Table;
+import org.apache.gravitino.rel.types.Types;
+import org.apache.gravitino.tag.Tag;
+import org.apache.gravitino.tag.TagChange;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
[email protected]("gravitino-docker-test")
+public class TagIT extends AbstractIT {
+
+  private static final ContainerSuite containerSuite = 
ContainerSuite.getInstance();
+
+  private static final String metalakeName = 
GravitinoITUtils.genRandomName("tag_it_metalake");
+
+  private static GravitinoMetalake metalake;
+  private static Catalog relationalCatalog;
+  private static Schema schema;
+  private static Table table;
+
+  @BeforeAll
+  public static void setUp() {
+    containerSuite.startHiveContainer();
+    String hmsUri =
+        String.format(
+            "thrift://%s:%d",
+            containerSuite.getHiveContainer().getContainerIpAddress(),
+            HiveContainer.HIVE_METASTORE_PORT);
+
+    // Create metalake
+    Assertions.assertFalse(client.metalakeExists(metalakeName));
+    metalake = client.createMetalake(metalakeName, "metalake", 
Collections.emptyMap());
+
+    // Create catalog
+    String catalogName = GravitinoITUtils.genRandomName("tag_it_catalog");
+    Assertions.assertFalse(metalake.catalogExists(catalogName));
+    relationalCatalog =
+        metalake.createCatalog(
+            catalogName,
+            Catalog.Type.RELATIONAL,
+            "hive",
+            "comment",
+            ImmutableMap.of("metastore.uris", hmsUri));
+
+    // Create schema
+    String schemaName = GravitinoITUtils.genRandomName("tag_it_schema");
+    
Assertions.assertFalse(relationalCatalog.asSchemas().schemaExists(schemaName));
+    schema =
+        relationalCatalog.asSchemas().createSchema(schemaName, "comment", 
Collections.emptyMap());
+
+    // Create table
+    String tableName = GravitinoITUtils.genRandomName("tag_it_table");
+    Assertions.assertFalse(
+        
relationalCatalog.asTableCatalog().tableExists(NameIdentifier.of(schemaName, 
tableName)));
+    table =
+        relationalCatalog
+            .asTableCatalog()
+            .createTable(
+                NameIdentifier.of(schemaName, tableName),
+                new Column[] {
+                  Column.of("col1", Types.IntegerType.get()),
+                  Column.of("col2", Types.StringType.get())
+                },
+                "comment",
+                Collections.emptyMap());
+  }
+
+  @AfterAll
+  public static void tearDown() {
+    
relationalCatalog.asTableCatalog().dropTable(NameIdentifier.of(schema.name(), 
table.name()));
+    relationalCatalog.asSchemas().dropSchema(schema.name(), true);
+    metalake.dropCatalog(relationalCatalog.name());
+    client.dropMetalake(metalakeName);
+
+    if (client != null) {
+      client.close();
+      client = null;
+    }
+
+    try {
+      closer.close();
+    } catch (Exception e) {
+      // Swallow exceptions
+    }
+  }
+
+  @AfterEach
+  public void cleanUp() {
+    String[] tableTags = table.supportsTags().listTags();
+    table.supportsTags().associateTags(null, tableTags);
+
+    String[] schemaTags = schema.supportsTags().listTags();
+    schema.supportsTags().associateTags(null, schemaTags);
+
+    String[] catalogTags = relationalCatalog.supportsTags().listTags();
+    relationalCatalog.supportsTags().associateTags(null, catalogTags);
+
+    String[] tags = metalake.listTags();
+    for (String tag : tags) {
+      metalake.deleteTag(tag);
+    }
+  }
+
+  @Test
+  public void testCreateGetAndListTag() {
+    String tagName = GravitinoITUtils.genRandomName("tag_it_tag");
+    Assertions.assertThrows(NoSuchTagException.class, () -> 
metalake.getTag(tagName));
+
+    // Test create
+    Tag tag = metalake.createTag(tagName, "comment", Collections.emptyMap());
+    Assertions.assertEquals(tagName, tag.name());
+    Assertions.assertEquals("comment", tag.comment());
+    Assertions.assertEquals(Collections.emptyMap(), tag.properties());
+    Assertions.assertFalse(tag.inherited().isPresent());
+
+    // Test already existed tag
+    Assertions.assertThrows(
+        TagAlreadyExistsException.class,
+        () -> metalake.createTag(tagName, "comment", Collections.emptyMap()));
+
+    // Test get
+    Tag fetchedTag = metalake.getTag(tagName);
+    Assertions.assertEquals(tag, fetchedTag);
+
+    // test List names
+    String tagName1 = GravitinoITUtils.genRandomName("tag_it_tag1");
+    Tag tag1 = metalake.createTag(tagName1, "comment1", 
Collections.emptyMap());
+    Assertions.assertEquals(tagName1, tag1.name());
+    Assertions.assertEquals("comment1", tag1.comment());
+    Assertions.assertEquals(Collections.emptyMap(), tag1.properties());
+    Assertions.assertFalse(tag1.inherited().isPresent());
+
+    String[] tagNames = metalake.listTags();
+    Assertions.assertEquals(2, tagNames.length);
+    Set<String> tagNamesSet = Sets.newHashSet(tagName, tagName1);
+    Set<String> resultTagNamesSet = Sets.newHashSet(tagNames);
+    Assertions.assertEquals(tagNamesSet, resultTagNamesSet);
+
+    // test List tags
+    Set<Tag> tags = Sets.newHashSet(metalake.listTagsInfo());
+    Set<Tag> expectedTags = Sets.newHashSet(tag, tag1);
+    Assertions.assertEquals(expectedTags, tags);
+  }
+
+  @Test
+  public void testCreateAndAlterTag() {
+    String tagName = GravitinoITUtils.genRandomName("tag_it_tag");
+    metalake.createTag(tagName, "comment", Collections.emptyMap());
+
+    // Test rename and update comment
+    String newTagName = GravitinoITUtils.genRandomName("tag_it_tag_new");
+    TagChange rename = TagChange.rename(newTagName);
+    TagChange updateComment = TagChange.updateComment("new comment");
+
+    Tag alteredTag = metalake.alterTag(tagName, rename, updateComment);
+    Assertions.assertEquals(newTagName, alteredTag.name());
+    Assertions.assertEquals("new comment", alteredTag.comment());
+    Assertions.assertEquals(Collections.emptyMap(), alteredTag.properties());
+    Assertions.assertFalse(alteredTag.inherited().isPresent());
+
+    // Test set properties
+    TagChange setProperty = TagChange.setProperty("k1", "v1");
+    Tag alteredTag1 = metalake.alterTag(newTagName, setProperty);
+    Assertions.assertEquals(ImmutableMap.of("k1", "v1"), 
alteredTag1.properties());
+    Assertions.assertFalse(alteredTag1.inherited().isPresent());
+
+    // Test remove properties
+    TagChange removeProperty = TagChange.removeProperty("k1");
+    Tag alteredTag2 = metalake.alterTag(newTagName, removeProperty);
+    Assertions.assertEquals(Collections.emptyMap(), alteredTag2.properties());
+    Assertions.assertFalse(alteredTag2.inherited().isPresent());
+
+    // Test set and remove same property
+    TagChange setProperty1 = TagChange.setProperty("k2", "v2");
+    TagChange removeProperty1 = TagChange.removeProperty("k2");
+    Tag alteredTag3 = metalake.alterTag(newTagName, setProperty1, 
removeProperty1);
+    Assertions.assertEquals(Collections.emptyMap(), alteredTag3.properties());
+    Assertions.assertFalse(alteredTag3.inherited().isPresent());
+
+    // Test remove non-existed property
+    TagChange setProperty2 = TagChange.setProperty("k3", "v3");
+    TagChange removeProperty2 = TagChange.removeProperty("k4");
+    Tag alteredTag4 = metalake.alterTag(newTagName, setProperty2, 
removeProperty2);
+    Assertions.assertEquals(ImmutableMap.of("k3", "v3"), 
alteredTag4.properties());
+
+    // Test throw NoSuchTagException
+    Assertions.assertThrows(
+        NoSuchTagException.class, () -> metalake.alterTag("non-existed-tag", 
rename));
+  }
+
+  @Test
+  public void testCreateAndDeleteTag() {
+    String tagName = GravitinoITUtils.genRandomName("tag_it_tag");
+    metalake.createTag(tagName, "comment", Collections.emptyMap());
+
+    // Test delete
+    Assertions.assertTrue(metalake.deleteTag(tagName));
+    Assertions.assertFalse(metalake.deleteTag(tagName));
+  }
+
+  @Test
+  public void testAssociateTagsToCatalog() {
+    Tag tag1 =
+        metalake.createTag(
+            GravitinoITUtils.genRandomName("tag_it_catalog_tag1"),
+            "comment1",
+            Collections.emptyMap());
+    Tag tag2 =
+        metalake.createTag(
+            GravitinoITUtils.genRandomName("tag_it_catalog_tag2"),
+            "comment2",
+            Collections.emptyMap());
+
+    // Test associate tags to catalog
+    String[] tags =
+        relationalCatalog
+            .supportsTags()
+            .associateTags(new String[] {tag1.name(), tag2.name()}, null);
+
+    Assertions.assertEquals(2, tags.length);
+    Set<String> tagNames = Sets.newHashSet(tags);
+    Assertions.assertTrue(tagNames.contains(tag1.name()));
+    Assertions.assertTrue(tagNames.contains(tag2.name()));
+
+    // Test disassociate tags from catalog
+    String[] tags1 =
+        relationalCatalog
+            .supportsTags()
+            .associateTags(null, new String[] {tag1.name(), tag2.name()});
+    Assertions.assertEquals(0, tags1.length);
+
+    // Test associate non-existed tags to catalog
+    String[] tags2 =
+        relationalCatalog.supportsTags().associateTags(new String[] 
{"non-existed-tag"}, null);
+    Assertions.assertEquals(0, tags2.length);
+
+    // Test disassociate non-existed tags from catalog
+    String[] tags3 =
+        relationalCatalog.supportsTags().associateTags(null, new String[] 
{"non-existed-tag"});
+    Assertions.assertEquals(0, tags3.length);
+
+    // Test associate same tags to catalog
+    String[] tags4 =
+        relationalCatalog
+            .supportsTags()
+            .associateTags(new String[] {tag1.name(), tag1.name()}, null);
+    Assertions.assertEquals(1, tags4.length);
+    Assertions.assertEquals(tag1.name(), tags4[0]);
+
+    // Test associate same tag again to catalog
+    Assertions.assertThrows(
+        TagAlreadyAssociatedException.class,
+        () -> relationalCatalog.supportsTags().associateTags(new String[] 
{tag1.name()}, null));
+
+    // Test associate and disassociate same tags to catalog
+    String[] tags5 =
+        relationalCatalog
+            .supportsTags()
+            .associateTags(new String[] {tag2.name()}, new String[] 
{tag2.name()});
+    Assertions.assertEquals(1, tags5.length);
+    Assertions.assertEquals(tag1.name(), tags5[0]);
+
+    // Test List associated tags for catalog
+    String[] tags6 = relationalCatalog.supportsTags().listTags();
+    Assertions.assertEquals(1, tags6.length);
+    Assertions.assertEquals(tag1.name(), tags6[0]);
+
+    // Test List associated tags with details for catalog
+    Tag[] tags7 = relationalCatalog.supportsTags().listTagsInfo();
+    Assertions.assertEquals(1, tags7.length);
+    Assertions.assertEquals(tag1, tags7[0]);
+    Assertions.assertFalse(tags7[0].inherited().get());
+
+    // Test get associated tag for catalog
+    Tag tag = relationalCatalog.supportsTags().getTag(tag1.name());
+    Assertions.assertEquals(tag1, tag);
+    Assertions.assertFalse(tag.inherited().get());
+
+    // Test get non-existed tag for catalog
+    Assertions.assertThrows(
+        NoSuchTagException.class, () -> 
relationalCatalog.supportsTags().getTag("non-existed-tag"));
+
+    // Test get objects associated with tag
+    Assertions.assertEquals(1, tag.associatedObjects().count());
+    MetadataObject catalogObject = tag.associatedObjects().objects()[0];
+    Assertions.assertEquals(relationalCatalog.name(), catalogObject.name());
+    Assertions.assertEquals(MetadataObject.Type.CATALOG, catalogObject.type());
+  }
+
+  @Test
+  public void testAssociateTagsToSchema() {
+    Tag tag1 =
+        metalake.createTag(
+            GravitinoITUtils.genRandomName("tag_it_schema_tag1"),
+            "comment1",
+            Collections.emptyMap());
+    Tag tag2 =
+        metalake.createTag(
+            GravitinoITUtils.genRandomName("tag_it_schema_tag2"),
+            "comment2",
+            Collections.emptyMap());
+
+    // Associate tags to catalog
+    relationalCatalog.supportsTags().associateTags(new String[] {tag1.name()}, 
null);
+
+    // Test associate tags to schema
+    String[] tags =
+        schema.supportsTags().associateTags(new String[] {tag1.name(), 
tag2.name()}, null);
+
+    Assertions.assertEquals(2, tags.length);
+    Set<String> tagNames = Sets.newHashSet(tags);
+    Assertions.assertTrue(tagNames.contains(tag1.name()));
+    Assertions.assertTrue(tagNames.contains(tag2.name()));
+
+    // Test list associated tags for schema
+    String[] tags1 = schema.supportsTags().listTags();
+    Assertions.assertEquals(2, tags1.length);
+
+    // Test list associated tags with details for schema
+    Tag[] tags2 = schema.supportsTags().listTagsInfo();
+    Assertions.assertEquals(3, tags2.length);
+
+    Set<Tag> nonInheritedTags =
+        Arrays.stream(tags2).filter(tag -> 
!tag.inherited().get()).collect(Collectors.toSet());
+    Set<Tag> inheritedTags =
+        Arrays.stream(tags2).filter(tag -> 
tag.inherited().get()).collect(Collectors.toSet());
+
+    Assertions.assertEquals(2, nonInheritedTags.size());
+    Assertions.assertEquals(1, inheritedTags.size());
+    Assertions.assertTrue(nonInheritedTags.contains(tag1));
+    Assertions.assertTrue(nonInheritedTags.contains(tag2));
+    Assertions.assertTrue(inheritedTags.contains(tag1));
+    Assertions.assertFalse(inheritedTags.contains(tag2));
+
+    // Test get associated tag for schema
+    Tag tag = schema.supportsTags().getTag(tag1.name());
+    Assertions.assertEquals(tag1, tag);
+    Assertions.assertFalse(tag.inherited().get());
+
+    // Test get objects associated with tag
+    Assertions.assertEquals(2, tag.associatedObjects().count());
+    Set<MetadataObject> resultObjects = 
Sets.newHashSet(tag.associatedObjects().objects());
+    Set<MetadataObject> expectedObjects =
+        Sets.newHashSet(
+            MetadataObjectDTO.builder()
+                .withName(relationalCatalog.name())
+                .withType(MetadataObject.Type.CATALOG)
+                .build(),
+            MetadataObjectDTO.builder()
+                .withParent(relationalCatalog.name())
+                .withName(schema.name())
+                .withType(MetadataObject.Type.SCHEMA)
+                .build());
+    Assertions.assertEquals(expectedObjects, resultObjects);
+  }
+
+  @Test
+  public void testAssociateTagsToTable() {
+    Tag tag1 =
+        metalake.createTag(
+            GravitinoITUtils.genRandomName("tag_it_table_tag1"),
+            "comment1",
+            Collections.emptyMap());
+    Tag tag2 =
+        metalake.createTag(
+            GravitinoITUtils.genRandomName("tag_it_table_tag2"),
+            "comment2",
+            Collections.emptyMap());
+    Tag tag3 =
+        metalake.createTag(
+            GravitinoITUtils.genRandomName("tag_it_table_tag3"),
+            "comment3",
+            Collections.emptyMap());
+
+    // Associate tags to catalog
+    relationalCatalog.supportsTags().associateTags(new String[] {tag1.name()}, 
null);
+
+    // Associate tags to schema
+    schema.supportsTags().associateTags(new String[] {tag2.name()}, null);
+
+    // Test associate tags to table
+    String[] tags = table.supportsTags().associateTags(new String[] 
{tag3.name()}, null);
+
+    Assertions.assertEquals(1, tags.length);
+    Assertions.assertEquals(tag3.name(), tags[0]);
+
+    // Test list associated tags for table
+    String[] tags1 = table.supportsTags().listTags();
+    Assertions.assertEquals(3, tags1.length);
+    Set<String> tagNames = Sets.newHashSet(tags1);
+    Assertions.assertTrue(tagNames.contains(tag1.name()));
+    Assertions.assertTrue(tagNames.contains(tag2.name()));
+    Assertions.assertTrue(tagNames.contains(tag3.name()));
+
+    // Test list associated tags with details for table
+    Tag[] tags2 = table.supportsTags().listTagsInfo();
+    Assertions.assertEquals(3, tags2.length);
+
+    Set<Tag> nonInheritedTags =
+        Arrays.stream(tags2).filter(tag -> 
!tag.inherited().get()).collect(Collectors.toSet());
+    Set<Tag> inheritedTags =
+        Arrays.stream(tags2).filter(tag -> 
tag.inherited().get()).collect(Collectors.toSet());
+
+    Assertions.assertEquals(1, nonInheritedTags.size());
+    Assertions.assertEquals(2, inheritedTags.size());
+    Assertions.assertTrue(nonInheritedTags.contains(tag3));
+    Assertions.assertTrue(inheritedTags.contains(tag1));
+    Assertions.assertTrue(inheritedTags.contains(tag2));
+
+    // Test get associated tag for table
+    Tag resultTag1 = table.supportsTags().getTag(tag1.name());
+    Assertions.assertEquals(tag1, resultTag1);
+    Assertions.assertTrue(resultTag1.inherited().get());
+
+    Tag resultTag2 = table.supportsTags().getTag(tag2.name());
+    Assertions.assertEquals(tag2, resultTag2);
+    Assertions.assertTrue(resultTag2.inherited().get());
+
+    Tag resultTag3 = table.supportsTags().getTag(tag3.name());
+    Assertions.assertEquals(tag3, resultTag3);
+    Assertions.assertFalse(resultTag3.inherited().get());
+
+    // Test get objects associated with tag
+    Assertions.assertEquals(1, tag1.associatedObjects().count());
+    Assertions.assertEquals(relationalCatalog.name(), 
tag1.associatedObjects().objects()[0].name());
+    Assertions.assertEquals(
+        MetadataObject.Type.CATALOG, 
tag1.associatedObjects().objects()[0].type());
+
+    Assertions.assertEquals(1, tag2.associatedObjects().count());
+    Assertions.assertEquals(schema.name(), 
tag2.associatedObjects().objects()[0].name());
+    Assertions.assertEquals(
+        MetadataObject.Type.SCHEMA, 
tag2.associatedObjects().objects()[0].type());
+
+    Assertions.assertEquals(1, tag3.associatedObjects().count());
+    Assertions.assertEquals(table.name(), 
tag3.associatedObjects().objects()[0].name());
+    Assertions.assertEquals(
+        MetadataObject.Type.TABLE, 
tag3.associatedObjects().objects()[0].type());
+  }
+}
diff --git 
a/server/src/main/java/org/apache/gravitino/server/web/rest/TagOperations.java 
b/server/src/main/java/org/apache/gravitino/server/web/rest/TagOperations.java
index 4a3abe7f9..733623977 100644
--- 
a/server/src/main/java/org/apache/gravitino/server/web/rest/TagOperations.java
+++ 
b/server/src/main/java/org/apache/gravitino/server/web/rest/TagOperations.java
@@ -281,7 +281,9 @@ public class TagOperations {
               return Utils.ok(new TagListResponse(tags.toArray(new 
TagDTO[0])));
 
             } else {
-              String[] tagNames = 
tags.stream().map(TagDTO::name).toArray(String[]::new);
+              // Due to same name tag will be associated to both parent and 
child objects, so we
+              // need to deduplicate the tag names.
+              String[] tagNames = 
tags.stream().map(TagDTO::name).distinct().toArray(String[]::new);
 
               LOG.info(
                   "List {} tags for object type: {}, full name: {} under 
metalake: {}",

Reply via email to