diff --git a/packages/grpc-js/src/index.ts b/packages/grpc-js/src/index.ts index d846b6c06..8c5851bfc 100644 --- a/packages/grpc-js/src/index.ts +++ b/packages/grpc-js/src/index.ts @@ -38,9 +38,10 @@ import { Serialize, } from './make-client'; import { Metadata } from './metadata'; -import { Server } from './server'; +import { Server, UntypedHandleCall, UntypedServiceImplementation } from './server'; import { KeyCertPair, ServerCredentials } from './server-credentials'; import { StatusBuilder } from './status-builder'; +import { ServerUnaryCall, ServerReadableStream, ServerWritableStream, ServerDuplexStream } from './server-call'; const supportedNodeVersions = require('../../package.json').engines.node; if (!semver.satisfies(process.version, supportedNodeVersions)) { @@ -213,6 +214,12 @@ export { CallOptions, StatusObject, ServiceError, + ServerUnaryCall, + ServerReadableStream, + ServerWritableStream, + ServerDuplexStream, + UntypedHandleCall, + UntypedServiceImplementation }; /* tslint:disable:no-any */ diff --git a/packages/grpc-js/src/server-call.ts b/packages/grpc-js/src/server-call.ts index 23d6b2470..c9a6b3f75 100644 --- a/packages/grpc-js/src/server-call.ts +++ b/packages/grpc-js/src/server-call.ts @@ -19,12 +19,12 @@ import { EventEmitter } from 'events'; import * as http2 from 'http2'; import { Duplex, Readable, Writable } from 'stream'; -import { ServiceError } from './call'; import { StatusObject } from './call-stream'; import { Status } from './constants'; import { Deserialize, Serialize } from './make-client'; import { Metadata } from './metadata'; import { StreamDecoder } from './stream-decoder'; +import { ObjectReadable, ObjectWritable } from './object-stream'; interface DeadlineUnitIndexSignature { [name: string]: number; @@ -56,6 +56,10 @@ const defaultResponseOptions = { waitForTrailers: true, } as http2.ServerStreamResponseOptions; +export type ServerStatusResponse = Partial; + +export type ServerErrorResponse = ServerStatusResponse & Error; + export type ServerSurfaceCall = { cancelled: boolean; getPeer(): string; @@ -68,13 +72,13 @@ export type ServerUnaryCall = ServerSurfaceCall & { export type ServerReadableStream< RequestType, ResponseType -> = ServerSurfaceCall & Readable; +> = ServerSurfaceCall & ObjectReadable; export type ServerWritableStream< RequestType, ResponseType -> = ServerSurfaceCall & Writable & { request: RequestType | null }; +> = ServerSurfaceCall & ObjectWritable & { request: RequestType | null }; export type ServerDuplexStream = ServerSurfaceCall & - Duplex; + ObjectReadable & ObjectWritable; export class ServerUnaryCallImpl extends EventEmitter implements ServerUnaryCall { @@ -152,7 +156,7 @@ export class ServerWritableStreamImpl this.call.setupSurfaceCall(this); this.on('error', err => { - this.call.sendError(err as ServiceError); + this.call.sendError(err); this.end(); }); } @@ -223,7 +227,7 @@ export class ServerDuplexStreamImpl extends Duplex this.call.setupReadable(this); this.on('error', err => { - this.call.sendError(err as ServiceError); + this.call.sendError(err); this.end(); }); } @@ -247,7 +251,7 @@ ServerDuplexStreamImpl.prototype.end = ServerWritableStreamImpl.prototype.end; // Unary response callback signature. export type sendUnaryData = ( - error: ServiceError | null, + error: ServerErrorResponse | ServerStatusResponse | null, value: ResponseType | null, trailer?: Metadata, flags?: number @@ -339,7 +343,7 @@ export class Http2ServerCallStream< ) { super(); - this.stream.once('error', (err: ServiceError) => { + this.stream.once('error', (err: ServerErrorResponse) => { err.code = Status.INTERNAL; this.sendError(err); }); @@ -379,7 +383,7 @@ export class Http2ServerCallStream< const match = timeoutHeader[0].toString().match(DEADLINE_REGEX); if (match === null) { - const err = new Error('Invalid deadline') as ServiceError; + const err = new Error('Invalid deadline') as ServerErrorResponse; err.code = Status.OUT_OF_RANGE; this.sendError(err); return; @@ -439,7 +443,7 @@ export class Http2ServerCallStream< } async sendUnaryMessage( - err: ServiceError | null, + err: ServerErrorResponse | ServerStatusResponse | null, value: ResponseType | null, metadata?: Metadata, flags?: number @@ -490,22 +494,22 @@ export class Http2ServerCallStream< } } - sendError(error: ServiceError) { + sendError(error: ServerErrorResponse | ServerStatusResponse) { const status: StatusObject = { code: Status.UNKNOWN, - details: error.hasOwnProperty('message') + details: ('message' in error) ? error.message : 'Unknown Error', - metadata: error.hasOwnProperty('metadata') + metadata: ('metadata' in error && error.metadata !== undefined) ? error.metadata : new Metadata(), }; - if (error.hasOwnProperty('code') && Number.isInteger(error.code)) { + if ('code' in error && typeof error.code === 'number' && Number.isInteger(error.code)) { status.code = error.code; - if (error.hasOwnProperty('details')) { - status.details = error.details; + if ('details' in error && typeof error.details === 'string') { + status.details = error.details!; } } @@ -637,7 +641,7 @@ export class Http2ServerCallStream< type UntypedServerCall = Http2ServerCallStream; function handleExpiredDeadline(call: UntypedServerCall) { - const err = new Error('Deadline exceeded') as ServiceError; + const err = new Error('Deadline exceeded') as ServerErrorResponse; err.code = Status.DEADLINE_EXCEEDED; call.sendError(err); diff --git a/packages/grpc-js/src/server.ts b/packages/grpc-js/src/server.ts index e6d176195..a568f9c1d 100644 --- a/packages/grpc-js/src/server.ts +++ b/packages/grpc-js/src/server.ts @@ -41,6 +41,8 @@ import { ServerWritableStream, ServerWritableStreamImpl, UnaryHandler, + ServerErrorResponse, + ServerStatusResponse, } from './server-call'; import { ServerCredentials } from './server-credentials'; @@ -57,9 +59,9 @@ type UntypedUnaryHandler = UnaryHandler; type UntypedClientStreamingHandler = ClientStreamingHandler; type UntypedServerStreamingHandler = ServerStreamingHandler; type UntypedBidiStreamingHandler = BidiStreamingHandler; -type UntypedHandleCall = HandleCall; +export type UntypedHandleCall = HandleCall; type UntypedHandler = Handler; -interface UntypedServiceImplementation { +export interface UntypedServiceImplementation { [name: string]: UntypedHandleCall; } @@ -100,7 +102,7 @@ export class Server { throw new Error('Not implemented. Use addService() instead'); } - addService(service: ServiceDefinition, implementation: object): void { + addService(service: ServiceDefinition, implementation: UntypedServiceImplementation): void { if (this.started === true) { throw new Error("Can't add a service to a started server."); } @@ -120,8 +122,6 @@ export class Server { throw new Error('Cannot add an empty service to a server'); } - const implMap: UntypedServiceImplementation = implementation as UntypedServiceImplementation; - serviceKeys.forEach(name => { const attrs = service[name]; let methodType: HandlerType; @@ -140,11 +140,11 @@ export class Server { } } - let implFn = implMap[name]; + let implFn = implementation[name]; let impl; if (implFn === undefined && typeof attrs.originalName === 'string') { - implFn = implMap[attrs.originalName]; + implFn = implementation[attrs.originalName]; } if (implFn !== undefined) { @@ -414,7 +414,7 @@ async function handleUnary( handler.func( emitter, ( - err: ServiceError | null, + err: ServerErrorResponse | ServerStatusResponse | null, value: ResponseType | null, trailer?: Metadata, flags?: number @@ -436,7 +436,7 @@ function handleClientStreaming( ); function respond( - err: ServiceError | null, + err: ServerErrorResponse | ServerStatusResponse | null, value: ResponseType | null, trailer?: Metadata, flags?: number diff --git a/packages/grpc-native-core/index.d.ts b/packages/grpc-native-core/index.d.ts index 718f6bdb4..bba7be727 100644 --- a/packages/grpc-native-core/index.d.ts +++ b/packages/grpc-native-core/index.d.ts @@ -412,13 +412,13 @@ declare module "grpc" { * User provided method to handle server streaming methods on the server. */ type handleServerStreamingCall = - (call: ServerWriteableStream) => void; + (call: ServerWritableStream) => void; /** * A stream that the server can write to. Used for calls that are streaming * from the server side. */ - export class ServerWriteableStream extends Writable { + export class ServerWritableStream extends Writable { /** * Indicates if the call has been cancelled */ @@ -449,6 +449,10 @@ declare module "grpc" { sendMetadata(responseMetadata: Metadata): void; } + /* This typo existed in previous versions of this file, so we provide this + * type alias for backwards compatibility. */ + export type ServerWriteableStream = ServerWritableStream; + /** * User provided method to handle bidirectional streaming calls on the server. */