Skip to content

Commit

Permalink
feature(@nestjs) close method, fixes, init (#20, #52, #40)
Browse files Browse the repository at this point in the history
  • Loading branch information
kamil.mysliwiec committed May 23, 2017
1 parent 2f83052 commit db0d0cc
Show file tree
Hide file tree
Showing 18 changed files with 228 additions and 26 deletions.
13 changes: 10 additions & 3 deletions example/modules/client/client.controller.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { Controller } from '../../../src/common/utils/decorators/controller.decorator';
import { Client } from '../../../src/microservices/utils/client.decorator';
import { RequestMapping } from '../../../src/common/utils/decorators/request-mapping.decorator';
import { Get } from '../../../src/common/utils/decorators/request-mapping.decorator';
import { ClientProxy } from '../../../src/microservices/client/client-proxy';
import { Observable } from 'rxjs';
import { Transport } from '../../../src/microservices/enums/transport.enum';
import 'rxjs/add/operator/catch';
import { MessagePattern } from '../../../src/microservices/index';

const MicroserviceClient = { transport: Transport.TCP, port: 5667 };

@Controller()
export class ClientController {
@Client(MicroserviceClient)
public client: ClientProxy;
private client: ClientProxy;

@RequestMapping({ path: 'client' })
@Get('client')
public sendMessage(req, res) {
const pattern = { command: 'add' };
const data = [ 1, 2, 3, 4, 5 ];
Expand All @@ -22,4 +23,10 @@ export class ClientController {
.catch((err) => Observable.empty())
.subscribe((result) => res.status(200).json({ result }));
}

@MessagePattern({ command: 'add' })
public add(data, respond) {
const numbers = data || [];
respond(null, numbers.reduce((a, b) => a + b));
}
}
15 changes: 9 additions & 6 deletions example/server.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { NestFactory } from './../src/';
import { NestFactory } from './../src/core';
import { ApplicationModule } from './modules/app.module';
import { Transport } from '../src/microservices/index';

const port = 3001;
const app = NestFactory.create(ApplicationModule);

app.init();
app.listen(port, (server) => {
const microservice = app.connectMicroservice({
transport: Transport.TCP,
port: 5667,
});
microservice.listen(() => ({}));
app.listen(port, () => {
console.log('Application listen on port:', port);
server.close();
process.exit();
//app.close();
});
9 changes: 8 additions & 1 deletion src/common/interfaces/nest-application.interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { MicroserviceConfiguration } from '@nestjs/microservices';
import { INestMicroservice } from './index';

export interface INestApplication {
init(): void;
listen(port: number, callback?: (server?) => void): void;
listen(port: number, callback?: () => void);
setGlobalPrefix(prefix: string): void;
connectMicroservice(config: MicroserviceConfiguration): INestMicroservice;
getMicroservices(): INestMicroservice[];
startAllMicroservices(callback: () => void): void;
close(): void;
}
1 change: 1 addition & 0 deletions src/common/interfaces/nest-microservice.interface.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export interface INestMicroservice {
listen(callback: () => void);
close(): void;
}
71 changes: 71 additions & 0 deletions src/common/test/utils/shared.utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { expect } from 'chai';
import { isUndefined, isFunction, isObject, isString, isConstructor, validatePath, isNil, isEmpty } from '../../utils/shared.utils';

describe('Shared utils', () => {
describe('isUndefined', () => {
it('should returns true when obj is undefined', () => {
expect(isUndefined(undefined)).to.be.true;
});
it('should returns false when object is not undefined', () => {
expect(isUndefined({})).to.be.false;
});
});
describe('isFunction', () => {
it('should returns true when obj is function', () => {
expect(isFunction(() => ({}))).to.be.true;
});
it('should returns false when object is not function', () => {
expect(isFunction(null)).to.be.false;
});
});
describe('isObject', () => {
it('should returns true when obj is object', () => {
expect(isObject({})).to.be.true;
});
it('should returns false when object is not object', () => {
expect(isObject(3)).to.be.false;
});
});
describe('isString', () => {
it('should returns true when obj is string', () => {
expect(isString('true')).to.be.true;
});
it('should returns false when object is not string', () => {
expect(isString(false)).to.be.false;
});
});
describe('isConstructor', () => {
it('should returns true when string is equal constructor', () => {
expect(isConstructor('constructor')).to.be.true;
});
it('should returns false when string is not equal constructor', () => {
expect(isConstructor('nope')).to.be.false;
});
});
describe('validatePath', () => {
it('should returns validated path ("add / if not exists")', () => {
expect(validatePath('nope')).to.be.eql('/nope');
});
it('should returns same path', () => {
expect(validatePath('/nope')).to.be.eql('/nope');
});
});
describe('isNil', () => {
it('should returns true when obj is undefined or null', () => {
expect(isNil(undefined)).to.be.true;
expect(isNil(null)).to.be.true;
});
it('should returns false when object is not undefined and null', () => {
expect(isNil('3')).to.be.false;
});
});
describe('isEmpty', () => {
it('should returns true when array is empty or not exists', () => {
expect(isEmpty([])).to.be.true;
expect(isEmpty(null)).to.be.true;
});
it('should returns false when array is not empty', () => {
expect(isEmpty([1, 2])).to.be.false;
});
});
});
1 change: 1 addition & 0 deletions src/core/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const messages = {
APPLICATION_START: `Starting Nest application...`,
APPLICATION_READY: `Nest application is ready!`,
MICROSERVICE_READY: `Nest microservice is ready!`,
UNKNOWN_EXCEPTION_MESSAGE: 'Unknown exception',
};
45 changes: 39 additions & 6 deletions src/core/nest-application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ import { Logger } from '@nestjs/common/services/logger.service';
import { messages } from './constants';
import { MicroservicesModule } from '@nestjs/microservices/microservices-module';
import { Resolver } from './router/interfaces/resolver.interface';
import { INestApplication } from '@nestjs/common';
import { INestApplication, INestMicroservice } from '@nestjs/common';
import { ApplicationConfig } from './application-config';
import { validatePath } from '@nestjs/common/utils/shared.utils';
import { MicroserviceConfiguration } from '@nestjs/microservices';
import { NestMicroservice } from './index';

export class NestApplication implements INestApplication {
private readonly config = new ApplicationConfig();
private readonly logger = new Logger(NestApplication.name);
private readonly routesResolver: Resolver;
private readonly routesResolver: Resolver = null;
private readonly microservices = [];
private isInitialized = false;
private server = null;

constructor(
private readonly container: NestContainer,
Expand All @@ -38,14 +43,36 @@ export class NestApplication implements INestApplication {
validatePath(this.config.getGlobalPrefix()),
router,
);

this.logger.log(messages.APPLICATION_READY);
}

public connectMicroservice(config: MicroserviceConfiguration): INestMicroservice {
const instance = new NestMicroservice(this.container, config);
instance.setupListeners();

this.microservices.push(instance);
return instance;
}

public getMicroservices(): INestMicroservice[] {
return this.microservices;
}

public startAllMicroservices(callback: () => void) {
Promise.all(this.microservices.map(this.listenToPromise)).then(callback);
}

public listen(port: number, callback?: () => void) {
const server = this.express.listen(port, () => {
callback && callback.call(callback, server);
});
(!this.isInitialized) && this.init();

this.server = this.express.listen(port, callback);
return this.server;
}

public close() {
SocketModule.close();
this.server && this.server.close();
this.microservices.forEach((microservice) => microservice.close());
}

public setGlobalPrefix(prefix: string) {
Expand All @@ -59,4 +86,10 @@ export class NestApplication implements INestApplication {
private setupRoutes(instance) {
this.routesResolver.resolve(instance);
}

private listenToPromise(microservice: INestMicroservice) {
return new Promise((resolve, reject) => {
microservice.listen(resolve);
});
}
}
10 changes: 7 additions & 3 deletions src/core/nest-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,13 @@ export class NestFactory {
return;

if (isFunction(receiver[prop])) {
return (...args) => ExceptionsZone.run(() => {
receiver[prop](...args);
});
return (...args) => {
let result;
ExceptionsZone.run(() => {
result = receiver[prop](...args);
});
return result;
};
}
return receiver[prop];
};
Expand Down
10 changes: 9 additions & 1 deletion src/core/nest-microservice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,19 @@ export class NestMicroservice implements INestMicroservice {

public setupModules() {
MicroservicesModule.setupClients(this.container);
this.setupListeners();
}

public setupListeners() {
MicroservicesModule.setupListeners(this.container, this.server);
}

public listen(callback: () => void) {
this.logger.log(messages.APPLICATION_READY);
this.logger.log(messages.MICROSERVICE_READY);
this.server.listen(callback);
}

public close() {
this.server.close();
}
}
21 changes: 21 additions & 0 deletions src/core/test/application-config.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { expect } from 'chai';
import { ApplicationConfig } from '../application-config';

describe('ApplicationConfig', () => {
let appConfig: ApplicationConfig;

beforeEach(() => {
appConfig = new ApplicationConfig();
});
describe('globalPath', () => {
it('should set global path', () => {
const path = 'test';
appConfig.setGlobalPrefix(path);

expect(appConfig.getGlobalPrefix()).to.be.eql(path);
});
it('should has empty string as a global path by default', () => {
expect(appConfig.getGlobalPrefix()).to.be.eql('');
});
});
});
20 changes: 14 additions & 6 deletions src/microservices/server/server-redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,28 @@ import { Server } from './server';
import { NO_PATTERN_MESSAGE } from '../constants';
import { MicroserviceConfiguration } from '../interfaces/microservice-configuration.interface';

const DEFAULT_URL = 'redis://localhost:6379';

export class ServerRedis extends Server {
private readonly url: string;
private readonly DEFAULT_URL = 'redis://localhost:6379';
private sub = null;
private pub = null;

constructor(config: MicroserviceConfiguration) {
super();
this.url = config.url || this.DEFAULT_URL;
this.url = config.url || DEFAULT_URL;
}

public listen(callback?: () => void) {
const sub = this.createRedisClient();
const pub = this.createRedisClient();
this.sub = this.createRedisClient();
this.pub = this.createRedisClient();

this.sub.on('connect', () => this.handleConnection(callback, this.sub, this.pub));
}

sub.on('connect', () => this.handleConnection(callback, sub, pub));
public close() {
this.pub && this.pub.quit();
this.sub && this.sub.quit();
}

public createRedisClient() {
Expand Down Expand Up @@ -69,7 +77,7 @@ export class ServerRedis extends Server {
};
}

public tryParse(content) {
public tryParse(content) {
try {
return JSON.parse(content);
}
Expand Down
4 changes: 4 additions & 0 deletions src/microservices/server/server-tcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export class ServerTCP extends Server {
this.server.listen(this.port, callback);
}

public close() {
this.server.close();
}

public bindHandler(socket) {
const sock = this.getSocketInstance(socket);
sock.on('message', (msg) => this.handleMessage(sock, msg));
Expand Down
1 change: 1 addition & 0 deletions src/microservices/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export abstract class Server {
protected readonly msgHandlers = {};

public abstract listen(callback: () => void);
public abstract close(): void;

public getHandlers() {
return this.msgHandlers;
Expand Down
14 changes: 14 additions & 0 deletions src/microservices/test/server/server-redis.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ describe('ServerRedis', () => {
expect(onSpy.getCall(0).args[0]).to.be.equal('connect');
});
});
describe('close', () => {
const pub = { quit: sinon.spy() };
const sub = { quit: sinon.spy() };
beforeEach(() => {
(server as any).pub = pub;
(server as any).sub = sub;
});
it('should close pub & sub server', () => {
server.close();

expect(pub.quit.called).to.be.true;
expect(sub.quit.called).to.be.true;
});
});
describe('handleConnection', () => {
let onSpy: sinon.SinonSpy,
subscribeSpy: sinon.SinonSpy,
Expand Down
10 changes: 10 additions & 0 deletions src/microservices/test/server/server-tcp.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ describe('ServerTCP', () => {
expect(socket.on.called).to.be.true;
});
});
describe('close', () => {
const tcpServer = { close: sinon.spy() };
beforeEach(() => {
(server as any).server = tcpServer;
});
it('should close server', () => {
server.close();
expect(tcpServer.close.called).to.be.true;
});
});
describe('listen', () => {
const serverMock = { listen: sinon.spy() };
beforeEach(() => {
Expand Down
Loading

0 comments on commit db0d0cc

Please sign in to comment.