Skip to content

Commit e1c27e2

Browse files
committed
feat: add validator for rest api types
1 parent e19f740 commit e1c27e2

File tree

5 files changed

+197
-3
lines changed

5 files changed

+197
-3
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 json_internal.cc)
18+
set(ICEBERG_REST_SOURCES rest_catalog.cc json_internal.cc validator.cc)
1919

2020
set(ICEBERG_REST_STATIC_BUILD_INTERFACE_LIBS)
2121
set(ICEBERG_REST_SHARED_BUILD_INTERFACE_LIBS)

src/iceberg/catalog/rest/json_internal.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <nlohmann/json.hpp>
2828

2929
#include "iceberg/catalog/rest/types.h"
30+
#include "iceberg/catalog/rest/validator.h"
3031
#include "iceberg/json_internal.h"
3132
#include "iceberg/table_identifier.h"
3233
#include "iceberg/util/json_util_internal.h"
@@ -139,6 +140,7 @@ Result<RenameTableRequest> RenameTableRequestFromJson(const nlohmann::json& json
139140
ICEBERG_ASSIGN_OR_RAISE(auto dest_json,
140141
GetJsonValue<nlohmann::json>(json, kDestination));
141142
ICEBERG_ASSIGN_OR_RAISE(request.destination, TableIdentifierFromJson(dest_json));
143+
ICEBERG_RETURN_UNEXPECTED(Validator::Validate(request));
142144
return request;
143145
}
144146

@@ -165,6 +167,7 @@ Result<LoadTableResult> LoadTableResultFromJson(const nlohmann::json& json) {
165167
ICEBERG_ASSIGN_OR_RAISE(
166168
result.config, (GetJsonValueOrDefault<std::unordered_map<std::string, std::string>>(
167169
json, kConfig)));
170+
ICEBERG_RETURN_UNEXPECTED(Validator::Validate(result));
168171
return result;
169172
}
170173

@@ -192,6 +195,7 @@ Result<ListNamespacesResponse> ListNamespacesResponseFromJson(
192195
ICEBERG_ASSIGN_OR_RAISE(auto ns, NamespaceFromJson(ns_json));
193196
response.namespaces.push_back(std::move(ns));
194197
}
198+
ICEBERG_RETURN_UNEXPECTED(Validator::Validate(response));
195199
return response;
196200
}
197201

@@ -253,6 +257,7 @@ Result<UpdateNamespacePropertiesResponse> UpdateNamespacePropertiesResponseFromJ
253257
GetJsonValue<std::vector<std::string>>(json, kRemoved));
254258
ICEBERG_ASSIGN_OR_RAISE(
255259
response.missing, GetJsonValueOrDefault<std::vector<std::string>>(json, kMissing));
260+
ICEBERG_RETURN_UNEXPECTED(Validator::Validate(response));
256261
return response;
257262
}
258263

@@ -279,6 +284,7 @@ Result<ListTablesResponse> ListTablesResponseFromJson(const nlohmann::json& json
279284
ICEBERG_ASSIGN_OR_RAISE(auto identifier, TableIdentifierFromJson(id_json));
280285
response.identifiers.push_back(std::move(identifier));
281286
}
287+
ICEBERG_RETURN_UNEXPECTED(Validator::Validate(response));
282288
return response;
283289
}
284290

src/iceberg/catalog/rest/meson.build

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717

18-
iceberg_rest_sources = files('json_internal.cc', 'rest_catalog.cc')
18+
iceberg_rest_sources = files(
19+
'json_internal.cc',
20+
'rest_catalog.cc',
21+
'validator.cc',
22+
)
1923
# cpr does not export symbols, so on Windows it must
2024
# be used as a static lib
2125
cpr_needs_static = (
@@ -46,4 +50,7 @@ iceberg_rest_dep = declare_dependency(
4650
meson.override_dependency('iceberg-rest', iceberg_rest_dep)
4751
pkg.generate(iceberg_rest_lib)
4852

49-
install_headers(['rest_catalog.h', 'types.h'], subdir: 'iceberg/catalog/rest')
53+
install_headers(
54+
['rest_catalog.h', 'types.h', 'json_internal.h', 'validator.h'],
55+
subdir: 'iceberg/catalog/rest',
56+
)
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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/validator.h"
21+
22+
#include <format>
23+
#include <ranges>
24+
#include <unordered_set>
25+
26+
#include "iceberg/catalog/rest/types.h"
27+
#include "iceberg/result.h"
28+
29+
namespace iceberg::rest {
30+
31+
// Namespace operations
32+
33+
Status Validator::Validate(const ListNamespacesResponse& response) { return {}; }
34+
35+
Status Validator::Validate(const CreateNamespaceRequest& request) { return {}; }
36+
37+
Status Validator::Validate(const CreateNamespaceResponse& response) { return {}; }
38+
39+
Status Validator::Validate(const GetNamespaceResponse& response) { return {}; }
40+
41+
Status Validator::Validate(const UpdateNamespacePropertiesRequest& request) {
42+
// keys in updates and removals must not overlap
43+
if (request.removals.empty() || request.updates.empty()) [[unlikely]] {
44+
return {};
45+
}
46+
47+
std::unordered_set<std::string> remove_set(std::from_range, request.removals);
48+
49+
auto common =
50+
request.updates | std::views::keys |
51+
std::views::filter([&](const std::string& k) { return remove_set.contains(k); }) |
52+
std::ranges::to<std::vector<std::string>>();
53+
54+
if (!common.empty()) {
55+
std::string keys;
56+
bool first = true;
57+
std::ranges::for_each(common, [&](const std::string& s) {
58+
if (!std::exchange(first, false)) keys += ", ";
59+
keys += s;
60+
});
61+
62+
return Invalid(
63+
"Invalid namespace properties update: cannot simultaneously set and remove keys: "
64+
"[{}]",
65+
keys);
66+
}
67+
return {};
68+
}
69+
70+
Status Validator::Validate(const UpdateNamespacePropertiesResponse& response) {
71+
return {};
72+
}
73+
74+
// Table operations
75+
76+
Status Validator::Validate(const ListTablesResponse& response) { return {}; }
77+
78+
Status Validator::Validate(const LoadTableResult& result) {
79+
if (!result.metadata) [[unlikely]] {
80+
return Invalid("Invalid metadata: null");
81+
}
82+
return {};
83+
}
84+
85+
Status Validator::Validate(const RegisterTableRequest& request) {
86+
if (request.name.empty()) [[unlikely]] {
87+
return Invalid("Invalid table name: empty");
88+
}
89+
90+
if (request.metadata_location.empty()) [[unlikely]] {
91+
return Invalid("Invalid metadata location: empty");
92+
}
93+
94+
return {};
95+
}
96+
97+
Status Validator::Validate(const RenameTableRequest& request) {
98+
if (request.source.ns.levels.empty() || request.source.name.empty()) [[unlikely]] {
99+
return Invalid("Invalid source identifier");
100+
}
101+
102+
if (request.destination.ns.levels.empty() || request.destination.name.empty())
103+
[[unlikely]] {
104+
return Invalid("Invalid destination identifier");
105+
}
106+
107+
return {};
108+
}
109+
110+
} // namespace iceberg::rest
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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 "iceberg/catalog/rest/iceberg_rest_export.h"
23+
#include "iceberg/catalog/rest/types.h"
24+
#include "iceberg/result.h"
25+
26+
/// \file iceberg/catalog/rest/validator.h
27+
/// Validator for REST Catalog API types.
28+
29+
namespace iceberg::rest {
30+
31+
/// \brief Validator for REST Catalog API types. Validation should be called after
32+
/// deserializing objects from external sources to ensure data integrity before the
33+
/// objects are used.
34+
class ICEBERG_REST_EXPORT Validator {
35+
public:
36+
// Namespace operations
37+
38+
/// \brief Validates a ListNamespacesResponse object.
39+
static Status Validate(const ListNamespacesResponse& response);
40+
41+
/// \brief Validates a CreateNamespaceRequest object.
42+
static Status Validate(const CreateNamespaceRequest& request);
43+
44+
/// \brief Validates a CreateNamespaceResponse object.
45+
static Status Validate(const CreateNamespaceResponse& response);
46+
47+
/// \brief Validates a GetNamespaceResponse object.
48+
static Status Validate(const GetNamespaceResponse& response);
49+
50+
/// \brief Validates an UpdateNamespacePropertiesRequest object.
51+
static Status Validate(const UpdateNamespacePropertiesRequest& request);
52+
53+
/// \brief Validates an UpdateNamespacePropertiesResponse object.
54+
static Status Validate(const UpdateNamespacePropertiesResponse& response);
55+
56+
// Table operations
57+
58+
/// \brief Validates a ListTablesResponse object.
59+
static Status Validate(const ListTablesResponse& response);
60+
61+
/// \brief Validates a LoadTableResult object.
62+
static Status Validate(const LoadTableResult& result);
63+
64+
/// \brief Validates a RegisterTableRequest object.
65+
static Status Validate(const RegisterTableRequest& request);
66+
67+
/// \brief Validates a RenameTableRequest object.
68+
static Status Validate(const RenameTableRequest& request);
69+
};
70+
71+
} // namespace iceberg::rest

0 commit comments

Comments
 (0)