/* * Copyright 2020 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * NOTE: The following implementation is a translation for the Swift-grpc * generator since flatbuffers doesnt allow plugins for now. if an issue arises * please open an issue in the flatbuffers repository. This file should always * be maintained according to the Swift-grpc repository */ #include "src/compiler/ts_generator.h" #include #include #include "flatbuffers/util.h" #include "src/compiler/schema_interface.h" namespace grpc_ts_generator { namespace { static grpc::string GenerateNamespace(const std::vector ns, const std::string filename, const bool include_separator) { grpc::string path = ""; if (include_separator) path += "."; for (auto it = ns.begin(); it < ns.end(); it++) { if (include_separator) path += "/"; path += include_separator ? flatbuffers::ConvertCase(*it, flatbuffers::Case::kDasher, flatbuffers::Case::kUpperCamel) : *it + "_"; } if (include_separator) path += "/"; path += include_separator ? flatbuffers::ConvertCase(filename, flatbuffers::Case::kDasher, flatbuffers::Case::kUpperCamel) : filename; return path; } // MARK: - Shared code static void GenerateImports(const grpc_generator::Service *service, grpc_generator::Printer *printer, std::map *dictonary, const bool grpc_var_import) { auto vars = *dictonary; printer->Print( "// Generated GRPC code for FlatBuffers TS *** DO NOT EDIT ***\n"); printer->Print("import * as flatbuffers from 'flatbuffers';\n"); std::set generated_imports; for (auto it = 0; it < service->method_count(); it++) { auto method = service->method(it); auto output = method->get_output_type_name(); auto input = method->get_input_type_name(); auto input_namespace = method->get_input_namespace_parts(); vars["OUTPUT"] = output; vars["INPUT"] = input; if (generated_imports.find(output) == generated_imports.end()) { generated_imports.insert(output); vars["OUTPUT_DIR"] = GenerateNamespace(method->get_output_namespace_parts(), output, true); vars["Output_alias"] = GenerateNamespace( method->get_output_namespace_parts(), output, false); printer->Print( vars, "import { $OUTPUT$ as $Output_alias$ } from '$OUTPUT_DIR$';\n"); } if (generated_imports.find(input) == generated_imports.end()) { generated_imports.insert(input); vars["INPUT_DIR"] = GenerateNamespace(method->get_output_namespace_parts(), input, true); vars["Input_alias"] = GenerateNamespace(method->get_output_namespace_parts(), input, false); printer->Print( vars, "import { $INPUT$ as $Input_alias$ } from '$INPUT_DIR$';\n"); } } printer->Print("\n"); if (grpc_var_import) printer->Print("var grpc = require('@grpc/grpc-js');\n"); else printer->Print("import * as grpc from '@grpc/grpc-js';\n"); printer->Print("\n"); } // MARK: - Generate Main GRPC Code static void GetStreamType(grpc_generator::Printer *printer, const grpc_generator::Method *method, std::map *dictonary) { auto vars = *dictonary; auto client_streaming = method->ClientStreaming() || method->BidiStreaming(); auto server_streaming = method->ServerStreaming() || method->BidiStreaming(); vars["ClientStreaming"] = client_streaming ? "true" : "false"; vars["ServerStreaming"] = server_streaming ? "true" : "false"; printer->Print(vars, "requestStream: $ClientStreaming$,\n"); printer->Print(vars, "responseStream: $ServerStreaming$,\n"); } static void GenerateSerializeMethod(grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; printer->Print(vars, "function serialize_$Type$(buffer_args) {\n"); printer->Indent(); printer->Print(vars, "if (!(buffer_args instanceof $Type$)) {\n"); printer->Indent(); printer->Print(vars, "throw new Error('Expected argument of type $VALUE$');\n"); printer->Outdent(); printer->Print("}\n"); printer->Print(vars, "return Buffer.from(buffer_args.serialize());\n"); printer->Outdent(); printer->Print("}\n\n"); } static void GenerateDeserializeMethod( grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; printer->Print(vars, "function deserialize_$Type$(buffer) {\n"); printer->Indent(); printer->Print(vars, "return $Type$.getRootAs$VALUE$(new " "flatbuffers.ByteBuffer(buffer))\n"); printer->Outdent(); printer->Print("}\n\n"); } static void GenerateMethods(const grpc_generator::Service *service, grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; std::set generated_functions; for (auto it = 0; it < service->method_count(); it++) { auto method = service->method(it); auto output = method->get_output_type_name(); auto input = method->get_input_type_name(); if (generated_functions.find(output) == generated_functions.end()) { generated_functions.insert(output); vars["VALUE"] = output; vars["Type"] = GenerateNamespace(method->get_output_namespace_parts(), output, false); GenerateSerializeMethod(printer, &vars); GenerateDeserializeMethod(printer, &vars); } printer->Print("\n"); if (generated_functions.find(input) == generated_functions.end()) { generated_functions.insert(input); vars["VALUE"] = input; vars["Type"] = GenerateNamespace(method->get_input_namespace_parts(), input, false); GenerateSerializeMethod(printer, &vars); GenerateDeserializeMethod(printer, &vars); } } } static void GenerateService(const grpc_generator::Service *service, grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; vars["NAME"] = service->name() + "Service"; printer->Print(vars, "var $NAME$ = exports.$NAME$ = {\n"); printer->Indent(); for (auto it = 0; it < service->method_count(); it++) { auto method = service->method(it); vars["MethodName"] = method->name(); vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(), method->get_output_type_name(), false); vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(), method->get_input_type_name(), false); printer->Print(vars, "$MethodName$: {\n"); printer->Indent(); printer->Print(vars, "path: '/$PATH$$ServiceName$/$MethodName$',\n"); GetStreamType(printer, &*method, &vars); printer->Print(vars, "requestType: flatbuffers.ByteBuffer,\n"); printer->Print(vars, "responseType: $OUTPUT$,\n"); printer->Print(vars, "requestSerialize: serialize_$INPUT$,\n"); printer->Print(vars, "requestDeserialize: deserialize_$INPUT$,\n"); printer->Print(vars, "responseSerialize: serialize_$OUTPUT$,\n"); printer->Print(vars, "responseDeserialize: deserialize_$OUTPUT$,\n"); printer->Outdent(); printer->Print("},\n"); } printer->Outdent(); printer->Print("};\n"); printer->Print(vars, "exports.$ServiceName$Client = " "grpc.makeGenericClientConstructor($NAME$);"); } } // namespace grpc::string Generate(grpc_generator::File *file, const grpc_generator::Service *service, const grpc::string &filename) { grpc::string output; std::map vars; vars["PATH"] = file->package(); if (!file->package().empty()) { vars["PATH"].append("."); } vars["ServiceName"] = service->name(); vars["FBSFile"] = service->name() + "_fbs"; vars["Filename"] = filename; auto printer = file->CreatePrinter(&output); GenerateImports(service, &*printer, &vars, true); GenerateMethods(service, &*printer, &vars); GenerateService(service, &*printer, &vars); return output; } namespace { // MARK: - Generate Interface static void FillInterface(grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; printer->Print(vars, "interface I$ServiceName$Service_I$MethodName$ extends " "grpc.MethodDefinition<$INPUT$, $OUTPUT$> {\n"); printer->Indent(); printer->Print(vars, "path: string; // /$PATH$$ServiceName$/$MethodName$\n"); printer->Print(vars, "requestStream: boolean; // $ClientStreaming$\n"); printer->Print(vars, "responseStream: boolean; // $ServerStreaming$\n"); printer->Print(vars, "requestSerialize: grpc.serialize<$INPUT$>;\n"); printer->Print(vars, "requestDeserialize: grpc.deserialize<$INPUT$>;\n"); printer->Print(vars, "responseSerialize: grpc.serialize<$OUTPUT$>;\n"); printer->Print(vars, "responseDeserialize: grpc.deserialize<$OUTPUT$>;\n"); printer->Outdent(); printer->Print("}\n"); } static void GenerateInterfaces(const grpc_generator::Service *service, grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; for (auto it = 0; it < service->method_count(); it++) { auto method = service->method(it); auto client_streaming = method->ClientStreaming() || method->BidiStreaming(); auto server_streaming = method->ServerStreaming() || method->BidiStreaming(); vars["ClientStreaming"] = client_streaming ? "true" : "false"; vars["ServerStreaming"] = server_streaming ? "true" : "false"; vars["MethodName"] = method->name(); vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(), method->get_output_type_name(), false); vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(), method->get_input_type_name(), false); FillInterface(printer, &vars); printer->Print("\n"); } } static void GenerateExportedInterface( const grpc_generator::Service *service, grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; printer->Print(vars, "export interface I$ServiceName$Server extends " "grpc.UntypedServiceImplementation {\n"); printer->Indent(); for (auto it = 0; it < service->method_count(); it++) { auto method = service->method(it); vars["Name"] = method->name(); vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(), method->get_output_type_name(), false); vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(), method->get_input_type_name(), false); if (method->BidiStreaming()) { printer->Print(vars, "$Name$: grpc.handleBidiStreamingCall<$INPUT$, " "$OUTPUT$>;\n"); continue; } if (method->NoStreaming()) { printer->Print(vars, "$Name$: grpc.handleUnaryCall<$INPUT$, " "$OUTPUT$>;\n"); continue; } if (method->ClientStreaming()) { printer->Print(vars, "$Name$: grpc.handleClientStreamingCall<$INPUT$, " "$OUTPUT$>;\n"); continue; } if (method->ServerStreaming()) { printer->Print(vars, "$Name$: grpc.handleServerStreamingCall<$INPUT$, " "$OUTPUT$>;\n"); continue; } } printer->Outdent(); printer->Print("}\n"); } static void GenerateMainInterface(const grpc_generator::Service *service, grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; printer->Print( vars, "interface I$ServiceName$Service extends " "grpc.ServiceDefinition {\n"); printer->Indent(); for (auto it = 0; it < service->method_count(); it++) { auto method = service->method(it); vars["MethodName"] = method->name(); printer->Print(vars, "$MethodName$: I$ServiceName$Service_I$MethodName$;\n"); } printer->Outdent(); printer->Print("}\n"); GenerateInterfaces(service, printer, &vars); printer->Print("\n"); printer->Print(vars, "export const $ServiceName$Service: I$ServiceName$Service;\n"); printer->Print("\n"); GenerateExportedInterface(service, printer, &vars); } static grpc::string GenerateMetaData() { return "metadata: grpc.Metadata"; } static grpc::string GenerateOptions() { return "options: Partial"; } static void GenerateUnaryClientInterface( grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; grpc::string main = "$ISPUBLIC$$MethodName$(request: $INPUT$, "; grpc::string callback = "callback: (error: grpc.ServiceError | null, response: " "$OUTPUT$) => void): grpc.ClientUnaryCall;\n"; auto meta_data = GenerateMetaData() + ", "; auto options = GenerateOptions() + ", "; printer->Print(vars, (main + callback).c_str()); printer->Print(vars, (main + meta_data + callback).c_str()); printer->Print(vars, (main + meta_data + options + callback).c_str()); } static void GenerateClientWriteStreamInterface( grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; grpc::string main = "$ISPUBLIC$$MethodName$("; grpc::string callback = "callback: (error: grpc.ServiceError | null, response: " "$INPUT$) => void): " "grpc.ClientWritableStream<$OUTPUT$>;\n"; auto meta_data = GenerateMetaData() + ", "; auto options = GenerateOptions() + ", "; printer->Print(vars, (main + callback).c_str()); printer->Print(vars, (main + meta_data + callback).c_str()); printer->Print(vars, (main + options + callback).c_str()); printer->Print(vars, (main + meta_data + options + callback).c_str()); } static void GenerateClientReadableStreamInterface( grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; grpc::string main = "$ISPUBLIC$$MethodName$(request: $INPUT$, "; grpc::string end_function = "): grpc.ClientReadableStream<$OUTPUT$>;\n"; auto meta_data = GenerateMetaData(); auto options = GenerateOptions(); printer->Print(vars, (main + meta_data + end_function).c_str()); printer->Print(vars, (main + options + end_function).c_str()); } static void GenerateDepluxStreamInterface( grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; grpc::string main = "$ISPUBLIC$$MethodName$("; grpc::string end_function = "): grpc.ClientDuplexStream<$INPUT$, $OUTPUT$>;\n"; auto meta_data = GenerateMetaData(); auto options = GenerateOptions(); printer->Print(vars, (main + end_function).c_str()); printer->Print(vars, (main + options + end_function).c_str()); printer->Print(vars, (main + meta_data + ", options?: Partial" + end_function) .c_str()); } static void GenerateClientInterface(const grpc_generator::Service *service, grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; printer->Print(vars, "export interface I$ServiceName$Client {\n"); printer->Indent(); for (auto it = 0; it < service->method_count(); it++) { auto method = service->method(it); vars["MethodName"] = method->name(); vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(), method->get_output_type_name(), false); vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(), method->get_input_type_name(), false); vars["ISPUBLIC"] = ""; if (method->NoStreaming()) { GenerateUnaryClientInterface(printer, &vars); continue; } if (method->BidiStreaming()) { GenerateDepluxStreamInterface(printer, &vars); continue; } if (method->ClientStreaming()) { GenerateClientWriteStreamInterface(printer, &vars); continue; } if (method->ServerStreaming()) { GenerateClientReadableStreamInterface(printer, &vars); continue; } } printer->Outdent(); printer->Print("}\n"); } static void GenerateClientClassInterface( const grpc_generator::Service *service, grpc_generator::Printer *printer, std::map *dictonary) { auto vars = *dictonary; printer->Print(vars, "export class $ServiceName$Client extends grpc.Client " "implements I$ServiceName$Client {\n"); printer->Indent(); printer->Print( "constructor(address: string, credentials: grpc.ChannelCredentials, " "options?: object);\n"); for (auto it = 0; it < service->method_count(); it++) { auto method = service->method(it); vars["MethodName"] = method->name(); vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(), method->get_output_type_name(), false); vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(), method->get_input_type_name(), false); vars["ISPUBLIC"] = "public "; if (method->NoStreaming()) { GenerateUnaryClientInterface(printer, &vars); continue; } if (method->BidiStreaming()) { GenerateDepluxStreamInterface(printer, &vars); continue; } if (method->ClientStreaming()) { GenerateClientWriteStreamInterface(printer, &vars); continue; } if (method->ServerStreaming()) { GenerateClientReadableStreamInterface(printer, &vars); continue; } } printer->Outdent(); printer->Print("}\n"); } } // namespace grpc::string GenerateInterface(grpc_generator::File *file, const grpc_generator::Service *service, const grpc::string &filename) { grpc::string output; std::set generated_functions; std::map vars; vars["PATH"] = file->package(); if (!file->package().empty()) { vars["PATH"].append("."); } vars["ServiceName"] = service->name(); vars["FBSFile"] = service->name() + "_fbs"; vars["Filename"] = filename; auto printer = file->CreatePrinter(&output); GenerateImports(service, &*printer, &vars, false); GenerateMainInterface(service, &*printer, &vars); printer->Print("\n"); GenerateClientInterface(service, &*printer, &vars); printer->Print("\n"); GenerateClientClassInterface(service, &*printer, &vars); return output; } } // namespace grpc_ts_generator