Skip to content

Commit

Permalink
#56 generate generic server interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
timostamm committed Dec 22, 2020
1 parent 30a7a43 commit 51a57ce
Show file tree
Hide file tree
Showing 8 changed files with 358 additions and 23 deletions.
3 changes: 1 addition & 2 deletions packages/example-node-generic-server/server.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import * as grpc from '@grpc/grpc-js';
import {ExampleRequest, ExampleResponse} from "./service-example";
import {ExampleRequest, ExampleResponse, ExampleService} from "./service-example";
import {IExampleService} from "./service-example.server";
import {RpcInputStream, RpcOutputStream, ServerCallContext} from "@protobuf-ts/runtime-rpc";
import {ExampleService} from "../example-node-grpc-server/service-example";
import {adaptService} from "@protobuf-ts/grpc-backend";


Expand Down
38 changes: 25 additions & 13 deletions packages/example-node-generic-server/service-example.server.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
import {ExampleRequest, ExampleResponse} from "./service-example";
import {RpcInputStream, RpcOutputStream, ServerCallContext} from "@protobuf-ts/runtime-rpc";


// TODO generate this interface if (ts.server) = GENERIC or --ts_opt server_generic
// @generated by protobuf-ts 2.0.0-alpha.8 with parameters server_generic,client_none,generate_dependencies,optimize_code_size
// @generated from protobuf file "service-example.proto" (package "spec", syntax proto3)
// tslint:disable
import { RpcOutputStream } from "@protobuf-ts/runtime-rpc";
import { RpcInputStream } from "@protobuf-ts/runtime-rpc";
import { ServerCallContext } from "@protobuf-ts/runtime-rpc";
import { ExampleResponse } from "./service-example";
import { ExampleRequest } from "./service-example";
/**
* @generated from protobuf service spec.ExampleService
*/
export interface IExampleService {

/**
* @generated from protobuf rpc: Unary(spec.ExampleRequest) returns (spec.ExampleResponse);
*/
unary(request: ExampleRequest, context: ServerCallContext): Promise<ExampleResponse>;

/**
* @generated from protobuf rpc: ServerStream(spec.ExampleRequest) returns (stream spec.ExampleResponse);
*/
serverStream(request: ExampleRequest, responses: RpcInputStream<ExampleResponse>, context: ServerCallContext): Promise<void>;

clientStream(requests: RpcOutputStream, context: ServerCallContext): Promise<ExampleResponse>;

bidi(requests: RpcOutputStream, responses: RpcInputStream<ExampleResponse>, context: ServerCallContext): Promise<void>;

/**
* @generated from protobuf rpc: ClientStream(stream spec.ExampleRequest) returns (spec.ExampleResponse);
*/
clientStream(requests: RpcOutputStream<ExampleRequest>, context: ServerCallContext): Promise<ExampleResponse>;
/**
* @generated from protobuf rpc: Bidi(stream spec.ExampleRequest) returns (stream spec.ExampleResponse);
*/
bidi(requests: RpcOutputStream<ExampleRequest>, responses: RpcInputStream<ExampleResponse>, context: ServerCallContext): Promise<void>;
}

2 changes: 1 addition & 1 deletion packages/example-node-generic-server/service-example.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @generated by protobuf-ts 2.0.0-alpha.8 with parameters server_grpc,client_none,generate_dependencies,optimize_code_size
// @generated by protobuf-ts 2.0.0-alpha.8 with parameters server_generic,client_none,generate_dependencies,optimize_code_size
// @generated from protobuf file "service-example.proto" (package "spec", syntax proto3)
// tslint:disable
import { ServiceType } from "@protobuf-ts/runtime-rpc";
Expand Down
2 changes: 1 addition & 1 deletion packages/example-node-grpc-client/service-example.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @generated by protobuf-ts 2.0.0-alpha.7 with parameters generate_dependencies,optimize_code_size
// @generated by protobuf-ts 2.0.0-alpha.8 with parameters generate_dependencies,optimize_code_size
// @generated from protobuf file "service-example.proto" (package "spec", syntax proto3)
// tslint:disable
import { RpcTransport } from "@protobuf-ts/runtime-rpc";
Expand Down
9 changes: 7 additions & 2 deletions packages/plugin/protobuf-ts.proto
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,12 @@ enum ServerStyle {
// This is the default behaviour.
NO_SERVER = 0;

// Generate a server for @grpc/grpc-js
GRPC_SERVER = 1;
// Generate a generic server interface.
// Adapters be used to serve the service, for example @protobuf-ts/grpc-backend
// for gRPC.
GENERIC_SERVER = 1;

// Generate a server for @grpc/grpc-js.
GRPC_SERVER = 2;

}
294 changes: 294 additions & 0 deletions packages/plugin/src/code-gen/service-server-generator-generic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
import {GeneratorBase} from "./generator-base";
import * as rpc from "@protobuf-ts/runtime-rpc";
import {
DescriptorRegistry,
ServiceDescriptorProto,
SymbolTable,
TypescriptFile,
TypeScriptImports
} from "@protobuf-ts/plugin-framework";
import {Interpreter} from "../interpreter";
import * as ts from "typescript";
import {assert} from "@protobuf-ts/runtime";
import {CommentGenerator} from "./comment-generator";
import {createLocalTypeName} from "./local-type-name";


export class ServiceServerGeneratorGeneric extends GeneratorBase {


private readonly symbolKindInterface = 'generic-server-interface';


constructor(symbols: SymbolTable, registry: DescriptorRegistry, imports: TypeScriptImports, comments: CommentGenerator, interpreter: Interpreter,
private readonly options: {
runtimeRpcImportPath: string;
}) {
super(symbols, registry, imports, comments, interpreter);
}


registerSymbols(source: TypescriptFile, descriptor: ServiceDescriptorProto): void {
const basename = createLocalTypeName(descriptor, this.registry);
const interfaceName = `I${basename}`;
this.symbols.register(interfaceName, descriptor, source, this.symbolKindInterface);
}


generateInterface(source: TypescriptFile, descriptor: ServiceDescriptorProto) {
const
interpreterType = this.interpreter.getServiceType(descriptor),
IGenericServer = this.imports.type(source, descriptor, this.symbolKindInterface)
;

const statement = ts.createInterfaceDeclaration(
undefined,
[ts.createModifier(ts.SyntaxKind.ExportKeyword)],
ts.createIdentifier(IGenericServer),
undefined,
undefined,
interpreterType.methods.map(mi => {
const methodDescriptor = descriptor.method.find(md => md.name === mi.name);
assert(methodDescriptor);
let signature: ts.MethodSignature;
if (mi.serverStreaming && mi.clientStreaming) {
signature = this.createBidi(source, mi);
} else if (mi.serverStreaming) {
signature = this.createServerStreaming(source, mi);
} else if (mi.clientStreaming) {
signature = this.createClientStreaming(source, mi);
} else {
signature = this.createUnary(source, mi);
}
this.comments.addCommentsForDescriptor(signature, methodDescriptor, 'appendToLeadingBlock');
return signature;
})
);

// add to our file
this.comments.addCommentsForDescriptor(statement, descriptor, 'appendToLeadingBlock');
source.addStatement(statement);
return statement;
}


private createUnary(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodSignature {
const
I = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(
source,
this.registry.resolveTypeName(methodInfo.I.typeName)
)), undefined),
O = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(
source,
this.registry.resolveTypeName(methodInfo.O.typeName)
)), undefined),
ServerCallContext = this.imports.name(source, 'ServerCallContext', this.options.runtimeRpcImportPath);
return ts.createMethodSignature(
undefined,
[
ts.createParameter(
undefined,
undefined,
undefined,
ts.createIdentifier("request"),
undefined,
I,
undefined
),
ts.createParameter(
undefined,
undefined,
undefined,
ts.createIdentifier("context"),
undefined,
ts.createTypeReferenceNode(
ts.createIdentifier(ServerCallContext),
undefined
),
undefined
)
],
ts.createTypeReferenceNode(
ts.createIdentifier("Promise"),
[O]
),
ts.createIdentifier(methodInfo.localName),
undefined
);
}


private createServerStreaming(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodSignature {
const
I = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(
source,
this.registry.resolveTypeName(methodInfo.I.typeName)
)), undefined),
O = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(
source,
this.registry.resolveTypeName(methodInfo.O.typeName)
)), undefined),
ServerCallContext = this.imports.name(source, 'ServerCallContext', this.options.runtimeRpcImportPath),
RpcInputStream = this.imports.name(source, 'RpcInputStream', this.options.runtimeRpcImportPath);
return ts.createMethodSignature(
undefined,
[
ts.createParameter(
undefined,
undefined,
undefined,
ts.createIdentifier("request"),
undefined,
I,
undefined
),
ts.createParameter(
undefined,
undefined,
undefined,
ts.createIdentifier("responses"),
undefined,
ts.createTypeReferenceNode(
ts.createIdentifier(RpcInputStream),
[O]
),
undefined
),
ts.createParameter(
undefined,
undefined,
undefined,
ts.createIdentifier("context"),
undefined,
ts.createTypeReferenceNode(
ts.createIdentifier(ServerCallContext),
undefined
),
undefined
)
],
ts.createTypeReferenceNode(
ts.createIdentifier("Promise"),
[ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)]
),
ts.createIdentifier(methodInfo.localName),
undefined
);
}



private createClientStreaming(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodSignature {
const
I = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(
source,
this.registry.resolveTypeName(methodInfo.I.typeName)
)), undefined),
O = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(
source,
this.registry.resolveTypeName(methodInfo.O.typeName)
)), undefined),
ServerCallContext = this.imports.name(source, 'ServerCallContext', this.options.runtimeRpcImportPath),
RpcOutputStream = this.imports.name(source, 'RpcOutputStream', this.options.runtimeRpcImportPath);
return ts.createMethodSignature(
undefined,
[
ts.createParameter(
undefined,
undefined,
undefined,
ts.createIdentifier("requests"),
undefined,
ts.createTypeReferenceNode(
ts.createIdentifier(RpcOutputStream),
[I]
),
undefined
),
ts.createParameter(
undefined,
undefined,
undefined,
ts.createIdentifier("context"),
undefined,
ts.createTypeReferenceNode(
ts.createIdentifier(ServerCallContext),
undefined
),
undefined
)
],
ts.createTypeReferenceNode(
ts.createIdentifier("Promise"),
[O]
),
ts.createIdentifier(methodInfo.localName),
undefined
);
}


private createBidi(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodSignature {
const
I = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(
source,
this.registry.resolveTypeName(methodInfo.I.typeName)
)), undefined),
O = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(
source,
this.registry.resolveTypeName(methodInfo.O.typeName)
)), undefined),
ServerCallContext = this.imports.name(source, 'ServerCallContext', this.options.runtimeRpcImportPath),
RpcOutputStream = this.imports.name(source, 'RpcOutputStream', this.options.runtimeRpcImportPath),
RpcInputStream = this.imports.name(source, 'RpcInputStream', this.options.runtimeRpcImportPath);
return ts.createMethodSignature(
undefined,
[
ts.createParameter(
undefined,
undefined,
undefined,
ts.createIdentifier("requests"),
undefined,
ts.createTypeReferenceNode(
ts.createIdentifier(RpcOutputStream),
[I]
),
undefined
),
ts.createParameter(
undefined,
undefined,
undefined,
ts.createIdentifier("responses"),
undefined,
ts.createTypeReferenceNode(
ts.createIdentifier(RpcInputStream),
[O]
),
undefined
),
ts.createParameter(
undefined,
undefined,
undefined,
ts.createIdentifier("context"),
undefined,
ts.createTypeReferenceNode(
ts.createIdentifier(ServerCallContext),
undefined
),
undefined
)
],
ts.createTypeReferenceNode(
ts.createIdentifier("Promise"),
[ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)]
),
ts.createIdentifier(methodInfo.localName),
undefined
);
}


}
Loading

0 comments on commit 51a57ce

Please sign in to comment.