Merge branch 'feature/grpc-server-package-definition' of https://github.com/krugi/nest into krugi-feature/grpc-server-package-definition

This commit is contained in:
Kamil Myśliwiec
2023-12-18 09:08:02 +01:00
10 changed files with 141 additions and 22 deletions

View File

@@ -10,6 +10,7 @@ import { ClientGrpc, GrpcOptions } from '../interfaces';
import { ClientProxy } from './client-proxy';
import { GRPC_CANCELLED } from './constants';
import { ChannelOptions } from '../external/grpc-options.interface';
import { getGrpcPackageDefinition } from '../helpers';
let grpcPackage: any = {};
let grpcProtoLoaderPackage: any = {};
@@ -300,16 +301,11 @@ export class ClientGrpcProxy extends ClientProxy implements ClientGrpc {
public loadProto(): any {
try {
const file = this.getOptionsProp(this.options, 'protoPath');
const loader = this.getOptionsProp(this.options, 'loader');
const packageDefinition =
this.getOptionsProp(this.options, 'packageDefinition') ||
grpcProtoLoaderPackage.loadSync(file, loader);
const packageObject =
grpcPackage.loadPackageDefinition(packageDefinition);
return packageObject;
const packageDefinition = getGrpcPackageDefinition(
this.options,
grpcProtoLoaderPackage,
);
return grpcPackage.loadPackageDefinition(packageDefinition);
} catch (err) {
const invalidProtoError = new InvalidProtoDefinitionException(err.path);
const message =

View File

@@ -0,0 +1,7 @@
import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception';
export class InvalidGrpcPackageDefinitionMissingPacakgeDefinitionException extends RuntimeException {
constructor() {
super('protoPath or packageDefinition must be defined');
}
}

View File

@@ -0,0 +1,9 @@
import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception';
export class InvalidGrpcPackageDefinitionMutexException extends RuntimeException {
constructor() {
super(
'Both protoPath and packageDefinition were provided, but only one can be defined',
);
}
}

View File

@@ -0,0 +1,24 @@
import { GrpcOptions } from '../interfaces';
import { InvalidGrpcPackageDefinitionMutexException } from '../errors/invalid-grpc-package-definition-mutex.exception';
import { InvalidGrpcPackageDefinitionMissingPacakgeDefinitionException } from '../errors/invalid-grpc-package-definition-missing-package-definition.exception';
export function getGrpcPackageDefinition(
options: GrpcOptions['options'],
grpcProtoLoaderPackage: any,
) {
const file = options['protoPath'];
const packageDefinition = options['packageDefinition'];
if ([file, packageDefinition].every(x => x != undefined)) {
throw new InvalidGrpcPackageDefinitionMutexException();
}
if ([file, packageDefinition].every(x => x == undefined)) {
throw new InvalidGrpcPackageDefinitionMissingPacakgeDefinitionException();
}
return (
packageDefinition ||
grpcProtoLoaderPackage.loadSync(file, options['loader'])
);
}

View File

@@ -3,3 +3,4 @@ export * from './kafka-logger';
export * from './kafka-parser';
export * from './kafka-reply-partition-assigner';
export * from './tcp-socket';
export * from './grpc-helpers';

View File

@@ -61,7 +61,7 @@ export interface GrpcOptions {
};
channelOptions?: ChannelOptions;
credentials?: any;
protoPath: string | string[];
protoPath?: string | string[];
package: string | string[];
protoLoader?: string;
packageDefinition?: any;

View File

@@ -26,6 +26,7 @@ import { ChannelOptions } from '../external/grpc-options.interface';
import { CustomTransportStrategy, MessageHandler } from '../interfaces';
import { GrpcOptions } from '../interfaces/microservice-configuration.interface';
import { Server } from './server';
import { getGrpcPackageDefinition } from '../helpers';
let grpcPackage: any = {};
let grpcProtoLoaderPackage: any = {};
@@ -501,20 +502,18 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
public loadProto(): any {
try {
const file = this.getOptionsProp(this.options, 'protoPath');
const loader = this.getOptionsProp(this.options, 'loader');
const packageDefinition = grpcProtoLoaderPackage.loadSync(file, loader);
const packageObject =
grpcPackage.loadPackageDefinition(packageDefinition);
return packageObject;
const packageDefinition = getGrpcPackageDefinition(
this.options,
grpcProtoLoaderPackage,
);
return grpcPackage.loadPackageDefinition(packageDefinition);
} catch (err) {
const invalidProtoError = new InvalidProtoDefinitionException(err.path);
const message =
err && err.message ? err.message : invalidProtoError.message;
this.logger.error(message, invalidProtoError.stack);
throw err;
throw invalidProtoError;
}
}

View File

@@ -3,10 +3,11 @@ import { expect } from 'chai';
import { join } from 'path';
import { Observable, Subject } from 'rxjs';
import * as sinon from 'sinon';
import { ClientGrpcProxy } from '../../client/client-grpc';
import { ClientGrpcProxy } from '../../client';
import { InvalidGrpcPackageException } from '../../errors/invalid-grpc-package.exception';
import { InvalidGrpcServiceException } from '../../errors/invalid-grpc-service.exception';
import { InvalidProtoDefinitionException } from '../../errors/invalid-proto-definition.exception';
import * as grpcHelpers from '../../helpers/grpc-helpers';
class NoopLogger extends Logger {
log(message: any, context?: string): void {}
@@ -443,13 +444,18 @@ describe('ClientGrpcProxy', () => {
describe('loadProto', () => {
describe('when proto is invalid', () => {
it('should throw InvalidProtoDefinitionException', () => {
sinon.stub(client, 'getOptionsProp' as any).callsFake(() => {
const getPackageDefinitionStub = sinon.stub(
grpcHelpers,
'getGrpcPackageDefinition' as any,
);
getPackageDefinitionStub.callsFake(() => {
throw new Error();
});
(client as any).logger = new NoopLogger();
expect(() => client.loadProto()).to.throws(
InvalidProtoDefinitionException,
);
getPackageDefinitionStub.restore();
});
});
});

View File

@@ -0,0 +1,56 @@
import { expect } from 'chai';
import { getGrpcPackageDefinition } from '../../helpers/grpc-helpers';
import { InvalidGrpcPackageDefinitionMutexException } from '../../errors/invalid-grpc-package-definition-mutex.exception';
import { InvalidGrpcPackageDefinitionMissingPacakgeDefinitionException } from '../../errors/invalid-grpc-package-definition-missing-package-definition.exception';
const grpcProtoLoaderPackage = { loadSync: (a, b) => 'withLoader' };
describe('getGrpcPackageDefinition', () => {
it('missing both protoPath and packageDefinition', () => {
expect(() =>
getGrpcPackageDefinition(
{
package: 'somePackage',
},
grpcProtoLoaderPackage,
),
).to.throw(InvalidGrpcPackageDefinitionMissingPacakgeDefinitionException);
});
it('got both protoPath and packageDefinition', () => {
expect(() =>
getGrpcPackageDefinition(
{
package: 'somePackage',
protoPath: 'some/path',
packageDefinition: {},
},
grpcProtoLoaderPackage,
),
).to.throw(InvalidGrpcPackageDefinitionMutexException);
});
it('success with protoPath', () => {
expect(() =>
getGrpcPackageDefinition(
{
package: 'somePackage',
protoPath: 'some/path',
},
grpcProtoLoaderPackage,
),
).to.not.throw(Error);
});
it('success with packageDef', () => {
expect(() =>
getGrpcPackageDefinition(
{
package: 'somePackage',
packageDefinition: {},
},
grpcProtoLoaderPackage,
),
).to.not.throw(Error);
});
});

View File

@@ -13,7 +13,9 @@ import * as sinon from 'sinon';
import { CANCEL_EVENT } from '../../constants';
import { InvalidGrpcPackageException } from '../../errors/invalid-grpc-package.exception';
import { GrpcMethodStreamingType } from '../../index';
import { ServerGrpc } from '../../server/server-grpc';
import { ServerGrpc } from '../../server';
import { InvalidProtoDefinitionException } from '../../errors/invalid-proto-definition.exception';
import * as grpcHelpers from '../../helpers/grpc-helpers';
class NoopLogger extends Logger {
log(message: any, context?: string): void {}
@@ -777,6 +779,25 @@ describe('ServerGrpc', () => {
});
});
describe('loadProto', () => {
describe('when proto is invalid', () => {
it('should throw InvalidProtoDefinitionException', () => {
const getPackageDefinitionStub = sinon.stub(
grpcHelpers,
'getGrpcPackageDefinition' as any,
);
getPackageDefinitionStub.callsFake(() => {
throw new Error();
});
(server as any).logger = new NoopLogger();
expect(() => server.loadProto()).to.throws(
InvalidProtoDefinitionException,
);
getPackageDefinitionStub.restore();
});
});
});
describe('close', () => {
it('should call "forceShutdown" by default', async () => {
const grpcClient = {