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