Skip to content

Commit c24102e

Browse files
authored
feat: add json serde for REST Catalog request/response models (#251)
1 parent 5cbf3df commit c24102e

File tree

11 files changed

+1380
-17
lines changed

11 files changed

+1380
-17
lines changed

src/iceberg/catalog/rest/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717

18-
set(ICEBERG_REST_SOURCES rest_catalog.cc)
18+
set(ICEBERG_REST_SOURCES rest_catalog.cc json_internal.cc)
1919

2020
set(ICEBERG_REST_STATIC_BUILD_INTERFACE_LIBS)
2121
set(ICEBERG_REST_SHARED_BUILD_INTERFACE_LIBS)
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#include "iceberg/catalog/rest/json_internal.h"
21+
22+
#include <string>
23+
#include <unordered_map>
24+
#include <utility>
25+
#include <vector>
26+
27+
#include <nlohmann/json.hpp>
28+
29+
#include "iceberg/catalog/rest/types.h"
30+
#include "iceberg/json_internal.h"
31+
#include "iceberg/table_identifier.h"
32+
#include "iceberg/util/json_util_internal.h"
33+
#include "iceberg/util/macros.h"
34+
35+
namespace iceberg::rest {
36+
37+
namespace {
38+
39+
// REST API JSON field constants
40+
constexpr std::string_view kNamespace = "namespace";
41+
constexpr std::string_view kNamespaces = "namespaces";
42+
constexpr std::string_view kProperties = "properties";
43+
constexpr std::string_view kRemovals = "removals";
44+
constexpr std::string_view kUpdates = "updates";
45+
constexpr std::string_view kUpdated = "updated";
46+
constexpr std::string_view kRemoved = "removed";
47+
constexpr std::string_view kMissing = "missing";
48+
constexpr std::string_view kNextPageToken = "next-page-token";
49+
constexpr std::string_view kName = "name";
50+
constexpr std::string_view kLocation = "location";
51+
constexpr std::string_view kSchema = "schema";
52+
constexpr std::string_view kPartitionSpec = "partition-spec";
53+
constexpr std::string_view kWriteOrder = "write-order";
54+
constexpr std::string_view kStageCreate = "stage-create";
55+
constexpr std::string_view kMetadataLocation = "metadata-location";
56+
constexpr std::string_view kOverwrite = "overwrite";
57+
constexpr std::string_view kSource = "source";
58+
constexpr std::string_view kDestination = "destination";
59+
constexpr std::string_view kMetadata = "metadata";
60+
constexpr std::string_view kConfig = "config";
61+
constexpr std::string_view kIdentifiers = "identifiers";
62+
63+
} // namespace
64+
65+
nlohmann::json ToJson(const CreateNamespaceRequest& request) {
66+
nlohmann::json json;
67+
json[kNamespace] = request.namespace_.levels;
68+
if (!request.properties.empty()) {
69+
json[kProperties] = request.properties;
70+
}
71+
return json;
72+
}
73+
74+
Result<CreateNamespaceRequest> CreateNamespaceRequestFromJson(
75+
const nlohmann::json& json) {
76+
CreateNamespaceRequest request;
77+
ICEBERG_ASSIGN_OR_RAISE(request.namespace_.levels,
78+
GetJsonValue<std::vector<std::string>>(json, kNamespace));
79+
ICEBERG_ASSIGN_OR_RAISE(
80+
request.properties,
81+
GetJsonValueOrDefault<decltype(request.properties)>(json, kProperties));
82+
return request;
83+
}
84+
85+
nlohmann::json ToJson(const UpdateNamespacePropertiesRequest& request) {
86+
// Initialize as an empty object so that when all optional fields are absent we return
87+
// {} instead of null
88+
nlohmann::json json = nlohmann::json::object();
89+
if (!request.removals.empty()) {
90+
json[kRemovals] = request.removals;
91+
}
92+
if (!request.updates.empty()) {
93+
json[kUpdates] = request.updates;
94+
}
95+
return json;
96+
}
97+
98+
Result<UpdateNamespacePropertiesRequest> UpdateNamespacePropertiesRequestFromJson(
99+
const nlohmann::json& json) {
100+
UpdateNamespacePropertiesRequest request;
101+
ICEBERG_ASSIGN_OR_RAISE(
102+
request.removals, GetJsonValueOrDefault<std::vector<std::string>>(json, kRemovals));
103+
ICEBERG_ASSIGN_OR_RAISE(
104+
request.updates, GetJsonValueOrDefault<decltype(request.updates)>(json, kUpdates));
105+
return request;
106+
}
107+
108+
nlohmann::json ToJson(const RegisterTableRequest& request) {
109+
nlohmann::json json;
110+
json[kName] = request.name;
111+
json[kMetadataLocation] = request.metadata_location;
112+
if (request.overwrite) {
113+
json[kOverwrite] = request.overwrite;
114+
}
115+
return json;
116+
}
117+
118+
Result<RegisterTableRequest> RegisterTableRequestFromJson(const nlohmann::json& json) {
119+
RegisterTableRequest request;
120+
ICEBERG_ASSIGN_OR_RAISE(request.name, GetJsonValue<std::string>(json, kName));
121+
ICEBERG_ASSIGN_OR_RAISE(request.metadata_location,
122+
GetJsonValue<std::string>(json, kMetadataLocation));
123+
ICEBERG_ASSIGN_OR_RAISE(request.overwrite,
124+
GetJsonValueOrDefault<bool>(json, kOverwrite, false));
125+
return request;
126+
}
127+
128+
nlohmann::json ToJson(const RenameTableRequest& request) {
129+
nlohmann::json json;
130+
json[kSource] = ToJson(request.source);
131+
json[kDestination] = ToJson(request.destination);
132+
return json;
133+
}
134+
135+
Result<RenameTableRequest> RenameTableRequestFromJson(const nlohmann::json& json) {
136+
RenameTableRequest request;
137+
ICEBERG_ASSIGN_OR_RAISE(auto source_json, GetJsonValue<nlohmann::json>(json, kSource));
138+
ICEBERG_ASSIGN_OR_RAISE(request.source, TableIdentifierFromJson(source_json));
139+
ICEBERG_ASSIGN_OR_RAISE(auto dest_json,
140+
GetJsonValue<nlohmann::json>(json, kDestination));
141+
ICEBERG_ASSIGN_OR_RAISE(request.destination, TableIdentifierFromJson(dest_json));
142+
return request;
143+
}
144+
145+
// LoadTableResult (used by CreateTableResponse, LoadTableResponse)
146+
nlohmann::json ToJson(const LoadTableResult& result) {
147+
nlohmann::json json;
148+
if (!result.metadata_location.empty()) {
149+
json[kMetadataLocation] = result.metadata_location;
150+
}
151+
json[kMetadata] = ToJson(*result.metadata);
152+
if (!result.config.empty()) {
153+
json[kConfig] = result.config;
154+
}
155+
return json;
156+
}
157+
158+
Result<LoadTableResult> LoadTableResultFromJson(const nlohmann::json& json) {
159+
LoadTableResult result;
160+
ICEBERG_ASSIGN_OR_RAISE(result.metadata_location,
161+
GetJsonValueOrDefault<std::string>(json, kMetadataLocation));
162+
ICEBERG_ASSIGN_OR_RAISE(auto metadata_json,
163+
GetJsonValue<nlohmann::json>(json, kMetadata));
164+
ICEBERG_ASSIGN_OR_RAISE(result.metadata, TableMetadataFromJson(metadata_json));
165+
ICEBERG_ASSIGN_OR_RAISE(
166+
result.config, (GetJsonValueOrDefault<std::unordered_map<std::string, std::string>>(
167+
json, kConfig)));
168+
return result;
169+
}
170+
171+
nlohmann::json ToJson(const ListNamespacesResponse& response) {
172+
nlohmann::json json;
173+
if (!response.next_page_token.empty()) {
174+
json[kNextPageToken] = response.next_page_token;
175+
}
176+
nlohmann::json namespaces = nlohmann::json::array();
177+
for (const auto& ns : response.namespaces) {
178+
namespaces.push_back(ToJson(ns));
179+
}
180+
json[kNamespaces] = std::move(namespaces);
181+
return json;
182+
}
183+
184+
Result<ListNamespacesResponse> ListNamespacesResponseFromJson(
185+
const nlohmann::json& json) {
186+
ListNamespacesResponse response;
187+
ICEBERG_ASSIGN_OR_RAISE(response.next_page_token,
188+
GetJsonValueOrDefault<std::string>(json, kNextPageToken));
189+
ICEBERG_ASSIGN_OR_RAISE(auto namespaces_json,
190+
GetJsonValue<nlohmann::json>(json, kNamespaces));
191+
for (const auto& ns_json : namespaces_json) {
192+
ICEBERG_ASSIGN_OR_RAISE(auto ns, NamespaceFromJson(ns_json));
193+
response.namespaces.push_back(std::move(ns));
194+
}
195+
return response;
196+
}
197+
198+
nlohmann::json ToJson(const CreateNamespaceResponse& response) {
199+
nlohmann::json json;
200+
json[kNamespace] = response.namespace_.levels;
201+
if (!response.properties.empty()) {
202+
json[kProperties] = response.properties;
203+
}
204+
return json;
205+
}
206+
207+
Result<CreateNamespaceResponse> CreateNamespaceResponseFromJson(
208+
const nlohmann::json& json) {
209+
CreateNamespaceResponse response;
210+
ICEBERG_ASSIGN_OR_RAISE(response.namespace_.levels,
211+
GetJsonValue<std::vector<std::string>>(json, kNamespace));
212+
ICEBERG_ASSIGN_OR_RAISE(
213+
response.properties,
214+
GetJsonValueOrDefault<decltype(response.properties)>(json, kProperties));
215+
return response;
216+
}
217+
218+
nlohmann::json ToJson(const GetNamespaceResponse& response) {
219+
nlohmann::json json;
220+
json[kNamespace] = response.namespace_.levels;
221+
if (!response.properties.empty()) {
222+
json[kProperties] = response.properties;
223+
}
224+
return json;
225+
}
226+
227+
Result<GetNamespaceResponse> GetNamespaceResponseFromJson(const nlohmann::json& json) {
228+
GetNamespaceResponse response;
229+
ICEBERG_ASSIGN_OR_RAISE(response.namespace_.levels,
230+
GetJsonValue<std::vector<std::string>>(json, kNamespace));
231+
ICEBERG_ASSIGN_OR_RAISE(
232+
response.properties,
233+
GetJsonValueOrDefault<decltype(response.properties)>(json, kProperties));
234+
return response;
235+
}
236+
237+
nlohmann::json ToJson(const UpdateNamespacePropertiesResponse& response) {
238+
nlohmann::json json;
239+
json[kUpdated] = response.updated;
240+
json[kRemoved] = response.removed;
241+
if (!response.missing.empty()) {
242+
json[kMissing] = response.missing;
243+
}
244+
return json;
245+
}
246+
247+
Result<UpdateNamespacePropertiesResponse> UpdateNamespacePropertiesResponseFromJson(
248+
const nlohmann::json& json) {
249+
UpdateNamespacePropertiesResponse response;
250+
ICEBERG_ASSIGN_OR_RAISE(response.updated,
251+
GetJsonValue<std::vector<std::string>>(json, kUpdated));
252+
ICEBERG_ASSIGN_OR_RAISE(response.removed,
253+
GetJsonValue<std::vector<std::string>>(json, kRemoved));
254+
ICEBERG_ASSIGN_OR_RAISE(
255+
response.missing, GetJsonValueOrDefault<std::vector<std::string>>(json, kMissing));
256+
return response;
257+
}
258+
259+
nlohmann::json ToJson(const ListTablesResponse& response) {
260+
nlohmann::json json;
261+
if (!response.next_page_token.empty()) {
262+
json[kNextPageToken] = response.next_page_token;
263+
}
264+
nlohmann::json identifiers_json = nlohmann::json::array();
265+
for (const auto& identifier : response.identifiers) {
266+
identifiers_json.push_back(ToJson(identifier));
267+
}
268+
json[kIdentifiers] = identifiers_json;
269+
return json;
270+
}
271+
272+
Result<ListTablesResponse> ListTablesResponseFromJson(const nlohmann::json& json) {
273+
ListTablesResponse response;
274+
ICEBERG_ASSIGN_OR_RAISE(response.next_page_token,
275+
GetJsonValueOrDefault<std::string>(json, kNextPageToken));
276+
ICEBERG_ASSIGN_OR_RAISE(auto identifiers_json,
277+
GetJsonValue<nlohmann::json>(json, kIdentifiers));
278+
for (const auto& id_json : identifiers_json) {
279+
ICEBERG_ASSIGN_OR_RAISE(auto identifier, TableIdentifierFromJson(id_json));
280+
response.identifiers.push_back(std::move(identifier));
281+
}
282+
return response;
283+
}
284+
285+
} // namespace iceberg::rest
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#pragma once
21+
22+
#include <nlohmann/json_fwd.hpp>
23+
24+
#include "iceberg/catalog/rest/types.h"
25+
#include "iceberg/result.h"
26+
27+
namespace iceberg::rest {
28+
29+
/// \brief Serializes a `ListNamespacesResponse` object to JSON.
30+
ICEBERG_REST_EXPORT nlohmann::json ToJson(const ListNamespacesResponse& response);
31+
32+
/// \brief Deserializes a JSON object into a `ListNamespacesResponse` object.
33+
ICEBERG_REST_EXPORT Result<ListNamespacesResponse> ListNamespacesResponseFromJson(
34+
const nlohmann::json& json);
35+
36+
/// \brief Serializes a `CreateNamespaceRequest` object to JSON.
37+
ICEBERG_REST_EXPORT nlohmann::json ToJson(const CreateNamespaceRequest& request);
38+
39+
/// \brief Deserializes a JSON object into a `CreateNamespaceRequest` object.
40+
ICEBERG_REST_EXPORT Result<CreateNamespaceRequest> CreateNamespaceRequestFromJson(
41+
const nlohmann::json& json);
42+
43+
/// \brief Serializes a `CreateNamespaceResponse` object to JSON.
44+
ICEBERG_REST_EXPORT nlohmann::json ToJson(const CreateNamespaceResponse& response);
45+
46+
/// \brief Deserializes a JSON object into a `CreateNamespaceResponse` object.
47+
ICEBERG_REST_EXPORT Result<CreateNamespaceResponse> CreateNamespaceResponseFromJson(
48+
const nlohmann::json& json);
49+
50+
/// \brief Serializes a `GetNamespaceResponse` object to JSON.
51+
ICEBERG_REST_EXPORT nlohmann::json ToJson(const GetNamespaceResponse& response);
52+
53+
/// \brief Deserializes a JSON object into a `GetNamespaceResponse` object.
54+
ICEBERG_REST_EXPORT Result<GetNamespaceResponse> GetNamespaceResponseFromJson(
55+
const nlohmann::json& json);
56+
57+
/// \brief Serializes an `UpdateNamespacePropertiesRequest` object to JSON.
58+
ICEBERG_REST_EXPORT nlohmann::json ToJson(
59+
const UpdateNamespacePropertiesRequest& request);
60+
61+
/// \brief Deserializes a JSON object into an `UpdateNamespacePropertiesRequest` object.
62+
ICEBERG_REST_EXPORT Result<UpdateNamespacePropertiesRequest>
63+
UpdateNamespacePropertiesRequestFromJson(const nlohmann::json& json);
64+
65+
/// \brief Serializes an `UpdateNamespacePropertiesResponse` object to JSON.
66+
ICEBERG_REST_EXPORT nlohmann::json ToJson(
67+
const UpdateNamespacePropertiesResponse& response);
68+
69+
/// \brief Deserializes a JSON object into an `UpdateNamespacePropertiesResponse` object.
70+
ICEBERG_REST_EXPORT Result<UpdateNamespacePropertiesResponse>
71+
UpdateNamespacePropertiesResponseFromJson(const nlohmann::json& json);
72+
73+
/// \brief Serializes a `ListTablesResponse` object to JSON.
74+
ICEBERG_REST_EXPORT nlohmann::json ToJson(const ListTablesResponse& response);
75+
76+
/// \brief Deserializes a JSON object into a `ListTablesResponse` object.
77+
ICEBERG_REST_EXPORT Result<ListTablesResponse> ListTablesResponseFromJson(
78+
const nlohmann::json& json);
79+
80+
/// \brief Serializes a `LoadTableResult` object to JSON.
81+
ICEBERG_REST_EXPORT nlohmann::json ToJson(const LoadTableResult& result);
82+
83+
/// \brief Deserializes a JSON object into a `LoadTableResult` object.
84+
ICEBERG_REST_EXPORT Result<LoadTableResult> LoadTableResultFromJson(
85+
const nlohmann::json& json);
86+
87+
/// \brief Serializes a `RegisterTableRequest` object to JSON.
88+
ICEBERG_REST_EXPORT nlohmann::json ToJson(const RegisterTableRequest& request);
89+
90+
/// \brief Deserializes a JSON object into a `RegisterTableRequest` object.
91+
ICEBERG_REST_EXPORT Result<RegisterTableRequest> RegisterTableRequestFromJson(
92+
const nlohmann::json& json);
93+
94+
/// \brief Serializes a `RenameTableRequest` object to JSON.
95+
ICEBERG_REST_EXPORT nlohmann::json ToJson(const RenameTableRequest& request);
96+
97+
/// \brief Deserializes a JSON object into a `RenameTableRequest` object.
98+
ICEBERG_REST_EXPORT Result<RenameTableRequest> RenameTableRequestFromJson(
99+
const nlohmann::json& json);
100+
101+
} // namespace iceberg::rest

0 commit comments

Comments
 (0)