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