Skip to content

Commit 90d72c0

Browse files
authored
impl(rest): support LRO operation types without name method (#14924)
1 parent bbb1355 commit 90d72c0

File tree

4 files changed

+222
-5
lines changed

4 files changed

+222
-5
lines changed

google/cloud/internal/async_rest_long_running_operation_custom.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,44 @@ future<StatusOr<ReturnType>> AsyncRestLongRunningOperation(
7171
});
7272
}
7373

74+
template <typename ReturnType, typename OperationType,
75+
typename GetOperationRequestType, typename CancelOperationRequestType,
76+
typename RequestType, typename StartFunctor, typename RetryPolicyType,
77+
typename CompletionQueue>
78+
future<StatusOr<ReturnType>> AsyncRestLongRunningOperation(
79+
CompletionQueue cq, internal::ImmutableOptions options,
80+
RequestType&& request, StartFunctor&& start,
81+
AsyncRestPollLongRunningOperation<OperationType, GetOperationRequestType>
82+
poll,
83+
AsyncRestCancelLongRunningOperation<CancelOperationRequestType> cancel,
84+
LongRunningOperationValueExtractor<ReturnType, OperationType>
85+
value_extractor,
86+
std::unique_ptr<RetryPolicyType> retry_policy,
87+
std::unique_ptr<BackoffPolicy> backoff_policy, Idempotency idempotent,
88+
std::unique_ptr<PollingPolicy> polling_policy, char const* location,
89+
std::function<bool(OperationType const&)> is_operation_done,
90+
std::function<void(std::string const&, GetOperationRequestType&)>
91+
get_request_set_operation_name,
92+
std::function<void(std::string const&, CancelOperationRequestType&)>
93+
cancel_request_set_operation_name,
94+
std::function<std::string(StatusOr<OperationType> const&)> operation_name) {
95+
auto operation =
96+
AsyncRestRetryLoop(std::move(retry_policy), std::move(backoff_policy),
97+
idempotent, cq, std::forward<StartFunctor>(start),
98+
options, std::forward<RequestType>(request), location);
99+
auto loc = std::string{location};
100+
return AsyncRestPollingLoop<OperationType, GetOperationRequestType,
101+
CancelOperationRequestType>(
102+
std::move(cq), std::move(options), std::move(operation),
103+
std::move(poll), std::move(cancel), std::move(polling_policy),
104+
std::move(location), is_operation_done,
105+
get_request_set_operation_name, cancel_request_set_operation_name,
106+
operation_name)
107+
.then([value_extractor, loc](future<StatusOr<OperationType>> g) {
108+
return value_extractor(g.get(), loc);
109+
});
110+
}
111+
74112
/*
75113
* AsyncAwaitRestLongRunningOperation for services that do not conform to
76114
* AIP-151.
@@ -105,6 +143,38 @@ future<StatusOr<ReturnType>> AsyncRestAwaitLongRunningOperation(
105143
});
106144
}
107145

146+
template <typename ReturnType, typename OperationType,
147+
typename GetOperationRequestType, typename CancelOperationRequestType,
148+
typename CompletionQueue>
149+
future<StatusOr<ReturnType>> AsyncRestAwaitLongRunningOperation(
150+
CompletionQueue cq, internal::ImmutableOptions options,
151+
OperationType operation,
152+
AsyncRestPollLongRunningOperation<OperationType, GetOperationRequestType>
153+
poll,
154+
AsyncRestCancelLongRunningOperation<CancelOperationRequestType> cancel,
155+
LongRunningOperationValueExtractor<ReturnType, OperationType>
156+
value_extractor,
157+
std::unique_ptr<PollingPolicy> polling_policy, char const* location,
158+
std::function<bool(OperationType const&)> is_operation_done,
159+
std::function<void(std::string const&, GetOperationRequestType&)>
160+
get_request_set_operation_name,
161+
std::function<void(std::string const&, CancelOperationRequestType&)>
162+
cancel_request_set_operation_name,
163+
std::function<std::string(StatusOr<OperationType> const&)> operation_name) {
164+
auto loc = std::string{location};
165+
return AsyncRestPollingLoop<OperationType, GetOperationRequestType,
166+
CancelOperationRequestType>(
167+
std::move(cq), std::move(options),
168+
make_ready_future(StatusOr<OperationType>(operation)),
169+
std::move(poll), std::move(cancel), std::move(polling_policy),
170+
std::move(location), is_operation_done,
171+
get_request_set_operation_name, cancel_request_set_operation_name,
172+
operation_name)
173+
.then([value_extractor, loc](future<StatusOr<OperationType>> g) {
174+
return value_extractor(g.get(), loc);
175+
});
176+
}
177+
108178
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
109179
} // namespace rest_internal
110180
} // namespace cloud

google/cloud/internal/async_rest_polling_loop_custom.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,30 @@ future<StatusOr<OperationType>> AsyncRestPollingLoop(
6464
return loop->Start(std::move(op));
6565
}
6666

67+
template <typename OperationType, typename GetOperationRequestType,
68+
typename CancelOperationRequestType>
69+
future<StatusOr<OperationType>> AsyncRestPollingLoop(
70+
google::cloud::CompletionQueue cq, internal::ImmutableOptions options,
71+
future<StatusOr<OperationType>> op,
72+
AsyncRestPollLongRunningOperation<OperationType, GetOperationRequestType>
73+
poll,
74+
AsyncRestCancelLongRunningOperation<CancelOperationRequestType> cancel,
75+
std::unique_ptr<PollingPolicy> polling_policy, std::string location,
76+
std::function<bool(OperationType const&)> is_operation_done,
77+
std::function<void(std::string const&, GetOperationRequestType&)>
78+
get_request_set_operation_name,
79+
std::function<void(std::string const&, CancelOperationRequestType&)>
80+
cancel_request_set_operation_name,
81+
std::function<std::string(StatusOr<OperationType> const&)> operation_name) {
82+
auto loop = std::make_shared<AsyncRestPollingLoopImpl<
83+
OperationType, GetOperationRequestType, CancelOperationRequestType>>(
84+
std::move(cq), options, std::move(poll), std::move(cancel),
85+
std::move(polling_policy), std::move(location), is_operation_done,
86+
get_request_set_operation_name, cancel_request_set_operation_name,
87+
operation_name);
88+
return loop->Start(std::move(op));
89+
}
90+
6791
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
6892
} // namespace rest_internal
6993
} // namespace cloud

google/cloud/internal/async_rest_polling_loop_custom_test.cc

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,22 @@ class BespokeOperationType {
8181
std::string name_;
8282
};
8383

84+
class BespokeOperationTypeNoNameMethod {
85+
public:
86+
bool is_done() const { return is_done_; }
87+
void set_is_done(bool is_done) { is_done_ = is_done; }
88+
std::string const& pseudo_name_function() const { return name_; }
89+
void set_name(std::string name) { name_ = std::move(name); }
90+
std::string* mutable_name() { return &name_; }
91+
bool operator==(BespokeOperationTypeNoNameMethod const& other) const {
92+
return is_done_ == other.is_done_ && name_ == other.name_;
93+
}
94+
95+
private:
96+
bool is_done_;
97+
std::string name_;
98+
};
99+
84100
class BespokeGetOperationRequestType {
85101
public:
86102
std::string const& name() const { return name_; }
@@ -176,6 +192,87 @@ TEST(AsyncRestPollingLoopTest, PollThenSuccessWithBespokeOperationTypes) {
176192
EXPECT_THAT(*actual, Eq(expected));
177193
}
178194

195+
class MockBespokeOperationNoNameMethodStub {
196+
public:
197+
MOCK_METHOD(future<StatusOr<BespokeOperationTypeNoNameMethod>>,
198+
AsyncGetOperation,
199+
(CompletionQueue&, std::unique_ptr<RestContext>, ImmutableOptions,
200+
BespokeGetOperationRequestType const&),
201+
());
202+
203+
MOCK_METHOD(future<Status>, AsyncCancelOperation,
204+
(CompletionQueue&, std::unique_ptr<RestContext>, ImmutableOptions,
205+
BespokeCancelOperationRequestType const&),
206+
());
207+
};
208+
209+
TEST(AsyncRestPollingLoopTest,
210+
PollThenSuccessWithBespokeOperationTypeNoNameMethod) {
211+
Response response;
212+
response.set_seconds(123456);
213+
BespokeOperationTypeNoNameMethod starting_op;
214+
starting_op.set_name("test-op-name");
215+
starting_op.set_is_done(false);
216+
BespokeOperationTypeNoNameMethod expected = starting_op;
217+
expected.set_is_done(true);
218+
219+
auto mock_cq = std::make_shared<MockCompletionQueueImpl>();
220+
EXPECT_CALL(*mock_cq, MakeRelativeTimer)
221+
.WillOnce([](std::chrono::nanoseconds) {
222+
return make_ready_future(
223+
make_status_or(std::chrono::system_clock::now()));
224+
});
225+
CompletionQueue cq(mock_cq);
226+
227+
auto mock = std::make_shared<MockBespokeOperationNoNameMethodStub>();
228+
EXPECT_CALL(*mock, AsyncGetOperation)
229+
.WillOnce([&](CompletionQueue&, std::unique_ptr<RestContext>,
230+
ImmutableOptions const& options,
231+
BespokeGetOperationRequestType const&) {
232+
EXPECT_EQ(options->get<StringOption>(), CurrentTestName());
233+
return make_ready_future(make_status_or(expected));
234+
});
235+
auto policy = std::make_unique<MockPollingPolicy>();
236+
EXPECT_CALL(*policy, clone()).Times(0);
237+
EXPECT_CALL(*policy, OnFailure).Times(0);
238+
EXPECT_CALL(*policy, WaitPeriod)
239+
.WillRepeatedly(Return(std::chrono::milliseconds(1)));
240+
241+
auto current = internal::MakeImmutableOptions(
242+
Options{}.set<StringOption>(CurrentTestName()));
243+
auto pending = AsyncRestPollingLoop<BespokeOperationTypeNoNameMethod,
244+
BespokeGetOperationRequestType,
245+
BespokeCancelOperationRequestType>(
246+
std::move(cq), current, make_ready_future(make_status_or(starting_op)),
247+
[mock](CompletionQueue& cq, std::unique_ptr<RestContext> context,
248+
ImmutableOptions options,
249+
BespokeGetOperationRequestType const& request) {
250+
return mock->AsyncGetOperation(cq, std::move(context),
251+
std::move(options), request);
252+
},
253+
[mock](CompletionQueue& cq, std::unique_ptr<RestContext> context,
254+
ImmutableOptions options,
255+
BespokeCancelOperationRequestType const& request) {
256+
return mock->AsyncCancelOperation(cq, std::move(context),
257+
std::move(options), request);
258+
},
259+
std::move(policy), "test-function",
260+
[](BespokeOperationTypeNoNameMethod const& op) { return op.is_done(); },
261+
[](std::string const& s, BespokeGetOperationRequestType& op) {
262+
op.set_name(s);
263+
},
264+
[](std::string const& s, BespokeCancelOperationRequestType& op) {
265+
op.set_name(s);
266+
},
267+
[](StatusOr<BespokeOperationTypeNoNameMethod> const& op) {
268+
return op->pseudo_name_function();
269+
});
270+
internal::OptionsSpan overlay(Options{}.set<StringOption>("uh-oh"));
271+
StatusOr<BespokeOperationTypeNoNameMethod> actual = pending.get();
272+
ASSERT_THAT(actual, IsOk());
273+
EXPECT_THAT(*actual, Eq(expected));
274+
}
275+
179276
} // namespace
180277
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
181278
} // namespace rest_internal

google/cloud/internal/async_rest_polling_loop_impl.h

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ namespace cloud {
3636
namespace rest_internal {
3737
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
3838

39+
template <typename OperationType>
40+
std::string DefaultOperationName(StatusOr<OperationType> const& operation) {
41+
return operation->name();
42+
}
43+
3944
template <typename OperationType, typename GetOperationRequestType,
4045
typename CancelOperationRequestType>
4146
class AsyncRestPollingLoopImpl
@@ -52,7 +57,8 @@ class AsyncRestPollingLoopImpl
5257
std::function<void(std::string const&, GetOperationRequestType&)>
5358
get_request_set_operation_name,
5459
std::function<void(std::string const&, CancelOperationRequestType&)>
55-
cancel_request_set_operation_name)
60+
cancel_request_set_operation_name,
61+
std::function<std::string(StatusOr<OperationType> const&)> operation_name)
5662
: cq_(std::move(cq)),
5763
options_(std::move(options)),
5864
poll_(std::move(poll)),
@@ -64,7 +70,8 @@ class AsyncRestPollingLoopImpl
6470
get_request_set_operation_name_(
6571
std::move(get_request_set_operation_name)),
6672
cancel_request_set_operation_name_(
67-
std::move(cancel_request_set_operation_name)) {}
73+
std::move(cancel_request_set_operation_name)),
74+
operation_name_(std::move(operation_name)) {}
6875

6976
AsyncRestPollingLoopImpl(
7077
google::cloud::CompletionQueue cq, internal::ImmutableOptions options,
@@ -85,6 +92,23 @@ class AsyncRestPollingLoopImpl
8592
r.set_name(name);
8693
}) {}
8794

95+
AsyncRestPollingLoopImpl(
96+
google::cloud::CompletionQueue cq, internal::ImmutableOptions options,
97+
AsyncRestPollLongRunningOperation<OperationType, GetOperationRequestType>
98+
poll,
99+
AsyncRestCancelLongRunningOperation<CancelOperationRequestType> cancel,
100+
std::unique_ptr<PollingPolicy> polling_policy, std::string location,
101+
std::function<bool(OperationType const&)> is_operation_done,
102+
std::function<void(std::string const&, GetOperationRequestType&)>
103+
get_request_set_operation_name,
104+
std::function<void(std::string const&, CancelOperationRequestType&)>
105+
cancel_request_set_operation_name)
106+
: AsyncRestPollingLoopImpl(
107+
std::move(cq), std::move(options), poll, cancel,
108+
std::move(polling_policy), std::move(location), is_operation_done,
109+
get_request_set_operation_name, cancel_request_set_operation_name,
110+
DefaultOperationName<OperationType>) {}
111+
88112
future<StatusOr<OperationType>> Start(future<StatusOr<OperationType>> op) {
89113
auto self = this->shared_from_this();
90114
auto w = WeakFromThis();
@@ -131,15 +155,16 @@ class AsyncRestPollingLoopImpl
131155

132156
void OnStart(StatusOr<OperationType> op) {
133157
if (!op) return promise_.set_value(std::move(op));
134-
internal::AddSpanAttribute(*options_, "gl-cpp.LRO_name", op->name());
158+
auto operation_name = operation_name_(op);
159+
internal::AddSpanAttribute(*options_, "gl-cpp.LRO_name", operation_name);
135160
if (is_operation_done_(*op)) return promise_.set_value(std::move(op));
136161
GCP_LOG(DEBUG) << location_ << "() polling loop starting for "
137-
<< op->name();
162+
<< operation_name;
138163
bool do_cancel = false;
139164
{
140165
std::unique_lock<std::mutex> lk(mu_);
141166
std::swap(delayed_cancel_, do_cancel);
142-
op_name_ = std::move(*op->mutable_name());
167+
op_name_ = std::move(operation_name);
143168
}
144169
if (do_cancel) DoCancel();
145170
return Wait();
@@ -212,6 +237,7 @@ class AsyncRestPollingLoopImpl
212237
get_request_set_operation_name_;
213238
std::function<void(std::string const&, CancelOperationRequestType&)>
214239
cancel_request_set_operation_name_;
240+
std::function<std::string(StatusOr<OperationType> const&)> operation_name_;
215241

216242
// `delayed_cancel_` and `op_name_`, in contrast, are also used from
217243
// `DoCancel()`, which is called asynchronously, so they need locking.

0 commit comments

Comments
 (0)