UNPKG

@protobuf-ts/grpc-backend

Version:

gRPC backend for servers generated by the protoc plugin "protobuf-ts"

297 lines (296 loc) 12.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createContext = exports.mapService = exports.createDefinition = exports.adaptService = void 0; const runtime_rpc_1 = require("@protobuf-ts/runtime-rpc"); const grpc = require("@grpc/grpc-js"); const runtime_1 = require("@protobuf-ts/runtime"); const util_1 = require("./util"); /** * Create a grpc service definition and an implementation for @grpc/grpc-js. */ function adaptService(serviceInfo, serviceImplementation, options = {}) { return [ createDefinition(serviceInfo), mapService(serviceInfo, serviceImplementation, options) ]; } exports.adaptService = adaptService; /** * Create a service definition for @grpc/grpc-js from service info. */ function createDefinition(serviceInfo) { const def = {}; for (let mi of serviceInfo.methods) { def[mi.localName] = { path: `/${serviceInfo.typeName}/${mi.name}`, originalName: mi.name, requestStream: mi.clientStreaming, responseStream: mi.serverStreaming, responseDeserialize: bytes => mi.O.fromBinary(bytes), requestDeserialize: bytes => mi.I.fromBinary(bytes), responseSerialize: value => Buffer.from(mi.O.toBinary(value)), requestSerialize: value => Buffer.from(mi.I.toBinary(value)), }; } return def; } exports.createDefinition = createDefinition; /** * Create a grpc service implementation that delegates to the given * protobuf-ts service implementation. */ function mapService(si, service, options) { var _a; const grpcImp = {}; const ctxFactory = (_a = options.contextFactory) !== null && _a !== void 0 ? _a : createContext; for (let mi of si.methods) { runtime_1.assert(typeof service[mi.localName] == "function", `implementation is missing method ${mi.localName}()`); const fn = service[mi.localName].bind(service); if (mi.serverStreaming && mi.clientStreaming) { grpcImp[mi.localName] = mapBidi(si, mi, ctxFactory, fn); } else if (mi.serverStreaming) { grpcImp[mi.localName] = mapServerStreaming(si, mi, ctxFactory, fn); } else if (mi.clientStreaming) { grpcImp[mi.localName] = mapClientStreaming(si, mi, ctxFactory, fn); } else { grpcImp[mi.localName] = mapUnary(si, mi, ctxFactory, fn); } } return grpcImp; } exports.mapService = mapService; function createContext(serviceInfo, methodInfo, call) { const deadlineGrpc = call.getDeadline(); const deadlineDate = typeof deadlineGrpc === 'number' ? new Date(deadlineGrpc) : deadlineGrpc; const sendHeaders = (data) => call.sendMetadata(util_1.metadataToGrpc(data)); const ctx = new runtime_rpc_1.ServerCallContextController(methodInfo, util_1.metadataFromGrpc(call.metadata), deadlineDate, sendHeaders); call.on('cancelled', () => ctx.notifyCancelled()); return ctx; } exports.createContext = createContext; function mapUnary(serviceInfo, methodInfo, ctxFactory, method) { return (call, callback) => __awaiter(this, void 0, void 0, function* () { const context = ctxFactory(serviceInfo, methodInfo, call); let response; try { response = yield method(call.request, context); } catch (e) { if (e instanceof runtime_rpc_1.RpcError) { const grpcStatusCode = util_1.rpcCodeToGrpc(e.code); if (grpcStatusCode === undefined) { return callback({ message: `invalid grpc status code ${e.code}`, code: grpc.status.INTERNAL, details: e.message, metadata: util_1.metadataToGrpc(e.meta), }); } return callback({ code: grpcStatusCode, details: e.message, metadata: util_1.metadataToGrpc(e.meta), }); } return callback({ code: grpc.status.INTERNAL, message: e.message, name: e.name, stack: e.stack }); } const grpcStatusCode = util_1.rpcCodeToGrpc(context.status.code); if (grpcStatusCode === undefined) { return callback({ code: grpc.status.INTERNAL, message: `invalid grpc status code ${context.status.code}`, details: context.status.detail, }); } if (grpcStatusCode !== grpc.status.OK) { return callback({ code: grpcStatusCode, details: context.status.detail, }); } callback(null, response, util_1.metadataToGrpc(context.trailers)); }); } function mapServerStreaming(serviceInfo, methodInfo, ctxFactory, method) { return (call) => __awaiter(this, void 0, void 0, function* () { const context = ctxFactory(serviceInfo, methodInfo, call); const responseStream = { send(message) { return new Promise((resolve, reject) => { call.write(message, resolve); }); }, complete() { return Promise.resolve(); } }; try { yield method(call.request, responseStream, context); } catch (e) { if (e instanceof runtime_rpc_1.RpcError) { const grpcStatusCode = util_1.rpcCodeToGrpc(e.code); if (grpcStatusCode === undefined) { let d = new Error(`invalid grpc status code ${e.code}`); d.code = grpc.status.INTERNAL; // this is picked up // d.metadata = new grpc.Metadata(); // but this isn't return call.destroy(d); } return call.destroy(e); } return call.destroy(e); } const grpcStatusCode = util_1.rpcCodeToGrpc(context.status.code); if (grpcStatusCode === undefined) { let d = new Error(`invalid grpc status code ${context.status.code}`); d.code = grpc.status.INTERNAL; // this is picked up // d.metadata = new grpc.Metadata(); // but this isn't return call.destroy(d); } if (grpcStatusCode !== grpc.status.OK) { let d = new Error(context.status.detail); d.code = grpcStatusCode; // this is picked up // d.metadata = new grpc.Metadata(); // but this isn't return call.destroy(d); } call.end(util_1.metadataToGrpc(context.trailers)); }); } function mapClientStreaming(serviceInfo, methodInfo, ctxFactory, method) { return (call, callback) => __awaiter(this, void 0, void 0, function* () { const context = ctxFactory(serviceInfo, methodInfo, call); const requestStream = new runtime_rpc_1.RpcOutputStreamController(); call.on('data', args => { runtime_1.assert(methodInfo.I.is(args)); requestStream.notifyMessage(args); }); call.on('end', () => { requestStream.notifyComplete(); }); call.on('error', e => { requestStream.notifyError(e); }); let response; try { response = yield method(requestStream, context); } catch (e) { if (e instanceof runtime_rpc_1.RpcError) { const grpcStatusCode = util_1.rpcCodeToGrpc(e.code); if (grpcStatusCode === undefined) { return callback({ message: `invalid grpc status code ${e.code}`, code: grpc.status.INTERNAL, details: e.message, metadata: util_1.metadataToGrpc(e.meta), }); } return callback({ code: grpcStatusCode, details: e.message, metadata: util_1.metadataToGrpc(e.meta), }); } return callback({ code: grpc.status.INTERNAL, message: e.message, name: e.name, stack: e.stack }); } const grpcStatusCode = util_1.rpcCodeToGrpc(context.status.code); if (grpcStatusCode === undefined) { return callback({ code: grpc.status.INTERNAL, message: `invalid grpc status code ${context.status.code}`, details: context.status.detail, }); } if (grpcStatusCode !== grpc.status.OK) { return callback({ code: grpcStatusCode, details: context.status.detail, }); } callback(null, response, util_1.metadataToGrpc(context.trailers)); }); } function mapBidi(serviceInfo, methodInfo, ctxFactory, method) { return (call) => __awaiter(this, void 0, void 0, function* () { const context = ctxFactory(serviceInfo, methodInfo, call); const requestStream = new runtime_rpc_1.RpcOutputStreamController(); call.on('data', args => { runtime_1.assert(methodInfo.I.is(args)); if (!requestStream.closed) { requestStream.notifyMessage(args); } }); call.on('end', () => { if (!requestStream.closed) { requestStream.notifyComplete(); } }); call.on('error', e => { if (!requestStream.closed) { requestStream.notifyError(e); } }); const responseStream = { send(message) { return new Promise((resolve, reject) => { call.write(message, resolve); }); }, complete() { return Promise.resolve(); } }; try { yield method(requestStream, responseStream, context); } catch (e) { if (e instanceof runtime_rpc_1.RpcError) { const grpcStatusCode = util_1.rpcCodeToGrpc(e.code); if (grpcStatusCode === undefined) { let d = new Error(`invalid grpc status code ${e.code}`); d.code = grpc.status.INTERNAL; // this is picked up // d.metadata = new grpc.Metadata(); // but this isn't return call.destroy(d); } return call.destroy(e); } return call.destroy(e); } const grpcStatusCode = util_1.rpcCodeToGrpc(context.status.code); if (grpcStatusCode === undefined) { let d = new Error(`invalid grpc status code ${context.status.code}`); d.code = grpc.status.INTERNAL; // this is picked up // d.metadata = new grpc.Metadata(); // but this isn't return call.destroy(d); } if (grpcStatusCode !== grpc.status.OK) { let d = new Error(context.status.detail); d.code = grpcStatusCode; // this is picked up // d.metadata = new grpc.Metadata(); // but this isn't return call.destroy(d); } call.end(util_1.metadataToGrpc(context.trailers)); }); }