wgtmac commented on code in PR #406: URL: https://github.com/apache/iceberg-cpp/pull/406#discussion_r2617781474
########## src/iceberg/catalog/rest/endpoint.h: ########## @@ -0,0 +1,147 @@ +/* + * 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. + */ + +#pragma once + +#include <string> +#include <string_view> + +#include "iceberg/catalog/rest/iceberg_rest_export.h" +#include "iceberg/result.h" + +/// \file iceberg/catalog/rest/endpoint.h +/// Endpoint definitions for Iceberg REST API operations. + +namespace iceberg::rest { + +/// \brief HTTP method enumeration. +enum class HttpMethod : uint8_t { GET, POST, PUT, DELETE, HEAD }; + +/// \brief Convert HttpMethod to string representation. +constexpr std::string_view ToString(HttpMethod method); + +/// \brief An Endpoint is an immutable value object identifying a specific REST API +/// operation. It consists of: +/// - HTTP method (GET, POST, DELETE, etc.) +/// - Path template (e.g., "/v1/{prefix}/namespaces/{namespace}") +class ICEBERG_REST_EXPORT Endpoint { + public: + /// \brief Create an endpoint with method and path template. + /// + /// \param method HTTP method (GET, POST, etc.) + /// \param path_template Path template with placeholders (e.g., "/v1/{prefix}/tables") + /// \return Endpoint instance or error if invalid + static Result<Endpoint> Create(HttpMethod method, std::string path_template); Review Comment: ```suggestion static Result<Endpoint> Make(HttpMethod method, std::string_view path); ``` The input does not have to be a string, and `template` suffix is confusing. BTW, we use `Make` instead of `Create` across this repo. ########## src/iceberg/catalog/rest/endpoint.h: ########## @@ -0,0 +1,147 @@ +/* + * 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. + */ + +#pragma once + +#include <string> +#include <string_view> + +#include "iceberg/catalog/rest/iceberg_rest_export.h" +#include "iceberg/result.h" + +/// \file iceberg/catalog/rest/endpoint.h +/// Endpoint definitions for Iceberg REST API operations. + +namespace iceberg::rest { + +/// \brief HTTP method enumeration. +enum class HttpMethod : uint8_t { GET, POST, PUT, DELETE, HEAD }; + +/// \brief Convert HttpMethod to string representation. +constexpr std::string_view ToString(HttpMethod method); + +/// \brief An Endpoint is an immutable value object identifying a specific REST API +/// operation. It consists of: +/// - HTTP method (GET, POST, DELETE, etc.) +/// - Path template (e.g., "/v1/{prefix}/namespaces/{namespace}") +class ICEBERG_REST_EXPORT Endpoint { + public: + /// \brief Create an endpoint with method and path template. + /// + /// \param method HTTP method (GET, POST, etc.) + /// \param path_template Path template with placeholders (e.g., "/v1/{prefix}/tables") + /// \return Endpoint instance or error if invalid + static Result<Endpoint> Create(HttpMethod method, std::string path_template); + + /// \brief Parse endpoint from string representation. + /// + /// \param str String in format "METHOD /path/template" (e.g., "GET /v1/namespaces") + /// \return Endpoint instance or error if malformed + static Result<Endpoint> FromString(std::string_view str); Review Comment: Does `METHOD` have to be all upper-cased? It would be good to document the expectation. ########## src/iceberg/catalog/rest/CMakeLists.txt: ########## @@ -18,6 +18,7 @@ set(ICEBERG_REST_SOURCES rest_catalog.cc catalog_properties.cc + endpoint.cc Review Comment: Please sort them alphabetically. ########## src/iceberg/catalog/rest/endpoint.h: ########## @@ -0,0 +1,147 @@ +/* + * 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. + */ + +#pragma once + +#include <string> +#include <string_view> + +#include "iceberg/catalog/rest/iceberg_rest_export.h" +#include "iceberg/result.h" + +/// \file iceberg/catalog/rest/endpoint.h +/// Endpoint definitions for Iceberg REST API operations. + +namespace iceberg::rest { + +/// \brief HTTP method enumeration. +enum class HttpMethod : uint8_t { GET, POST, PUT, DELETE_, HEAD }; Review Comment: ```suggestion enum class HttpMethod : uint8_t { kGet, kPost, kPut, kDelete, kHead }; ``` ########## src/iceberg/catalog/rest/json_internal.cc: ########## @@ -73,7 +73,10 @@ nlohmann::json ToJson(const CatalogConfig& config) { nlohmann::json json; json[kOverrides] = config.overrides; json[kDefaults] = config.defaults; - SetContainerField(json, kEndpoints, config.endpoints); + for (const auto& endpoint : config.endpoints) { + auto endpoint_str = endpoint.ToString(); + json[kEndpoints].push_back(endpoint_str); Review Comment: ```suggestion json[kEndpoints].emplace_back(endpoint.ToString()); ``` ########## src/iceberg/catalog/rest/endpoint.h: ########## @@ -0,0 +1,147 @@ +/* + * 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. + */ + +#pragma once + +#include <string> +#include <string_view> + +#include "iceberg/catalog/rest/iceberg_rest_export.h" +#include "iceberg/result.h" + +/// \file iceberg/catalog/rest/endpoint.h +/// Endpoint definitions for Iceberg REST API operations. + +namespace iceberg::rest { + +/// \brief HTTP method enumeration. +enum class HttpMethod : uint8_t { GET, POST, PUT, DELETE_, HEAD }; + +/// \brief Convert HttpMethod to string representation. +constexpr std::string_view ToString(HttpMethod method); + +/// \brief An Endpoint is an immutable value object identifying a specific REST API +/// operation. It consists of: +/// - HTTP method (GET, POST, DELETE, etc.) +/// - Path template (e.g., "/v1/{prefix}/namespaces/{namespace}") +class ICEBERG_REST_EXPORT Endpoint { + public: + /// \brief Create an endpoint with method and path template. + /// + /// \param method HTTP method (GET, POST, etc.) + /// \param path_template Path template with placeholders (e.g., "/v1/{prefix}/tables") + /// \return Endpoint instance or error if invalid + static Result<Endpoint> Create(HttpMethod method, std::string path_template); + + /// \brief Parse endpoint from string representation. + /// + /// \param str String in format "METHOD /path/template" (e.g., "GET /v1/namespaces") + /// \return Endpoint instance or error if malformed + static Result<Endpoint> FromString(std::string_view str); + + /// \brief Get the HTTP method. + constexpr HttpMethod method() const { return method_; } + + /// \brief Get the path template. + std::string_view path_template() const { return path_template_; } + + /// \brief Serialize to "METHOD /path" format. + std::string ToString() const; + + /// \brief Equality comparison operator. + constexpr bool operator==(const Endpoint& other) const { + return method_ == other.method_ && path_template_ == other.path_template_; + } + + /// \brief Three-way comparison operator (C++20). Review Comment: ```suggestion ``` ########## src/iceberg/catalog/rest/endpoint.h: ########## @@ -0,0 +1,147 @@ +/* + * 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. + */ + +#pragma once + +#include <string> +#include <string_view> + +#include "iceberg/catalog/rest/iceberg_rest_export.h" +#include "iceberg/result.h" + +/// \file iceberg/catalog/rest/endpoint.h +/// Endpoint definitions for Iceberg REST API operations. + +namespace iceberg::rest { + +/// \brief HTTP method enumeration. +enum class HttpMethod : uint8_t { GET, POST, PUT, DELETE, HEAD }; Review Comment: To follow our style, please use `kGet`, `kPost`, etc. ########## src/iceberg/catalog/rest/endpoint.h: ########## @@ -0,0 +1,147 @@ +/* + * 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. + */ + +#pragma once + +#include <string> +#include <string_view> + +#include "iceberg/catalog/rest/iceberg_rest_export.h" +#include "iceberg/result.h" + +/// \file iceberg/catalog/rest/endpoint.h +/// Endpoint definitions for Iceberg REST API operations. + +namespace iceberg::rest { + +/// \brief HTTP method enumeration. +enum class HttpMethod : uint8_t { GET, POST, PUT, DELETE_, HEAD }; + +/// \brief Convert HttpMethod to string representation. +constexpr std::string_view ToString(HttpMethod method); + +/// \brief An Endpoint is an immutable value object identifying a specific REST API +/// operation. It consists of: +/// - HTTP method (GET, POST, DELETE, etc.) +/// - Path template (e.g., "/v1/{prefix}/namespaces/{namespace}") +class ICEBERG_REST_EXPORT Endpoint { + public: + /// \brief Create an endpoint with method and path template. + /// + /// \param method HTTP method (GET, POST, etc.) + /// \param path_template Path template with placeholders (e.g., "/v1/{prefix}/tables") + /// \return Endpoint instance or error if invalid + static Result<Endpoint> Create(HttpMethod method, std::string path_template); + + /// \brief Parse endpoint from string representation. + /// + /// \param str String in format "METHOD /path/template" (e.g., "GET /v1/namespaces") + /// \return Endpoint instance or error if malformed + static Result<Endpoint> FromString(std::string_view str); + + /// \brief Get the HTTP method. + constexpr HttpMethod method() const { return method_; } + + /// \brief Get the path template. + std::string_view path_template() const { return path_template_; } + + /// \brief Serialize to "METHOD /path" format. + std::string ToString() const; + + /// \brief Equality comparison operator. Review Comment: ```suggestion ``` ########## src/iceberg/catalog/rest/endpoint.cc: ########## @@ -0,0 +1,89 @@ +/* + * 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. + */ + +#include "iceberg/catalog/rest/endpoint.h" + +#include <format> + +namespace iceberg::rest { + +constexpr std::string_view ToString(HttpMethod method) { + switch (method) { + case HttpMethod::GET: + return "GET"; + case HttpMethod::POST: + return "POST"; + case HttpMethod::PUT: + return "PUT"; + case HttpMethod::DELETE_: + return "DELETE"; + case HttpMethod::HEAD: + return "HEAD"; + } + return "UNKNOWN"; +} + +Result<Endpoint> Endpoint::Create(HttpMethod method, std::string path_template) { + if (path_template.empty()) { + return InvalidArgument("Path template cannot be empty"); + } + return Endpoint(method, path_template); +} + +Result<Endpoint> Endpoint::FromString(std::string_view str) { + auto space_pos = str.find(' '); + if (space_pos == std::string_view::npos || + str.find(' ', space_pos + 1) != std::string_view::npos) { + return InvalidArgument( + "Invalid endpoint format (must consist of two elements separated by a single " + "space): {}", Review Comment: ```suggestion "space): '{}'", ``` ########## src/iceberg/catalog/rest/endpoint.h: ########## @@ -0,0 +1,147 @@ +/* + * 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. + */ + +#pragma once + +#include <string> +#include <string_view> + +#include "iceberg/catalog/rest/iceberg_rest_export.h" +#include "iceberg/result.h" + +/// \file iceberg/catalog/rest/endpoint.h +/// Endpoint definitions for Iceberg REST API operations. + +namespace iceberg::rest { + +/// \brief HTTP method enumeration. +enum class HttpMethod : uint8_t { GET, POST, PUT, DELETE_, HEAD }; + +/// \brief Convert HttpMethod to string representation. +constexpr std::string_view ToString(HttpMethod method); + +/// \brief An Endpoint is an immutable value object identifying a specific REST API +/// operation. It consists of: +/// - HTTP method (GET, POST, DELETE, etc.) +/// - Path template (e.g., "/v1/{prefix}/namespaces/{namespace}") +class ICEBERG_REST_EXPORT Endpoint { + public: + /// \brief Create an endpoint with method and path template. + /// + /// \param method HTTP method (GET, POST, etc.) + /// \param path_template Path template with placeholders (e.g., "/v1/{prefix}/tables") + /// \return Endpoint instance or error if invalid + static Result<Endpoint> Create(HttpMethod method, std::string path_template); + + /// \brief Parse endpoint from string representation. + /// + /// \param str String in format "METHOD /path/template" (e.g., "GET /v1/namespaces") + /// \return Endpoint instance or error if malformed + static Result<Endpoint> FromString(std::string_view str); + + /// \brief Get the HTTP method. + constexpr HttpMethod method() const { return method_; } + + /// \brief Get the path template. + std::string_view path_template() const { return path_template_; } Review Comment: ```suggestion /// \brief Get the path. std::string_view path() const { return path_; } ``` Let's make it shorter. ########## src/iceberg/catalog/rest/endpoint.h: ########## @@ -0,0 +1,147 @@ +/* + * 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. + */ + +#pragma once + +#include <string> +#include <string_view> + +#include "iceberg/catalog/rest/iceberg_rest_export.h" +#include "iceberg/result.h" + +/// \file iceberg/catalog/rest/endpoint.h +/// Endpoint definitions for Iceberg REST API operations. + +namespace iceberg::rest { + +/// \brief HTTP method enumeration. +enum class HttpMethod : uint8_t { GET, POST, PUT, DELETE_, HEAD }; + +/// \brief Convert HttpMethod to string representation. +constexpr std::string_view ToString(HttpMethod method); + +/// \brief An Endpoint is an immutable value object identifying a specific REST API +/// operation. It consists of: +/// - HTTP method (GET, POST, DELETE, etc.) +/// - Path template (e.g., "/v1/{prefix}/namespaces/{namespace}") +class ICEBERG_REST_EXPORT Endpoint { + public: + /// \brief Create an endpoint with method and path template. + /// + /// \param method HTTP method (GET, POST, etc.) + /// \param path_template Path template with placeholders (e.g., "/v1/{prefix}/tables") + /// \return Endpoint instance or error if invalid + static Result<Endpoint> Create(HttpMethod method, std::string path_template); + + /// \brief Parse endpoint from string representation. + /// + /// \param str String in format "METHOD /path/template" (e.g., "GET /v1/namespaces") + /// \return Endpoint instance or error if malformed + static Result<Endpoint> FromString(std::string_view str); + + /// \brief Get the HTTP method. + constexpr HttpMethod method() const { return method_; } + + /// \brief Get the path template. + std::string_view path_template() const { return path_template_; } + + /// \brief Serialize to "METHOD /path" format. + std::string ToString() const; + + /// \brief Equality comparison operator. Review Comment: We don't need comment like this ########## src/iceberg/catalog/rest/json_internal.cc: ########## @@ -85,8 +88,15 @@ Result<CatalogConfig> CatalogConfigFromJson(const nlohmann::json& json) { ICEBERG_ASSIGN_OR_RAISE( config.defaults, GetJsonValueOrDefault<decltype(config.defaults)>(json, kDefaults)); ICEBERG_ASSIGN_OR_RAISE( - config.endpoints, - GetJsonValueOrDefault<std::vector<std::string>>(json, kEndpoints)); + auto endpoints, GetJsonValueOrDefault<std::vector<std::string>>(json, kEndpoints)); + for (const auto& endpoint_str : endpoints) { + auto endpoint_result = Endpoint::FromString(endpoint_str); + if (!endpoint_result.has_value()) { + // Convert to JsonParseError in JSON deserialization context + return JsonParseError("{}", endpoint_result.error().message); + } + config.endpoints.push_back(std::move(*endpoint_result)); Review Comment: ```suggestion config.endpoints.emplace_back(std::move(endpoint_result.value())); ``` ########## src/iceberg/catalog/rest/json_internal.cc: ########## @@ -85,8 +88,15 @@ Result<CatalogConfig> CatalogConfigFromJson(const nlohmann::json& json) { ICEBERG_ASSIGN_OR_RAISE( config.defaults, GetJsonValueOrDefault<decltype(config.defaults)>(json, kDefaults)); ICEBERG_ASSIGN_OR_RAISE( - config.endpoints, - GetJsonValueOrDefault<std::vector<std::string>>(json, kEndpoints)); + auto endpoints, GetJsonValueOrDefault<std::vector<std::string>>(json, kEndpoints)); + for (const auto& endpoint_str : endpoints) { Review Comment: ```suggestion config.endpoints.reserve(endpoints.size()); for (const auto& endpoint_str : endpoints) { ``` ########## src/iceberg/catalog/rest/rest_catalog.cc: ########## @@ -44,53 +46,85 @@ namespace iceberg::rest { namespace { -// Fetch server config and merge it with client config -Result<std::unique_ptr<RestCatalogProperties>> FetchConfig( - const ResourcePaths& paths, const RestCatalogProperties& config) { - ICEBERG_ASSIGN_OR_RAISE(auto config_endpoint, paths.Config()); - HttpClient client(config.ExtractHeaders()); +/// \brief Get the default set of endpoints for backwards compatibility according to the +/// iceberg rest spec. +std::set<Endpoint> GetDefaultEndpoints() { Review Comment: Why not `unordered_set`? Since this set is frequently called, it would be good if we use `unordered_set` to improve performance. ########## src/iceberg/catalog/rest/rest_catalog.cc: ########## @@ -131,29 +168,50 @@ Status RestCatalog::CreateNamespace( Result<std::unordered_map<std::string, std::string>> RestCatalog::GetNamespaceProperties( const Namespace& ns) const { - ICEBERG_ASSIGN_OR_RAISE(auto endpoint, paths_->Namespace_(ns)); + ICEBERG_RETURN_UNEXPECTED( + CheckEndpoint(supported_endpoints_, Endpoint::GetNamespaceProperties())); + + ICEBERG_ASSIGN_OR_RAISE(auto path, paths_->Namespace_(ns)); ICEBERG_ASSIGN_OR_RAISE(const auto response, - client_->Get(endpoint, /*params=*/{}, /*headers=*/{}, + client_->Get(path, /*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; } Status RestCatalog::DropNamespace(const Namespace& ns) { - ICEBERG_ASSIGN_OR_RAISE(auto endpoint, paths_->Namespace_(ns)); + ICEBERG_RETURN_UNEXPECTED( + CheckEndpoint(supported_endpoints_, Endpoint::DropNamespace())); + ICEBERG_ASSIGN_OR_RAISE(auto path, paths_->Namespace_(ns)); ICEBERG_ASSIGN_OR_RAISE( const auto response, - client_->Delete(endpoint, /*headers=*/{}, *DropNamespaceErrorHandler::Instance())); + client_->Delete(path, /*headers=*/{}, *DropNamespaceErrorHandler::Instance())); return {}; } Result<bool> 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 check = CheckEndpoint(supported_endpoints_, Endpoint::NamespaceExists()); + // If server does not support HEAD endpoint, fall back to GetNamespaceProperties + if (!check.has_value()) { + ICEBERG_ASSIGN_OR_RAISE(auto path, paths_->Namespace_(ns)); + auto response = client_->Get(path, /*params=*/{}, /*headers=*/{}, Review Comment: Can we directly call `GetNamespaceProperties` and wrap the error? -- 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] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
