|
1 | 1 | #include <expected> |
2 | 2 | #include <s3cpp/s3.h> |
3 | 3 |
|
4 | | -std::expected<ListObjectsResult, Error> S3Client::ListObjects(const std::string& bucket, const std::string& prefix, int maxKeys, const std::string& continuationToken) { |
| 4 | +std::expected<ListObjectsResult, Error> S3Client::ListObjects(const std::string& bucket, const ListObjectsInput& options) { |
5 | 5 | // Silent-ly accept maxKeys > 1000, even though we will return 1K at most |
6 | 6 | // Pagination is opt-in as in the Go SDK, the user must be aware of this |
7 | | - const std::string baseUrl = buildURL(bucket); |
8 | | - std::string url; |
9 | | - if (continuationToken.size() > 0) { |
10 | | - url = baseUrl + std::format("?list-type=2&prefix={}&max-keys={}&continuation-token={}", prefix, maxKeys, continuationToken); |
11 | | - } else { |
12 | | - url = baseUrl + std::format("?list-type=2&prefix={}&max-keys={}", prefix, maxKeys); |
13 | | - } |
| 7 | + |
| 8 | + // Build URL with query parameters |
| 9 | + std::string url = std::format("{}?list-type=2", buildURL(bucket)); |
| 10 | + |
| 11 | + if (options.Prefix.has_value()) |
| 12 | + url += std::format("&prefix={}", options.Prefix.value()); |
| 13 | + |
| 14 | + int maxKeys = options.MaxKeys.value_or(1000); |
| 15 | + url += std::format("&max-keys={}", maxKeys); |
| 16 | + |
| 17 | + if (options.ContinuationToken.has_value()) |
| 18 | + url += std::format("&continuation-token={}", options.ContinuationToken.value()); |
| 19 | + if (options.Delimiter.has_value()) |
| 20 | + url += std::format("&delimiter={}", options.Delimiter.value()); |
| 21 | + if (options.EncodingType.has_value()) |
| 22 | + url += std::format("&encoding-type={}", options.EncodingType.value()); |
| 23 | + if (options.StartAfter.has_value()) |
| 24 | + url += std::format("&start-after={}", options.StartAfter.value()); |
| 25 | + if (options.FetchOwner.has_value() && options.FetchOwner.value()) |
| 26 | + url += "&fetch-owner=true"; |
14 | 27 |
|
15 | 28 | HttpRequest req = Client.get(url).header("Host", getHostHeader(bucket)); |
| 29 | + |
| 30 | + // opt headers |
| 31 | + if (options.ExpectedBucketOwner.has_value()) |
| 32 | + req.header("x-amz-expected-bucket-owner", options.ExpectedBucketOwner.value()); |
| 33 | + |
| 34 | + if (options.RequestPayer.has_value()) |
| 35 | + req.header("x-amz-request-payer", options.RequestPayer.value()); |
| 36 | + |
16 | 37 | Signer.sign(req); |
17 | 38 | HttpResponse res = req.execute(); |
18 | 39 |
|
19 | 40 | const std::vector<XMLNode>& XMLBody = Parser.parse(res.body()); |
20 | 41 |
|
21 | | - if (res.status() != 200) { |
22 | | - return std::unexpected<Error>(deserializeError(XMLBody)); |
| 42 | + if (res.status() >= 200 && res.status() < 300) { |
| 43 | + return deserializeListBucketResult(XMLBody, maxKeys); |
23 | 44 | } |
24 | | - return deserializeListBucketResult(XMLBody, maxKeys); |
| 45 | + return std::unexpected<Error>(deserializeError(XMLBody)); |
25 | 46 | } |
26 | 47 |
|
27 | 48 | std::expected<ListObjectsResult, Error> S3Client::deserializeListBucketResult(const std::vector<XMLNode>& nodes, const int maxKeys) { |
@@ -103,6 +124,8 @@ std::expected<ListObjectsResult, Error> S3Client::deserializeListBucketResult(co |
103 | 124 | response.Contents[contentsIdx].Size = Parser.parseNumber<long>(node.value); |
104 | 125 | } else if (node.tag == "ListBucketResult.Contents.StorageClass") { |
105 | 126 | response.Contents[contentsIdx].StorageClass = std::move(node.value); |
| 127 | + } else if (node.tag == "ListBucketResult.CommonPrefixes.Prefix") { |
| 128 | + response.CommonPrefixes[commonPrefixesIdx].Prefix = std::move(node.value); |
106 | 129 | } else { |
107 | 130 | // Detect and parse error |
108 | 131 | // Note(cristian): This fallback should not be needed as we have |
@@ -132,20 +155,32 @@ std::expected<ListObjectsResult, Error> S3Client::deserializeListBucketResult(co |
132 | 155 | return response; |
133 | 156 | } |
134 | 157 |
|
135 | | -std::expected<std::string, Error> S3Client::GetObject(const std::string& bucket, const std::string& key) { |
| 158 | +std::expected<std::string, Error> S3Client::GetObject(const std::string& bucket, const std::string& key, const GetObjectInput& options) { |
136 | 159 | std::string url = buildURL(bucket) + std::format("/{}", key); |
137 | 160 |
|
138 | 161 | HttpRequest req = Client.get(url).header("Host", getHostHeader(bucket)); |
| 162 | + |
| 163 | + // opt headers |
| 164 | + if (options.Range.has_value()) |
| 165 | + req.header("Range", options.Range.value()); |
| 166 | + if (options.If_Match.has_value()) |
| 167 | + req.header("If-Match", options.If_Match.value()); |
| 168 | + if (options.If_None_Match.has_value()) |
| 169 | + req.header("If-None-Match", options.If_None_Match.value()); |
| 170 | + if (options.If_Modified_Since.has_value()) |
| 171 | + req.header("If-Modified-Since", options.If_Modified_Since.value()); |
| 172 | + if (options.If_Unmodified_Since.has_value()) |
| 173 | + req.header("If-Unmodified-Since", options.If_Unmodified_Since.value()); |
| 174 | + |
139 | 175 | Signer.sign(req); |
140 | 176 | HttpResponse res = req.execute(); |
141 | 177 |
|
142 | 178 | const std::vector<XMLNode>& XMLBody = Parser.parse(res.body()); |
143 | 179 |
|
144 | | - if (res.status() != 200) { |
145 | | - return std::unexpected<Error>(deserializeError(XMLBody)); |
| 180 | + if (res.status() >= 200 && res.status() < 300) { |
| 181 | + return res.body(); |
146 | 182 | } |
147 | | - |
148 | | - return res.body(); |
| 183 | + return std::unexpected<Error>(deserializeError(XMLBody)); |
149 | 184 | } |
150 | 185 |
|
151 | 186 | Error S3Client::deserializeError(const std::vector<XMLNode>& nodes) { |
|
0 commit comments