diff --git a/internal/generator/runer_generate_service.go b/internal/generator/runer_generate_service.go index c79b8f4..016a483 100644 --- a/internal/generator/runer_generate_service.go +++ b/internal/generator/runer_generate_service.go @@ -10,23 +10,54 @@ import ( func (r *Runner) generateTypescriptServiceDefinitions(generatedFileStream *protogen.GeneratedFile, protoFile *protogen.File) { for _, serviceSpec := range protoFile.Proto.GetService() { + r.generateTypescriptServiceImplementationInterface(generatedFileStream, serviceSpec) r.generateTypescriptServiceDefinition(generatedFileStream, serviceSpec) + r.generateTypescriptClient(generatedFileStream, serviceSpec) } } +func (r *Runner) generateTypescriptServiceImplementationInterface(generatedFileStream *protogen.GeneratedFile, serviceSpec *descriptorpb.ServiceDescriptorProto) { + r.P(generatedFileStream, "export interface I"+strcase.ToCamel(serviceSpec.GetName())+"ServiceImplementation {") + r.indentLevel += 2 + + for _, methodSpec := range serviceSpec.GetMethod() { + clientStream := methodSpec.GetClientStreaming() + serverStream := methodSpec.GetServerStreaming() + + inputTypeName := methodSpec.GetInputType() + inputInterface := r.getEnumOrMessageTypeName(inputTypeName, true) + + outputTypeName := methodSpec.GetOutputType() + outputInterface := r.getEnumOrMessageTypeName(outputTypeName, true) + + if !clientStream && !serverStream { + r.P(generatedFileStream, methodSpec.GetName()+": grpc.handleUnaryCall<"+inputInterface+", "+outputInterface+">") + } else if clientStream && serverStream { + r.P(generatedFileStream, methodSpec.GetName()+": grpc.handleBidiStreamingCall<"+inputInterface+", "+outputInterface+">") + } else if clientStream { + r.P(generatedFileStream, methodSpec.GetName()+": grpc.handleClientStreamingCall<"+inputInterface+", "+outputInterface+">") + } else { //if serverStream { + r.P(generatedFileStream, methodSpec.GetName()+": grpc.handleServerStreamingCall<"+inputInterface+", "+outputInterface+">") + } + } + + r.indentLevel -= 2 + r.P(generatedFileStream, "}\n") +} + func (r *Runner) generateTypescriptServiceDefinition(generatedFileStream *protogen.GeneratedFile, serviceSpec *descriptorpb.ServiceDescriptorProto) { - r.P(generatedFileStream, "export const "+strcase.ToLowerCamel(serviceSpec.GetName())+"ServiceDefinition = {") + r.P(generatedFileStream, "export const "+strcase.ToLowerCamel(serviceSpec.GetName())+"ServiceDefinition: grpc.ServiceDefinition = {") r.indentLevel += 2 for _, methodSpec := range serviceSpec.GetMethod() { - r.generateTypescriptServiceMethod(generatedFileStream, serviceSpec, methodSpec) + r.generateTypescriptServiceDefinitionMethod(generatedFileStream, serviceSpec, methodSpec) } r.indentLevel -= 2 r.P(generatedFileStream, "}\n") } -func (r *Runner) generateTypescriptServiceMethod(generatedFileStream *protogen.GeneratedFile, serviceSpec *descriptorpb.ServiceDescriptorProto, methodSpec *descriptorpb.MethodDescriptorProto) { +func (r *Runner) generateTypescriptServiceDefinitionMethod(generatedFileStream *protogen.GeneratedFile, serviceSpec *descriptorpb.ServiceDescriptorProto, methodSpec *descriptorpb.MethodDescriptorProto) { methodName := methodSpec.GetName() r.P(generatedFileStream, methodName+": {") @@ -42,16 +73,85 @@ func (r *Runner) generateTypescriptServiceMethod(generatedFileStream *protogen.G inputClass := r.getEnumOrMessageTypeName(inputTypeName, false) r.P( generatedFileStream, - "requestSerialize: (request: "+inputInterface+") => "+inputClass+".encodePatched(request).finish(),", + "requestSerialize: (request: "+inputInterface+") => "+inputClass+".encodePatched(request).finish() as Buffer,", ) r.P(generatedFileStream, "requestDeserialize: "+inputClass+".decodePatched,") outputTypeName := methodSpec.GetOutputType() outputInterface := r.getEnumOrMessageTypeName(outputTypeName, true) outputClass := r.getEnumOrMessageTypeName(outputTypeName, false) - r.P(generatedFileStream, "responseSerialize: (response: "+outputInterface+") => "+outputClass+".encodePatched(response).finish(),") + r.P(generatedFileStream, "responseSerialize: (response: "+outputInterface+") => "+outputClass+".encodePatched(response).finish() as Buffer,") r.P(generatedFileStream, "responseDeserialize: "+outputClass+".decodePatched,") r.indentLevel -= 2 r.P(generatedFileStream, "},") } + +func (r *Runner) generateTypescriptClient(generatedFileStream *protogen.GeneratedFile, serviceSpec *descriptorpb.ServiceDescriptorProto) { + r.P( + generatedFileStream, + "export class "+strcase.ToCamel(serviceSpec.GetName())+"Client", + "extends joinGRPC.Client> ", + "implements joinGRPC.IExtendedClient {", + ) + r.indentLevel += 2 + + for _, methodSpec := range serviceSpec.GetMethod() { + r.generateTypescriptClientMethod(generatedFileStream, serviceSpec, methodSpec) + } + + r.indentLevel -= 2 + r.P(generatedFileStream, "}\n") +} + +func (r *Runner) generateTypescriptClientMethod(generatedFileStream *protogen.GeneratedFile, serviceSpec *descriptorpb.ServiceDescriptorProto, methodSpec *descriptorpb.MethodDescriptorProto) { + // Function's Signature + methodName := strcase.ToCamel(methodSpec.GetName()) + r.P(generatedFileStream, "public "+methodName+"(") + r.indentLevel += 2 + + inputTypeName := methodSpec.GetInputType() + inputInterface := r.getEnumOrMessageTypeName(inputTypeName, true) + outputTypeName := methodSpec.GetOutputType() + outputInterface := r.getEnumOrMessageTypeName(outputTypeName, true) + + clientStream := methodSpec.GetClientStreaming() + serverStrean := methodSpec.GetServerStreaming() + + if !clientStream { + r.P(generatedFileStream, "request: "+inputInterface+",") + } + + r.P(generatedFileStream, + "metadata?: Record,", + "options?: grpc.CallOptions,", + ) + + var returnType string + if clientStream && serverStrean { + returnType = "grpc.ClientDuplexStream<" + inputInterface + ", " + outputInterface + ">" + } else if !clientStream && !serverStrean { + returnType = "joinGRPC.IUnaryRequest<" + outputInterface + ">" + } else if clientStream { + returnType = "joinGRPC.IClientStreamRequest<" + inputInterface + ", " + outputInterface + ">" + } else { // if serverStream + returnType = "grpc.ClientReadableStream<" + outputInterface + ">" + } + + r.indentLevel -= 2 + r.P(generatedFileStream, "): "+returnType+" {") + r.indentLevel += 2 + + if clientStream && serverStrean { + r.P(generatedFileStream, "return this.makeBidiStreamRequest('"+methodName+"', metadata, options)") + } else if !clientStream && !serverStrean { + r.P(generatedFileStream, "return this.makeUnaryRequest('"+methodName+"', request, metadata, options)") + } else if clientStream { + r.P(generatedFileStream, "return this.makeClientStreamRequest('"+methodName+"', metadata, options)") + } else { // if serverStream + r.P(generatedFileStream, "return this.makeServerStreamRequest('"+methodName+"', request, metadata, options)") + } + + r.indentLevel -= 2 + r.P(generatedFileStream, "}\n") +} diff --git a/internal/generator/runner_generate.go b/internal/generator/runner_generate.go index c20d6cf..7a2ebce 100644 --- a/internal/generator/runner_generate.go +++ b/internal/generator/runner_generate.go @@ -19,14 +19,20 @@ func (r *Runner) generateTypescriptFile(protoFile *protogen.File, generatedFileS // TODO: Generate comment with version, in order to improve traceability & debugging experience generatedFileStream.P("// GENERATED CODE -- DO NOT EDIT!\n") - r.generateTypescriptImports(protoFile.Desc.Path(), protoFile.Proto.GetDependency(), generatedFileStream) + r.generateTypescriptImports(protoFile, generatedFileStream) r.generateTypescriptNamespace(generatedFileStream, protoFile) } -func (r *Runner) generateTypescriptImports(currentSourcePath string, importSourcePaths []string, generatedFileStream *protogen.GeneratedFile) { +func (r *Runner) generateTypescriptImports(protoFile *protogen.File, generatedFileStream *protogen.GeneratedFile) { + currentSourcePath := protoFile.Desc.Path() + importSourcePaths := protoFile.Proto.GetDependency() + // Generic imports - generatedFileStream.P("// import * as joinGRPC from '@join-com/grpc'") // TODO: Remove comment when import is used - generatedFileStream.P("// import * as nodeTrace from '@join-com/node-trace'") // TODO: Remove comment when import is used + if len(protoFile.Proto.GetService()) > 0 { + generatedFileStream.P("// import * as nodeTrace from '@join-com/node-trace'") // TODO: Remove comment when import is used + generatedFileStream.P("import * as joinGRPC from '@join-com/grpc'") + generatedFileStream.P("import { grpc } from '@join-com/grpc'") + } generatedFileStream.P("import * as protobufjs from 'protobufjs/light'") generatedFileStream.P("") diff --git a/tests/__tests__/v2/generated/Test.ts b/tests/__tests__/v2/generated/Test.ts index 6e10957..64994ad 100644 --- a/tests/__tests__/v2/generated/Test.ts +++ b/tests/__tests__/v2/generated/Test.ts @@ -1,7 +1,8 @@ // GENERATED CODE -- DO NOT EDIT! -// import * as joinGRPC from '@join-com/grpc' // import * as nodeTrace from '@join-com/node-trace' +import * as joinGRPC from '@join-com/grpc' +import { grpc } from '@join-com/grpc' import * as protobufjs from 'protobufjs/light' import { GoogleProtobuf } from './google/protobuf/Timestamp' @@ -413,16 +414,34 @@ export namespace Foo { } } - export const usersServiceDefinition = { + export interface IUsersServiceImplementation { + Find: grpc.handleUnaryCall + FindClientStream: grpc.handleClientStreamingCall< + IRequest, + Common_Common.IOtherPkgMessage + > + FindServerStream: grpc.handleServerStreamingCall< + IRequest, + Common_Common.IOtherPkgMessage + > + FindBidiStream: grpc.handleBidiStreamingCall< + IRequest, + Common_Common.IOtherPkgMessage + > + } + + export const usersServiceDefinition: grpc.ServiceDefinition = { Find: { path: '/foo.Users/Find', requestStream: false, responseStream: false, requestSerialize: (request: IRequest) => - Request.encodePatched(request).finish(), + Request.encodePatched(request).finish() as Buffer, requestDeserialize: Request.decodePatched, responseSerialize: (response: Common_Common.IOtherPkgMessage) => - Common_Common.OtherPkgMessage.encodePatched(response).finish(), + Common_Common.OtherPkgMessage.encodePatched( + response + ).finish() as Buffer, responseDeserialize: Common_Common.OtherPkgMessage.decodePatched, }, FindClientStream: { @@ -430,10 +449,12 @@ export namespace Foo { requestStream: true, responseStream: false, requestSerialize: (request: IRequest) => - Request.encodePatched(request).finish(), + Request.encodePatched(request).finish() as Buffer, requestDeserialize: Request.decodePatched, responseSerialize: (response: Common_Common.IOtherPkgMessage) => - Common_Common.OtherPkgMessage.encodePatched(response).finish(), + Common_Common.OtherPkgMessage.encodePatched( + response + ).finish() as Buffer, responseDeserialize: Common_Common.OtherPkgMessage.decodePatched, }, FindServerStream: { @@ -441,10 +462,12 @@ export namespace Foo { requestStream: false, responseStream: true, requestSerialize: (request: IRequest) => - Request.encodePatched(request).finish(), + Request.encodePatched(request).finish() as Buffer, requestDeserialize: Request.decodePatched, responseSerialize: (response: Common_Common.IOtherPkgMessage) => - Common_Common.OtherPkgMessage.encodePatched(response).finish(), + Common_Common.OtherPkgMessage.encodePatched( + response + ).finish() as Buffer, responseDeserialize: Common_Common.OtherPkgMessage.decodePatched, }, FindBidiStream: { @@ -452,11 +475,52 @@ export namespace Foo { requestStream: true, responseStream: true, requestSerialize: (request: IRequest) => - Request.encodePatched(request).finish(), + Request.encodePatched(request).finish() as Buffer, requestDeserialize: Request.decodePatched, responseSerialize: (response: Common_Common.IOtherPkgMessage) => - Common_Common.OtherPkgMessage.encodePatched(response).finish(), + Common_Common.OtherPkgMessage.encodePatched( + response + ).finish() as Buffer, responseDeserialize: Common_Common.OtherPkgMessage.decodePatched, }, } + + export class UsersClient + extends joinGRPC.Client> + implements joinGRPC.IExtendedClient { + public Find( + request: IRequest, + metadata?: Record, + options?: grpc.CallOptions + ): joinGRPC.IUnaryRequest { + return this.makeUnaryRequest('Find', request, metadata, options) + } + + public FindClientStream( + metadata?: Record, + options?: grpc.CallOptions + ): joinGRPC.IClientStreamRequest { + return this.makeClientStreamRequest('FindClientStream', metadata, options) + } + + public FindServerStream( + request: IRequest, + metadata?: Record, + options?: grpc.CallOptions + ): grpc.ClientReadableStream { + return this.makeServerStreamRequest( + 'FindServerStream', + request, + metadata, + options + ) + } + + public FindBidiStream( + metadata?: Record, + options?: grpc.CallOptions + ): grpc.ClientDuplexStream { + return this.makeBidiStreamRequest('FindBidiStream', metadata, options) + } + } } diff --git a/tests/__tests__/v2/generated/common/Common.ts b/tests/__tests__/v2/generated/common/Common.ts index 058d08a..5861f8c 100644 --- a/tests/__tests__/v2/generated/common/Common.ts +++ b/tests/__tests__/v2/generated/common/Common.ts @@ -1,7 +1,5 @@ // GENERATED CODE -- DO NOT EDIT! -// import * as joinGRPC from '@join-com/grpc' -// import * as nodeTrace from '@join-com/node-trace' import * as protobufjs from 'protobufjs/light' // eslint-disable-next-line @typescript-eslint/no-namespace diff --git a/tests/__tests__/v2/generated/common/Extra.ts b/tests/__tests__/v2/generated/common/Extra.ts index b51efd7..e1a9065 100644 --- a/tests/__tests__/v2/generated/common/Extra.ts +++ b/tests/__tests__/v2/generated/common/Extra.ts @@ -1,7 +1,5 @@ // GENERATED CODE -- DO NOT EDIT! -// import * as joinGRPC from '@join-com/grpc' -// import * as nodeTrace from '@join-com/node-trace' import * as protobufjs from 'protobufjs/light' import { GoogleProtobuf } from '../google/protobuf/Timestamp' diff --git a/tests/__tests__/v2/generated/google/protobuf/Timestamp.ts b/tests/__tests__/v2/generated/google/protobuf/Timestamp.ts index 2b7d0b8..8a90cf7 100644 --- a/tests/__tests__/v2/generated/google/protobuf/Timestamp.ts +++ b/tests/__tests__/v2/generated/google/protobuf/Timestamp.ts @@ -1,7 +1,5 @@ // GENERATED CODE -- DO NOT EDIT! -// import * as joinGRPC from '@join-com/grpc' -// import * as nodeTrace from '@join-com/node-trace' import * as protobufjs from 'protobufjs/light' // eslint-disable-next-line @typescript-eslint/no-namespace diff --git a/tests/package.json b/tests/package.json index b0e5c0b..65f0992 100644 --- a/tests/package.json +++ b/tests/package.json @@ -20,7 +20,7 @@ }, "dependencies": { "@join-com/gcloud-logger-trace": "^0.1.17", - "@join-com/grpc": "^0.1.0", + "@join-com/grpc": "^0.1.1", "@join-com/grpc-ts": "^2.0.2", "grpc": "^1.24.6", "protobufjs": "^6.10.2" diff --git a/tests/yarn.lock b/tests/yarn.lock index b83812d..cdf2809 100644 --- a/tests/yarn.lock +++ b/tests/yarn.lock @@ -590,12 +590,13 @@ dependencies: grpc "^1.24.5" -"@join-com/grpc@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@join-com/grpc/-/grpc-0.1.0.tgz#8cfe0c51fd4aed9a7504605962d927e819ba9379" - integrity sha512-x+IWclVMEPSKAjPiC8LUmfUzI8MU3UPwIuBAnxS1JzyQZOFV44gu21qheVcYMmDDN0XxKtT9x79m1IIrHVnGYQ== +"@join-com/grpc@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@join-com/grpc/-/grpc-0.1.1.tgz#e8e8631d4e0075de014cec9fe6b22d77da69e667" + integrity sha512-haZ92zW9oxMl2Rw3DmIPgo6eOzOocjwxrRGJfMkANdH46Jo/4WnxrCSOc/Z/MH0chpEnf1zbCD87TdGiUkKfKA== dependencies: "@grpc/grpc-js" "^1.2.12" + protobufjs "^6.10.2" "@join-com/node-trace@^0.1.8": version "0.1.8" @@ -786,19 +787,19 @@ integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== "@types/node@*", "@types/node@>=12.12.47", "@types/node@^14.14.37": - version "14.14.37" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e" - integrity sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw== + version "14.14.40" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.40.tgz#05a7cd31154487f357ca0bec4334ed1b1ab825a0" + integrity sha512-2HoZZGylcnz19ZSbvWhgWHqvprw1ZGHanxIrDWYykPD4CauLW4gcyLzCVfUN2kv/1t1F3CurQIdi+s1l9+XgEA== "@types/node@^10.3.4": - version "10.17.56" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.56.tgz#010c9e047c3ff09ddcd11cbb6cf5912725cdc2b3" - integrity sha512-LuAa6t1t0Bfw4CuSR0UITsm1hP17YL+u82kfHGrHUWdhlBtH7sa7jGY5z7glGaIj/WDYDkRtgGd+KCjCzxBW1w== + version "10.17.57" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.57.tgz#b99c5dbd97ad1af6b912fc6594d3c0e1e1b01423" + integrity sha512-9ejqfD/nkpl2RTUByUnkhE1xQFw6NWBE/CVsMuKnUvHRGm+HKFvSdHoyuJqKpG/N0hX7i3QHuf+OddN5WIHxMQ== "@types/node@^13.7.0": - version "13.13.48" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.48.tgz#46a3df718aed5217277f2395a682e055a487e341" - integrity sha512-z8wvSsgWQzkr4sVuMEEOvwMdOQjiRY2Y/ZW4fDfjfe3+TfQrZqFKOthBgk2RnVEmtOKrkwdZ7uTvsxTBLjKGDQ== + version "13.13.49" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.49.tgz#c274f1d842879082237f700a0e3140f875764aa5" + integrity sha512-v5fPzKjPAZ33/G4X8EXvGlbHFKQClfKAz1bKF/+cKaWss9lAIqrOETfcFNL3xG+DG2VCEev+dK4/lCa+YZaxBA== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -1736,9 +1737,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.712: - version "1.3.713" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.713.tgz#4583efb17f2d1e9ec07a44c8004ea73c013ad146" - integrity sha512-HWgkyX4xTHmxcWWlvv7a87RHSINEcpKYZmDMxkUlHcY+CJcfx7xEfBHuXVsO1rzyYs1WQJ7EgDp2CoErakBIow== + version "1.3.717" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.717.tgz#78d4c857070755fb58ab64bcc173db1d51cbc25f" + integrity sha512-OfzVPIqD1MkJ7fX+yTl2nKyOE4FReeVfMCzzxQS+Kp43hZYwHwThlGP+EGIZRXJsxCM7dqo8Y65NOX/HP12iXQ== emittery@^0.7.1: version "0.7.2" @@ -4798,9 +4799,9 @@ symbol-tree@^3.2.4: integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== table@^6.0.4: - version "6.0.9" - resolved "https://registry.yarnpkg.com/table/-/table-6.0.9.tgz#790a12bf1e09b87b30e60419bafd6a1fd85536fb" - integrity sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ== + version "6.1.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.1.0.tgz#676a0cfb206008b59e783fcd94ef8ba7d67d966c" + integrity sha512-T4G5KMmqIk6X87gLKWyU5exPpTjLjY5KyrFWaIjv3SvgaIUGXV7UEzGEnZJdTA38/yUS6f9PlKezQ0bYXG3iIQ== dependencies: ajv "^8.0.1" is-boolean-object "^1.1.0" @@ -4919,9 +4920,9 @@ tr46@^2.0.2: punycode "^2.1.1" ts-jest@^26.5.4: - version "26.5.4" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.4.tgz#207f4c114812a9c6d5746dd4d1cdf899eafc9686" - integrity sha512-I5Qsddo+VTm94SukBJ4cPimOoFZsYTeElR2xy6H2TOVs+NsvgYglW8KuQgKoApOKuaU/Ix/vrF9ebFZlb5D2Pg== + version "26.5.5" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.5.tgz#e40481b6ee4dd162626ba481a2be05fa57160ea5" + integrity sha512-7tP4m+silwt1NHqzNRAPjW1BswnAhopTdc2K3HEkRZjF0ZG2F/e/ypVH0xiZIMfItFtD3CX0XFbwPzp9fIEUVg== dependencies: bs-logger "0.x" buffer-from "1.x"