1 // Copyright 2020 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 "generator/internal/connection_generator.h"
16 #include "absl/memory/memory.h"
17 #include "generator/internal/codegen_utils.h"
18 #include "generator/internal/descriptor_utils.h"
19 #include "generator/internal/predicate_utils.h"
20 #include "generator/internal/printer.h"
21 #include <google/api/client.pb.h>
22 #include <google/protobuf/descriptor.h>
23
24 namespace google {
25 namespace cloud {
26 namespace generator_internal {
27
ConnectionGenerator(google::protobuf::ServiceDescriptor const * service_descriptor,VarsDictionary service_vars,std::map<std::string,VarsDictionary> service_method_vars,google::protobuf::compiler::GeneratorContext * context)28 ConnectionGenerator::ConnectionGenerator(
29 google::protobuf::ServiceDescriptor const* service_descriptor,
30 VarsDictionary service_vars,
31 std::map<std::string, VarsDictionary> service_method_vars,
32 google::protobuf::compiler::GeneratorContext* context)
33 : ServiceCodeGenerator("connection_header_path", "connection_cc_path",
34 service_descriptor, std::move(service_vars),
35 std::move(service_method_vars), context) {}
36
GenerateHeader()37 Status ConnectionGenerator::GenerateHeader() {
38 HeaderPrint(CopyrightLicenseFileHeader());
39 HeaderPrint( // clang-format off
40 "// Generated by the Codegen C++ plugin.\n"
41 "// If you make any local changes, they will be lost.\n"
42 "// source: $proto_file_name$\n"
43 "#ifndef $header_include_guard$\n"
44 "#define $header_include_guard$\n"
45 "\n");
46 // clang-format on
47
48 // includes
49 HeaderLocalIncludes(
50 {vars("connection_options_header_path"),
51 vars("idempotency_policy_header_path"), vars("stub_header_path"),
52 vars("retry_policy_header_path"), "google/cloud/backoff_policy.h",
53 "google/cloud/future.h", "google/cloud/internal/pagination_range.h",
54 "google/cloud/polling_policy.h", "google/cloud/status_or.h",
55 "google/cloud/version.h"});
56 HeaderSystemIncludes({"google/longrunning/operations.grpc.pb.h", "memory"});
57 HeaderPrint("\n");
58
59 auto result = HeaderOpenNamespaces();
60 if (!result.ok()) return result;
61
62 // List*Range types
63 for (auto const& method : methods()) {
64 HeaderPrintMethod(
65 method,
66 {MethodPattern(
67 {
68 // clang-format off
69 {"using $method_name$Range = "
70 "google::cloud::internal::PaginationRange<\n"
71 " $range_output_type$>;\n\n"},
72 // clang-format on
73 },
74 All(IsNonStreaming, Not(IsLongrunningOperation), IsPaginated))},
75 __FILE__, __LINE__);
76 }
77
78 // Abstract interface Connection base class
79 HeaderPrint( // clang-format off
80 "class $connection_class_name$ {\n"
81 " public:\n"
82 " virtual ~$connection_class_name$() = 0;\n"
83 "\n");
84 // clang-format on
85
86 for (auto const& method : methods()) {
87 HeaderPrintMethod(
88 method,
89 {MethodPattern(
90 {
91 {IsResponseTypeEmpty,
92 // clang-format off
93 " virtual Status\n",
94 " virtual StatusOr<$response_type$>\n"},
95 {" $method_name$($request_type$ const& request);\n"
96 "\n",}
97 // clang-format on
98 },
99 All(IsNonStreaming, Not(IsLongrunningOperation),
100 Not(IsPaginated))),
101 MethodPattern(
102 {
103 {IsResponseTypeEmpty,
104 // clang-format off
105 " virtual future<Status>\n",
106 " virtual future<StatusOr<$longrunning_deduced_response_type$>>\n"},
107 {" $method_name$($request_type$ const& request);\n"
108 "\n",}
109 // clang-format on
110 },
111 All(IsNonStreaming, IsLongrunningOperation, Not(IsPaginated))),
112 MethodPattern(
113 {
114 // clang-format off
115 {" virtual $method_name$Range\n"
116 " $method_name$($request_type$ request);\n\n"},
117 // clang-format on
118 },
119 All(IsNonStreaming, Not(IsLongrunningOperation), IsPaginated))},
120 __FILE__, __LINE__);
121 }
122
123 // close abstract interface Connection base class
124 HeaderPrint( // clang-format off
125 "};\n\n");
126 // clang-format on
127
128 HeaderPrint( // clang-format off
129 "std::shared_ptr<$connection_class_name$> Make$connection_class_name$(\n"
130 " ConnectionOptions const& options = ConnectionOptions());\n\n");
131 // clang-format on
132
133 HeaderPrint( // clang-format off
134 "std::shared_ptr<$connection_class_name$> Make$connection_class_name$(\n"
135 " ConnectionOptions const& options,\n"
136 " std::unique_ptr<RetryPolicy> retry_policy,\n"
137 " std::unique_ptr<BackoffPolicy> backoff_policy,\n"
138 " std::unique_ptr<PollingPolicy> polling_policy,\n"
139 " std::unique_ptr<$idempotency_class_name$> idempotency_policy);\n\n");
140 // clang-format on
141
142 HeaderPrint( // clang-format off
143 "std::shared_ptr<$connection_class_name$> Make$connection_class_name$(\n"
144 " std::shared_ptr<$product_internal_namespace$::$stub_class_name$> stub,\n"
145 " std::unique_ptr<RetryPolicy> retry_policy,\n"
146 " std::unique_ptr<BackoffPolicy> backoff_policy,\n"
147 " std::unique_ptr<PollingPolicy> polling_policy,\n"
148 " std::unique_ptr<$idempotency_class_name$> idempotency_policy);\n\n");
149 // clang-format on
150
151 HeaderCloseNamespaces();
152 // close header guard
153 HeaderPrint( // clang-format off
154 "#endif // $header_include_guard$\n");
155 // clang-format on
156 return {};
157 }
158
GenerateCc()159 Status ConnectionGenerator::GenerateCc() {
160 CcPrint(CopyrightLicenseFileHeader());
161 CcPrint( // clang-format off
162 "// Generated by the Codegen C++ plugin.\n"
163 "// If you make any local changes, they will be lost.\n"
164 "// source: $proto_file_name$\n\n");
165 // clang-format on
166
167 // includes
168 CcLocalIncludes({vars("connection_header_path"),
169 vars("stub_factory_header_path"),
170 "google/cloud/internal/polling_loop.h",
171 "google/cloud/internal/retry_loop.h"});
172 CcSystemIncludes({"memory"});
173 CcPrint("\n");
174
175 auto result = CcOpenNamespaces();
176 if (!result.ok()) return result;
177
178 CcPrint( // clang-format off
179 "$connection_class_name$::~$connection_class_name$() = default;\n\n");
180 // clang-format on
181
182 for (auto const& method : methods()) {
183 CcPrintMethod(
184 method,
185 {MethodPattern(
186 {
187 {IsResponseTypeEmpty,
188 // clang-format off
189 "Status\n",
190 "StatusOr<$response_type$>\n"},
191 {"$connection_class_name$::$method_name$(\n"
192 " $request_type$ const&) {\n"
193 " return Status(StatusCode::kUnimplemented, \"not implemented\");\n"
194 "}\n\n"
195 },
196 // clang-format on
197 },
198 All(IsNonStreaming, Not(IsLongrunningOperation),
199 Not(IsPaginated))),
200 MethodPattern(
201 {
202 {IsResponseTypeEmpty,
203 // clang-format off
204 "future<Status>\n",
205 "future<StatusOr<$longrunning_deduced_response_type$>>\n"},
206 {"$connection_class_name$::$method_name$(\n"
207 " $request_type$ const&) {\n"
208 " return google::cloud::make_ready_future<\n"
209 " StatusOr<$longrunning_deduced_response_type$>>(\n"
210 " Status(StatusCode::kUnimplemented, \"not implemented\"));\n"
211 "}\n\n"
212 },
213 // clang-format on
214 },
215 All(IsNonStreaming, IsLongrunningOperation, Not(IsPaginated))),
216 MethodPattern(
217 {
218 // clang-format off
219 {"$method_name$Range $connection_class_name$::$method_name$(\n"
220 " $request_type$ request) {\n"
221 " return google::cloud::internal::MakePaginationRange<$method_name$Range>(\n"
222 " std::move(request),\n"
223 " []($request_type$ const&) {\n"
224 " return StatusOr<$response_type$>{};\n"
225 " },\n"
226 " []($response_type$ const&) {\n"
227 " return std::vector<$range_output_type$>();\n"
228 " });\n"
229 "}\n\n"
230 // clang-format on
231 },
232 },
233 All(IsNonStreaming, Not(IsLongrunningOperation), IsPaginated))},
234 __FILE__, __LINE__);
235 }
236
237 // open anonymous namespace
238 CcPrint("namespace {\n");
239
240 // default policies
241 CcPrint( // clang-format off
242 "std::unique_ptr<RetryPolicy> DefaultRetryPolicy() {\n"
243 " return LimitedTimeRetryPolicy(std::chrono::minutes(30)).clone();\n"
244 "}\n"
245 "\n"
246 "std::unique_ptr<BackoffPolicy> DefaultBackoffPolicy() {\n"
247 " auto constexpr kBackoffScaling = 2.0;\n"
248 " return ExponentialBackoffPolicy(std::chrono::seconds(1),\n"
249 " std::chrono::minutes(5), kBackoffScaling)\n"
250 " .clone();\n"
251 "}\n"
252 "\n"
253 "std::unique_ptr<PollingPolicy> DefaultPollingPolicy() {\n"
254 " auto constexpr kBackoffScaling = 2.0;\n"
255 " return GenericPollingPolicy<LimitedTimeRetryPolicy, ExponentialBackoffPolicy>(\n"
256 " LimitedTimeRetryPolicy(std::chrono::minutes(30)),\n"
257 " ExponentialBackoffPolicy(std::chrono::seconds(10),\n"
258 " std::chrono::minutes(5), kBackoffScaling))\n"
259 " .clone();\n"
260 "}\n\n"
261 // clang-format on
262 );
263
264 // default connection implementation class
265 CcPrint( //clang-format off
266 "class $connection_class_name$Impl : public $connection_class_name$ {\n"
267 " public:\n"
268 " explicit $connection_class_name$Impl(\n"
269 " std::shared_ptr<$product_internal_namespace$::$stub_class_name$> "
270 "stub,\n"
271 " std::unique_ptr<RetryPolicy> retry_policy,\n"
272 " std::unique_ptr<BackoffPolicy> backoff_policy,\n"
273 " std::unique_ptr<PollingPolicy> polling_policy,\n"
274 " std::unique_ptr<$idempotency_class_name$> "
275 "idempotency_policy)\n"
276 " : stub_(std::move(stub)),\n"
277 " retry_policy_prototype_(std::move(retry_policy)),\n"
278 " backoff_policy_prototype_(std::move(backoff_policy)),\n"
279 " polling_policy_prototype_(std::move(polling_policy)),\n"
280 " idempotency_policy_(std::move(idempotency_policy)) {}\n"
281 "\n"
282 " explicit $connection_class_name$Impl(\n"
283 " std::shared_ptr<$product_internal_namespace$::$stub_class_name$> "
284 "stub)\n"
285 " : $connection_class_name$Impl(\n"
286 " std::move(stub),\n"
287 " DefaultRetryPolicy(),\n"
288 " DefaultBackoffPolicy(),\n"
289 " DefaultPollingPolicy(),\n"
290 " MakeDefault$idempotency_class_name$()) {}\n"
291 "\n"
292 " ~$connection_class_name$Impl() override = default;\n\n");
293 // clang-format on
294
295 for (auto const& method : methods()) {
296 CcPrintMethod(
297 method,
298 {MethodPattern(
299 {
300 {IsResponseTypeEmpty,
301 // clang-format off
302 " Status\n",
303 " StatusOr<$response_type$>\n"},
304 {" $method_name$(\n"
305 " $request_type$ const& request) override {\n"
306 " return google::cloud::internal::RetryLoop(\n"
307 " retry_policy_prototype_->clone(), backoff_policy_prototype_->clone(),\n"
308 " idempotency_policy_->$method_name$(request),\n"
309 " [this](grpc::ClientContext& context,\n"
310 " $request_type$ const& request) {\n"
311 " return stub_->$method_name$(context, request);\n"
312 " },\n"
313 " request, __func__);\n"
314 "}\n"
315 "\n",}
316 // clang-format on
317 },
318 All(IsNonStreaming, Not(IsLongrunningOperation),
319 Not(IsPaginated))),
320 MethodPattern(
321 {
322 {IsResponseTypeEmpty,
323 // clang-format off
324 " future<Status>\n",
325 " future<StatusOr<$longrunning_deduced_response_type$>>\n"},
326 {" $method_name$(\n"
327 " $request_type$ const& request) override {\n"
328 " auto operation = google::cloud::internal::RetryLoop(\n"
329 " retry_policy_prototype_->clone(), backoff_policy_prototype_->clone(),\n"
330 " idempotency_policy_->$method_name$(request),\n"
331 " [this](grpc::ClientContext& context,\n"
332 " $request_type$ const& request) {\n"
333 " return stub_->$method_name$(context, request);\n"
334 " },\n"
335 " request, __func__);\n"
336 " if (!operation) {\n"
337 " return google::cloud::make_ready_future(\n"
338 " StatusOr<$longrunning_deduced_response_type$>(operation.status()));\n"
339 " }\n"
340 "\n"
341 " return Await$method_name$(*std::move(operation));\n"
342 "}\n"
343 "\n",}
344 // clang-format on
345 },
346 All(IsNonStreaming, IsLongrunningOperation, Not(IsPaginated))),
347 MethodPattern(
348 {
349 // clang-format off
350 {" $method_name$Range $method_name$(\n"
351 " $request_type$ request) override {\n"
352 " request.clear_page_token();\n"
353 " auto stub = stub_;\n"
354 " auto retry =\n"
355 " std::shared_ptr<RetryPolicy const>(retry_policy_prototype_->clone());\n"
356 " auto backoff = std::shared_ptr<BackoffPolicy const>(\n"
357 " backoff_policy_prototype_->clone());\n"
358 " auto idempotency = idempotency_policy_->$method_name$(request);\n"
359 " char const* function_name = __func__;\n"
360 " return google::cloud::internal::MakePaginationRange<$method_name$Range>(\n"
361 " std::move(request),\n"
362 " [stub, retry, backoff, idempotency, function_name]\n"
363 " ($request_type$ const& r) {\n"
364 " return google::cloud::internal::RetryLoop(\n"
365 " retry->clone(), backoff->clone(), idempotency,\n"
366 " [stub](grpc::ClientContext& context,\n"
367 " $request_type$ const& request) {\n"
368 " return stub->$method_name$(context, request);\n"
369 " },\n"
370 " r, function_name);\n"
371 " },\n"
372 " []($response_type$ r) {\n"
373 " std::vector<$range_output_type$> result(r.$range_output_field_name$().size());\n"
374 " auto& messages = *r.mutable_$range_output_field_name$();\n"
375 " std::move(messages.begin(), messages.end(), result.begin());\n"
376 " return result;\n"
377 " });\n"
378 " }\n\n"
379 // clang-format on
380 },
381 },
382 All(IsNonStreaming, Not(IsLongrunningOperation), IsPaginated))},
383 __FILE__, __LINE__);
384 }
385
386 CcPrint( // clang-format off
387 " private:\n");
388 // clang-format on
389
390 // TODO(#4038) - use the (implicit) completion queue to run this loop, and
391 // once using a completion queue, consider changing to AsyncCancelOperation.
392 CcPrint( // clang-format off
393 " template <typename MethodResponse, template<typename> class Extractor,\n"
394 " typename Stub>\n"
395 " future<StatusOr<MethodResponse>>\n"
396 " AwaitLongrunningOperation(google::longrunning::Operation operation) { // NOLINT\n"
397 " using ResponseExtractor = Extractor<MethodResponse>;\n"
398 " std::weak_ptr<Stub> cancel_stub(stub_);\n"
399 " promise<typename ResponseExtractor::ReturnType> pr(\n"
400 " [cancel_stub, operation]() {\n"
401 " grpc::ClientContext context;\n"
402 " context.set_deadline(std::chrono::system_clock::now() +\n"
403 " std::chrono::seconds(60));\n"
404 " google::longrunning::CancelOperationRequest request;\n"
405 " request.set_name(operation.name());\n"
406 " if (auto ptr = cancel_stub.lock()) {\n"
407 " ptr->CancelOperation(context, request);\n"
408 " }\n"
409 " });\n"
410 " auto f = pr.get_future();\n"
411 " std::thread t(\n"
412 " [](std::shared_ptr<Stub> stub,\n"
413 " google::longrunning::Operation operation,\n"
414 " std::unique_ptr<PollingPolicy> polling_policy,\n"
415 " google::cloud::promise<typename ResponseExtractor::ReturnType> promise,\n"
416 " char const* location) mutable {\n"
417 " auto result = google::cloud::internal::PollingLoop<ResponseExtractor>(\n"
418 " std::move(polling_policy),\n"
419 " [stub](grpc::ClientContext& context,\n"
420 " google::longrunning::GetOperationRequest const& request) {\n"
421 " return stub->GetOperation(context, request);\n"
422 " },\n"
423 " std::move(operation), location);\n"
424 " stub.reset();\n"
425 " promise.set_value(std::move(result));\n"
426 " },\n"
427 " stub_, std::move(operation), polling_policy_prototype_->clone(),\n"
428 " std::move(pr), __func__);\n"
429 " t.detach();\n"
430 " return f;\n"
431 " }\n\n"
432 );
433 // clang-format on
434
435 for (auto const& method : methods()) {
436 CcPrintMethod(
437 method,
438 {MethodPattern(
439 {
440 {IsResponseTypeEmpty,
441 // clang-format off
442 " future<Status>\n",
443 " future<StatusOr<$longrunning_deduced_response_type$>>\n"},
444 {" Await$method_name$(\n"
445 " google::longrunning::Operation operation) {\n"
446 " return AwaitLongrunningOperation<\n"
447 " $longrunning_deduced_response_type$,\n"},
448 {IsLongrunningMetadataTypeUsedAsResponse,
449 " google::cloud::internal::PollingLoopMetadataExtractor,\n",
450 " google::cloud::internal::PollingLoopResponseExtractor,\n"},
451 {" golden_internal::DatabaseAdminStub>(std::move(operation));\n"
452 " }\n\n"}
453 // clang-format on
454 },
455 All(IsNonStreaming, IsLongrunningOperation, Not(IsPaginated)))},
456 __FILE__, __LINE__);
457 }
458
459 CcPrint( // clang-format off
460 " std::shared_ptr<$product_internal_namespace$::$stub_class_name$> stub_;\n"
461 " std::unique_ptr<RetryPolicy const> retry_policy_prototype_;\n"
462 " std::unique_ptr<BackoffPolicy const> backoff_policy_prototype_;\n"
463 " std::unique_ptr<PollingPolicy const> polling_policy_prototype_;\n"
464 " std::unique_ptr<$idempotency_class_name$> idempotency_policy_;\n"
465 "};\n");
466 // clang-format on
467
468 CcPrint("} // namespace\n\n");
469
470 CcPrint( // clang-format off
471 "std::shared_ptr<$connection_class_name$> Make$connection_class_name$(\n"
472 " ConnectionOptions const& options) {\n"
473 " return std::make_shared<$connection_class_name$Impl>(\n"
474 " $product_internal_namespace$::CreateDefault$stub_class_name$(options));\n"
475 "}\n\n");
476 // clang-format on
477
478 CcPrint( // clang-format off
479 "std::shared_ptr<$connection_class_name$> Make$connection_class_name$(\n"
480 " ConnectionOptions const& options, std::unique_ptr<RetryPolicy> "
481 "retry_policy,\n"
482 " std::unique_ptr<BackoffPolicy> backoff_policy,\n"
483 " std::unique_ptr<PollingPolicy> polling_policy,\n"
484 " std::unique_ptr<$idempotency_class_name$> idempotency_policy) {\n"
485 " return std::make_shared<$connection_class_name$Impl>(\n"
486 " $product_internal_namespace$::CreateDefault$stub_class_name$(options),\n"
487 " std::move(retry_policy), std::move(backoff_policy),\n"
488 " std::move(polling_policy), std::move(idempotency_policy));\n"
489 "}\n\n");
490 // clang-format on
491
492 CcPrint( // clang-format off
493 "std::shared_ptr<$connection_class_name$> Make$connection_class_name$(\n"
494 " std::shared_ptr<$product_internal_namespace$::$stub_class_name$> stub,\n"
495 " std::unique_ptr<RetryPolicy> retry_policy,\n"
496 " std::unique_ptr<BackoffPolicy> backoff_policy,\n"
497 " std::unique_ptr<PollingPolicy> polling_policy,\n"
498 " std::unique_ptr<$idempotency_class_name$> idempotency_policy) {\n"
499 " return std::make_shared<$connection_class_name$Impl>(\n"
500 " std::move(stub), std::move(retry_policy), std::move(backoff_policy),\n"
501 " std::move(polling_policy), std::move(idempotency_policy));\n"
502 "}\n\n");
503 // clang-format on
504
505 CcCloseNamespaces();
506 return {};
507 }
508
509 } // namespace generator_internal
510 } // namespace cloud
511 } // namespace google
512