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