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 9d37f2d3e9 [#9883] improvement(python-client): Add TagUpdatesRequest
and TagUpdateRequest (#9885)
9d37f2d3e9 is described below
commit 9d37f2d3e935c56b46e5c1dbe1b806cd19186d3d
Author: Lord of Abyss <[email protected]>
AuthorDate: Tue Mar 24 17:23:19 2026 +0800
[#9883] improvement(python-client): Add TagUpdatesRequest and
TagUpdateRequest (#9885)
### What changes were proposed in this pull request?
Add `TagUpdatesRequest` and `TagUpdateRequest`
### Why are the changes needed?
Fix: #9883
### Does this PR introduce _any_ user-facing change?
no
### How was this patch tested?
local unittest.
---
.../gravitino/dto/requests/__init__.py | 4 +
.../gravitino/dto/requests/tag_update_request.py | 173 +++++++++++++++++++++
.../gravitino/dto/requests/tag_updates_request.py | 56 +++++++
.../dto/requests/test_tag_update_request.py | 116 ++++++++++++++
4 files changed, 349 insertions(+)
diff --git a/clients/client-python/gravitino/dto/requests/__init__.py
b/clients/client-python/gravitino/dto/requests/__init__.py
index 440b9e645b..c8c8c9800e 100644
--- a/clients/client-python/gravitino/dto/requests/__init__.py
+++ b/clients/client-python/gravitino/dto/requests/__init__.py
@@ -16,7 +16,11 @@
# under the License.
from gravitino.dto.requests.tag_create_request import TagCreateRequest
+from gravitino.dto.requests.tag_update_request import TagUpdateRequest
+from gravitino.dto.requests.tag_updates_request import TagUpdatesRequest
__all__ = [
"TagCreateRequest",
+ "TagUpdatesRequest",
+ "TagUpdateRequest",
]
diff --git a/clients/client-python/gravitino/dto/requests/tag_update_request.py
b/clients/client-python/gravitino/dto/requests/tag_update_request.py
new file mode 100644
index 0000000000..f99deb98d6
--- /dev/null
+++ b/clients/client-python/gravitino/dto/requests/tag_update_request.py
@@ -0,0 +1,173 @@
+# 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.
+
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from dataclasses import dataclass, field
+
+from dataclasses_json import config, dataclass_json
+
+from gravitino.api.tag.tag_change import TagChange
+from gravitino.rest.rest_message import RESTRequest
+from gravitino.utils.precondition import Precondition
+
+
+@dataclass
+@dataclass_json
+class TagUpdateRequestBase(RESTRequest, ABC):
+ _type: str = field(init=False, metadata=config(field_name="@type"))
+
+ @abstractmethod
+ def tag_change(self) -> TagChange:
+ """
+ Returns the tag change.
+
+ Raises:
+ NotImplementedError: if the method is not implemented.
+
+ Returns:
+ TagUpdateRequestBase: the tag change.
+ """
+ raise NotImplementedError()
+
+
+class TagUpdateRequest:
+ """Request to update a tag."""
+
+ @dataclass_json
+ @dataclass
+ class RenameTagRequest(TagUpdateRequestBase):
+ """The tag update request for renaming a tag."""
+
+ _new_name: str = field(init=True,
metadata=config(field_name="newName"))
+
+ def __post_init__(self) -> None:
+ self._type = "rename"
+
+ def validate(self) -> None:
+ """
+ Validate the request.
+
+ Raises:
+ ValueError: If the request is invalid, this exception is
thrown.
+ """
+ Precondition.check_string_not_empty(
+ self._new_name,
+ '"newName" must not be blank',
+ )
+
+ @property
+ def new_name(self) -> str:
+ return self._new_name
+
+ def tag_change(self) -> TagChange.RenameTag:
+ return TagChange.rename(self._new_name)
+
+ @dataclass_json
+ @dataclass
+ class UpdateTagCommentRequest(TagUpdateRequestBase):
+ """The tag update request for updating a tag comment."""
+
+ _new_comment: str = field(metadata=config(field_name="newComment"))
+
+ def __post_init__(self) -> None:
+ self._type = "updateComment"
+
+ def validate(self) -> None:
+ """
+ Validate the request.
+
+ Raises:
+ ValueError: If the request is invalid, this exception is
thrown.
+ """
+ # always pass
+ pass
+
+ @property
+ def new_comment(self) -> str:
+ return self._new_comment
+
+ def tag_change(self) -> TagChange.UpdateTagComment:
+ return TagChange.UpdateTagComment(self._new_comment)
+
+ @dataclass_json
+ @dataclass
+ class SetTagPropertyRequest(TagUpdateRequestBase):
+ """The tag update request for setting a tag property."""
+
+ _property: str = field(metadata=config(field_name="property"))
+ _value: str = field(metadata=config(field_name="value"))
+
+ def __post_init__(self) -> None:
+ self._type = "setProperty"
+
+ def validate(self) -> None:
+ """
+ Validate the request.
+
+ Raises:
+ ValueError: If the request is invalid, this exception is
thrown.
+ """
+ Precondition.check_string_not_empty(
+ self._property,
+ '"property" must not be blank',
+ )
+ Precondition.check_string_not_empty(
+ self._value,
+ '"value" must not be blank',
+ )
+
+ @property
+ def prop(self) -> str:
+ return self._property
+
+ @property
+ def value(self) -> str:
+ return self._value
+
+ def tag_change(self) -> TagChange.SetProperty:
+ return TagChange.set_property(self._property, self._value)
+
+ @dataclass_json
+ @dataclass
+ class RemoveTagPropertyRequest(TagUpdateRequestBase):
+ """The tag update request for removing a tag property."""
+
+ _property: str = field(metadata=config(field_name="property"))
+
+ def __post_init__(self) -> None:
+ self._type = "removeProperty"
+
+ def validate(self) -> None:
+ """
+ Validate the request.
+
+ Raises:
+ ValueError: If the request is invalid, this exception is
thrown.
+ """
+ Precondition.check_string_not_empty(
+ self._property,
+ '"property" must not be blank',
+ )
+
+ @property
+ def prop(self) -> str:
+ return self._property
+
+ def tag_change(self) -> TagChange.RemoveProperty:
+ return TagChange.remove_property(self._property)
diff --git
a/clients/client-python/gravitino/dto/requests/tag_updates_request.py
b/clients/client-python/gravitino/dto/requests/tag_updates_request.py
new file mode 100644
index 0000000000..c4be42a561
--- /dev/null
+++ b/clients/client-python/gravitino/dto/requests/tag_updates_request.py
@@ -0,0 +1,56 @@
+# 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.
+
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+from typing import List
+
+from dataclasses_json import config
+
+from gravitino.dto.requests.tag_update_request import TagUpdateRequest
+from gravitino.rest.rest_message import RESTRequest
+
+
+@dataclass
+class TagUpdatesRequest(RESTRequest):
+ """Represents a request to update a tag."""
+
+ _updates: list[TagUpdateRequest] = field(
+ metadata=config(field_name="updates"), default_factory=list
+ )
+
+ def __init__(self, updates: List[TagUpdateRequest]) -> None:
+ """
+ Creates a new TagUpdatesRequest.
+
+ Args:
+ updates (List[TagUpdateRequest]): The updates to apply to the tag.
+ """
+ self._updates = updates
+
+ def validate(self) -> None:
+ """Validates the request.
+
+ Raises:
+ IllegalArgumentException If the request is invalid, this exception
is thrown.
+ """
+ if not self._updates:
+ raise ValueError("updates must not be null")
+
+ for update_request in self._updates:
+ update_request.validate()
diff --git
a/clients/client-python/tests/unittests/dto/requests/test_tag_update_request.py
b/clients/client-python/tests/unittests/dto/requests/test_tag_update_request.py
new file mode 100644
index 0000000000..cc406c032f
--- /dev/null
+++
b/clients/client-python/tests/unittests/dto/requests/test_tag_update_request.py
@@ -0,0 +1,116 @@
+# 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.
+from __future__ import annotations
+
+import json as _json
+import unittest
+
+from gravitino.api.tag.tag_change import TagChange
+from gravitino.dto.requests.tag_update_request import TagUpdateRequest
+
+
+class TestTagUpdateRequest(unittest.TestCase):
+ def test_tag_rename_tag_request_validate(self) -> None:
+ invalid_request = TagUpdateRequest.RenameTagRequest(_new_name="")
+ with self.assertRaises(ValueError):
+ invalid_request.validate()
+
+ def test_tag_rename_tag_request_serde(self) -> None:
+ request = TagUpdateRequest.RenameTagRequest(_new_name="new_name")
+ json_str = _json.dumps(
+ {
+ "@type": "rename",
+ "newName": "new_name",
+ }
+ )
+ self.assertEqual(json_str, request.to_json())
+
+ deserialized_request =
TagUpdateRequest.RenameTagRequest.from_json(json_str)
+ self.assertEqual("new_name", deserialized_request.new_name)
+ self.assertIsInstance(deserialized_request.tag_change(),
TagChange.RenameTag)
+
+ def test_update_tag_comment_request_serde(self) -> None:
+ request = TagUpdateRequest.UpdateTagCommentRequest("new_comment")
+ json_str = _json.dumps(
+ {
+ "@type": "updateComment",
+ "newComment": "new_comment",
+ }
+ )
+ self.assertEqual(json_str, request.to_json())
+
+ deserialized_request =
TagUpdateRequest.UpdateTagCommentRequest.from_json(
+ json_str
+ )
+ self.assertEqual("new_comment", deserialized_request.new_comment)
+ self.assertIsInstance(
+ deserialized_request.tag_change(), TagChange.UpdateTagComment
+ )
+
+ def test_set_tag_property_request_validate(self) -> None:
+ invalid_request = TagUpdateRequest.SetTagPropertyRequest("key", "")
+ with self.assertRaises(ValueError):
+ invalid_request.validate()
+
+ invalid_request = TagUpdateRequest.SetTagPropertyRequest("", "value")
+ with self.assertRaises(ValueError):
+ invalid_request.validate()
+
+ invalid_request = TagUpdateRequest.SetTagPropertyRequest("", "")
+ with self.assertRaises(ValueError):
+ invalid_request.validate()
+
+ def test_set_tag_property_request_serde(self) -> None:
+ request = TagUpdateRequest.SetTagPropertyRequest("key", "value")
+ json_str = _json.dumps(
+ {
+ "@type": "setProperty",
+ "property": "key",
+ "value": "value",
+ }
+ )
+ self.assertEqual(json_str, request.to_json())
+
+ deserialized_request =
TagUpdateRequest.SetTagPropertyRequest.from_json(
+ json_str
+ )
+ self.assertEqual("key", deserialized_request.prop)
+ self.assertEqual("value", deserialized_request.value)
+ self.assertIsInstance(deserialized_request.tag_change(),
TagChange.SetProperty)
+
+ def test_remove_tag_property_request_validate(self) -> None:
+ invalid_request = TagUpdateRequest.RemoveTagPropertyRequest("")
+ with self.assertRaises(ValueError):
+ invalid_request.validate()
+
+ def test_remove_tag_property_request_serde(self) -> None:
+ request = TagUpdateRequest.RemoveTagPropertyRequest("key")
+ json_str = _json.dumps(
+ {
+ "@type": "removeProperty",
+ "property": "key",
+ }
+ )
+ self.assertEqual(json_str, request.to_json())
+
+ deserialized_request =
TagUpdateRequest.RemoveTagPropertyRequest.from_json(
+ json_str
+ )
+ self.assertEqual("key", deserialized_request.prop)
+ self.assertIsInstance(
+ deserialized_request.tag_change(), TagChange.RemoveProperty
+ )