1 /*
2  * Copyright 2020 Google Inc. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * NOTE: The following implementation is a translation for the Swift-grpc
19  * generator since flatbuffers doesnt allow plugins for now. if an issue arises
20  * please open an issue in the flatbuffers repository. This file should always
21  * be maintained according to the Swift-grpc repository
22  */
23 
24 #include <map>
25 #include <sstream>
26 
27 #include "flatbuffers/util.h"
28 #include "src/compiler/schema_interface.h"
29 #include "src/compiler/ts_generator.h"
30 
31 namespace grpc_ts_generator {
32 
ToDasherizedCase(const grpc::string pascal_case)33 grpc::string ToDasherizedCase(const grpc::string pascal_case) {
34   std::string dasherized_case;
35   char p = 0;
36   for (size_t i = 0; i < pascal_case.length(); i++) {
37     char const &c = pascal_case[i];
38     if (flatbuffers::is_alpha_upper(c)) {
39       if (i > 0 && p != flatbuffers::kPathSeparator) dasherized_case += "-";
40       dasherized_case += flatbuffers::CharToLower(c);
41     } else {
42       dasherized_case += c;
43     }
44     p = c;
45   }
46   return dasherized_case;
47 }
48 
GenerateNamespace(const std::vector<std::string> namepsace,const std::string filename,const bool include_separator)49 grpc::string GenerateNamespace(const std::vector<std::string> namepsace,
50                                const std::string filename,
51                                const bool include_separator) {
52   grpc::string path = "";
53   if (include_separator) path += ".";
54 
55   for (auto it = namepsace.begin(); it < namepsace.end(); it++) {
56     if (include_separator) path += "/";
57     path += include_separator ? ToDasherizedCase(*it) : *it + "_";
58   }
59 
60   if (include_separator) path += "/";
61   path += include_separator ? ToDasherizedCase(filename) : filename;
62   return path;
63 }
64 
65 // MARK: - Shared code
66 
GenerateImports(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary,const bool grpc_var_import)67 void GenerateImports(const grpc_generator::Service *service,
68                      grpc_generator::Printer *printer,
69                      std::map<grpc::string, grpc::string> *dictonary,
70                      const bool grpc_var_import) {
71   auto vars = *dictonary;
72   printer->Print(
73       "// Generated GRPC code for FlatBuffers TS *** DO NOT EDIT ***\n");
74   printer->Print("import * as flatbuffers from 'flatbuffers';\n");
75 
76   std::set<grpc::string> generated_imports;
77 
78   for (auto it = 0; it < service->method_count(); it++) {
79     auto method = service->method(it);
80     auto output = method->get_output_type_name();
81     auto input = method->get_input_type_name();
82     auto input_namespace = method->get_input_namespace_parts();
83 
84     vars["OUTPUT"] = output;
85     vars["INPUT"] = input;
86 
87     if (generated_imports.find(output) == generated_imports.end()) {
88       generated_imports.insert(output);
89       vars["OUTPUT_DIR"] =
90           GenerateNamespace(method->get_output_namespace_parts(), output, true);
91       vars["Output_alias"] = GenerateNamespace(
92           method->get_output_namespace_parts(), output, false);
93       printer->Print(
94           vars, "import { $OUTPUT$ as $Output_alias$ } from '$OUTPUT_DIR$';\n");
95     }
96     if (generated_imports.find(input) == generated_imports.end()) {
97       generated_imports.insert(input);
98       vars["INPUT_DIR"] =
99           GenerateNamespace(method->get_output_namespace_parts(), input, true);
100       vars["Input_alias"] =
101           GenerateNamespace(method->get_output_namespace_parts(), input, false);
102       printer->Print(
103           vars, "import { $INPUT$ as $Input_alias$ } from '$INPUT_DIR$';\n");
104     }
105   }
106   printer->Print("\n");
107   if (grpc_var_import)
108     printer->Print("var grpc = require('grpc');\n");
109   else
110     printer->Print("import * as grpc from 'grpc';\n");
111   printer->Print("\n");
112 }
113 
114 // MARK: - Generate Main GRPC Code
115 
GetStreamType(grpc_generator::Printer * printer,const grpc_generator::Method * method,std::map<grpc::string,grpc::string> * dictonary)116 void GetStreamType(grpc_generator::Printer *printer,
117                    const grpc_generator::Method *method,
118                    std::map<grpc::string, grpc::string> *dictonary) {
119   auto vars = *dictonary;
120   auto client_streaming = method->ClientStreaming() || method->BidiStreaming();
121   auto server_streaming = method->ServerStreaming() || method->BidiStreaming();
122   vars["ClientStreaming"] = client_streaming ? "true" : "false";
123   vars["ServerStreaming"] = server_streaming ? "true" : "false";
124   printer->Print(vars, "requestStream: $ClientStreaming$,\n");
125   printer->Print(vars, "responseStream: $ServerStreaming$,\n");
126 }
127 
GenerateSerializeMethod(grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)128 void GenerateSerializeMethod(grpc_generator::Printer *printer,
129                              std::map<grpc::string, grpc::string> *dictonary) {
130   auto vars = *dictonary;
131   printer->Print(vars, "function serialize_$Type$(buffer_args) {\n");
132   printer->Indent();
133   printer->Print(vars, "if (!(buffer_args instanceof $Type$)) {\n");
134   printer->Indent();
135   printer->Print(vars,
136                  "throw new Error('Expected argument of type $VALUE$');\n");
137   printer->Outdent();
138   printer->Print("}\n");
139   printer->Print(vars, "return buffer_args.serialize();\n");
140   printer->Outdent();
141   printer->Print("}\n\n");
142 }
143 
GenerateDeserializeMethod(grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)144 void GenerateDeserializeMethod(
145     grpc_generator::Printer *printer,
146     std::map<grpc::string, grpc::string> *dictonary) {
147   auto vars = *dictonary;
148   printer->Print(vars, "function deserialize_$Type$(buffer) {\n");
149   printer->Indent();
150   printer->Print(vars,
151                  "return $Type$.getRootAs$VALUE$(new "
152                  "flatbuffers.ByteBuffer(buffer))\n");
153   printer->Outdent();
154   printer->Print("}\n\n");
155 }
156 
GenerateMethods(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)157 void GenerateMethods(const grpc_generator::Service *service,
158                      grpc_generator::Printer *printer,
159                      std::map<grpc::string, grpc::string> *dictonary) {
160   auto vars = *dictonary;
161 
162   std::set<grpc::string> generated_functions;
163 
164   for (auto it = 0; it < service->method_count(); it++) {
165     auto method = service->method(it);
166     auto output = method->get_output_type_name();
167     auto input = method->get_input_type_name();
168 
169     if (generated_functions.find(output) == generated_functions.end()) {
170       generated_functions.insert(output);
171       vars["VALUE"] = output;
172       vars["Type"] = GenerateNamespace(method->get_output_namespace_parts(),
173                                        output, false);
174       GenerateSerializeMethod(printer, &vars);
175       GenerateDeserializeMethod(printer, &vars);
176     }
177     printer->Print("\n");
178     if (generated_functions.find(input) == generated_functions.end()) {
179       generated_functions.insert(input);
180       vars["VALUE"] = input;
181       vars["Type"] =
182           GenerateNamespace(method->get_input_namespace_parts(), input, false);
183       GenerateSerializeMethod(printer, &vars);
184       GenerateDeserializeMethod(printer, &vars);
185     }
186   }
187 }
188 
GenerateService(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)189 void GenerateService(const grpc_generator::Service *service,
190                      grpc_generator::Printer *printer,
191                      std::map<grpc::string, grpc::string> *dictonary) {
192   auto vars = *dictonary;
193   vars["NAME"] = service->name() + "Service";
194 
195   printer->Print(vars, "var $NAME$ = exports.$NAME$ = {\n");
196   printer->Indent();
197   for (auto it = 0; it < service->method_count(); it++) {
198     auto method = service->method(it);
199     vars["MethodName"] = method->name();
200     vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(),
201                                        method->get_output_type_name(), false);
202     vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(),
203                                       method->get_input_type_name(), false);
204     printer->Print(vars, "$MethodName$: {\n");
205     printer->Indent();
206     printer->Print(vars, "path: '/$PATH$$ServiceName$/$MethodName$',\n");
207     GetStreamType(printer, &*method, &vars);
208     printer->Print(vars, "requestType: flatbuffers.ByteBuffer,\n");
209     printer->Print(vars, "responseType: $OUTPUT$,\n");
210     printer->Print(vars, "requestSerialize: serialize_$INPUT$,\n");
211     printer->Print(vars, "requestDeserialize: deserialize_$INPUT$,\n");
212     printer->Print(vars, "responseSerialize: serialize_$OUTPUT$,\n");
213     printer->Print(vars, "responseDeserialize: deserialize_$OUTPUT$,\n");
214     printer->Outdent();
215     printer->Print("},\n");
216   }
217   printer->Outdent();
218   printer->Print("};\n");
219   printer->Print(vars,
220                  "exports.$ServiceName$Client = "
221                  "grpc.makeGenericClientConstructor($NAME$);");
222 }
223 
Generate(grpc_generator::File * file,const grpc_generator::Service * service,const grpc::string & filename)224 grpc::string Generate(grpc_generator::File *file,
225                       const grpc_generator::Service *service,
226                       const grpc::string &filename) {
227   grpc::string output;
228   std::map<grpc::string, grpc::string> vars;
229 
230   vars["PATH"] = file->package();
231 
232   if (!file->package().empty()) { vars["PATH"].append("."); }
233 
234   vars["ServiceName"] = service->name();
235   vars["FBSFile"] = service->name() + "_fbs";
236   vars["Filename"] = filename;
237   auto printer = file->CreatePrinter(&output);
238 
239   GenerateImports(service, &*printer, &vars, true);
240   GenerateMethods(service, &*printer, &vars);
241   GenerateService(service, &*printer, &vars);
242   return output;
243 }
244 
245 // MARK: - Generate Interface
246 
FillInterface(grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)247 void FillInterface(grpc_generator::Printer *printer,
248                    std::map<grpc::string, grpc::string> *dictonary) {
249   auto vars = *dictonary;
250   printer->Print(vars,
251                  "interface I$ServiceName$Service_I$MethodName$ extends "
252                  "grpc.MethodDefinition<$INPUT$, $OUTPUT$> {\n");
253   printer->Indent();
254   printer->Print(vars, "path: string; // /$PATH$$ServiceName$/$MethodName$\n");
255   printer->Print(vars, "requestStream: boolean; // $ClientStreaming$\n");
256   printer->Print(vars, "responseStream: boolean; // $ServerStreaming$\n");
257   printer->Print(vars, "requestSerialize: grpc.serialize<$INPUT$>;\n");
258   printer->Print(vars, "requestDeserialize: grpc.deserialize<$INPUT$>;\n");
259   printer->Print(vars, "responseSerialize: grpc.serialize<$OUTPUT$>;\n");
260   printer->Print(vars, "responseDeserialize: grpc.deserialize<$OUTPUT$>;\n");
261   printer->Outdent();
262   printer->Print("}\n");
263 }
264 
GenerateInterfaces(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)265 void GenerateInterfaces(const grpc_generator::Service *service,
266                         grpc_generator::Printer *printer,
267                         std::map<grpc::string, grpc::string> *dictonary) {
268   auto vars = *dictonary;
269   for (auto it = 0; it < service->method_count(); it++) {
270     auto method = service->method(it);
271     auto client_streaming =
272         method->ClientStreaming() || method->BidiStreaming();
273     auto server_streaming =
274         method->ServerStreaming() || method->BidiStreaming();
275     vars["ClientStreaming"] = client_streaming ? "true" : "false";
276     vars["ServerStreaming"] = server_streaming ? "true" : "false";
277     vars["MethodName"] = method->name();
278     vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(),
279                                        method->get_output_type_name(), false);
280     vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(),
281                                       method->get_input_type_name(), false);
282     FillInterface(printer, &vars);
283     printer->Print("\n");
284   }
285 }
286 
GenerateExportedInterface(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)287 void GenerateExportedInterface(
288     const grpc_generator::Service *service, grpc_generator::Printer *printer,
289     std::map<grpc::string, grpc::string> *dictonary) {
290   auto vars = *dictonary;
291   printer->Print(vars, "export interface I$ServiceName$Server {\n");
292   printer->Indent();
293   for (auto it = 0; it < service->method_count(); it++) {
294     auto method = service->method(it);
295     vars["Name"] = method->name();
296     vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(),
297                                        method->get_output_type_name(), false);
298     vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(),
299                                       method->get_input_type_name(), false);
300     if (method->BidiStreaming()) {
301       printer->Print(vars,
302                      "$Name$: grpc.handleBidiStreamingCall<$INPUT$, "
303                      "$OUTPUT$>;\n");
304       continue;
305     }
306     if (method->NoStreaming()) {
307       printer->Print(vars,
308                      "$Name$: grpc.handleUnaryCall<$INPUT$, "
309                      "$OUTPUT$>;\n");
310       continue;
311     }
312     if (method->ClientStreaming()) {
313       printer->Print(vars,
314                      "$Name$: grpc.handleClientStreamingCall<$INPUT$, "
315                      "$OUTPUT$>;\n");
316       continue;
317     }
318     if (method->ServerStreaming()) {
319       printer->Print(vars,
320                      "$Name$: grpc.handleServerStreamingCall<$INPUT$, "
321                      "$OUTPUT$>;\n");
322       continue;
323     }
324   }
325   printer->Outdent();
326   printer->Print("}\n");
327 }
328 
GenerateMainInterface(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)329 void GenerateMainInterface(const grpc_generator::Service *service,
330                            grpc_generator::Printer *printer,
331                            std::map<grpc::string, grpc::string> *dictonary) {
332   auto vars = *dictonary;
333   printer->Print(
334       vars,
335       "interface I$ServiceName$Service extends "
336       "grpc.ServiceDefinition<grpc.UntypedServiceImplementation> {\n");
337   printer->Indent();
338   for (auto it = 0; it < service->method_count(); it++) {
339     auto method = service->method(it);
340     vars["MethodName"] = method->name();
341     printer->Print(vars,
342                    "$MethodName$: I$ServiceName$Service_I$MethodName$;\n");
343   }
344   printer->Outdent();
345   printer->Print("}\n");
346   GenerateInterfaces(service, printer, &vars);
347   printer->Print("\n");
348   printer->Print(vars,
349                  "export const $ServiceName$Service: I$ServiceName$Service;\n");
350   printer->Print("\n");
351   GenerateExportedInterface(service, printer, &vars);
352 }
353 
GenerateMetaData()354 grpc::string GenerateMetaData() { return "metadata: grpc.Metadata"; }
355 
GenerateOptions()356 grpc::string GenerateOptions() { return "options: Partial<grpc.CallOptions>"; }
357 
GenerateUnaryClientInterface(grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)358 void GenerateUnaryClientInterface(
359     grpc_generator::Printer *printer,
360     std::map<grpc::string, grpc::string> *dictonary) {
361   auto vars = *dictonary;
362   grpc::string main = "$ISPUBLIC$$MethodName$(request: $INPUT$, ";
363   grpc::string callback =
364       "callback: (error: grpc.ServiceError | null, response: "
365       "$OUTPUT$) => void): grpc.ClientUnaryCall;\n";
366   auto meta_data = GenerateMetaData() + ", ";
367   auto options = GenerateOptions() + ", ";
368   printer->Print(vars, (main + callback).c_str());
369   printer->Print(vars, (main + meta_data + callback).c_str());
370   printer->Print(vars, (main + meta_data + options + callback).c_str());
371 }
372 
GenerateClientWriteStreamInterface(grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)373 void GenerateClientWriteStreamInterface(
374     grpc_generator::Printer *printer,
375     std::map<grpc::string, grpc::string> *dictonary) {
376   auto vars = *dictonary;
377   grpc::string main = "$ISPUBLIC$$MethodName$(";
378   grpc::string callback =
379       "callback: (error: grpc.ServiceError | null, response: "
380       "$INPUT$) => void): "
381       "grpc.ClientWritableStream<$OUTPUT$>;\n";
382   auto meta_data = GenerateMetaData() + ", ";
383   auto options = GenerateOptions() + ", ";
384   printer->Print(vars, (main + callback).c_str());
385   printer->Print(vars, (main + meta_data + callback).c_str());
386   printer->Print(vars, (main + options + callback).c_str());
387   printer->Print(vars, (main + meta_data + options + callback).c_str());
388 }
389 
GenerateClientReadableStreamInterface(grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)390 void GenerateClientReadableStreamInterface(
391     grpc_generator::Printer *printer,
392     std::map<grpc::string, grpc::string> *dictonary) {
393   auto vars = *dictonary;
394   grpc::string main = "$ISPUBLIC$$MethodName$(request: $INPUT$, ";
395   grpc::string end_function = "): grpc.ClientReadableStream<$OUTPUT$>;\n";
396   auto meta_data = GenerateMetaData();
397   auto options = GenerateOptions();
398   printer->Print(vars, (main + meta_data + end_function).c_str());
399   printer->Print(vars, (main + options + end_function).c_str());
400 }
401 
GenerateDepluxStreamInterface(grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)402 void GenerateDepluxStreamInterface(
403     grpc_generator::Printer *printer,
404     std::map<grpc::string, grpc::string> *dictonary) {
405   auto vars = *dictonary;
406   grpc::string main = "$ISPUBLIC$$MethodName$(";
407   grpc::string end_function =
408       "): grpc.ClientDuplexStream<$INPUT$, $OUTPUT$>;\n";
409   auto meta_data = GenerateMetaData();
410   auto options = GenerateOptions();
411   printer->Print(vars, (main + end_function).c_str());
412   printer->Print(vars, (main + options + end_function).c_str());
413   printer->Print(vars, (main + meta_data +
414                         ", options?: Partial<grpc.CallOptions>" + end_function)
415                            .c_str());
416 }
417 
GenerateClientInterface(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)418 void GenerateClientInterface(const grpc_generator::Service *service,
419                              grpc_generator::Printer *printer,
420                              std::map<grpc::string, grpc::string> *dictonary) {
421   auto vars = *dictonary;
422   printer->Print(vars, "export interface I$ServiceName$Client {\n");
423   printer->Indent();
424   for (auto it = 0; it < service->method_count(); it++) {
425     auto method = service->method(it);
426     vars["MethodName"] = method->name();
427     vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(),
428                                        method->get_output_type_name(), false);
429     vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(),
430                                       method->get_input_type_name(), false);
431     vars["ISPUBLIC"] = "";
432 
433     if (method->NoStreaming()) {
434       GenerateUnaryClientInterface(printer, &vars);
435       continue;
436     }
437     if (method->BidiStreaming()) {
438       GenerateDepluxStreamInterface(printer, &vars);
439       continue;
440     }
441 
442     if (method->ClientStreaming()) {
443       GenerateClientWriteStreamInterface(printer, &vars);
444       continue;
445     }
446 
447     if (method->ServerStreaming()) {
448       GenerateClientReadableStreamInterface(printer, &vars);
449       continue;
450     }
451   }
452   printer->Outdent();
453   printer->Print("}\n");
454 }
455 
GenerateClientClassInterface(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)456 void GenerateClientClassInterface(
457     const grpc_generator::Service *service, grpc_generator::Printer *printer,
458     std::map<grpc::string, grpc::string> *dictonary) {
459   auto vars = *dictonary;
460   printer->Print(vars,
461                  "export class $ServiceName$Client extends grpc.Client "
462                  "implements I$ServiceName$Client {\n");
463   printer->Indent();
464   printer->Print(
465       "constructor(address: string, credentials: grpc.ChannelCredentials, "
466       "options?: object);");
467   for (auto it = 0; it < service->method_count(); it++) {
468     auto method = service->method(it);
469     vars["MethodName"] = method->name();
470     vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(),
471                                        method->get_output_type_name(), false);
472     vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(),
473                                       method->get_input_type_name(), false);
474     vars["ISPUBLIC"] = "public ";
475     if (method->NoStreaming()) {
476       GenerateUnaryClientInterface(printer, &vars);
477       continue;
478     }
479     if (method->BidiStreaming()) {
480       GenerateDepluxStreamInterface(printer, &vars);
481       continue;
482     }
483 
484     if (method->ClientStreaming()) {
485       GenerateClientWriteStreamInterface(printer, &vars);
486       continue;
487     }
488 
489     if (method->ServerStreaming()) {
490       GenerateClientReadableStreamInterface(printer, &vars);
491       continue;
492     }
493   }
494   printer->Outdent();
495   printer->Print("}\n");
496 }
497 
GenerateInterface(grpc_generator::File * file,const grpc_generator::Service * service,const grpc::string & filename)498 grpc::string GenerateInterface(grpc_generator::File *file,
499                                const grpc_generator::Service *service,
500                                const grpc::string &filename) {
501   grpc::string output;
502 
503   std::set<grpc::string> generated_functions;
504   std::map<grpc::string, grpc::string> vars;
505 
506   vars["PATH"] = file->package();
507 
508   if (!file->package().empty()) { vars["PATH"].append("."); }
509 
510   vars["ServiceName"] = service->name();
511   vars["FBSFile"] = service->name() + "_fbs";
512   vars["Filename"] = filename;
513   auto printer = file->CreatePrinter(&output);
514 
515   GenerateImports(service, &*printer, &vars, false);
516   GenerateMainInterface(service, &*printer, &vars);
517   printer->Print("\n");
518   GenerateClientInterface(service, &*printer, &vars);
519   printer->Print("\n");
520   GenerateClientClassInterface(service, &*printer, &vars);
521   return output;
522 }
523 }  // namespace grpc_ts_generator
524