Skip to content

Commit 315c752

Browse files
authored
feat(storage): support Autoclass feature (#10003)
This implements support for REST and gRPC, as well as the unit tests and the samples.
1 parent dd47583 commit 315c752

17 files changed

+470
-7
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "google/cloud/storage/bucket_autoclass.h"
16+
#include "google/cloud/internal/format_time_point.h"
17+
#include "google/cloud/internal/ios_flags_saver.h"
18+
#include <iomanip>
19+
#include <iostream>
20+
21+
namespace google {
22+
namespace cloud {
23+
namespace storage {
24+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
25+
26+
std::ostream& operator<<(std::ostream& os, BucketAutoclass const& rhs) {
27+
google::cloud::internal::IosFlagsSaver flags(os);
28+
return os << "{enabled=" << std::boolalpha << rhs.enabled << ", toggle_time="
29+
<< google::cloud::internal::FormatRfc3339(rhs.toggle_time) << "}";
30+
}
31+
32+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
33+
} // namespace storage
34+
} // namespace cloud
35+
} // namespace google
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_BUCKET_AUTOCLASS_H
16+
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_BUCKET_AUTOCLASS_H
17+
18+
#include "google/cloud/storage/version.h"
19+
#include <chrono>
20+
#include <iosfwd>
21+
#include <tuple>
22+
23+
namespace google {
24+
namespace cloud {
25+
namespace storage {
26+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
27+
28+
/**
29+
* The autoclass configuration for a Bucket.
30+
*
31+
* @par Example
32+
*
33+
* @snippet storage_bucket_autoclass_samples.cc get-autoclass
34+
*
35+
* @par Example
36+
*
37+
* @snippet storage_bucket_autoclass_samples.cc set-autoclass
38+
*/
39+
struct BucketAutoclass {
40+
explicit BucketAutoclass(bool e) : enabled(e) {}
41+
explicit BucketAutoclass(bool e, std::chrono::system_clock::time_point tp)
42+
: enabled(e), toggle_time(tp) {}
43+
44+
bool enabled;
45+
std::chrono::system_clock::time_point toggle_time;
46+
};
47+
48+
inline bool operator==(BucketAutoclass const& lhs, BucketAutoclass const& rhs) {
49+
return std::tie(lhs.enabled, lhs.toggle_time) ==
50+
std::tie(rhs.enabled, rhs.toggle_time);
51+
}
52+
53+
inline bool operator!=(BucketAutoclass const& lhs, BucketAutoclass const& rhs) {
54+
return !(lhs == rhs);
55+
}
56+
57+
std::ostream& operator<<(std::ostream& os, BucketAutoclass const& rhs);
58+
59+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
60+
} // namespace storage
61+
} // namespace cloud
62+
} // namespace google
63+
64+
#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_BUCKET_AUTOCLASS_H

google/cloud/storage/bucket_metadata.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ nlohmann::json ActionAsPatch(LifecycleRuleAction const& a) {
8383

8484
bool operator==(BucketMetadata const& lhs, BucketMetadata const& rhs) {
8585
return lhs.acl_ == rhs.acl_ //
86+
&& lhs.autoclass_ == rhs.autoclass_ //
8687
&& lhs.billing_ == rhs.billing_ //
8788
&& lhs.cors_ == rhs.cors_ //
8889
&& lhs.custom_placement_config_ == rhs.custom_placement_config_ //
@@ -121,6 +122,9 @@ std::ostream& operator<<(std::ostream& os, BucketMetadata const& rhs) {
121122
os << absl::StrJoin(rhs.acl(), ", ", absl::StreamFormatter());
122123
os << "]";
123124

125+
if (rhs.has_autoclass()) {
126+
os << ", autoclass=" << rhs.autoclass();
127+
}
124128
if (rhs.has_billing()) {
125129
auto previous_flags = os.flags();
126130
os << ", billing.requesterPays=" << std::boolalpha
@@ -249,6 +253,18 @@ BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::ResetAcl() {
249253
return *this;
250254
}
251255

256+
BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::SetAutoclass(
257+
BucketAutoclass const& v) {
258+
impl_.AddSubPatch(
259+
"autoclass", internal::PatchBuilder().SetBoolField("enabled", v.enabled));
260+
return *this;
261+
}
262+
263+
BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::ResetAutoclass() {
264+
impl_.RemoveField("autoclass");
265+
return *this;
266+
}
267+
252268
BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::SetBilling(
253269
BucketBilling const& v) {
254270
impl_.AddSubPatch("billing", internal::PatchBuilder().SetBoolField(

google/cloud/storage/bucket_metadata.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_BUCKET_METADATA_H
1717

1818
#include "google/cloud/storage/bucket_access_control.h"
19+
#include "google/cloud/storage/bucket_autoclass.h"
1920
#include "google/cloud/storage/bucket_billing.h"
2021
#include "google/cloud/storage/bucket_cors_entry.h"
2122
#include "google/cloud/storage/bucket_custom_placement_config.h"
@@ -69,6 +70,23 @@ class BucketMetadata {
6970
}
7071
///@}
7172

73+
/// @name Accessors and modifiers for Autoclass configuration.
74+
///@{
75+
bool has_autoclass() const { return autoclass_.has_value(); }
76+
BucketAutoclass const& autoclass() const { return *autoclass_; }
77+
absl::optional<BucketAutoclass> const& autoclass_as_optional() const {
78+
return autoclass_;
79+
}
80+
BucketMetadata& set_autoclass(BucketAutoclass v) {
81+
autoclass_ = std::move(v);
82+
return *this;
83+
}
84+
BucketMetadata& reset_autoclass() {
85+
autoclass_.reset();
86+
return *this;
87+
}
88+
///@}
89+
7290
/**
7391
* @name Get and set billing configuration for the Bucket.
7492
*
@@ -549,6 +567,7 @@ class BucketMetadata {
549567
friend std::ostream& operator<<(std::ostream& os, BucketMetadata const& rhs);
550568
// Keep the fields in alphabetical order.
551569
std::vector<BucketAccessControl> acl_;
570+
absl::optional<BucketAutoclass> autoclass_;
552571
absl::optional<BucketBilling> billing_;
553572
std::vector<CorsEntry> cors_;
554573
absl::optional<BucketCustomPlacementConfig> custom_placement_config_;
@@ -607,6 +626,9 @@ class BucketMetadataPatchBuilder {
607626
*/
608627
BucketMetadataPatchBuilder& ResetAcl();
609628

629+
BucketMetadataPatchBuilder& SetAutoclass(BucketAutoclass const& v);
630+
BucketMetadataPatchBuilder& ResetAutoclass();
631+
610632
BucketMetadataPatchBuilder& SetBilling(BucketBilling const& v);
611633
BucketMetadataPatchBuilder& ResetBilling();
612634

google/cloud/storage/bucket_metadata_test.cc

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ BucketMetadata CreateBucketMetadataForTest() {
6868
"etag": "AYX="
6969
}
7070
],
71+
"autoclass": {
72+
"enabled": true,
73+
"toggleTime": "2022-10-07T01:02:03Z"
74+
},
7175
"billing": {
7276
"requesterPays": true
7377
},
@@ -178,6 +182,14 @@ TEST(BucketMetadataTest, Parse) {
178182
EXPECT_EQ(2, actual.acl().size());
179183
EXPECT_EQ("acl-id-0", actual.acl().at(0).id());
180184
EXPECT_EQ("acl-id-1", actual.acl().at(1).id());
185+
186+
auto const expected_autoclass_toggle =
187+
google::cloud::internal::ParseRfc3339("2022-10-07T01:02:03Z");
188+
ASSERT_STATUS_OK(expected_autoclass_toggle);
189+
ASSERT_TRUE(actual.has_autoclass());
190+
EXPECT_EQ(actual.autoclass(),
191+
BucketAutoclass(true, *expected_autoclass_toggle));
192+
181193
EXPECT_TRUE(actual.billing().requester_pays);
182194
EXPECT_EQ(2, actual.cors().size());
183195
auto expected_cors_0 =
@@ -302,6 +314,12 @@ TEST(BucketMetadataTest, IOStream) {
302314
// acl()
303315
EXPECT_THAT(actual, HasSubstr("acl-id-0"));
304316
EXPECT_THAT(actual, HasSubstr("acl-id-1"));
317+
318+
// autoclass()
319+
EXPECT_THAT(
320+
actual,
321+
HasSubstr("autoclass={enabled=true, toggle_time=2022-10-07T01:02:03Z}"));
322+
305323
// billing()
306324
EXPECT_THAT(actual, HasSubstr("enabled=true"));
307325

@@ -383,6 +401,15 @@ TEST(BucketMetadataTest, ToJsonString) {
383401
EXPECT_EQ("user-test-user", actual["acl"][0].value("entity", ""));
384402
EXPECT_EQ("user-test-user2", actual["acl"][1].value("entity", ""));
385403

404+
// autoclass()
405+
ASSERT_TRUE(actual.contains("autoclass"));
406+
auto const expected_autoclass = nlohmann::json{
407+
{"enabled", true},
408+
// "toggleTime" is OUTPUT_ONLY and thus not included in the
409+
// JSON string for create/update.
410+
};
411+
EXPECT_EQ(actual["autoclass"], expected_autoclass);
412+
386413
// billing()
387414
ASSERT_EQ(1U, actual.count("billing")) << actual;
388415
EXPECT_TRUE(actual["billing"].value("requesterPays", false));
@@ -573,6 +600,32 @@ TEST(BucketMetadataTest, SetAcl) {
573600
EXPECT_EQ("READER", copy.acl().at(0).role());
574601
}
575602

603+
/// @test Verify we can change the autoclass configuration in BucketMetadata.
604+
TEST(BucketMetadataTest, SetAutoclass) {
605+
auto expected = CreateBucketMetadataForTest();
606+
auto copy = expected;
607+
ASSERT_TRUE(copy.has_autoclass());
608+
ASSERT_TRUE(copy.autoclass().enabled);
609+
copy.set_autoclass(
610+
BucketAutoclass{false, std::chrono::system_clock::time_point()});
611+
EXPECT_NE(expected, copy);
612+
ASSERT_TRUE(copy.has_autoclass());
613+
ASSERT_FALSE(copy.autoclass().enabled);
614+
}
615+
616+
/// @test Verify we can reset the autoclass configuration in BucketMetadata.
617+
TEST(BucketMetadataTest, ResetAutoclass) {
618+
auto expected = CreateBucketMetadataForTest();
619+
EXPECT_TRUE(expected.has_autoclass());
620+
auto copy = expected;
621+
copy.reset_autoclass();
622+
EXPECT_FALSE(copy.has_autoclass());
623+
EXPECT_NE(expected, copy);
624+
std::ostringstream os;
625+
os << copy;
626+
EXPECT_THAT(os.str(), Not(HasSubstr("autoclass")));
627+
}
628+
576629
/// @test Verify we can change the billing configuration in BucketMetadata.
577630
TEST(BucketMetadataTest, SetBilling) {
578631
auto expected = CreateBucketMetadataForTest();
@@ -945,6 +998,27 @@ TEST(BucketMetadataPatchBuilder, ResetAcl) {
945998
ASSERT_TRUE(json["acl"].is_null()) << json;
946999
}
9471000

1001+
TEST(BucketMetadataPatchBuilder, SetAutoclass) {
1002+
BucketMetadataPatchBuilder builder;
1003+
builder.SetAutoclass(BucketAutoclass(true));
1004+
1005+
auto actual = builder.BuildPatch();
1006+
auto const json = nlohmann::json::parse(actual);
1007+
ASSERT_TRUE(json.contains("autoclass")) << json;
1008+
auto const expected_autoclass = nlohmann::json{{"enabled", true}};
1009+
EXPECT_EQ(expected_autoclass, json["autoclass"]);
1010+
}
1011+
1012+
TEST(BucketMetadataPatchBuilder, ResetAutoclass) {
1013+
BucketMetadataPatchBuilder builder;
1014+
builder.ResetAutoclass();
1015+
1016+
auto actual = builder.BuildPatch();
1017+
auto json = nlohmann::json::parse(actual);
1018+
ASSERT_TRUE(json.contains("autoclass")) << json;
1019+
ASSERT_TRUE(json["autoclass"].is_null()) << json;
1020+
}
1021+
9481022
TEST(BucketMetadataPatchBuilder, SetBilling) {
9491023
BucketMetadataPatchBuilder builder;
9501024
builder.SetBilling(BucketBilling{true});

google/cloud/storage/examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ if (BUILD_TESTING)
2424
set(storage_examples
2525
# cmake-format: sort
2626
storage_bucket_acl_samples.cc
27+
storage_bucket_autoclass_samples.cc
2728
storage_bucket_cors_samples.cc
2829
storage_bucket_default_kms_key_samples.cc
2930
storage_bucket_iam_samples.cc

0 commit comments

Comments
 (0)