Skip to content

Commit

Permalink
feat(intel-service): add exception and guard
Browse files Browse the repository at this point in the history
  • Loading branch information
zlq4863947 committed Mar 11, 2019
1 parent 21f8210 commit 8819c51
Show file tree
Hide file tree
Showing 25 changed files with 309 additions and 122 deletions.
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module.exports = {
testMatch: ['<rootDir>/modules/**/*.spec.ts', '<rootDir>/projects/**/*.spec.ts'],
testURL: 'http://localhost/',
collectCoverageFrom: ['modules/**/*.{js,ts}', 'projects/**/*.{js,ts}', '!projects/**/main.{js,ts}', '!modules/**/index.{js,ts}', '!**/node_modules/**', '!**/vendor/**'],
coveragePathIgnorePatterns: ['/node_modules/', '/test-helpers/', '/main.{js,ts}', '/dist/', 'entity-test-bed', '/types/'],
coveragePathIgnorePatterns: ['/node_modules/', '/test-helpers/', '/main.{js,ts}', '/dist/', 'entity-test-bed', '/types/', '/config/'],
coverageReporters: ['json', 'lcov'],
verbose: true,
preset: 'ts-jest',
Expand Down
57 changes: 12 additions & 45 deletions modules/common/socketio/socketio-rxjs.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { Observable, ReplaySubject } from 'rxjs';
import { filter, take } from 'rxjs/operators';
import { Observable, Subject } from 'rxjs';
import * as io from 'socket.io-client';

export class SocketIORxjs<T = any> {
readonly socket: SocketIOClient.Socket;
private readonly data$ = new ReplaySubject<T>(1);
private readonly opened$ = new ReplaySubject<boolean>(1);
private readonly data$ = new Subject<T>();

/**
* message stream
Expand All @@ -16,60 +14,29 @@ export class SocketIORxjs<T = any> {

constructor(url: string) {
this.socket = io(url);
this.socket.on('connect', () => {
this.opened$.next(true);
});
this.socket.on('connect_timeout', () => {
this.opened$.next(false);
});
this.socket.on('connect_error', () => {
this.opened$.next(false);
});
this.socket.on('disconnect', () => {
this.opened$.next(false);
});
this.socket.on('error', () => {
this.opened$.next(false);
});
this.socket.on('message', (data: any) => this.data$.next(data));
}

/**
* @param args
*/
send(args: any | any[]): void {
// wait until socket open and send the text only once per call
this.opened$
.pipe(
take(1),
filter((opened) => opened),
)
.subscribe(() => {
if (args instanceof Array) {
this.socket.send(...args);
} else {
this.socket.send(args);
}
});
if (args instanceof Array) {
this.socket.send(...args);
} else {
this.socket.send(args);
}
}

/**
* @param args
*/
emit(event: string, args: any | any[]): void {
// wait until socket open and send the text only once per call
this.opened$
.pipe(
take(1),
filter((opened) => opened),
)
.subscribe(() => {
if (args instanceof Array) {
this.socket.emit(event, ...args);
} else {
this.socket.emit(event, args);
}
});
if (args instanceof Array) {
this.socket.emit(event, ...args);
} else {
this.socket.emit(event, args);
}
}

close(): void {
Expand Down
5 changes: 5 additions & 0 deletions modules/types/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# ignore the .ts files
*.ts

# include the .d.ts files
!*.d.ts
2 changes: 1 addition & 1 deletion modules/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface ConfigContainer {
export interface ConfigIntelServer {
port: number;
username: string;
password: number;
password: string;
}

export interface ConfigExchange {
Expand Down
6 changes: 2 additions & 4 deletions modules/types/package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
{
"name": "dripjs-types",
"version": "0.1.9",
"version": "0.1.10",
"description": "dripjs types",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"scripts": {
"build:prod": "yarn build:preprocess && yarn tsc && yarn build:postprocess",
"build:prod": "yarn build:preprocess && yarn tsc",
"build:preprocess": "rm -rf dist",
"build:postprocess": "cp package.json README.md dist",
"pub": "yarn build:prod && npm publish dist",
"tsc": "tsc -p tsconfig.json"
},
"repository": {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@
"config": "^3.0.1",
"dotenv": "^6.2.0",
"dripjs-common": "^0.1.1",
"dripjs-types": "^0.1.9",
"dripjs-types": "^0.1.10",
"lodash": "^4.17.11",
"moment": "^2.24.0",
"mysql": "^2.16.0",
Expand All @@ -156,6 +156,6 @@
"rxjs": "^6.4.0",
"socket.io-client": "^2.2.0",
"typeorm": "0.2.14",
"ws": "^6.1.4"
"ws": "^6.2.0"
}
}
25 changes: 25 additions & 0 deletions projects/intelligence/service/config/default.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
require('dotenv').config();

module.exports = {
env: 'default',
production: false,
container: {
intelService: {
port: 6531,
username: 'test',
password: 'test2',
}
},
exchange: {
crypto: {
bitmex: {
apiKey: process.env.SPEC_BITMEX_REAL_API_KEY,
apiSecret: process.env.SPEC_BITMEX_REAL_API_SECRET,
},
bitmexTestNet: {
apiKey: process.env.SPEC_BITMEX_TEST_API_KEY,
apiSecret: process.env.SPEC_BITMEX_TEST_API_SECRET,
},
}
}
};
34 changes: 22 additions & 12 deletions projects/intelligence/service/src/app.module.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ConfigIntelServer, Symbol } from 'dripjs-types';
import * as io from 'socket.io-client';

import { ApplicationModule } from './app.module';
import { IntelErrorResponse, IntelGetSymbolsResponse } from './intel';
import { IntelErrorResponse, IntelGetSymbolsResponse } from './common';

// tslint:disable-next-line
const config: ConfigIntelServer = require('config').container.intelService;
Expand All @@ -20,24 +20,34 @@ describe('app.module.events', () => {
});

await app.listenAsync(serverPort);
socket = io(`http://localhost:${serverPort}`);
socket = io(`http://localhost:${serverPort}`, {
transportOptions: {
polling: {
extraHeaders: {
username: config.username,
password: config.password,
},
},
},
});
});

afterAll(async () => {
await app.close();
socket.close();
});

describe('getSymbols', () => {
it('simple non-depth ch', async (done) => {
socket.emit('symbols', 'bitmex', (res: IntelGetSymbolsResponse) => {
expect(res.error).toBeUndefined();
expect(res.symbols.length).toBeGreaterThan(0);
});

setTimeout(() => {
done();
}, 5000);
it('getSymbols', async (done) => {
socket.on('exception', (e: any) => {
console.log(JSON.stringify(e));
});
socket.emit('symbols', 'bitmex', (res: IntelGetSymbolsResponse) => {
expect(res.error).toBeUndefined();
expect(res.symbols.length).toBeGreaterThan(0);
});

setTimeout(() => {
done();
}, 1000);
});
});
17 changes: 17 additions & 0 deletions projects/intelligence/service/src/common/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ExchangeCryptoAuthConfig, SupportedExchange } from 'dripjs-types';

import { BitmexSpy, IntelFactory, Spy } from '../../..';

export function findSpy(exchange: string, config: ExchangeCryptoAuthConfig): Spy {
switch (exchange) {
case SupportedExchange.Bitmex: {
return IntelFactory.create(BitmexSpy, { ...config, testnet: false });
}
case SupportedExchange.BitmexTestNet: {
return IntelFactory.create(BitmexSpy, { ...config, testnet: true });
}
default: {
return IntelFactory.create(BitmexSpy, { ...config, testnet: false });
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
import { Symbol } from 'dripjs-types';

import { Spy } from '../../../..';

export interface IntelErrorResponse {
name: string;
message: string;
}

export interface IntelGetSymbolsResponse {
symbols: Symbol[];
error?: IntelErrorResponse;
}

export interface SpyImpl {
spy?: Spy;
error?: IntelErrorResponse;
}
2 changes: 2 additions & 0 deletions projects/intelligence/service/src/exceptions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './intel-exception-filter';
export * from './intel-exception';
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ArgumentsHost } from '@nestjs/common';
import { WsArgumentsHost } from '@nestjs/common/interfaces';

import { IntelServiceException } from './intel-exception';
import { IntelServiceExceptionFilter } from './intel-exception-filter';

describe('IntelServiceExceptionFilter', () => {
let intelServiceExceptionFilter: IntelServiceExceptionFilter;
const host = <ArgumentsHost>{};

beforeAll(() => {
intelServiceExceptionFilter = new IntelServiceExceptionFilter();
host.switchToWs = jest.fn(() => {
return <WsArgumentsHost>{
getClient: () => {
return { emit: (event: string, res: any) => {} };
},
};
});
});

it('catch', () => {
expect(intelServiceExceptionFilter.catch(new IntelServiceException('test'), host)).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common';

import { IntelErrorResponse } from '../common';
import { IntelServiceException } from './intel-exception';

@Catch(IntelServiceException)
export class IntelServiceExceptionFilter implements ExceptionFilter {
catch(exception: IntelServiceException, host: ArgumentsHost): void {
const client = host.switchToWs().getClient();
const errorRespsonse: IntelErrorResponse = {
name: exception.name,
message: exception.message,
};
client.emit('exception', errorRespsonse);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class IntelServiceException extends Error {
constructor(message: string) {
super(message);
this.name = 'IntelServiceException';
}
}
46 changes: 46 additions & 0 deletions projects/intelligence/service/src/guards/auth.guard.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { ExecutionContext } from '@nestjs/common';

import { AuthGuard } from './auth.guard';
import { getSwitchToWs } from './test-helpers';

describe('AuthGuard', () => {
let auth: AuthGuard;
const context: ExecutionContext = <any>{};

beforeAll(() => {
auth = new AuthGuard();
context.switchToWs = getSwitchToWs({});
});

it('canActivate return true', () => {
expect(auth.canActivate(context)).toBeTruthy();
});

describe('Auth info not found', () => {
const ctx: ExecutionContext = <any>{};

it('username is undefined', () => {
ctx.switchToWs = getSwitchToWs({ noUsername: true });
expect(auth.canActivate(ctx)).toBeFalsy();
});

it('password is undefined', () => {
ctx.switchToWs = getSwitchToWs({ noPassword: true });
expect(auth.canActivate(ctx)).toBeFalsy();
});
});

describe('Authentication failed', () => {
const ctx: ExecutionContext = <any>{};

it('username is wrong', () => {
ctx.switchToWs = getSwitchToWs({ username: 'z' });
expect(auth.canActivate(ctx)).toBeFalsy();
});

it('password is wrong', () => {
ctx.switchToWs = getSwitchToWs({ password: 'z' });
expect(auth.canActivate(ctx)).toBeFalsy();
});
});
});
29 changes: 29 additions & 0 deletions projects/intelligence/service/src/guards/auth.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { ConfigIntelServer } from 'dripjs-types';

// tslint:disable-next-line
const config: ConfigIntelServer = require('config').container.intelService;

@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
try {
const client = context.switchToWs().getClient();
const username = client.handshake.headers['username'];
const password = client.handshake.headers['password'];

if (!username || !password) {
throw new Error('Auth info not found.');
}
if (config.username !== username || config.password !== password) {
throw new Error('Authentication failed.');
}
} catch (e) {
// TODO: output log
// console.error(e.message);
return false;
}

return true;
}
}
1 change: 1 addition & 0 deletions projects/intelligence/service/src/guards/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './auth.guard';
Loading

0 comments on commit 8819c51

Please sign in to comment.