Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/iceberg/catalog/rest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@
# specific language governing permissions and limitations
# under the License.

set(ICEBERG_REST_SOURCES rest_catalog.cc json_internal.cc)
set(ICEBERG_REST_SOURCES
rest_catalog.cc
catalog_properties.cc
error_handlers.cc
http_client.cc
json_internal.cc
resource_paths.cc
rest_util.cc)

set(ICEBERG_REST_STATIC_BUILD_INTERFACE_LIBS)
set(ICEBERG_REST_SHARED_BUILD_INTERFACE_LIBS)
Expand Down
51 changes: 51 additions & 0 deletions src/iceberg/catalog/rest/catalog_properties.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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/catalog_properties.h"

#include <string_view>

namespace iceberg::rest {

std::unique_ptr<RestCatalogProperties> RestCatalogProperties::default_properties() {
return std::unique_ptr<RestCatalogProperties>(new RestCatalogProperties());
}

std::unique_ptr<RestCatalogProperties> RestCatalogProperties::FromMap(
const std::unordered_map<std::string, std::string>& properties) {
auto rest_catalog_config =
std::unique_ptr<RestCatalogProperties>(new RestCatalogProperties());
rest_catalog_config->configs_ = properties;
return rest_catalog_config;
}

std::unordered_map<std::string, std::string> RestCatalogProperties::ExtractHeaders()
const {
return Extract(kHeaderPrefix);
}

Result<std::string_view> RestCatalogProperties::Uri() const {
auto it = configs_.find(kUri.key());
if (it == configs_.end() || it->second.empty()) {
return InvalidArgument("Rest catalog configuration property 'uri' is required.");
}
return it->second;
}

} // namespace iceberg::rest
71 changes: 71 additions & 0 deletions src/iceberg/catalog/rest/catalog_properties.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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 <memory>
#include <string>
#include <unordered_map>

#include "iceberg/catalog/rest/iceberg_rest_export.h"
#include "iceberg/result.h"
#include "iceberg/util/config.h"

/// \file iceberg/catalog/rest/catalog_properties.h
/// \brief RestCatalogProperties implementation for Iceberg REST API.

namespace iceberg::rest {

/// \brief Configuration class for a REST Catalog.
class ICEBERG_REST_EXPORT RestCatalogProperties
: public ConfigBase<RestCatalogProperties> {
public:
template <typename T>
using Entry = const ConfigBase<RestCatalogProperties>::Entry<T>;

/// \brief The URI of the REST catalog server.
inline static Entry<std::string> kUri{"uri", ""};
/// \brief The name of the catalog.
inline static Entry<std::string> kName{"name", ""};
/// \brief The warehouse path.
inline static Entry<std::string> kWarehouse{"warehouse", ""};
/// \brief The optional prefix for REST API paths.
inline static Entry<std::string> kPrefix{"prefix", ""};
/// \brief The prefix for HTTP headers.
inline static constexpr std::string_view kHeaderPrefix = "header.";

/// \brief Create a default RestCatalogProperties instance.
static std::unique_ptr<RestCatalogProperties> default_properties();

/// \brief Create a RestCatalogProperties instance from a map of key-value pairs.
static std::unique_ptr<RestCatalogProperties> FromMap(
const std::unordered_map<std::string, std::string>& properties);

/// \brief Returns HTTP headers to be added to every request.
std::unordered_map<std::string, std::string> ExtractHeaders() const;

/// \brief Get the URI of the REST catalog server.
/// \return The URI if configured, or an error if not set or empty.
Result<std::string_view> Uri() const;

private:
RestCatalogProperties() = default;
};

} // namespace iceberg::rest
44 changes: 44 additions & 0 deletions src/iceberg/catalog/rest/constant.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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 "iceberg/version.h"

/// \file iceberg/catalog/rest/constant.h
/// Constant values for Iceberg REST API.

namespace iceberg::rest {

inline const std::string kHeaderContentType = "Content-Type";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you apply this change? I would prefer constexpr std::string_view instead of const std::string.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reason is that we still need to wrap it with std::string every time to create http headers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So @HeartLinked may need to remove std::string(...) wrapper in some files, such as src/iceberg/catalog/rest/config.cc.

inline const std::string kHeaderAccept = "Accept";
inline const std::string kHeaderXClientVersion = "X-Client-Version";
inline const std::string kHeaderUserAgent = "User-Agent";

inline const std::string kMimeTypeApplicationJson = "application/json";
inline const std::string kMimeTypeFormUrlEncoded = "application/x-www-form-urlencoded";
inline const std::string kUserAgentPrefix = "iceberg-cpp/";
inline const std::string kUserAgent = "iceberg-cpp/" ICEBERG_VERSION_STRING;

inline const std::string kQueryParamParent = "parent";
inline const std::string kQueryParamPageToken = "page_token";

} // namespace iceberg::rest
186 changes: 186 additions & 0 deletions src/iceberg/catalog/rest/error_handlers.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
* 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/error_handlers.h"

#include <string_view>

#include "iceberg/catalog/rest/types.h"

namespace iceberg::rest {

namespace {

constexpr std::string_view kIllegalArgumentException = "IllegalArgumentException";
constexpr std::string_view kNoSuchNamespaceException = "NoSuchNamespaceException";
constexpr std::string_view kNamespaceNotEmptyException = "NamespaceNotEmptyException";

} // namespace

const std::shared_ptr<DefaultErrorHandler>& DefaultErrorHandler::Instance() {
static const std::shared_ptr<DefaultErrorHandler> instance{new DefaultErrorHandler()};
return instance;
}

Status DefaultErrorHandler::Accept(const ErrorModel& error) const {
switch (error.code) {
case 400:
if (error.type == kIllegalArgumentException) {
return InvalidArgument(error.message);
}
return BadRequest("Malformed request: {}", error.message);
case 401:
return NotAuthorized("Not authorized: {}", error.message);
case 403:
return Forbidden("Forbidden: {}", error.message);
case 405:
case 406:
break;
case 500:
return InternalServerError("Server error: {}: {}", error.type, error.message);
case 501:
return NotSupported(error.message);
case 503:
return ServiceUnavailable("Service unavailable: {}", error.message);
}

return RestError("Code: {}, message: {}", error.code, error.message);
}

const std::shared_ptr<NamespaceErrorHandler>& NamespaceErrorHandler::Instance() {
static const std::shared_ptr<NamespaceErrorHandler> instance{
new NamespaceErrorHandler()};
return instance;
}

Status NamespaceErrorHandler::Accept(const ErrorModel& error) const {
switch (error.code) {
case 400:
if (error.type == kNamespaceNotEmptyException) {
return NamespaceNotEmpty(error.message);
}
return BadRequest("Malformed request: {}", error.message);
case 404:
return NoSuchNamespace(error.message);
case 409:
return AlreadyExists(error.message);
case 422:
return RestError("Unable to process: {}", error.message);
}

return DefaultErrorHandler::Accept(error);
}

const std::shared_ptr<DropNamespaceErrorHandler>& DropNamespaceErrorHandler::Instance() {
static const std::shared_ptr<DropNamespaceErrorHandler> instance{
new DropNamespaceErrorHandler()};
return instance;
}

Status DropNamespaceErrorHandler::Accept(const ErrorModel& error) const {
if (error.code == 409) {
return NamespaceNotEmpty(error.message);
}

return NamespaceErrorHandler::Accept(error);
}

const std::shared_ptr<TableErrorHandler>& TableErrorHandler::Instance() {
static const std::shared_ptr<TableErrorHandler> instance{new TableErrorHandler()};
return instance;
}

Status TableErrorHandler::Accept(const ErrorModel& error) const {
switch (error.code) {
case 404:
if (error.type == kNoSuchNamespaceException) {
return NoSuchNamespace(error.message);
}
return NoSuchTable(error.message);
case 409:
return AlreadyExists(error.message);
}

return DefaultErrorHandler::Accept(error);
}

const std::shared_ptr<ViewErrorHandler>& ViewErrorHandler::Instance() {
static const std::shared_ptr<ViewErrorHandler> instance{new ViewErrorHandler()};
return instance;
}

Status ViewErrorHandler::Accept(const ErrorModel& error) const {
switch (error.code) {
case 404:
if (error.type == kNoSuchNamespaceException) {
return NoSuchNamespace(error.message);
}
return NoSuchView(error.message);
case 409:
return AlreadyExists(error.message);
}

return DefaultErrorHandler::Accept(error);
}

const std::shared_ptr<TableCommitErrorHandler>& TableCommitErrorHandler::Instance() {
static const std::shared_ptr<TableCommitErrorHandler> instance{
new TableCommitErrorHandler()};
return instance;
}

Status TableCommitErrorHandler::Accept(const ErrorModel& error) const {
switch (error.code) {
case 404:
return NoSuchTable(error.message);
case 409:
return CommitFailed("Commit failed: {}", error.message);
case 500:
case 502:
case 503:
case 504:
return CommitStateUnknown("Service failed: {}: {}", error.code, error.message);
}

return DefaultErrorHandler::Accept(error);
}

const std::shared_ptr<ViewCommitErrorHandler>& ViewCommitErrorHandler::Instance() {
static const std::shared_ptr<ViewCommitErrorHandler> instance{
new ViewCommitErrorHandler()};
return instance;
}

Status ViewCommitErrorHandler::Accept(const ErrorModel& error) const {
switch (error.code) {
case 404:
return NoSuchView(error.message);
case 409:
return CommitFailed("Commit failed: {}", error.message);
case 500:
case 502:
case 503:
case 504:
return CommitStateUnknown("Service failed: {}: {}", error.code, error.message);
}

return DefaultErrorHandler::Accept(error);
}

} // namespace iceberg::rest
Loading
Loading