1#include <userver/s3api/models/multipart_upload/responses.hpp>
8#include <userver/s3api/models/errors.hpp>
9#include <userver/utils/datetime.hpp>
10#include <userver/utils/from_string.hpp>
12USERVER_NAMESPACE_BEGIN
14namespace s3api::multipart_upload {
17pugi::xml_node GetRequiredChildNode(
const pugi::xml_document& doc,
const char* child_name) {
18 auto child = doc.child(child_name);
25std::optional<std::string_view> ExtractChildValue(
26 const pugi::xml_node& node,
27 const char* child_name,
28 bool is_empty_allowed =
true
30 const auto child = node.child(child_name);
31 auto result = (!child.empty() ? std::make_optional<std::string_view>(child.child_value()) : std::nullopt);
32 if (!is_empty_allowed && result && result->empty()) {
33 throw ResponseParsingError(fmt::format(
"node '{}.{}' is not expected to be empty", node.name(), child_name));
38std::string_view ExtractRequiredChildValue(
const pugi::xml_node& node,
const char* child_name) {
39 const auto result = ExtractChildValue(node, child_name,
false);
42 fmt::format(
"node '{}' is missing the required child node '{}'", node.name(), child_name)
48bool ToBoolean(
const std::optional<std::string_view>& maybe_str) {
49 if (maybe_str.value_or(
"false") !=
"true") {
55template <std::integral T>
56std::optional<T> ExtractChildValueAsIntegral(
const pugi::xml_node& node,
const char* child_name)
try
58 const auto maybe_str = ExtractChildValue(node, child_name,
false);
63 return USERVER_NAMESPACE::
utils::FromString<T>(*maybe_str);
65}
catch (
const std::exception& exc) {
67 fmt::format(
"failed to parse node '{}.{}' value as integral type - {}", node.name(), child_name, exc.what())
71template <std::integral T>
72T ExtractRequiredChildValueAsIntegral(
const pugi::xml_node& node,
const char* child_name) {
73 const auto maybe_value = ExtractChildValueAsIntegral<T>(node, child_name);
76 fmt::format(
"node '{}' is missing the required child node '{}'", node.name(), child_name)
82constexpr auto kExtractRequiredChildValueAsUInt = ExtractRequiredChildValueAsIntegral<
unsigned>;
83constexpr auto kExtractChildValueAsUInt = ExtractChildValueAsIntegral<
unsigned>;
84constexpr auto kExtractChildValueAsULong = ExtractChildValueAsIntegral<
unsigned long>;
90 pugi::xml_document xml;
91 const pugi::xml_parse_result
92 parse_result = xml.load_string(http_s3_response_body.c_str(), pugi::parse_default | pugi::parse_escapes);
93 if (parse_result.status != pugi::status_ok) {
96 const auto xml_root_item = GetRequiredChildNode(xml,
"InitiateMultipartUploadResult");
97 result.bucket = ExtractRequiredChildValue(xml_root_item,
"Bucket");
98 result.upload_id = ExtractRequiredChildValue(xml_root_item,
"UploadId");
99 result.key = ExtractRequiredChildValue(xml_root_item,
"Key");
105 pugi::xml_document xml;
106 const pugi::xml_parse_result
107 parse_result = xml.load_string(http_s3_response_body.c_str(), pugi::parse_default | pugi::parse_escapes);
108 if (parse_result.status != pugi::status_ok) {
111 const auto xml_root_item = GetRequiredChildNode(xml,
"CompleteMultipartUploadResult");
112 result.location = ExtractRequiredChildValue(xml_root_item,
"Location");
113 result.bucket = ExtractRequiredChildValue(xml_root_item,
"Bucket");
114 result.key = ExtractRequiredChildValue(xml_root_item,
"Key");
120 pugi::xml_document xml;
121 const pugi::xml_parse_result
122 parse_result = xml.load_string(http_s3_response_body.c_str(), pugi::parse_default | pugi::parse_escapes);
123 if (parse_result.status != pugi::status_ok) {
127 const auto xml_root_item = GetRequiredChildNode(xml,
"ListMultipartUploadsResult");
128 result.bucket = ExtractRequiredChildValue(xml_root_item,
"Bucket");
129 result.key_marker = ExtractChildValue(xml_root_item,
"KeyMarker");
130 result.upload_id_marker = ExtractChildValue(xml_root_item,
"UploadIdMarker");
131 result.next_key_marker = ExtractChildValue(xml_root_item,
"NextKeyMarker");
132 result.next_upload_id_marker = ExtractChildValue(xml_root_item,
"NextUploadIdMarker");
133 result.delimiter = ExtractChildValue(xml_root_item,
"Delimiter");
134 result.is_truncated = ToBoolean(ExtractChildValue(xml_root_item,
"IsTruncated"));
135 result.max_uploads = kExtractChildValueAsUInt(xml_root_item,
"MaxUploads");
137 for (
auto node = xml_root_item.child(
"Upload"); node; node = node.next_sibling(
"Upload")) {
139 multipart_upload.key = ExtractRequiredChildValue(node,
"Key");
140 multipart_upload.upload_id = ExtractRequiredChildValue(node,
"UploadId");
141 if (
const auto maybe_initiated_str = ExtractChildValue(node,
"Initiated")) {
144 multipart_upload.initiated_ts =
147 result.uploads.push_back(std::move(multipart_upload));
150 for (
auto node = xml_root_item.child(
"CommonPrefixes"); node; node = node.next_sibling(
"CommonPrefixes")) {
151 result.common_prefixes.emplace_back(ExtractRequiredChildValue(node,
"Prefix"));
159 pugi::xml_document xml;
160 const pugi::xml_parse_result
161 parse_result = xml.load_string(http_s3_response_body.c_str(), pugi::parse_default | pugi::parse_escapes);
162 if (parse_result.status != pugi::status_ok) {
166 const auto xml_root_item = GetRequiredChildNode(xml,
"ListPartsResult");
167 result.bucket = ExtractRequiredChildValue(xml_root_item,
"Bucket");
168 result.upload_id = ExtractRequiredChildValue(xml_root_item,
"UploadId");
169 result.max_parts = kExtractChildValueAsUInt(xml_root_item,
"MaxParts");
170 result.part_number_marker = kExtractChildValueAsUInt(xml_root_item,
"PartNumberMarker");
171 result.next_part_number_marker = kExtractChildValueAsUInt(xml_root_item,
"NextPartNumberMarker");
172 result.key = ExtractRequiredChildValue(xml_root_item,
"Key");
173 result.is_truncated = ToBoolean(ExtractChildValue(xml_root_item,
"IsTruncated"));
175 for (
auto part_node = xml_root_item.child(
"Part"); part_node; part_node = part_node.next_sibling(
"Part")) {
177 part.etag = ExtractRequiredChildValue(part_node,
"ETag");
178 part.part_number = kExtractRequiredChildValueAsUInt(part_node,
"PartNumber");
179 part.byte_size = kExtractChildValueAsULong(part_node,
"Size");
181 if (
const auto maybe_last_modified_ts_str = ExtractChildValue(part_node,
"LastModified")) {
184 part.last_modified_ts =
188 result.parts.push_back(std::move(part));