mchades commented on code in PR #13794: URL: https://github.com/apache/gravitino/pull/13794#discussion_r4254437600
########## 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: [P1] Land the authorization prerequisite before merging The separate server fix mentioned in the description is still required: [PythonIT](https://github.com/apache/gravitino/actions/runs/38279277367/job/114943027452) fails here with `ForbiddenException not raised`. On the current base, this reader can retrieve `reviewed` without tag privileges. Please link and land that prerequisite, then rerun this test against the resulting base. Keep this assertion so the authorization gap remains covered. -- 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]
