This is an automated email from the ASF dual-hosted git repository.
lzljs3620320 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new 56ad27bc99 [python] Make mock REST server behave closer to real REST
server (#7575)
56ad27bc99 is described below
commit 56ad27bc996c8af7f840addf59a995dc98f64c2f
Author: XiaoHongbo <[email protected]>
AuthorDate: Wed Apr 1 21:19:42 2026 +0800
[python] Make mock REST server behave closer to real REST server (#7575)
---
.../pypaimon/tests/rest/rest_base_test.py | 23 ++++++++++++
paimon-python/pypaimon/tests/rest/rest_server.py | 42 ++++++++++++++++++----
2 files changed, 59 insertions(+), 6 deletions(-)
diff --git a/paimon-python/pypaimon/tests/rest/rest_base_test.py
b/paimon-python/pypaimon/tests/rest/rest_base_test.py
index eb0905e692..9448e72713 100644
--- a/paimon-python/pypaimon/tests/rest/rest_base_test.py
+++ b/paimon-python/pypaimon/tests/rest/rest_base_test.py
@@ -339,6 +339,29 @@ class RESTBaseTest(unittest.TestCase):
)
self.assertEqual(len(result.elements), 3)
+ def test_alter_database(self):
+ """Test alter_database sets and removes properties."""
+ from pypaimon.catalog.rest.property_change import PropertyChange
+ db_name = "alter_db_test"
+ self.rest_catalog.create_database(db_name, True)
+
+ # set property
+ self.rest_catalog.alter_database(
+ db_name,
+ [PropertyChange.set_property("key1", "value1"),
+ PropertyChange.set_property("key2", "value2")])
+ db = self.rest_catalog.get_database(db_name)
+ self.assertEqual(db.options.get("key1"), "value1")
+ self.assertEqual(db.options.get("key2"), "value2")
+
+ # remove property
+ self.rest_catalog.alter_database(
+ db_name,
+ [PropertyChange.remove_property("key1")])
+ db = self.rest_catalog.get_database(db_name)
+ self.assertNotIn("key1", db.options)
+ self.assertEqual(db.options.get("key2"), "value2")
+
def test_list_partitions_paged_empty(self):
"""Test list_partitions_paged returns empty when no partitions."""
identifier = Identifier.from_string('default.test_reader_iterator')
diff --git a/paimon-python/pypaimon/tests/rest/rest_server.py
b/paimon-python/pypaimon/tests/rest/rest_server.py
index 8eaebcffa4..937d99f1bd 100755
--- a/paimon-python/pypaimon/tests/rest/rest_server.py
+++ b/paimon-python/pypaimon/tests/rest/rest_server.py
@@ -29,7 +29,8 @@ from urllib.parse import urlparse
if TYPE_CHECKING:
from pypaimon.catalog.rest.rest_token import RESTToken
-from pypaimon.api.api_request import (AlterTableRequest, CreateDatabaseRequest,
+from pypaimon.api.api_request import (AlterDatabaseRequest, AlterTableRequest,
+ CreateDatabaseRequest,
CreateTableRequest, RenameTableRequest)
from pypaimon.api.api_response import (ConfigResponse, GetDatabaseResponse,
GetTableResponse, ListDatabasesResponse,
@@ -45,6 +46,7 @@ from pypaimon.catalog.catalog_exception import
(DatabaseNoPermissionException,
TableAlreadyExistException)
from pypaimon.catalog.rest.table_metadata import TableMetadata
from pypaimon.common.identifier import Identifier
+from pypaimon.api.typedef import RESTAuthParameter
from pypaimon.common.json_util import JSON
from pypaimon import Schema
from pypaimon.schema.schema_change import Actions, SchemaChange
@@ -258,11 +260,11 @@ class RESTCatalogServer:
content_length = int(self.headers.get('Content-Length', 0))
data = self.rfile.read(content_length).decode('utf-8') if
content_length > 0 else ""
- # Get headers
+ # Get headers (case-insensitive from HTTPMessage)
+ auth_token = self.headers.get(AUTHORIZATION_HEADER_KEY)
headers = dict(self.headers)
# Handle authentication
- auth_token = headers.get(AUTHORIZATION_HEADER_KEY.lower())
if not self._authenticate(auth_token, resource_path,
parameters, method, data):
self._send_response(401, "Unauthorized")
return
@@ -292,9 +294,25 @@ class RESTCatalogServer:
def _authenticate(self, token: str, path: str, params: Dict[str,
str],
method: str, data: str) -> bool:
- """Authenticate request"""
- # Simplified authentication - always return True for mock
- return True
+ """Authenticate request by verifying Authorization header."""
+ if server_instance.auth_provider is None:
+ return True
+ if path.startswith("/ram/security-credential"):
+ return True
+ if not token:
+ return False
+ rest_auth_parameter = RESTAuthParameter(
+ method=method,
+ path=path,
+ data=data or "",
+ parameters=params or {},
+ )
+ from pypaimon.api.auth.base import RESTAuthFunction
+ auth_fn = RESTAuthFunction({}, server_instance.auth_provider)
+ expected_headers = auth_fn(rest_auth_parameter)
+ expected_token = expected_headers.get(
+ AUTHORIZATION_HEADER_KEY, "")
+ return token == expected_token
def _send_response(self, status_code: int, body: str):
"""Send HTTP response"""
@@ -514,6 +532,18 @@ class RESTCatalogServer:
response = database
return self._mock_response(response, 200)
+ elif method == "POST":
+ request_body = JSON.from_json(data, AlterDatabaseRequest)
+ removals = request_body.removals or []
+ updates = request_body.updates or {}
+ options = dict(database.options) if database.options else {}
+ options.update(updates)
+ for key in removals:
+ options.pop(key, None)
+ self.database_store[database_name] = self.mock_database(
+ database_name, options)
+ return self._mock_response("", 200)
+
elif method == "DELETE":
del self.database_store[database_name]
return self._mock_response("", 200)