diff --git a/google/cloud/storage/async/client.cc b/google/cloud/storage/async/client.cc index 509b6294a7a96..392b6b12d835c 100644 --- a/google/cloud/storage/async/client.cc +++ b/google/cloud/storage/async/client.cc @@ -153,6 +153,12 @@ AsyncClient::ResumeAppendableObjectUpload(BucketName const& bucket_name, append_object_spec.set_object(std::move(object_name)); append_object_spec.set_generation(generation); + return ResumeAppendableObjectUpload(std::move(request), std::move(opts)); +} + +future>> +AsyncClient::ResumeAppendableObjectUpload( + google::storage::v2::BidiWriteObjectRequest request, Options opts) { return connection_ ->ResumeAppendableObjectUpload( {std::move(request), diff --git a/google/cloud/storage/async/client.h b/google/cloud/storage/async/client.h index f1514a810f54d..6f33b51567c96 100644 --- a/google/cloud/storage/async/client.h +++ b/google/cloud/storage/async/client.h @@ -432,7 +432,7 @@ class AsyncClient { * @param bucket_name the name of the bucket that contains the object. * @param object_name the name of the object to be uploaded. * @param generation the object generation to be uploaded. - * @param opts options controlling the behaviour of this RPC, for example the + * @param opts options controlling the behavior of this RPC, for example the * application may change the retry policy. */ future>> @@ -440,6 +440,19 @@ class AsyncClient { std::string object_name, std::int64_t generation, Options opts = {}); + /** + * Resume a resumable upload session for appendable objects and automatic + * recovery from transient failures. + * + * @param request the request contents, it must include the bucket name, + * object name, and generation. Many other fields are optional. + * @param opts options controlling the behavior of this RPC, for example the + * application may change the retry policy. + */ + future>> + ResumeAppendableObjectUpload( + google::storage::v2::BidiWriteObjectRequest request, Options opts = {}); + /* [start-buffered-upload-common] This function always uses [resumable uploads][resumable-link]. The objects diff --git a/google/cloud/storage/async/client_test.cc b/google/cloud/storage/async/client_test.cc index 7feddd2627a64..ed089792262e1 100644 --- a/google/cloud/storage/async/client_test.cc +++ b/google/cloud/storage/async/client_test.cc @@ -1398,6 +1398,55 @@ TEST(AsyncClient, ResumeRewrite2) { } } // namespace + +TEST(AsyncClient, ResumeAppendableObjectUpload2) { + auto constexpr kExpectedRequest = R"pb( + append_object_spec { + bucket: "projects/_/buckets/test-bucket", + object: "test-object", + generation: 42 + } + flush: true + )pb"; + auto mock = std::make_shared(); + EXPECT_CALL(*mock, options) + .WillRepeatedly( + Return(Options{}.set>("O0").set>("O1"))); + + EXPECT_CALL(*mock, ResumeAppendableObjectUpload) + .WillOnce([&](AsyncConnection::AppendableUploadParams const& p) { + EXPECT_THAT(p.options.get>(), "O0"); + EXPECT_THAT(p.options.get>(), "O1-function"); + EXPECT_THAT(p.options.get>(), "O2-function"); + auto expected = google::storage::v2::BidiWriteObjectRequest{}; + EXPECT_TRUE(TextFormat::ParseFromString(kExpectedRequest, &expected)); + EXPECT_THAT(p.request, IsProtoEqual(expected)); + auto writer = std::make_unique(); + EXPECT_CALL(*writer, PersistedState) + .WillRepeatedly(Return(TestProtoObject())); + + return make_ready_future(make_status_or( + std::unique_ptr(std::move(writer)))); + }); + + auto client = AsyncClient(mock); + auto request = google::storage::v2::BidiWriteObjectRequest{}; + ASSERT_TRUE(TextFormat::ParseFromString(kExpectedRequest, &request)); + auto wt = client + .ResumeAppendableObjectUpload( + std::move(request), Options{} + .set>("O1-function") + .set>("O2-function")) + .get(); + ASSERT_STATUS_OK(wt); + AsyncWriter w; + AsyncToken t; + std::tie(w, t) = *std::move(wt); + EXPECT_TRUE(t.valid()); + EXPECT_THAT(w.PersistedState(), VariantWith( + IsProtoEqual(TestProtoObject()))); +} + GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace storage_experimental } // namespace cloud