From d8e4601926fc9f9643fe99c3ed2bf7a33e6513c0 Mon Sep 17 00:00:00 2001 From: Li Feiyang Date: Mon, 8 Dec 2025 18:26:49 +0800 Subject: [PATCH 1/3] 1 --- src/iceberg/catalog/rest/http_client.cc | 37 +++++- src/iceberg/catalog/rest/json_internal.cc | 3 + src/iceberg/catalog/rest/rest_catalog.cc | 70 +++++++--- src/iceberg/catalog/rest/rest_util.cc | 43 ++++++ src/iceberg/catalog/rest/rest_util.h | 9 ++ src/iceberg/catalog/rest/types.h | 4 +- src/iceberg/test/rest_catalog_test.cc | 151 ++++++++++++++++++++++ 7 files changed, 294 insertions(+), 23 deletions(-) diff --git a/src/iceberg/catalog/rest/http_client.cc b/src/iceberg/catalog/rest/http_client.cc index 3e70b9d94..0560676c3 100644 --- a/src/iceberg/catalog/rest/http_client.cc +++ b/src/iceberg/catalog/rest/http_client.cc @@ -25,6 +25,7 @@ #include "iceberg/catalog/rest/constant.h" #include "iceberg/catalog/rest/error_handlers.h" #include "iceberg/catalog/rest/json_internal.h" +#include "iceberg/catalog/rest/rest_util.h" #include "iceberg/json_internal.h" #include "iceberg/result.h" #include "iceberg/util/macros.h" @@ -63,6 +64,9 @@ std::unordered_map HttpResponse::headers() const { namespace { +/// \brief Default error type for unparseable REST responses. +constexpr std::string_view kRestExceptionType = "RESTException"; + /// \brief Merges global default headers with request-specific headers. /// /// Combines the global headers derived from RestCatalogProperties with the headers @@ -96,16 +100,37 @@ bool IsSuccessful(int32_t status_code) { || status_code == 304; // Not Modified } +/// \brief Builds a default ErrorResponse when the response body cannot be parsed. +ErrorResponse BuildDefaultErrorResponse(const cpr::Response& response) { + return { + .code = static_cast(response.status_code), + .type = std::string(kRestExceptionType), + .message = !response.reason.empty() ? response.reason + : GetStandardReasonPhrase(response.status_code), + }; +} + +/// \brief Tries to parse the response body as an ErrorResponse. +Result TryParseErrorResponse(const std::string& text) { + if (text.empty()) { + return InvalidArgument("Empty response body"); + } + ICEBERG_ASSIGN_OR_RAISE(auto json_result, FromJsonString(text)); + ICEBERG_ASSIGN_OR_RAISE(auto error_result, ErrorResponseFromJson(json_result)); + return error_result; +} + /// \brief Handles failure responses by invoking the provided error handler. Status HandleFailureResponse(const cpr::Response& response, const ErrorHandler& error_handler) { - if (!IsSuccessful(response.status_code)) { - // TODO(gangwu): response status code is lost, wrap it with RestError. - ICEBERG_ASSIGN_OR_RAISE(auto json, FromJsonString(response.text)); - ICEBERG_ASSIGN_OR_RAISE(auto error_response, ErrorResponseFromJson(json)); - return error_handler.Accept(error_response); + if (IsSuccessful(response.status_code)) { + return {}; } - return {}; + auto parse_result = TryParseErrorResponse(response.text); + const ErrorResponse final_error = parse_result.has_value() + ? std::move(*parse_result) + : BuildDefaultErrorResponse(response); + return error_handler.Accept(final_error); } } // namespace diff --git a/src/iceberg/catalog/rest/json_internal.cc b/src/iceberg/catalog/rest/json_internal.cc index 61fce9381..c60b406d9 100644 --- a/src/iceberg/catalog/rest/json_internal.cc +++ b/src/iceberg/catalog/rest/json_internal.cc @@ -213,6 +213,7 @@ Result LoadTableResultFromJson(const nlohmann::json& json) { ICEBERG_ASSIGN_OR_RAISE(result.metadata, TableMetadataFromJson(metadata_json)); ICEBERG_ASSIGN_OR_RAISE(result.config, GetJsonValueOrDefault(json, kConfig)); + ICEBERG_RETURN_UNEXPECTED(result.Validate()); return result; } @@ -257,6 +258,7 @@ Result CreateNamespaceResponseFromJson( ICEBERG_ASSIGN_OR_RAISE( response.properties, GetJsonValueOrDefault(json, kProperties)); + ICEBERG_RETURN_UNEXPECTED(response.Validate()); return response; } @@ -274,6 +276,7 @@ Result GetNamespaceResponseFromJson(const nlohmann::json& ICEBERG_ASSIGN_OR_RAISE( response.properties, GetJsonValueOrDefault(json, kProperties)); + ICEBERG_RETURN_UNEXPECTED(response.Validate()); return response; } diff --git a/src/iceberg/catalog/rest/rest_catalog.cc b/src/iceberg/catalog/rest/rest_catalog.cc index e4553ace1..0e3d9f196 100644 --- a/src/iceberg/catalog/rest/rest_catalog.cc +++ b/src/iceberg/catalog/rest/rest_catalog.cc @@ -34,7 +34,9 @@ #include "iceberg/catalog/rest/rest_catalog.h" #include "iceberg/catalog/rest/rest_util.h" #include "iceberg/json_internal.h" +#include "iceberg/partition_spec.h" #include "iceberg/result.h" +#include "iceberg/schema.h" #include "iceberg/table.h" #include "iceberg/util/macros.h" @@ -115,29 +117,67 @@ Result> RestCatalog::ListNamespaces(const Namespace& ns) } Status RestCatalog::CreateNamespace( - [[maybe_unused]] const Namespace& ns, - [[maybe_unused]] const std::unordered_map& properties) { - return NotImplemented("Not implemented"); + const Namespace& ns, const std::unordered_map& properties) { + ICEBERG_ASSIGN_OR_RAISE(auto endpoint, paths_->Namespaces()); + CreateNamespaceRequest request{.namespace_ = ns, .properties = properties}; + ICEBERG_ASSIGN_OR_RAISE(auto json_request, ToJsonString(ToJson(request))); + ICEBERG_ASSIGN_OR_RAISE(const auto& response, + client_->Post(endpoint, json_request, /*headers=*/{}, + *NamespaceErrorHandler::Instance())); + ICEBERG_ASSIGN_OR_RAISE(auto json, FromJsonString(response.body())); + ICEBERG_ASSIGN_OR_RAISE(auto create_response, CreateNamespaceResponseFromJson(json)); + return {}; } Result> RestCatalog::GetNamespaceProperties( - [[maybe_unused]] const Namespace& ns) const { - return NotImplemented("Not implemented"); -} - -Status RestCatalog::DropNamespace([[maybe_unused]] const Namespace& ns) { - return NotImplemented("Not implemented"); + const Namespace& ns) const { + ICEBERG_ASSIGN_OR_RAISE(auto endpoint, paths_->Namespace_(ns)); + ICEBERG_ASSIGN_OR_RAISE(const auto& response, + client_->Get(endpoint, /*params=*/{}, /*headers=*/{}, + *NamespaceErrorHandler::Instance())); + ICEBERG_ASSIGN_OR_RAISE(auto json, FromJsonString(response.body())); + ICEBERG_ASSIGN_OR_RAISE(auto get_response, GetNamespaceResponseFromJson(json)); + return get_response.properties; } -Result RestCatalog::NamespaceExists([[maybe_unused]] const Namespace& ns) const { - return NotImplemented("Not implemented"); +Status RestCatalog::DropNamespace(const Namespace& ns) { + ICEBERG_ASSIGN_OR_RAISE(auto endpoint, paths_->Namespace_(ns)); + ICEBERG_ASSIGN_OR_RAISE( + const auto& response, + client_->Delete(endpoint, /*headers=*/{}, *DropNamespaceErrorHandler::Instance())); + return {}; +} + +Result RestCatalog::NamespaceExists(const Namespace& ns) const { + ICEBERG_ASSIGN_OR_RAISE(auto endpoint, paths_->Namespace_(ns)); + auto response_or_error = + client_->Head(endpoint, /*headers=*/{}, *NamespaceErrorHandler::Instance()); + if (!response_or_error.has_value()) { + const auto& error = response_or_error.error(); + // catch NoSuchNamespaceException/404 and return false + if (error.kind == ErrorKind::kNoSuchNamespace) { + return false; + } + ICEBERG_RETURN_UNEXPECTED(response_or_error); + } + return true; } Status RestCatalog::UpdateNamespaceProperties( - [[maybe_unused]] const Namespace& ns, - [[maybe_unused]] const std::unordered_map& updates, - [[maybe_unused]] const std::unordered_set& removals) { - return NotImplemented("Not implemented"); + const Namespace& ns, const std::unordered_map& updates, + const std::unordered_set& removals) { + ICEBERG_ASSIGN_OR_RAISE(auto endpoint, paths_->NamespaceProperties(ns)); + UpdateNamespacePropertiesRequest request{ + .removals = std::vector(removals.begin(), removals.end()), + .updates = updates}; + ICEBERG_ASSIGN_OR_RAISE(auto json_request, ToJsonString(ToJson(request))); + ICEBERG_ASSIGN_OR_RAISE(const auto& response, + client_->Post(endpoint, json_request, /*headers=*/{}, + *NamespaceErrorHandler::Instance())); + ICEBERG_ASSIGN_OR_RAISE(auto json, FromJsonString(response.body())); + ICEBERG_ASSIGN_OR_RAISE(auto update_response, + UpdateNamespacePropertiesResponseFromJson(json)); + return {}; } Result> RestCatalog::ListTables( diff --git a/src/iceberg/catalog/rest/rest_util.cc b/src/iceberg/catalog/rest/rest_util.cc index 5a0f166d5..ae33a541b 100644 --- a/src/iceberg/catalog/rest/rest_util.cc +++ b/src/iceberg/catalog/rest/rest_util.cc @@ -19,6 +19,8 @@ #include "iceberg/catalog/rest/rest_util.h" +#include + #include #include "iceberg/table_identifier.h" @@ -120,4 +122,45 @@ std::unordered_map MergeConfigs( return merged; } +std::string GetStandardReasonPhrase(int32_t status_code) { + switch (status_code) { + case 200: + return "OK"; + case 201: + return "Created"; + case 202: + return "Accepted"; + case 204: + return "No Content"; + case 400: + return "Bad Request"; + case 401: + return "Unauthorized"; + case 403: + return "Forbidden"; + case 404: + return "Not Found"; + case 405: + return "Method Not Allowed"; + case 406: + return "Not Acceptable"; + case 409: + return "Conflict"; + case 422: + return "Unprocessable Entity"; + case 500: + return "Internal Server Error"; + case 501: + return "Not Implemented"; + case 502: + return "Bad Gateway"; + case 503: + return "Service Unavailable"; + case 504: + return "Gateway Timeout"; + default: + return std::format("HTTP {}", status_code); + } +} + } // namespace iceberg::rest diff --git a/src/iceberg/catalog/rest/rest_util.h b/src/iceberg/catalog/rest/rest_util.h index 895bb2fb4..fde67a842 100644 --- a/src/iceberg/catalog/rest/rest_util.h +++ b/src/iceberg/catalog/rest/rest_util.h @@ -81,4 +81,13 @@ ICEBERG_REST_EXPORT std::unordered_map MergeConfigs( const std::unordered_map& client_configs, const std::unordered_map& server_overrides); +/// \brief Get the standard HTTP reason phrase for a status code. +/// +/// \details Returns the standard English reason phrase for common HTTP status codes. +/// For unknown status codes, returns a generic "HTTP {code}" message. +/// \param status_code The HTTP status code (e.g., 200, 404, 500). +/// \return The standard reason phrase string (e.g., "OK", "Not Found", "Internal Server +/// Error"). +ICEBERG_REST_EXPORT std::string GetStandardReasonPhrase(int32_t status_code); + } // namespace iceberg::rest diff --git a/src/iceberg/catalog/rest/types.h b/src/iceberg/catalog/rest/types.h index dbc772ec4..7760e1781 100644 --- a/src/iceberg/catalog/rest/types.h +++ b/src/iceberg/catalog/rest/types.h @@ -53,9 +53,9 @@ struct ICEBERG_REST_EXPORT CatalogConfig { /// \brief JSON error payload returned in a response with further details on the error. struct ICEBERG_REST_EXPORT ErrorResponse { - std::string message; // required - std::string type; // required uint32_t code; // required + std::string type; // required + std::string message; // required std::vector stack; /// \brief Validates the ErrorResponse. diff --git a/src/iceberg/test/rest_catalog_test.cc b/src/iceberg/test/rest_catalog_test.cc index f91782a03..49c527f64 100644 --- a/src/iceberg/test/rest_catalog_test.cc +++ b/src/iceberg/test/rest_catalog_test.cc @@ -148,4 +148,155 @@ TEST_F(RestCatalogIntegrationTest, ListNamespaces) { EXPECT_TRUE(result->empty()); } +TEST_F(RestCatalogIntegrationTest, CreateNamespace) { + auto catalog_result = CreateCatalog(); + ASSERT_THAT(catalog_result, IsOk()); + auto& catalog = catalog_result.value(); + + // Create a simple namespace + Namespace ns{.levels = {"test_ns"}}; + auto status = catalog->CreateNamespace(ns, {}); + EXPECT_THAT(status, IsOk()); + + // Verify it was created by listing + Namespace root{.levels = {}}; + auto list_result = catalog->ListNamespaces(root); + ASSERT_THAT(list_result, IsOk()); + EXPECT_EQ(list_result->size(), 1); + EXPECT_EQ(list_result->at(0).levels, std::vector{"test_ns"}); +} + +TEST_F(RestCatalogIntegrationTest, CreateNamespaceWithProperties) { + auto catalog_result = CreateCatalog(); + ASSERT_THAT(catalog_result, IsOk()); + auto& catalog = catalog_result.value(); + + // Create namespace with properties + Namespace ns{.levels = {"test_ns_props"}}; + std::unordered_map properties{ + {"owner", "test_user"}, {"description", "Test namespace with properties"}}; + auto status = catalog->CreateNamespace(ns, properties); + EXPECT_THAT(status, IsOk()); + + // Verify properties were set + auto props_result = catalog->GetNamespaceProperties(ns); + ASSERT_THAT(props_result, IsOk()); + EXPECT_EQ(props_result->at("owner"), "test_user"); + EXPECT_EQ(props_result->at("description"), "Test namespace with properties"); +} + +TEST_F(RestCatalogIntegrationTest, CreateNestedNamespace) { + auto catalog_result = CreateCatalog(); + ASSERT_THAT(catalog_result, IsOk()); + auto& catalog = catalog_result.value(); + + // Create parent namespace + Namespace parent{.levels = {"parent"}}; + auto status = catalog->CreateNamespace(parent, {}); + EXPECT_THAT(status, IsOk()); + + // Create nested namespace + Namespace child{.levels = {"parent", "child"}}; + status = catalog->CreateNamespace(child, {}); + EXPECT_THAT(status, IsOk()); + + // Verify nested namespace exists + auto list_result = catalog->ListNamespaces(parent); + ASSERT_THAT(list_result, IsOk()); + EXPECT_EQ(list_result->size(), 1); + EXPECT_EQ(list_result->at(0).levels, (std::vector{"parent", "child"})); +} + +TEST_F(RestCatalogIntegrationTest, GetNamespaceProperties) { + auto catalog_result = CreateCatalog(); + ASSERT_THAT(catalog_result, IsOk()); + auto& catalog = catalog_result.value(); + + // Create namespace with properties + Namespace ns{.levels = {"test_get_props"}}; + std::unordered_map properties{{"key1", "value1"}, + {"key2", "value2"}}; + auto status = catalog->CreateNamespace(ns, properties); + EXPECT_THAT(status, IsOk()); + + // Get properties + auto props_result = catalog->GetNamespaceProperties(ns); + ASSERT_THAT(props_result, IsOk()); + EXPECT_EQ(props_result->at("key1"), "value1"); + EXPECT_EQ(props_result->at("key2"), "value2"); +} + +TEST_F(RestCatalogIntegrationTest, NamespaceExists) { + auto catalog_result = CreateCatalog(); + ASSERT_THAT(catalog_result, IsOk()); + auto& catalog = catalog_result.value(); + + // Check non-existent namespace + Namespace ns{.levels = {"non_existent"}}; + auto exists_result = catalog->NamespaceExists(ns); + ASSERT_THAT(exists_result, IsOk()); + EXPECT_FALSE(*exists_result); + + // Create namespace + auto status = catalog->CreateNamespace(ns, {}); + EXPECT_THAT(status, IsOk()); + + // Check it now exists + exists_result = catalog->NamespaceExists(ns); + ASSERT_THAT(exists_result, IsOk()); + EXPECT_TRUE(*exists_result); +} + +TEST_F(RestCatalogIntegrationTest, UpdateNamespaceProperties) { + auto catalog_result = CreateCatalog(); + ASSERT_THAT(catalog_result, IsOk()); + auto& catalog = catalog_result.value(); + + // Create namespace with initial properties + Namespace ns{.levels = {"test_update"}}; + std::unordered_map initial_props{{"key1", "value1"}, + {"key2", "value2"}}; + auto status = catalog->CreateNamespace(ns, initial_props); + EXPECT_THAT(status, IsOk()); + + // Update properties: modify key1, add key3, remove key2 + std::unordered_map updates{{"key1", "updated_value1"}, + {"key3", "value3"}}; + std::unordered_set removals{"key2"}; + status = catalog->UpdateNamespaceProperties(ns, updates, removals); + EXPECT_THAT(status, IsOk()); + + // Verify updated properties + auto props_result = catalog->GetNamespaceProperties(ns); + ASSERT_THAT(props_result, IsOk()); + EXPECT_EQ(props_result->at("key1"), "updated_value1"); + EXPECT_EQ(props_result->at("key3"), "value3"); + EXPECT_EQ(props_result->count("key2"), 0); // Should be removed +} + +TEST_F(RestCatalogIntegrationTest, DropNamespace) { + auto catalog_result = CreateCatalog(); + ASSERT_THAT(catalog_result, IsOk()); + auto& catalog = catalog_result.value(); + + // Create namespace + Namespace ns{.levels = {"test_drop"}}; + auto status = catalog->CreateNamespace(ns, {}); + EXPECT_THAT(status, IsOk()); + + // Verify it exists + auto exists_result = catalog->NamespaceExists(ns); + ASSERT_THAT(exists_result, IsOk()); + EXPECT_TRUE(*exists_result); + + // Drop namespace + status = catalog->DropNamespace(ns); + EXPECT_THAT(status, IsOk()); + + // Verify it no longer exists + exists_result = catalog->NamespaceExists(ns); + ASSERT_THAT(exists_result, IsOk()); + EXPECT_FALSE(*exists_result); +} + } // namespace iceberg::rest From 2d0ce1d0fb05171e1f58125cb31a5871d0c40f6f Mon Sep 17 00:00:00 2001 From: Li Feiyang Date: Tue, 9 Dec 2025 10:36:30 +0800 Subject: [PATCH 2/3] 1 --- src/iceberg/test/rest_json_internal_test.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/iceberg/test/rest_json_internal_test.cc b/src/iceberg/test/rest_json_internal_test.cc index 2cae201fc..ca2671fae 100644 --- a/src/iceberg/test/rest_json_internal_test.cc +++ b/src/iceberg/test/rest_json_internal_test.cc @@ -870,26 +870,26 @@ INSTANTIATE_TEST_SUITE_P( .test_name = "WithoutStack", .expected_json_str = R"({"error":{"message":"The given namespace does not exist","type":"NoSuchNamespaceException","code":404}})", - .model = {.message = "The given namespace does not exist", + .model = {.code = 404, .type = "NoSuchNamespaceException", - .code = 404}}, + .message = "The given namespace does not exist"}}, // Error with stack trace ErrorResponseParam{ .test_name = "WithStack", .expected_json_str = R"({"error":{"message":"The given namespace does not exist","type":"NoSuchNamespaceException","code":404,"stack":["a","b"]}})", - .model = {.message = "The given namespace does not exist", + .model = {.code = 404, .type = "NoSuchNamespaceException", - .code = 404, + .message = "The given namespace does not exist", .stack = {"a", "b"}}}, // Different error type ErrorResponseParam{ .test_name = "DifferentError", .expected_json_str = R"({"error":{"message":"Internal server error","type":"InternalServerError","code":500,"stack":["line1","line2","line3"]}})", - .model = {.message = "Internal server error", + .model = {.code = 500, .type = "InternalServerError", - .code = 500, + .message = "Internal server error", .stack = {"line1", "line2", "line3"}}}), [](const ::testing::TestParamInfo& info) { return info.param.test_name; @@ -905,17 +905,17 @@ INSTANTIATE_TEST_SUITE_P( .test_name = "NullStack", .json_str = R"({"error":{"message":"The given namespace does not exist","type":"NoSuchNamespaceException","code":404,"stack":null}})", - .expected_model = {.message = "The given namespace does not exist", + .expected_model = {.code = 404, .type = "NoSuchNamespaceException", - .code = 404}}, + .message = "The given namespace does not exist"}}, // Stack field is missing (should deserialize to empty vector) ErrorResponseDeserializeParam{ .test_name = "MissingStack", .json_str = R"({"error":{"message":"The given namespace does not exist","type":"NoSuchNamespaceException","code":404}})", - .expected_model = {.message = "The given namespace does not exist", + .expected_model = {.code = 404, .type = "NoSuchNamespaceException", - .code = 404}}), + .message = "The given namespace does not exist"}}), [](const ::testing::TestParamInfo& info) { return info.param.test_name; }); From f1fbd240c57442802bb0e9cd6b211f33dcecc2f2 Mon Sep 17 00:00:00 2001 From: Li Feiyang Date: Thu, 11 Dec 2025 14:58:44 +0800 Subject: [PATCH 3/3] 1 --- src/iceberg/catalog/rest/http_client.cc | 5 +- src/iceberg/catalog/rest/rest_catalog.cc | 12 ++-- src/iceberg/catalog/rest/rest_util.cc | 90 +++++++++++++++++++++++- 3 files changed, 98 insertions(+), 9 deletions(-) diff --git a/src/iceberg/catalog/rest/http_client.cc b/src/iceberg/catalog/rest/http_client.cc index 0560676c3..d1138b787 100644 --- a/src/iceberg/catalog/rest/http_client.cc +++ b/src/iceberg/catalog/rest/http_client.cc @@ -127,9 +127,8 @@ Status HandleFailureResponse(const cpr::Response& response, return {}; } auto parse_result = TryParseErrorResponse(response.text); - const ErrorResponse final_error = parse_result.has_value() - ? std::move(*parse_result) - : BuildDefaultErrorResponse(response); + const ErrorResponse final_error = + parse_result.value_or(BuildDefaultErrorResponse(response)); return error_handler.Accept(final_error); } diff --git a/src/iceberg/catalog/rest/rest_catalog.cc b/src/iceberg/catalog/rest/rest_catalog.cc index 0e3d9f196..4a77f6585 100644 --- a/src/iceberg/catalog/rest/rest_catalog.cc +++ b/src/iceberg/catalog/rest/rest_catalog.cc @@ -101,7 +101,7 @@ Result> RestCatalog::ListNamespaces(const Namespace& ns) if (!next_token.empty()) { params[kQueryParamPageToken] = next_token; } - ICEBERG_ASSIGN_OR_RAISE(const auto& response, + ICEBERG_ASSIGN_OR_RAISE(const auto response, client_->Get(endpoint, params, /*headers=*/{}, *NamespaceErrorHandler::Instance())); ICEBERG_ASSIGN_OR_RAISE(auto json, FromJsonString(response.body())); @@ -121,7 +121,7 @@ Status RestCatalog::CreateNamespace( ICEBERG_ASSIGN_OR_RAISE(auto endpoint, paths_->Namespaces()); CreateNamespaceRequest request{.namespace_ = ns, .properties = properties}; ICEBERG_ASSIGN_OR_RAISE(auto json_request, ToJsonString(ToJson(request))); - ICEBERG_ASSIGN_OR_RAISE(const auto& response, + ICEBERG_ASSIGN_OR_RAISE(const auto response, client_->Post(endpoint, json_request, /*headers=*/{}, *NamespaceErrorHandler::Instance())); ICEBERG_ASSIGN_OR_RAISE(auto json, FromJsonString(response.body())); @@ -132,7 +132,7 @@ Status RestCatalog::CreateNamespace( Result> RestCatalog::GetNamespaceProperties( const Namespace& ns) const { ICEBERG_ASSIGN_OR_RAISE(auto endpoint, paths_->Namespace_(ns)); - ICEBERG_ASSIGN_OR_RAISE(const auto& response, + ICEBERG_ASSIGN_OR_RAISE(const auto response, client_->Get(endpoint, /*params=*/{}, /*headers=*/{}, *NamespaceErrorHandler::Instance())); ICEBERG_ASSIGN_OR_RAISE(auto json, FromJsonString(response.body())); @@ -143,13 +143,15 @@ Result> RestCatalog::GetNamespacePr Status RestCatalog::DropNamespace(const Namespace& ns) { ICEBERG_ASSIGN_OR_RAISE(auto endpoint, paths_->Namespace_(ns)); ICEBERG_ASSIGN_OR_RAISE( - const auto& response, + const auto response, client_->Delete(endpoint, /*headers=*/{}, *DropNamespaceErrorHandler::Instance())); return {}; } Result RestCatalog::NamespaceExists(const Namespace& ns) const { ICEBERG_ASSIGN_OR_RAISE(auto endpoint, paths_->Namespace_(ns)); + // TODO(Feiyang Li): checks if the server supports the namespace exists endpoint, if + // not, triggers a fallback mechanism auto response_or_error = client_->Head(endpoint, /*headers=*/{}, *NamespaceErrorHandler::Instance()); if (!response_or_error.has_value()) { @@ -171,7 +173,7 @@ Status RestCatalog::UpdateNamespaceProperties( .removals = std::vector(removals.begin(), removals.end()), .updates = updates}; ICEBERG_ASSIGN_OR_RAISE(auto json_request, ToJsonString(ToJson(request))); - ICEBERG_ASSIGN_OR_RAISE(const auto& response, + ICEBERG_ASSIGN_OR_RAISE(const auto response, client_->Post(endpoint, json_request, /*headers=*/{}, *NamespaceErrorHandler::Instance())); ICEBERG_ASSIGN_OR_RAISE(auto json, FromJsonString(response.body())); diff --git a/src/iceberg/catalog/rest/rest_util.cc b/src/iceberg/catalog/rest/rest_util.cc index ae33a541b..a1a63fa1c 100644 --- a/src/iceberg/catalog/rest/rest_util.cc +++ b/src/iceberg/catalog/rest/rest_util.cc @@ -124,18 +124,56 @@ std::unordered_map MergeConfigs( std::string GetStandardReasonPhrase(int32_t status_code) { switch (status_code) { + case 100: + return "Continue"; + case 101: + return "Switching Protocols"; + case 102: + return "Processing"; + case 103: + return "Early Hints"; case 200: return "OK"; case 201: return "Created"; case 202: return "Accepted"; + case 203: + return "Non Authoritative Information"; case 204: return "No Content"; + case 205: + return "Reset Content"; + case 206: + return "Partial Content"; + case 207: + return "Multi-Status"; + case 208: + return "Already Reported"; + case 226: + return "IM Used"; + case 300: + return "Multiple Choices"; + case 301: + return "Moved Permanently"; + case 302: + return "Moved Temporarily"; + case 303: + return "See Other"; + case 304: + return "Not Modified"; + case 305: + return "Use Proxy"; + case 307: + return "Temporary Redirect"; + case 308: + return "Permanent Redirect"; case 400: return "Bad Request"; case 401: return "Unauthorized"; + case 402: + return "Payment Required"; case 403: return "Forbidden"; case 404: @@ -144,10 +182,48 @@ std::string GetStandardReasonPhrase(int32_t status_code) { return "Method Not Allowed"; case 406: return "Not Acceptable"; + case 407: + return "Proxy Authentication Required"; + case 408: + return "Request Timeout"; case 409: return "Conflict"; + case 410: + return "Gone"; + case 411: + return "Length Required"; + case 412: + return "Precondition Failed"; + case 413: + return "Request Too Long"; + case 414: + return "Request-URI Too Long"; + case 415: + return "Unsupported Media Type"; + case 416: + return "Requested Range Not Satisfiable"; + case 417: + return "Expectation Failed"; + case 421: + return "Misdirected Request"; case 422: - return "Unprocessable Entity"; + return "Unprocessable Content"; + case 423: + return "Locked"; + case 424: + return "Failed Dependency"; + case 425: + return "Too Early"; + case 426: + return "Upgrade Required"; + case 428: + return "Precondition Required"; + case 429: + return "Too Many Requests"; + case 431: + return "Request Header Fields Too Large"; + case 451: + return "Unavailable For Legal Reasons"; case 500: return "Internal Server Error"; case 501: @@ -158,6 +234,18 @@ std::string GetStandardReasonPhrase(int32_t status_code) { return "Service Unavailable"; case 504: return "Gateway Timeout"; + case 505: + return "Http Version Not Supported"; + case 506: + return "Variant Also Negotiates"; + case 507: + return "Insufficient Storage"; + case 508: + return "Loop Detected"; + case 510: + return "Not Extended"; + case 511: + return "Network Authentication Required"; default: return std::format("HTTP {}", status_code); }