Skip to content

Instantly share code, notes, and snippets.

@ericchanky
Last active May 21, 2020 09:04
Show Gist options
  • Save ericchanky/bbf26033bc226bc7a85347ea7d1498c8 to your computer and use it in GitHub Desktop.
Save ericchanky/bbf26033bc226bc7a85347ea7d1498c8 to your computer and use it in GitHub Desktop.
Generate promisified grpc client
syntax = "proto3";
message GreetRequest {
string name = 1;
}
message GreetResponse {
string message = 1;
}
service Echo {
rpc Greet(GreetRequest) returns (GreetResponse) {}
}
message Empty {}
service Another {
rpc CallOne(stream Empty) returns (Empty) {}
rpc CallTwo(Empty) returns (stream Empty) {}
rpc CallThree(stream Empty) returns (stream Empty) {}
}
#!/usr/bin/env node
const protocPlugin = require('protoc-plugin')
const camelCase = (s) => {
return `${s.charAt(0).toLowerCase() + s.slice(1)}`
}
protocPlugin((protos) => {
const js = protos.map((proto) => {
const protoName = proto.name.split('.')[0]
return {
name: `${protoName}_promise_pb.js`,
content: `// GENERATED CODE -- DO NOT EDIT!
const ${protoName}_grpc_pb = require('./${protoName}_grpc_pb')
const ${protoName}_pb = require('./${protoName}_pb')
const { Metadata } = require('grpc')
${proto.serviceList.map((service) => {
return `
class ${service.name}Client extends ${protoName}_grpc_pb.${service.name}Client {
constructor(address, credentials, options) {
super(address, credentials, options)
}
${service.methodList.filter((method) => !method.serverStreaming && !method.clientStreaming).map((method) => {
return `
${camelCase(method.name)} = (request = {}, metadata = {}) => {
const req = new ${protoName}_pb${method.inputType}()
Object.values(request).forEach(([k, v]) => {
const fn = \`set\${k.charAt(0).toUpperCase() + k.slice(1)}\`
if (typeof req[fn] === 'function') {
req[fn](v)
}
})
const md = new Metadata()
Object.values(metadata).forEach(([k, v]) => {
md.set(k, v)
})
return new Promise((resolve, reject) => {
super.${camelCase(method.name)}(req, md, (err, res) => {
if (err) {
reject(err)
} else {
resolve(res.toObject())
}
})
})
}`
}).join('\n')}
}`
}).join('\n')}
module.exports = {
${proto.serviceList.map((service) => {
return ` ${service.name}Client,`
}).join('\n')}
}
`
}
})
const ts = protos.map((proto) => {
const protoName = proto.name.split('.')[0]
return {
name: `${protoName}_promise_pb.d.ts`,
content: `// GENERATED CODE -- DO NOT EDIT!
import * as grpc from "grpc";
import * as ${protoName}_pb from "./${protoName}_pb";
${proto.serviceList.map((service) => {
return `
// ${service.name}Client
export class ${service.name}Client {
constructor(address: string, credentials: grpc.ChannelCredentials, options?: object);
${service.methodList.filter((method) => !method.serverStreaming && !method.clientStreaming).map((method) => {
return `
${camelCase(method.name)}(request?: ${protoName}_pb${method.inputType}.AsObject, metadata?: object): Promise<${protoName}_pb${method.outputType}.AsObject>;`
}).join('\n')}
}
`
}).join('')}`,
}
})
return [...js, ...ts]
})
{
"name": "protoc-gen-promise",
"version": "1.0.0",
"description": "",
"main": "index.js",
"directories": {
"test": "test"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"protoc-gen-grpc": "^1.4.0",
"protoc-plugin": "0.0.6",
"ts-protoc-gen": "^0.12.0"
},
"devDependencies": {
"grpc": "^1.24.2",
"grpc_tools_node_protoc_ts": "^3.0.0",
"lodash": "^4.17.15"
}
}
npx protoc-gen-grpc \
--plugin="protoc-gen-ts=./node_modules/.bin/protoc-gen-ts" \
--plugin="protoc-gen-promise=./index.js" \
--js_out="import_style=commonjs,binary:./generated" \
--ts_out="service=grpc-node:./generated" \
--grpc_out="./generated" \
--promise_out="./generated" \
--proto_path="." \
echo.proto
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment