blob: 324a8408b25d70e68214096fd0b1a567255b448e [file]
/*
* Copyright 2026-present Alibaba Inc.
*
* Licensed 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 "paimon/rest/rest_messages.h"
#include <stdexcept>
#include "paimon/rest/rest_util.h"
namespace paimon {
namespace {
constexpr const char kFieldMessage[] = "message";
constexpr const char kFieldResourceType[] = "resourceType";
constexpr const char kFieldResourceName[] = "resourceName";
constexpr const char kFieldCode[] = "code";
constexpr const char kFieldDefaults[] = "defaults";
constexpr const char kFieldOverrides[] = "overrides";
constexpr const char kFieldOwner[] = "owner";
constexpr const char kFieldCreatedAt[] = "createdAt";
constexpr const char kFieldCreatedBy[] = "createdBy";
constexpr const char kFieldUpdatedAt[] = "updatedAt";
constexpr const char kFieldUpdatedBy[] = "updatedBy";
constexpr const char kFieldName[] = "name";
constexpr const char kFieldOptions[] = "options";
constexpr const char kFieldId[] = "id";
constexpr const char kFieldLocation[] = "location";
constexpr const char kFieldDatabases[] = "databases";
constexpr const char kFieldTables[] = "tables";
constexpr const char kFieldSnapshots[] = "snapshots";
constexpr const char kFieldNextPageToken[] = "nextPageToken";
constexpr const char kFieldPath[] = "path";
constexpr const char kFieldIsExternal[] = "isExternal";
constexpr const char kFieldSchemaId[] = "schemaId";
constexpr const char kFieldSchema[] = "schema";
constexpr const char kFieldIdentifier[] = "identifier";
constexpr const char kFieldDatabase[] = "database";
constexpr const char kFieldObject[] = "object";
constexpr const char kFieldSource[] = "source";
constexpr const char kFieldDestination[] = "destination";
void AddOptionalStringMember(rapidjson::Value* obj, const char* key,
const std::optional<std::string>& value,
rapidjson::Document::AllocatorType* allocator) {
if (value) {
obj->AddMember(rapidjson::StringRef(key),
RapidJsonUtil::SerializeValue(value.value(), allocator).Move(), *allocator);
}
}
rapidjson::Value SerializeIdentifier(const std::string& database, const std::string& table,
rapidjson::Document::AllocatorType* allocator) {
rapidjson::Value obj(rapidjson::kObjectType);
obj.AddMember(rapidjson::StringRef(kFieldDatabase),
RapidJsonUtil::SerializeValue(database, allocator).Move(), *allocator);
obj.AddMember(rapidjson::StringRef(kFieldObject),
RapidJsonUtil::SerializeValue(table, allocator).Move(), *allocator);
return obj;
}
void DeserializeIdentifier(const rapidjson::Value& obj, const char* key, std::string* database,
std::string* table) {
if (!obj.IsObject() || !obj.HasMember(key) || !obj[key].IsObject()) {
throw std::invalid_argument(std::string("member '") + key +
"' must exist and be an object");
}
const rapidjson::Value& identifier = obj[key];
*database = RapidJsonUtil::DeserializeKeyValue<std::string>(identifier, kFieldDatabase);
*table = RapidJsonUtil::DeserializeKeyValue<std::string>(identifier, kFieldObject);
}
std::string DeserializeRawJsonMember(const rapidjson::Value& obj, const char* key) {
if (!obj.IsObject() || !obj.HasMember(key) || !obj[key].IsObject()) {
throw std::invalid_argument(std::string("member '") + key +
"' must exist and be an object");
}
return RestUtil::JsonToString(obj[key]);
}
} // namespace
rapidjson::Value ErrorResponse::ToJson(rapidjson::Document::AllocatorType* allocator) const
noexcept(false) {
rapidjson::Value obj(rapidjson::kObjectType);
obj.AddMember(rapidjson::StringRef(kFieldMessage),
RapidJsonUtil::SerializeValue(message_, allocator).Move(), *allocator);
if (!resource_type_.empty()) {
obj.AddMember(rapidjson::StringRef(kFieldResourceType),
RapidJsonUtil::SerializeValue(resource_type_, allocator).Move(), *allocator);
}
if (!resource_name_.empty()) {
obj.AddMember(rapidjson::StringRef(kFieldResourceName),
RapidJsonUtil::SerializeValue(resource_name_, allocator).Move(), *allocator);
}
obj.AddMember(rapidjson::StringRef(kFieldCode),
RapidJsonUtil::SerializeValue(code_, allocator).Move(), *allocator);
return obj;
}
void ErrorResponse::FromJson(const rapidjson::Value& obj) noexcept(false) {
message_ = RapidJsonUtil::DeserializeKeyValue<std::string>(obj, kFieldMessage, std::string());
resource_type_ =
RapidJsonUtil::DeserializeKeyValue<std::string>(obj, kFieldResourceType, std::string());
resource_name_ =
RapidJsonUtil::DeserializeKeyValue<std::string>(obj, kFieldResourceName, std::string());
code_ = RapidJsonUtil::DeserializeKeyValue<int32_t>(obj, kFieldCode, 0);
}
rapidjson::Value ConfigResponse::ToJson(rapidjson::Document::AllocatorType* allocator) const
noexcept(false) {
rapidjson::Value obj(rapidjson::kObjectType);
obj.AddMember(rapidjson::StringRef(kFieldDefaults),
RapidJsonUtil::SerializeValue(defaults_, allocator).Move(), *allocator);
obj.AddMember(rapidjson::StringRef(kFieldOverrides),
RapidJsonUtil::SerializeValue(overrides_, allocator).Move(), *allocator);
return obj;
}
namespace {
// The server may send null values in `defaults`/`overrides`; a null carries no value to
// merge, so it is collected in `null_keys` (when given) instead of failing the parse.
std::map<std::string, std::string> DeserializeStringMap(const rapidjson::Value& obj,
const char* key,
std::set<std::string>* null_keys) {
std::map<std::string, std::string> result;
if (!obj.IsObject() || !obj.HasMember(key) || obj[key].IsNull()) {
return result;
}
const rapidjson::Value& map_value = obj[key];
if (!map_value.IsObject()) {
throw std::invalid_argument(std::string("member '") + key + "' must be an object");
}
for (auto iter = map_value.MemberBegin(); iter != map_value.MemberEnd(); ++iter) {
if (iter->value.IsNull()) {
if (null_keys != nullptr) {
null_keys->insert(iter->name.GetString());
}
continue;
}
if (!iter->value.IsString()) {
throw std::invalid_argument(std::string("member '") + key +
"' must only contain string values");
}
result[iter->name.GetString()] = iter->value.GetString();
}
return result;
}
} // namespace
void ConfigResponse::FromJson(const rapidjson::Value& obj) noexcept(false) {
removed_keys_.clear();
// A null default carries no value and never unsets anything; only a null override
// removes a key.
defaults_ = DeserializeStringMap(obj, kFieldDefaults, nullptr);
overrides_ = DeserializeStringMap(obj, kFieldOverrides, &removed_keys_);
}
std::map<std::string, std::string> ConfigResponse::Merge(
const std::map<std::string, std::string>& client_options) const {
std::map<std::string, std::string> merged = defaults_;
for (const auto& [key, value] : client_options) {
merged[key] = value;
}
for (const auto& [key, value] : overrides_) {
merged[key] = value;
}
for (const std::string& key : removed_keys_) {
merged.erase(key);
}
return merged;
}
void RestAuditFields::ParseFrom(const rapidjson::Value& obj) {
owner = RapidJsonUtil::DeserializeKeyValue<std::optional<std::string>>(obj, kFieldOwner,
std::nullopt);
created_at = RapidJsonUtil::DeserializeKeyValue<std::optional<int64_t>>(obj, kFieldCreatedAt,
std::nullopt);
created_by = RapidJsonUtil::DeserializeKeyValue<std::optional<std::string>>(
obj, kFieldCreatedBy, std::nullopt);
updated_at = RapidJsonUtil::DeserializeKeyValue<std::optional<int64_t>>(obj, kFieldUpdatedAt,
std::nullopt);
updated_by = RapidJsonUtil::DeserializeKeyValue<std::optional<std::string>>(
obj, kFieldUpdatedBy, std::nullopt);
}
void RestAuditFields::AddTo(rapidjson::Value* obj,
rapidjson::Document::AllocatorType* allocator) const {
AddOptionalStringMember(obj, kFieldOwner, owner, allocator);
if (created_at) {
obj->AddMember(rapidjson::StringRef(kFieldCreatedAt),
RapidJsonUtil::SerializeValue(created_at.value(), allocator).Move(),
*allocator);
}
AddOptionalStringMember(obj, kFieldCreatedBy, created_by, allocator);
if (updated_at) {
obj->AddMember(rapidjson::StringRef(kFieldUpdatedAt),
RapidJsonUtil::SerializeValue(updated_at.value(), allocator).Move(),
*allocator);
}
AddOptionalStringMember(obj, kFieldUpdatedBy, updated_by, allocator);
}
void RestAuditFields::PutAuditOptionsTo(std::map<std::string, std::string>* options) const {
if (owner) {
(*options)[kFieldOwner] = owner.value();
}
if (created_at) {
(*options)[kFieldCreatedAt] = std::to_string(created_at.value());
}
if (created_by) {
(*options)[kFieldCreatedBy] = created_by.value();
}
if (updated_at) {
(*options)[kFieldUpdatedAt] = std::to_string(updated_at.value());
}
if (updated_by) {
(*options)[kFieldUpdatedBy] = updated_by.value();
}
}
rapidjson::Value CreateDatabaseRequest::ToJson(rapidjson::Document::AllocatorType* allocator) const
noexcept(false) {
rapidjson::Value obj(rapidjson::kObjectType);
obj.AddMember(rapidjson::StringRef(kFieldName),
RapidJsonUtil::SerializeValue(name_, allocator).Move(), *allocator);
obj.AddMember(rapidjson::StringRef(kFieldOptions),
RapidJsonUtil::SerializeValue(options_, allocator).Move(), *allocator);
return obj;
}
void CreateDatabaseRequest::FromJson(const rapidjson::Value& obj) noexcept(false) {
name_ = RapidJsonUtil::DeserializeKeyValue<std::string>(obj, kFieldName);
options_ = RapidJsonUtil::DeserializeKeyValue<std::map<std::string, std::string>>(
obj, kFieldOptions, {});
}
rapidjson::Value GetDatabaseResponse::ToJson(rapidjson::Document::AllocatorType* allocator) const
noexcept(false) {
rapidjson::Value obj(rapidjson::kObjectType);
obj.AddMember(rapidjson::StringRef(kFieldId),
RapidJsonUtil::SerializeValue(id_, allocator).Move(), *allocator);
obj.AddMember(rapidjson::StringRef(kFieldName),
RapidJsonUtil::SerializeValue(name_, allocator).Move(), *allocator);
obj.AddMember(rapidjson::StringRef(kFieldLocation),
RapidJsonUtil::SerializeValue(location_, allocator).Move(), *allocator);
obj.AddMember(rapidjson::StringRef(kFieldOptions),
RapidJsonUtil::SerializeValue(options_, allocator).Move(), *allocator);
audit_.AddTo(&obj, allocator);
return obj;
}
void GetDatabaseResponse::FromJson(const rapidjson::Value& obj) noexcept(false) {
id_ = RapidJsonUtil::DeserializeKeyValue<std::string>(obj, kFieldId, std::string());
name_ = RapidJsonUtil::DeserializeKeyValue<std::string>(obj, kFieldName);
location_ = RapidJsonUtil::DeserializeKeyValue<std::string>(obj, kFieldLocation, std::string());
options_ = RapidJsonUtil::DeserializeKeyValue<std::map<std::string, std::string>>(
obj, kFieldOptions, {});
audit_.ParseFrom(obj);
}
rapidjson::Value ListDatabasesResponse::ToJson(rapidjson::Document::AllocatorType* allocator) const
noexcept(false) {
rapidjson::Value obj(rapidjson::kObjectType);
obj.AddMember(rapidjson::StringRef(kFieldDatabases),
RapidJsonUtil::SerializeValue(databases_, allocator).Move(), *allocator);
AddOptionalStringMember(&obj, kFieldNextPageToken, next_page_token_, allocator);
return obj;
}
void ListDatabasesResponse::FromJson(const rapidjson::Value& obj) noexcept(false) {
databases_ =
RapidJsonUtil::DeserializeKeyValue<std::vector<std::string>>(obj, kFieldDatabases, {});
next_page_token_ = RapidJsonUtil::DeserializeKeyValue<std::optional<std::string>>(
obj, kFieldNextPageToken, std::nullopt);
}
rapidjson::Value ListTablesResponse::ToJson(rapidjson::Document::AllocatorType* allocator) const
noexcept(false) {
rapidjson::Value obj(rapidjson::kObjectType);
obj.AddMember(rapidjson::StringRef(kFieldTables),
RapidJsonUtil::SerializeValue(tables_, allocator).Move(), *allocator);
AddOptionalStringMember(&obj, kFieldNextPageToken, next_page_token_, allocator);
return obj;
}
void ListTablesResponse::FromJson(const rapidjson::Value& obj) noexcept(false) {
tables_ = RapidJsonUtil::DeserializeKeyValue<std::vector<std::string>>(obj, kFieldTables, {});
next_page_token_ = RapidJsonUtil::DeserializeKeyValue<std::optional<std::string>>(
obj, kFieldNextPageToken, std::nullopt);
}
rapidjson::Value ListSnapshotsResponse::ToJson(rapidjson::Document::AllocatorType* allocator) const
noexcept(false) {
rapidjson::Value obj(rapidjson::kObjectType);
obj.AddMember(rapidjson::StringRef(kFieldSnapshots),
RapidJsonUtil::SerializeValue(snapshots_, allocator).Move(), *allocator);
AddOptionalStringMember(&obj, kFieldNextPageToken, next_page_token_, allocator);
return obj;
}
void ListSnapshotsResponse::FromJson(const rapidjson::Value& obj) noexcept(false) {
snapshots_ =
RapidJsonUtil::DeserializeKeyValue<std::vector<Snapshot>>(obj, kFieldSnapshots, {});
next_page_token_ = RapidJsonUtil::DeserializeKeyValue<std::optional<std::string>>(
obj, kFieldNextPageToken, std::nullopt);
}
rapidjson::Value GetTableResponse::ToJson(rapidjson::Document::AllocatorType* allocator) const
noexcept(false) {
rapidjson::Value obj(rapidjson::kObjectType);
obj.AddMember(rapidjson::StringRef(kFieldId),
RapidJsonUtil::SerializeValue(id_, allocator).Move(), *allocator);
obj.AddMember(rapidjson::StringRef(kFieldDatabase),
RapidJsonUtil::SerializeValue(database_, allocator).Move(), *allocator);
obj.AddMember(rapidjson::StringRef(kFieldName),
RapidJsonUtil::SerializeValue(name_, allocator).Move(), *allocator);
obj.AddMember(rapidjson::StringRef(kFieldPath),
RapidJsonUtil::SerializeValue(path_, allocator).Move(), *allocator);
obj.AddMember(rapidjson::StringRef(kFieldIsExternal),
RapidJsonUtil::SerializeValue(is_external_, allocator).Move(), *allocator);
obj.AddMember(rapidjson::StringRef(kFieldSchemaId),
RapidJsonUtil::SerializeValue(schema_id_, allocator).Move(), *allocator);
obj.AddMember(rapidjson::StringRef(kFieldSchema),
RestUtil::ParseToValue(schema_json_, allocator).Move(), *allocator);
audit_.AddTo(&obj, allocator);
return obj;
}
void GetTableResponse::FromJson(const rapidjson::Value& obj) noexcept(false) {
id_ = RapidJsonUtil::DeserializeKeyValue<std::string>(obj, kFieldId, std::string());
database_ = RapidJsonUtil::DeserializeKeyValue<std::string>(obj, kFieldDatabase, std::string());
name_ = RapidJsonUtil::DeserializeKeyValue<std::string>(obj, kFieldName, std::string());
path_ = RapidJsonUtil::DeserializeKeyValue<std::string>(obj, kFieldPath);
is_external_ = RapidJsonUtil::DeserializeKeyValue<bool>(obj, kFieldIsExternal, false);
schema_id_ = RapidJsonUtil::DeserializeKeyValue<int64_t>(obj, kFieldSchemaId);
schema_json_ = DeserializeRawJsonMember(obj, kFieldSchema);
audit_.ParseFrom(obj);
}
rapidjson::Value CreateTableRequest::ToJson(rapidjson::Document::AllocatorType* allocator) const
noexcept(false) {
rapidjson::Value obj(rapidjson::kObjectType);
obj.AddMember(rapidjson::StringRef(kFieldIdentifier),
SerializeIdentifier(database_, table_, allocator).Move(), *allocator);
obj.AddMember(rapidjson::StringRef(kFieldSchema),
RestUtil::ParseToValue(schema_json_, allocator).Move(), *allocator);
return obj;
}
void CreateTableRequest::FromJson(const rapidjson::Value& obj) noexcept(false) {
DeserializeIdentifier(obj, kFieldIdentifier, &database_, &table_);
schema_json_ = DeserializeRawJsonMember(obj, kFieldSchema);
}
rapidjson::Value RenameTableRequest::ToJson(rapidjson::Document::AllocatorType* allocator) const
noexcept(false) {
rapidjson::Value obj(rapidjson::kObjectType);
obj.AddMember(rapidjson::StringRef(kFieldSource),
SerializeIdentifier(source_database_, source_table_, allocator).Move(),
*allocator);
obj.AddMember(rapidjson::StringRef(kFieldDestination),
SerializeIdentifier(destination_database_, destination_table_, allocator).Move(),
*allocator);
return obj;
}
void RenameTableRequest::FromJson(const rapidjson::Value& obj) noexcept(false) {
DeserializeIdentifier(obj, kFieldSource, &source_database_, &source_table_);
DeserializeIdentifier(obj, kFieldDestination, &destination_database_, &destination_table_);
}
} // namespace paimon