Skip to content

Commit ec32964

Browse files
authored
Merge pull request #82 from agreatfool/add-doc
Add doc and examples for the breaking change v5.1.0
2 parents 8d406ad + d1f7f74 commit ec32964

File tree

5 files changed

+161
-28
lines changed

5 files changed

+161
-28
lines changed

README.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ grpc_tools_node_protoc_ts
99
* [Breaking changes](#breaking-changes)
1010
* [How to use](#how-to-use)
1111
* [Example](#example)
12-
* [Changes](changes)
12+
* [Changes](#changes)
1313
* [About jstype options of protobuf](#about-jstype-options-of-protobuf)
1414
* [About vulnerability](#about-vulnerability)
1515
* [About Docker](#about-docker)
@@ -19,7 +19,7 @@ grpc_tools_node_protoc_ts
1919
## Aim
2020
Generate corresponding TypeScript d.ts codes according to js codes generated by [grpc_tools_node_protoc](https://www.npmjs.com/package/grpc-tools).
2121

22-
More information about grpc_tools_node_protoc:
22+
More information about grpc_tools_node_protoc (grpc-tools):
2323

2424
* [npm](https://www.npmjs.com/package/grpc-tools)
2525
* [source code](https://github.com/grpc/grpc-node/tree/master/packages/grpc-tools)
@@ -31,7 +31,7 @@ And the versions over 3.0.0 support [@grpc/grpc-js](https://www.npmjs.com/packag
3131
### v5.1.0
3232
Fix server implementation signature issue of `grpc_js` side. See: [Issue#79](https://github.com/agreatfool/grpc_tools_node_protoc_ts/issues/79).
3333

34-
About the code changes, please read the doc at **Changes** part.
34+
About the code changes, please read the doc at [**Changes**](#changes) part. Also, please read this doc for more information: [@grpc/grpc-js server implementation signature issue](https://github.com/agreatfool/grpc_tools_node_protoc_ts/blob/v5.1.1/doc/server_impl_signature.md).
3535

3636
### v4.0.0
3737
Fix the issues along with [[email protected]](https://github.com/grpc/grpc-node/releases/tag/grpc-tools%401.9.0), see: [PR#55](https://github.com/agreatfool/grpc_tools_node_protoc_ts/pull/55). If you are using grpc-tools with version under `1.9.0`, you should `NOT` upgrade.
@@ -495,6 +495,18 @@ TS2420: Class 'ServerImpl' incorrectly implements interface 'IBookServiceServer'
495495

496496
Have a look at: [Typescript: Index signature is missing in type](https://stackoverflow.com/questions/37006008/typescript-index-signature-is-missing-in-type).
497497

498+
Or, you can switch the server implementation from `Class style` to `Object style`:
499+
500+
```typescript
501+
const ServerImpl: IBookServiceServer = {
502+
// implementations
503+
}
504+
```
505+
506+
Object style requires no additional statement, and can be appended with more attributes.
507+
508+
Also, please read this doc for more information: [@grpc/grpc-js server implementation signature issue](https://github.com/agreatfool/grpc_tools_node_protoc_ts/blob/v5.1.1/doc/server_impl_signature.md).
509+
498510
### 5.0.1
499511
Fix array.includes issue. See: [Issue#73](https://github.com/agreatfool/grpc_tools_node_protoc_ts/issues/73), [Commit#1e4ae67](https://github.com/agreatfool/grpc_tools_node_protoc_ts/commit/1e4ae677d2f1f0066a21fe5e9dd48b10dd24f5af).
500512

doc/server_impl_signature.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# @grpc/grpc-js server implementation signature issue
2+
3+
## Status
4+
Issue: [Issue#79](https://github.com/agreatfool/grpc_tools_node_protoc_ts/issues/79).
5+
6+
This one is fixed in version `v5.1.0`, and there should be some document to explain more.
7+
8+
## Issue
9+
If you are using this tool of version `v5.0.1`, you may have already noticed a signature issue here: [examples/src/grpcjs/server.ts#L74-L75](https://github.com/agreatfool/grpc_tools_node_protoc_ts/blob/v5.0.1/examples/src/grpcjs/server.ts#L74-L75). Note the `// @ts-ignore`:
10+
11+
```typescript
12+
// @ts-ignore
13+
server.addService(BookServiceService, new ServerImpl());
14+
```
15+
16+
The signature of implementation class looks like:
17+
18+
```typescript
19+
export const BookServiceService: IBookServiceService;
20+
21+
export interface IBookServiceServer {
22+
getBook: grpc.handleUnaryCall<book_pb.GetBookRequest, book_pb.Book>;
23+
getBooksViaAuthor: grpc.handleServerStreamingCall<book_pb.GetBookViaAuthor, book_pb.Book>;
24+
getGreatestBook: handleClientStreamingCall<book_pb.GetBookRequest, book_pb.Book>;
25+
getBooks: grpc.handleBidiStreamingCall<book_pb.GetBookRequest, book_pb.Book>;
26+
}
27+
28+
class ServerImpl implements IBookServiceServer {
29+
}
30+
```
31+
32+
The signature of `addService` is:
33+
34+
```typescript
35+
export class Server {
36+
addService(
37+
service: ServiceDefinition,
38+
implementation: UntypedServiceImplementation
39+
): void {}
40+
}
41+
```
42+
43+
As you can see the type of implementation need to be `UntypedServiceImplementation`. So it's inconsistent.
44+
45+
## Fixing
46+
The way of fixing is to define IBookServiceServer to extends `UntypedServiceImplementation`.
47+
48+
```typescript
49+
export interface IBookServiceServer extends grpc.UntypedServiceImplementation {
50+
}
51+
```
52+
53+
Though this fixing solved the issue of signature issue here (the original one):
54+
55+
```typescript
56+
// @ts-ignore
57+
server.addService(BookServiceService, new ServerImpl());
58+
```
59+
60+
It brings the new issue.
61+
62+
Because `UntypedServiceImplementation` is defined like:
63+
64+
```typescript
65+
export interface UntypedServiceImplementation {
66+
[name: string]: UntypedHandleCall;
67+
}
68+
```
69+
70+
This means all the attributes inside the `UntypedServiceImplementation` (and any derived implementation, like `class ServerImpl implements IBookServiceServer extends grpc.UntypedServiceImplementation`) have to be type of `UntypedHandleCall`.
71+
72+
So when you define a class to implement `IBookServiceServer`, you have to add a line of code, like:
73+
74+
```typescript
75+
class Impl implements IBookServiceServer {
76+
[name: string]: grpc.UntypedHandleCall;
77+
}
78+
```
79+
80+
Otherwise, tsc would throw errors: [Typescript: Index signature is missing in type](https://stackoverflow.com/questions/37006008/typescript-index-signature-is-missing-in-type).
81+
82+
## Implementation
83+
According to this breaking change, there could be two styles of server side implementation. One is `Object style`, the other is `Class style`.
84+
85+
### Object Style
86+
87+
```typescript
88+
const Impl: IBookServiceServer = {
89+
getBook: (call: grpc.ServerUnaryCall<GetBookRequest, Book>, callback: sendUnaryData<Book>): void => {},
90+
91+
getBooks: (call: grpc.ServerDuplexStream<GetBookRequest, Book>): void => {},
92+
93+
getBooksViaAuthor: (call: grpc.ServerWritableStream<GetBookViaAuthor, Book>): void => {},
94+
95+
getGreatestBook: (call: grpc.ServerReadableStream<GetBookRequest, Book>, callback: sendUnaryData<Book>): void => {},
96+
};
97+
98+
const server = new grpc.Server();
99+
server.addService(BookServiceService, Impl);
100+
```
101+
102+
This style is `recommended`. Since you can append more attributes in the `Impl` object, though they are not defined in `IBookServiceServer`, and you don't need to add `[name: string]: grpc.UntypedHandleCall` in your codes.
103+
104+
### Class Style
105+
106+
```typescript
107+
class Impl implements IBookServiceServer {
108+
[name: string]: grpc.UntypedHandleCall;
109+
110+
public getBook(call: grpc.ServerUnaryCall<GetBookRequest, Book>, callback: sendUnaryData<Book>): void {}
111+
112+
public getBooks(call: grpc.ServerDuplexStream<GetBookRequest, Book>) {}
113+
114+
public getBooksViaAuthor(call: grpc.ServerWritableStream<GetBookViaAuthor, Book>) {}
115+
116+
public getGreatestBook(call: grpc.ServerReadableStream<GetBookRequest, Book>, callback: sendUnaryData<Book>) {}
117+
}
118+
119+
const server = new grpc.Server();
120+
server.addService(BookServiceService, new Impl());
121+
```
122+
123+
This style is `NOT` recommended. Since only those already defined in the `IBookServiceServer` can be existing in this `Impl` class, and `[name: string]: grpc.UntypedHandleCall` is required for Class style.

examples/build/grpcjs/server.js

Lines changed: 11 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/build/grpcjs/server.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/src/grpcjs/server.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,19 @@ import { Book, GetBookRequest, GetBookViaAuthor } from "./proto/book_pb";
99

1010
const log = debug("SampleServer");
1111

12-
class ServerImpl implements IBookServiceServer {
12+
const ServerImpl: IBookServiceServer = {
1313

14-
[name: string]: grpc.UntypedHandleCall;
15-
16-
public getBook(call: grpc.ServerUnaryCall<GetBookRequest, Book>, callback: sendUnaryData<Book>): void {
14+
getBook: (call: grpc.ServerUnaryCall<GetBookRequest, Book>, callback: sendUnaryData<Book>): void => {
1715
const book = new Book();
1816

1917
book.setTitle("DefaultBook");
2018
book.setAuthor("DefaultAuthor");
2119

2220
log(`[getBook] Done: ${JSON.stringify(book.toObject())}`);
2321
callback(null, book);
24-
}
22+
},
2523

26-
public getBooks(call: grpc.ServerDuplexStream<GetBookRequest, Book>) {
24+
getBooks: (call: grpc.ServerDuplexStream<GetBookRequest, Book>): void => {
2725
call.on("data", (request: GetBookRequest) => {
2826
const reply = new Book();
2927
reply.setTitle(`Book${request.getIsbn()}`);
@@ -36,9 +34,9 @@ class ServerImpl implements IBookServiceServer {
3634
log("[getBooks] Done.");
3735
call.end();
3836
});
39-
}
37+
},
4038

41-
public getBooksViaAuthor(call: grpc.ServerWritableStream<GetBookViaAuthor, Book>) {
39+
getBooksViaAuthor: (call: grpc.ServerWritableStream<GetBookViaAuthor, Book>): void => {
4240
log(`[getBooksViaAuthor] Request: ${JSON.stringify(call.request.toObject())}`);
4341
for (let i = 1; i <= 10; i++) {
4442
const reply = new Book();
@@ -50,9 +48,9 @@ class ServerImpl implements IBookServiceServer {
5048
}
5149
log("[getBooksViaAuthor] Done.");
5250
call.end();
53-
}
51+
},
5452

55-
public getGreatestBook(call: grpc.ServerReadableStream<GetBookRequest, Book>, callback: sendUnaryData<Book>) {
53+
getGreatestBook: (call: grpc.ServerReadableStream<GetBookRequest, Book>, callback: sendUnaryData<Book>): void => {
5654
let lastOne: GetBookRequest;
5755
call.on("data", (request: GetBookRequest) => {
5856
log(`[getGreatestBook] Request: ${JSON.stringify(request.toObject())}`);
@@ -66,14 +64,14 @@ class ServerImpl implements IBookServiceServer {
6664
log(`[getGreatestBook] Done: ${JSON.stringify(reply.toObject())}`);
6765
callback(null, reply);
6866
});
69-
}
67+
},
7068

71-
}
69+
};
7270

7371
function startServer() {
7472
const server = new grpc.Server();
7573

76-
server.addService(BookServiceService, new ServerImpl());
74+
server.addService(BookServiceService, ServerImpl);
7775
server.bindAsync("127.0.0.1:50051", grpc.ServerCredentials.createInsecure(), (err, port) => {
7876
if (err) {
7977
throw err;

0 commit comments

Comments
 (0)