@@ -12,4 +12,130 @@ The backend server is running on `http://localhost:8900` while the frontend will
12
12
## Notable setup points
13
13
14
14
### Disable TSLint for protobuf generated classes
15
- https://github.com/easyCZ/grpc-web-hacker-news/blob/master/app/tslint.json#L4
15
+ Gernerated proto classes do not confirm to TS lint requirements, [ disable linting] ( https://github.com/easyCZ/grpc-web-hacker-news/blob/master/app/tslint.json#L4 )
16
+ ``` json
17
+ {
18
+ "linterOptions" : {
19
+ "exclude" : [
20
+ " src/proto/*"
21
+ ]
22
+ },
23
+ }
24
+ ```
25
+
26
+ ### Configure protobuf compiler script
27
+ In this example, we're using a ` protoc.sh ` script to aid compilation
28
+ ``` bash
29
+ protoc \
30
+ --go_out=plugins=grpc:./server \
31
+ --plugin=protoc-gen-ts=./app/node_modules/.bin/protoc-gen-ts \
32
+ --ts_out=service=true:./app/src \
33
+ --js_out=import_style=commonjs,binary:./app/src \
34
+ ./proto/hackernews.proto
35
+ ```
36
+
37
+ ### gRPC-Web Redux Middleware
38
+ An example redux middleware is included.
39
+ ``` js
40
+ import { Action , Dispatch , Middleware , MiddlewareAPI } from ' redux' ;
41
+ import { Code , grpc , Metadata , Transport } from ' grpc-web-client' ;
42
+ import * as jspb from ' google-protobuf' ;
43
+
44
+ const GRPC_WEB_REQUEST = ' GRPC_WEB_REQUEST' ;
45
+ // const GRPC_WEB_INVOKE = 'GRPC_WEB_INVOKE';
46
+
47
+ // Descriptor of a grpc-web payload
48
+ // life-cycle methods mirror grpc-web but allow for an action to be dispatched when triggered
49
+ export type GrpcActionPayload < RequestType extends jspb .Message , ResponseType extends jspb .Message > = {
50
+ // The method descriptor to use for a gRPC request, equivalent to grpc.invoke(methodDescriptor, ...)
51
+ methodDescriptor : grpc .MethodDefinition <RequestType , ResponseType >,
52
+ // The transport to use for grpc-web, automatically selected if empty
53
+ transport ?: Transport ,
54
+ // toggle debug messages
55
+ debug ?: boolean ,
56
+ // the URL of a host this request should go to
57
+ host : string ,
58
+ // An instance of of the request message
59
+ request : RequestType ,
60
+ // Additional metadata to attach to the request, the same as grpc-web
61
+ metadata ?: Metadata .ConstructorArg ,
62
+ // Called immediately before the request is started, useful for toggling a loading status
63
+ onStart ?: () => Action | void ,
64
+ // Called when response headers are received
65
+ onHeaders ?: (headers : Metadata ) => Action | void ,
66
+ // Called on each incoming message
67
+ onMessage ?: (res : ResponseType ) => Action | void ,
68
+ // Called at the end of a request, make sure to check the exit code
69
+ onEnd : (code : Code , message : string , trailers : Metadata ) => Action | void ,
70
+ };
71
+
72
+ // Basic type for a gRPC Action
73
+ export type GrpcAction < RequestType extends jspb .Message , ResponseType extends jspb .Message > = {
74
+ type : typeof GRPC_WEB_REQUEST ,
75
+ payload : GrpcActionPayload <RequestType , ResponseType >,
76
+ };
77
+
78
+ // Action creator, Use it to create a new grpc action
79
+ export function grpcRequest<RequestType extends jspb.Message, ResponseType extends jspb.Message>(
80
+ payload : GrpcActionPayload < RequestType , ResponseType >
81
+ ): GrpcAction<RequestType, ResponseType> {
82
+ return {
83
+ type: GRPC_WEB_REQUEST ,
84
+ payload,
85
+ };
86
+ }
87
+
88
+ export function newGrpcMiddleware (): Middleware {
89
+ return ({getState, dispatch}: MiddlewareAPI < {}> ) => (next : Dispatch < {}> ) => (action : any ) => {
90
+ // skip non-grpc actions
91
+ if (! isGrpcWebUnaryAction (action)) {
92
+ return next (action);
93
+ }
94
+
95
+ const payload = action .payload ;
96
+
97
+ if (payload .onStart ) {
98
+ payload .onStart ();
99
+ }
100
+
101
+ grpc .invoke (payload .methodDescriptor , {
102
+ debug: payload .debug ,
103
+ host: payload .host ,
104
+ request: payload .request ,
105
+ metadata: payload .metadata ,
106
+ transport: payload .transport ,
107
+ onHeaders : headers => {
108
+ if (! payload .onHeaders ) { return ; }
109
+ const actionToDispatch = payload .onHeaders (headers);
110
+ return actionToDispatch && dispatch (actionToDispatch);
111
+ },
112
+ onMessage : res => {
113
+ if (! payload .onMessage ) { return ; }
114
+ const actionToDispatch = payload .onMessage (res);
115
+ return actionToDispatch && dispatch (actionToDispatch);
116
+ },
117
+ onEnd : (code , msg , trailers ) => {
118
+ const actionToDispatch = payload .onEnd (code, msg, trailers);
119
+ return actionToDispatch && dispatch (actionToDispatch);
120
+ },
121
+ });
122
+
123
+ return next (action);
124
+ };
125
+ }
126
+
127
+ function isGrpcWebUnaryAction (action : any ): action is GrpcAction<jspb.Message, jspb.Message> {
128
+ return action && action .type && action .type === GRPC_WEB_REQUEST && isGrpcWebPayload (action);
129
+ }
130
+
131
+ function isGrpcWebPayload (action : any ): boolean {
132
+ return action &&
133
+ action .payload &&
134
+ action .payload .methodDescriptor &&
135
+ action .payload .request &&
136
+ action .payload .onEnd &&
137
+ action .payload .host ;
138
+ }
139
+
140
+
141
+ ```
0 commit comments