laserninja commented on code in PR #13794:
URL: https://github.com/apache/gravitino/pull/13794#discussion_r4254831215


##########
clients/client-python/tests/integration/test_semantic_model_tags_and_owner.py:
##########
@@ -0,0 +1,305 @@
+# 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.
+
+import os
+import shutil
+import tempfile
+import uuid
+from unittest.mock import patch
+
+from gravitino import Catalog, GravitinoClient, NameIdentifier
+from gravitino.api.authorization.owner import Owner
+from gravitino.api.authorization.privileges import Privileges
+from gravitino.api.authorization.securable_objects import SecurableObjects
+from gravitino.api.metadata_object import MetadataObject
+from gravitino.api.metadata_objects import MetadataObjects
+from gravitino.api.rel.column import Column
+from gravitino.api.rel.types.types import Types
+from gravitino.api.semantic.dataset import Dataset
+from gravitino.api.semantic.semantic_model_change import SemanticModelChange
+from gravitino.api.semantic.semantic_model_definition import 
SemanticModelDefinition
+from gravitino.auth.simple_auth_provider import SimpleAuthProvider
+from gravitino.exceptions.base import (
+    ForbiddenException,
+    NoSuchMetadataObjectException,
+    NoSuchTagException,
+    NotFoundException,
+    TagAlreadyAssociatedException,
+)
+from tests.integration.integration_test_env import 
AuthorizationIntegrationTestEnv
+
+
+class TestSemanticModelTagsAndOwner(AuthorizationIntegrationTestEnv):
+    """Exercise Semantic Model tags and generic ownership with authorization 
enabled."""
+
+    def setUp(self):
+        self._metalake_name = f"semantic_tags_{uuid.uuid4().hex[:8]}"
+        super().setUp()
+        self.addCleanup(self._gravitino_client.close)
+        warehouse = tempfile.mkdtemp(prefix="semantic_tags_")
+        self.addCleanup(shutil.rmtree, warehouse)
+        self.addCleanup(super().tearDown)
+        catalog = self._gravitino_client.create_catalog(
+            "iceberg",
+            Catalog.Type.RELATIONAL,
+            "lakehouse-iceberg",
+            "",
+            properties={
+                "catalog-backend": "memory",
+                "warehouse": warehouse,
+                "uri": "http://localhost";,
+            },
+        )
+        catalog.as_schemas().create_schema("semantic", "", {})
+        catalog.as_table_catalog().create_table(
+            NameIdentifier.of("semantic", "orders"),
+            [Column.of("id", Types.LongType.get())],
+            properties={},
+        )
+        definition = SemanticModelDefinition(
+            [Dataset("orders", NameIdentifier.of("iceberg", "semantic", 
"orders"))]
+        )
+        self._models = catalog.as_semantic_model_catalog()
+        self._ident = NameIdentifier.of("semantic", "sales")
+        self._model = self._models.create_semantic_model(
+            self._ident, "", definition, {}
+        )
+        self._gravitino_client.create_tag(
+            "reviewed", "review status", {"team": "finance"}
+        )
+        self._gravitino_client.create_tag("hidden", "", {})
+        self._object = MetadataObjects.of(
+            ["iceberg", "semantic", "sales"], 
MetadataObject.Type.SEMANTIC_MODEL
+        )
+
+    def tearDown(self):
+        # Registered cleanups also run when setup fails.
+        pass
+
+    def test_tag_lifecycle(self):
+        tags = self._model.supports_tags()
+        self.assertEqual([], tags.list_tags())
+        self.assertEqual([], tags.list_tags_info())
+        self.assertEqual(["reviewed"], tags.associate_tags(["reviewed"], []))
+        loaded = self._models.load_semantic_model(self._ident).supports_tags()
+        self.assertEqual(["reviewed"], loaded.list_tags())
+        details = loaded.list_tags_info()
+        self.assertEqual(["reviewed"], [tag.name() for tag in details])
+        tag = loaded.get_tag("reviewed")
+        self.assertEqual("review status", tag.comment())
+        self.assertEqual({"team": "finance"}, tag.properties())
+        self.assertEqual(tag.name(), details[0].name())
+        self.assertEqual(
+            [(self._object.type(), self._object.full_name())],
+            [
+                (obj.type(), obj.full_name())
+                for obj in tag.associated_objects().objects()
+            ],
+        )
+
+        renamed = self._models.alter_semantic_model(
+            self._ident, SemanticModelChange.rename("renamed")
+        ).supports_tags()
+        self.assertEqual(["reviewed"], renamed.list_tags())
+        self.assertEqual([], renamed.associate_tags([], ["reviewed"]))
+        self.assertEqual([], renamed.list_tags_info())
+
+    def test_tag_values(self):
+        tags = self._model.supports_tags()
+        self.assertEqual(
+            ["reviewed"], tags.assign_tags([{"name": "reviewed", "value": 
"yes"}])
+        )
+        self.assertEqual(["reviewed"], tags.list_tags())
+        self.assertEqual(
+            [], tags.assign_tags(tags_to_remove=[{"name": "reviewed", "value": 
"yes"}])
+        )
+        self.assertEqual([], tags.list_tags())
+
+    def test_tag_errors(self):
+        tags = self._model.supports_tags()
+        for name in ("missing", "reviewed"):
+            with self.subTest(tag=name), self.assertRaises(NoSuchTagException):
+                tags.get_tag(name)
+        tags.associate_tags(["reviewed"], [])
+        with self.assertRaises(TagAlreadyAssociatedException):
+            tags.associate_tags(["reviewed"], [])
+        self.assertEqual(["reviewed"], tags.list_tags())
+        # Missing tag additions/removals are ignored by the existing tag API.
+        self.assertEqual(["reviewed"], tags.associate_tags(["missing"], 
["absent"]))
+        tags.associate_tags([], ["reviewed"])
+        with self.assertRaises(NoSuchTagException):
+            tags.get_tag("reviewed")
+        self._models.drop_semantic_model(self._ident)
+        with self.assertRaises(NotFoundException):
+            tags.list_tags()
+
+    def test_tag_authorization(self):
+        reader = self._reader()
+        model_privilege = Privileges.allow("USE_SEMANTIC_MODEL")
+        self._grant(self._object, model_privilege)
+        tags = (
+            reader.load_catalog("iceberg")
+            .as_semantic_model_catalog()
+            .load_semantic_model(self._ident)
+            .supports_tags()
+        )
+        self._model.supports_tags().associate_tags(["reviewed", "hidden"], [])
+        self.assertEqual([], tags.list_tags())
+        self.assertEqual([], tags.list_tags_info())
+        with self.assertRaises(ForbiddenException):
+            tags.get_tag("reviewed")

Review Comment:
   The denied-read assertion is retained unchanged. The separate server fix is 
prepared and pushed as 
[f5e232c5f](https://github.com/laserninja/gravitino/commit/f5e232c5f5dee483e51525601b017f8c454b0e7b),
 but has not landed, so this remains a merge blocker.
   
   Against current main (`0817563e8`) plus that fix, all 25 live Python 
integration tests passed, including this authorization test; 484 server-common 
and 10 focused server authorization tests also passed. PythonIT still needs to 
run against main after the prerequisite lands. I am leaving this thread 
unresolved until then.
   
   The conflicts are resolved and pushed in `6acf4e354`. This includes the 
merged mapping prerequisite #13793 and preserves tag context for models 
returned by the newly merged import API. GitHub now reports no merge conflicts; 
all 1,234 Python unit tests pass.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to