| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { ChannelCredentials } from './channel-credentials'; |
| import { ChannelOptions } from './channel-options'; |
| import { Client } from './client'; |
| import { UntypedServiceImplementation } from './server'; |
|
|
| export interface Serialize<T> { |
| (value: T): Buffer; |
| } |
|
|
| export interface Deserialize<T> { |
| (bytes: Buffer): T; |
| } |
|
|
| export interface ClientMethodDefinition<RequestType, ResponseType> { |
| path: string; |
| requestStream: boolean; |
| responseStream: boolean; |
| requestSerialize: Serialize<RequestType>; |
| responseDeserialize: Deserialize<ResponseType>; |
| originalName?: string; |
| } |
|
|
| export interface ServerMethodDefinition<RequestType, ResponseType> { |
| path: string; |
| requestStream: boolean; |
| responseStream: boolean; |
| responseSerialize: Serialize<ResponseType>; |
| requestDeserialize: Deserialize<RequestType>; |
| originalName?: string; |
| } |
|
|
| export interface MethodDefinition<RequestType, ResponseType> |
| extends ClientMethodDefinition<RequestType, ResponseType>, |
| ServerMethodDefinition<RequestType, ResponseType> {} |
|
|
| |
| export type ServiceDefinition< |
| ImplementationType = UntypedServiceImplementation |
| > = { |
| readonly [index in keyof ImplementationType]: MethodDefinition<any, any>; |
| }; |
| |
|
|
| export interface ProtobufTypeDefinition { |
| format: string; |
| type: object; |
| fileDescriptorProtos: Buffer[]; |
| } |
|
|
| export interface PackageDefinition { |
| [index: string]: ServiceDefinition | ProtobufTypeDefinition; |
| } |
|
|
| |
| |
| |
| |
| |
| const requesterFuncs = { |
| unary: Client.prototype.makeUnaryRequest, |
| server_stream: Client.prototype.makeServerStreamRequest, |
| client_stream: Client.prototype.makeClientStreamRequest, |
| bidi: Client.prototype.makeBidiStreamRequest, |
| }; |
|
|
| export interface ServiceClient extends Client { |
| [methodName: string]: Function; |
| } |
|
|
| export interface ServiceClientConstructor { |
| new ( |
| address: string, |
| credentials: ChannelCredentials, |
| options?: Partial<ChannelOptions> |
| ): ServiceClient; |
| service: ServiceDefinition; |
| serviceName: string; |
| } |
|
|
| |
| |
| |
| |
| |
| function isPrototypePolluted(key: string): boolean { |
| return ['__proto__', 'prototype', 'constructor'].includes(key); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function makeClientConstructor( |
| methods: ServiceDefinition, |
| serviceName: string, |
| classOptions?: {} |
| ): ServiceClientConstructor { |
| if (!classOptions) { |
| classOptions = {}; |
| } |
|
|
| class ServiceClientImpl extends Client implements ServiceClient { |
| static service: ServiceDefinition; |
| static serviceName: string; |
| [methodName: string]: Function; |
| } |
|
|
| Object.keys(methods).forEach(name => { |
| if (isPrototypePolluted(name)) { |
| return; |
| } |
| const attrs = methods[name]; |
| let methodType: keyof typeof requesterFuncs; |
| |
| if (typeof name === 'string' && name.charAt(0) === '$') { |
| throw new Error('Method names cannot start with $'); |
| } |
| if (attrs.requestStream) { |
| if (attrs.responseStream) { |
| methodType = 'bidi'; |
| } else { |
| methodType = 'client_stream'; |
| } |
| } else { |
| if (attrs.responseStream) { |
| methodType = 'server_stream'; |
| } else { |
| methodType = 'unary'; |
| } |
| } |
| const serialize = attrs.requestSerialize; |
| const deserialize = attrs.responseDeserialize; |
| const methodFunc = partial( |
| requesterFuncs[methodType], |
| attrs.path, |
| serialize, |
| deserialize |
| ); |
| ServiceClientImpl.prototype[name] = methodFunc; |
| |
| Object.assign(ServiceClientImpl.prototype[name], attrs); |
| if (attrs.originalName && !isPrototypePolluted(attrs.originalName)) { |
| ServiceClientImpl.prototype[attrs.originalName] = |
| ServiceClientImpl.prototype[name]; |
| } |
| }); |
|
|
| ServiceClientImpl.service = methods; |
| ServiceClientImpl.serviceName = serviceName; |
|
|
| return ServiceClientImpl; |
| } |
|
|
| function partial( |
| fn: Function, |
| path: string, |
| serialize: Function, |
| deserialize: Function |
| ): Function { |
| |
| return function (this: any, ...args: any[]) { |
| return fn.call(this, path, serialize, deserialize, ...args); |
| }; |
| } |
|
|
| export interface GrpcObject { |
| [index: string]: |
| | GrpcObject |
| | ServiceClientConstructor |
| | ProtobufTypeDefinition; |
| } |
|
|
| function isProtobufTypeDefinition( |
| obj: ServiceDefinition | ProtobufTypeDefinition |
| ): obj is ProtobufTypeDefinition { |
| return 'format' in obj; |
| } |
|
|
| |
| |
| |
| |
| |
| export function loadPackageDefinition( |
| packageDef: PackageDefinition |
| ): GrpcObject { |
| const result: GrpcObject = {}; |
| for (const serviceFqn in packageDef) { |
| if (Object.prototype.hasOwnProperty.call(packageDef, serviceFqn)) { |
| const service = packageDef[serviceFqn]; |
| const nameComponents = serviceFqn.split('.'); |
| if (nameComponents.some((comp: string) => isPrototypePolluted(comp))) { |
| continue; |
| } |
| const serviceName = nameComponents[nameComponents.length - 1]; |
| let current = result; |
| for (const packageName of nameComponents.slice(0, -1)) { |
| if (!current[packageName]) { |
| current[packageName] = {}; |
| } |
| current = current[packageName] as GrpcObject; |
| } |
| if (isProtobufTypeDefinition(service)) { |
| current[serviceName] = service; |
| } else { |
| current[serviceName] = makeClientConstructor(service, serviceName, {}); |
| } |
| } |
| } |
| return result; |
| } |
|
|