Skip to content

Commit 63ea521

Browse files
authored
Add setup points
1 parent 6086d53 commit 63ea521

File tree

1 file changed

+127
-1
lines changed

1 file changed

+127
-1
lines changed

README.md

+127-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,130 @@ The backend server is running on `http://localhost:8900` while the frontend will
1212
## Notable setup points
1313

1414
### 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

Comments
 (0)