• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/compiler/ts_generator.h"
25 
26 #include <map>
27 #include <sstream>
28 
29 #include "flatbuffers/util.h"
30 #include "src/compiler/schema_interface.h"
31 
32 namespace grpc_ts_generator {
33 namespace {
34 
GenerateNamespace(const std::vector<std::string> ns,const std::string filename,const bool include_separator)35 static grpc::string GenerateNamespace(const std::vector<std::string> ns,
36                                const std::string filename,
37                                const bool include_separator) {
38   grpc::string path = "";
39   if (include_separator) path += ".";
40 
41   for (auto it = ns.begin(); it < ns.end(); it++) {
42     if (include_separator) path += "/";
43     path += include_separator
44                 ? flatbuffers::ConvertCase(*it, flatbuffers::Case::kDasher,
45                                            flatbuffers::Case::kUpperCamel)
46                 : *it + "_";
47   }
48 
49   if (include_separator) path += "/";
50   path += include_separator
51               ? flatbuffers::ConvertCase(filename, flatbuffers::Case::kDasher,
52                                          flatbuffers::Case::kUpperCamel)
53               : filename;
54   return path;
55 }
56 
57 // MARK: - Shared code
58 
GenerateImports(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary,const bool grpc_var_import)59 static void GenerateImports(const grpc_generator::Service *service,
60                      grpc_generator::Printer *printer,
61                      std::map<grpc::string, grpc::string> *dictonary,
62                      const bool grpc_var_import) {
63   auto vars = *dictonary;
64   printer->Print(
65       "// Generated GRPC code for FlatBuffers TS *** DO NOT EDIT ***\n");
66   printer->Print("import * as flatbuffers from 'flatbuffers';\n");
67 
68   std::set<grpc::string> generated_imports;
69 
70   for (auto it = 0; it < service->method_count(); it++) {
71     auto method = service->method(it);
72     auto output = method->get_output_type_name();
73     auto input = method->get_input_type_name();
74     auto input_namespace = method->get_input_namespace_parts();
75 
76     vars["OUTPUT"] = output;
77     vars["INPUT"] = input;
78 
79     if (generated_imports.find(output) == generated_imports.end()) {
80       generated_imports.insert(output);
81       vars["OUTPUT_DIR"] =
82           GenerateNamespace(method->get_output_namespace_parts(), output, true);
83       vars["Output_alias"] = GenerateNamespace(
84           method->get_output_namespace_parts(), output, false);
85       printer->Print(
86           vars, "import { $OUTPUT$ as $Output_alias$ } from '$OUTPUT_DIR$';\n");
87     }
88     if (generated_imports.find(input) == generated_imports.end()) {
89       generated_imports.insert(input);
90       vars["INPUT_DIR"] =
91           GenerateNamespace(method->get_output_namespace_parts(), input, true);
92       vars["Input_alias"] =
93           GenerateNamespace(method->get_output_namespace_parts(), input, false);
94       printer->Print(
95           vars, "import { $INPUT$ as $Input_alias$ } from '$INPUT_DIR$';\n");
96     }
97   }
98   printer->Print("\n");
99   if (grpc_var_import)
100     printer->Print("var grpc = require('@grpc/grpc-js');\n");
101   else
102     printer->Print("import * as grpc from '@grpc/grpc-js';\n");
103   printer->Print("\n");
104 }
105 
106 // MARK: - Generate Main GRPC Code
107 
GetStreamType(grpc_generator::Printer * printer,const grpc_generator::Method * method,std::map<grpc::string,grpc::string> * dictonary)108 static void GetStreamType(grpc_generator::Printer *printer,
109                    const grpc_generator::Method *method,
110                    std::map<grpc::string, grpc::string> *dictonary) {
111   auto vars = *dictonary;
112   auto client_streaming = method->ClientStreaming() || method->BidiStreaming();
113   auto server_streaming = method->ServerStreaming() || method->BidiStreaming();
114   vars["ClientStreaming"] = client_streaming ? "true" : "false";
115   vars["ServerStreaming"] = server_streaming ? "true" : "false";
116   printer->Print(vars, "requestStream: $ClientStreaming$,\n");
117   printer->Print(vars, "responseStream: $ServerStreaming$,\n");
118 }
119 
GenerateSerializeMethod(grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)120 static void GenerateSerializeMethod(grpc_generator::Printer *printer,
121                              std::map<grpc::string, grpc::string> *dictonary) {
122   auto vars = *dictonary;
123   printer->Print(vars, "function serialize_$Type$(buffer_args) {\n");
124   printer->Indent();
125   printer->Print(vars, "if (!(buffer_args instanceof $Type$)) {\n");
126   printer->Indent();
127   printer->Print(vars,
128                  "throw new Error('Expected argument of type $VALUE$');\n");
129   printer->Outdent();
130   printer->Print("}\n");
131   printer->Print(vars, "return Buffer.from(buffer_args.serialize());\n");
132   printer->Outdent();
133   printer->Print("}\n\n");
134 }
135 
GenerateDeserializeMethod(grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)136 static void GenerateDeserializeMethod(
137     grpc_generator::Printer *printer,
138     std::map<grpc::string, grpc::string> *dictonary) {
139   auto vars = *dictonary;
140   printer->Print(vars, "function deserialize_$Type$(buffer) {\n");
141   printer->Indent();
142   printer->Print(vars,
143                  "return $Type$.getRootAs$VALUE$(new "
144                  "flatbuffers.ByteBuffer(buffer))\n");
145   printer->Outdent();
146   printer->Print("}\n\n");
147 }
148 
GenerateMethods(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)149 static void GenerateMethods(const grpc_generator::Service *service,
150                      grpc_generator::Printer *printer,
151                      std::map<grpc::string, grpc::string> *dictonary) {
152   auto vars = *dictonary;
153 
154   std::set<grpc::string> generated_functions;
155 
156   for (auto it = 0; it < service->method_count(); it++) {
157     auto method = service->method(it);
158     auto output = method->get_output_type_name();
159     auto input = method->get_input_type_name();
160 
161     if (generated_functions.find(output) == generated_functions.end()) {
162       generated_functions.insert(output);
163       vars["VALUE"] = output;
164       vars["Type"] = GenerateNamespace(method->get_output_namespace_parts(),
165                                        output, false);
166       GenerateSerializeMethod(printer, &vars);
167       GenerateDeserializeMethod(printer, &vars);
168     }
169     printer->Print("\n");
170     if (generated_functions.find(input) == generated_functions.end()) {
171       generated_functions.insert(input);
172       vars["VALUE"] = input;
173       vars["Type"] =
174           GenerateNamespace(method->get_input_namespace_parts(), input, false);
175       GenerateSerializeMethod(printer, &vars);
176       GenerateDeserializeMethod(printer, &vars);
177     }
178   }
179 }
180 
GenerateService(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)181 static void GenerateService(const grpc_generator::Service *service,
182                      grpc_generator::Printer *printer,
183                      std::map<grpc::string, grpc::string> *dictonary) {
184   auto vars = *dictonary;
185   vars["NAME"] = service->name() + "Service";
186 
187   printer->Print(vars, "var $NAME$ = exports.$NAME$ = {\n");
188   printer->Indent();
189   for (auto it = 0; it < service->method_count(); it++) {
190     auto method = service->method(it);
191     vars["MethodName"] = method->name();
192     vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(),
193                                        method->get_output_type_name(), false);
194     vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(),
195                                       method->get_input_type_name(), false);
196     printer->Print(vars, "$MethodName$: {\n");
197     printer->Indent();
198     printer->Print(vars, "path: '/$PATH$$ServiceName$/$MethodName$',\n");
199     GetStreamType(printer, &*method, &vars);
200     printer->Print(vars, "requestType: flatbuffers.ByteBuffer,\n");
201     printer->Print(vars, "responseType: $OUTPUT$,\n");
202     printer->Print(vars, "requestSerialize: serialize_$INPUT$,\n");
203     printer->Print(vars, "requestDeserialize: deserialize_$INPUT$,\n");
204     printer->Print(vars, "responseSerialize: serialize_$OUTPUT$,\n");
205     printer->Print(vars, "responseDeserialize: deserialize_$OUTPUT$,\n");
206     printer->Outdent();
207     printer->Print("},\n");
208   }
209   printer->Outdent();
210   printer->Print("};\n");
211   printer->Print(vars,
212                  "exports.$ServiceName$Client = "
213                  "grpc.makeGenericClientConstructor($NAME$);");
214 }
215 
216 } // namespace
217 
Generate(grpc_generator::File * file,const grpc_generator::Service * service,const grpc::string & filename)218 grpc::string Generate(grpc_generator::File *file,
219                       const grpc_generator::Service *service,
220                       const grpc::string &filename) {
221   grpc::string output;
222   std::map<grpc::string, grpc::string> vars;
223 
224   vars["PATH"] = file->package();
225 
226   if (!file->package().empty()) { vars["PATH"].append("."); }
227 
228   vars["ServiceName"] = service->name();
229   vars["FBSFile"] = service->name() + "_fbs";
230   vars["Filename"] = filename;
231   auto printer = file->CreatePrinter(&output);
232 
233   GenerateImports(service, &*printer, &vars, true);
234   GenerateMethods(service, &*printer, &vars);
235   GenerateService(service, &*printer, &vars);
236   return output;
237 }
238 
239 namespace {
240 
241 // MARK: - Generate Interface
242 
FillInterface(grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)243 static void FillInterface(grpc_generator::Printer *printer,
244                    std::map<grpc::string, grpc::string> *dictonary) {
245   auto vars = *dictonary;
246   printer->Print(vars,
247                  "interface I$ServiceName$Service_I$MethodName$ extends "
248                  "grpc.MethodDefinition<$INPUT$, $OUTPUT$> {\n");
249   printer->Indent();
250   printer->Print(vars, "path: string; // /$PATH$$ServiceName$/$MethodName$\n");
251   printer->Print(vars, "requestStream: boolean; // $ClientStreaming$\n");
252   printer->Print(vars, "responseStream: boolean; // $ServerStreaming$\n");
253   printer->Print(vars, "requestSerialize: grpc.serialize<$INPUT$>;\n");
254   printer->Print(vars, "requestDeserialize: grpc.deserialize<$INPUT$>;\n");
255   printer->Print(vars, "responseSerialize: grpc.serialize<$OUTPUT$>;\n");
256   printer->Print(vars, "responseDeserialize: grpc.deserialize<$OUTPUT$>;\n");
257   printer->Outdent();
258   printer->Print("}\n");
259 }
260 
GenerateInterfaces(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)261 static void GenerateInterfaces(const grpc_generator::Service *service,
262                         grpc_generator::Printer *printer,
263                         std::map<grpc::string, grpc::string> *dictonary) {
264   auto vars = *dictonary;
265   for (auto it = 0; it < service->method_count(); it++) {
266     auto method = service->method(it);
267     auto client_streaming =
268         method->ClientStreaming() || method->BidiStreaming();
269     auto server_streaming =
270         method->ServerStreaming() || method->BidiStreaming();
271     vars["ClientStreaming"] = client_streaming ? "true" : "false";
272     vars["ServerStreaming"] = server_streaming ? "true" : "false";
273     vars["MethodName"] = method->name();
274     vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(),
275                                        method->get_output_type_name(), false);
276     vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(),
277                                       method->get_input_type_name(), false);
278     FillInterface(printer, &vars);
279     printer->Print("\n");
280   }
281 }
282 
GenerateExportedInterface(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)283 static void GenerateExportedInterface(
284     const grpc_generator::Service *service, grpc_generator::Printer *printer,
285     std::map<grpc::string, grpc::string> *dictonary) {
286   auto vars = *dictonary;
287   printer->Print(vars,
288                  "export interface I$ServiceName$Server extends "
289                  "grpc.UntypedServiceImplementation {\n");
290   printer->Indent();
291   for (auto it = 0; it < service->method_count(); it++) {
292     auto method = service->method(it);
293     vars["Name"] = method->name();
294     vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(),
295                                        method->get_output_type_name(), false);
296     vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(),
297                                       method->get_input_type_name(), false);
298     if (method->BidiStreaming()) {
299       printer->Print(vars,
300                      "$Name$: grpc.handleBidiStreamingCall<$INPUT$, "
301                      "$OUTPUT$>;\n");
302       continue;
303     }
304     if (method->NoStreaming()) {
305       printer->Print(vars,
306                      "$Name$: grpc.handleUnaryCall<$INPUT$, "
307                      "$OUTPUT$>;\n");
308       continue;
309     }
310     if (method->ClientStreaming()) {
311       printer->Print(vars,
312                      "$Name$: grpc.handleClientStreamingCall<$INPUT$, "
313                      "$OUTPUT$>;\n");
314       continue;
315     }
316     if (method->ServerStreaming()) {
317       printer->Print(vars,
318                      "$Name$: grpc.handleServerStreamingCall<$INPUT$, "
319                      "$OUTPUT$>;\n");
320       continue;
321     }
322   }
323   printer->Outdent();
324   printer->Print("}\n");
325 }
326 
GenerateMainInterface(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)327 static void GenerateMainInterface(const grpc_generator::Service *service,
328                            grpc_generator::Printer *printer,
329                            std::map<grpc::string, grpc::string> *dictonary) {
330   auto vars = *dictonary;
331   printer->Print(
332       vars,
333       "interface I$ServiceName$Service extends "
334       "grpc.ServiceDefinition<grpc.UntypedServiceImplementation> {\n");
335   printer->Indent();
336   for (auto it = 0; it < service->method_count(); it++) {
337     auto method = service->method(it);
338     vars["MethodName"] = method->name();
339     printer->Print(vars,
340                    "$MethodName$: I$ServiceName$Service_I$MethodName$;\n");
341   }
342   printer->Outdent();
343   printer->Print("}\n");
344   GenerateInterfaces(service, printer, &vars);
345   printer->Print("\n");
346   printer->Print(vars,
347                  "export const $ServiceName$Service: I$ServiceName$Service;\n");
348   printer->Print("\n");
349   GenerateExportedInterface(service, printer, &vars);
350 }
351 
GenerateMetaData()352 static grpc::string GenerateMetaData() { return "metadata: grpc.Metadata"; }
353 
GenerateOptions()354 static grpc::string GenerateOptions() { return "options: Partial<grpc.CallOptions>"; }
355 
GenerateUnaryClientInterface(grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)356 static void GenerateUnaryClientInterface(
357     grpc_generator::Printer *printer,
358     std::map<grpc::string, grpc::string> *dictonary) {
359   auto vars = *dictonary;
360   grpc::string main = "$ISPUBLIC$$MethodName$(request: $INPUT$, ";
361   grpc::string callback =
362       "callback: (error: grpc.ServiceError | null, response: "
363       "$OUTPUT$) => void): grpc.ClientUnaryCall;\n";
364   auto meta_data = GenerateMetaData() + ", ";
365   auto options = GenerateOptions() + ", ";
366   printer->Print(vars, (main + callback).c_str());
367   printer->Print(vars, (main + meta_data + callback).c_str());
368   printer->Print(vars, (main + meta_data + options + callback).c_str());
369 }
370 
GenerateClientWriteStreamInterface(grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)371 static void GenerateClientWriteStreamInterface(
372     grpc_generator::Printer *printer,
373     std::map<grpc::string, grpc::string> *dictonary) {
374   auto vars = *dictonary;
375   grpc::string main = "$ISPUBLIC$$MethodName$(";
376   grpc::string callback =
377       "callback: (error: grpc.ServiceError | null, response: "
378       "$INPUT$) => void): "
379       "grpc.ClientWritableStream<$OUTPUT$>;\n";
380   auto meta_data = GenerateMetaData() + ", ";
381   auto options = GenerateOptions() + ", ";
382   printer->Print(vars, (main + callback).c_str());
383   printer->Print(vars, (main + meta_data + callback).c_str());
384   printer->Print(vars, (main + options + callback).c_str());
385   printer->Print(vars, (main + meta_data + options + callback).c_str());
386 }
387 
GenerateClientReadableStreamInterface(grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)388 static void GenerateClientReadableStreamInterface(
389     grpc_generator::Printer *printer,
390     std::map<grpc::string, grpc::string> *dictonary) {
391   auto vars = *dictonary;
392   grpc::string main = "$ISPUBLIC$$MethodName$(request: $INPUT$, ";
393   grpc::string end_function = "): grpc.ClientReadableStream<$OUTPUT$>;\n";
394   auto meta_data = GenerateMetaData();
395   auto options = GenerateOptions();
396   printer->Print(vars, (main + meta_data + end_function).c_str());
397   printer->Print(vars, (main + options + end_function).c_str());
398 }
399 
GenerateDepluxStreamInterface(grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)400 static void GenerateDepluxStreamInterface(
401     grpc_generator::Printer *printer,
402     std::map<grpc::string, grpc::string> *dictonary) {
403   auto vars = *dictonary;
404   grpc::string main = "$ISPUBLIC$$MethodName$(";
405   grpc::string end_function =
406       "): grpc.ClientDuplexStream<$INPUT$, $OUTPUT$>;\n";
407   auto meta_data = GenerateMetaData();
408   auto options = GenerateOptions();
409   printer->Print(vars, (main + end_function).c_str());
410   printer->Print(vars, (main + options + end_function).c_str());
411   printer->Print(vars, (main + meta_data +
412                         ", options?: Partial<grpc.CallOptions>" + end_function)
413                            .c_str());
414 }
415 
GenerateClientInterface(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)416 static void GenerateClientInterface(const grpc_generator::Service *service,
417                              grpc_generator::Printer *printer,
418                              std::map<grpc::string, grpc::string> *dictonary) {
419   auto vars = *dictonary;
420   printer->Print(vars, "export interface I$ServiceName$Client {\n");
421   printer->Indent();
422   for (auto it = 0; it < service->method_count(); it++) {
423     auto method = service->method(it);
424     vars["MethodName"] = method->name();
425     vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(),
426                                        method->get_output_type_name(), false);
427     vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(),
428                                       method->get_input_type_name(), false);
429     vars["ISPUBLIC"] = "";
430 
431     if (method->NoStreaming()) {
432       GenerateUnaryClientInterface(printer, &vars);
433       continue;
434     }
435     if (method->BidiStreaming()) {
436       GenerateDepluxStreamInterface(printer, &vars);
437       continue;
438     }
439 
440     if (method->ClientStreaming()) {
441       GenerateClientWriteStreamInterface(printer, &vars);
442       continue;
443     }
444 
445     if (method->ServerStreaming()) {
446       GenerateClientReadableStreamInterface(printer, &vars);
447       continue;
448     }
449   }
450   printer->Outdent();
451   printer->Print("}\n");
452 }
453 
GenerateClientClassInterface(const grpc_generator::Service * service,grpc_generator::Printer * printer,std::map<grpc::string,grpc::string> * dictonary)454 static void GenerateClientClassInterface(
455     const grpc_generator::Service *service, grpc_generator::Printer *printer,
456     std::map<grpc::string, grpc::string> *dictonary) {
457   auto vars = *dictonary;
458   printer->Print(vars,
459                  "export class $ServiceName$Client extends grpc.Client "
460                  "implements I$ServiceName$Client {\n");
461   printer->Indent();
462   printer->Print(
463       "constructor(address: string, credentials: grpc.ChannelCredentials, "
464       "options?: object);\n");
465   for (auto it = 0; it < service->method_count(); it++) {
466     auto method = service->method(it);
467     vars["MethodName"] = method->name();
468     vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(),
469                                        method->get_output_type_name(), false);
470     vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(),
471                                       method->get_input_type_name(), false);
472     vars["ISPUBLIC"] = "public ";
473     if (method->NoStreaming()) {
474       GenerateUnaryClientInterface(printer, &vars);
475       continue;
476     }
477     if (method->BidiStreaming()) {
478       GenerateDepluxStreamInterface(printer, &vars);
479       continue;
480     }
481 
482     if (method->ClientStreaming()) {
483       GenerateClientWriteStreamInterface(printer, &vars);
484       continue;
485     }
486 
487     if (method->ServerStreaming()) {
488       GenerateClientReadableStreamInterface(printer, &vars);
489       continue;
490     }
491   }
492   printer->Outdent();
493   printer->Print("}\n");
494 }
495 } // namespace
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