Skip to content

Commit

Permalink
feat(nats): implements NatsParser and test cases (#47)
Browse files Browse the repository at this point in the history
Similarly to the MqttParser, to get the IP address out of the NATS request, it has to be sent over
with the payload. Otherwise, even more straightforward parser than the Mqtt one as Nats has a
function to get the subject, because it accetps wildcards.

fix #19
  • Loading branch information
jmcdo29 authored May 17, 2020
1 parent b83b65c commit b9136f8
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 9 deletions.
6 changes: 5 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@ services:
# rabbitmq:
# kafka:
# grpc:
# nats:
nats:
image: nats
container_name: nats
ports:
- "4222:4222"
# redis:
4 changes: 3 additions & 1 deletion integration/test/rpc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Type,
} from '@ogma/nestjs-module';
import { MqttParser } from '@ogma/platform-mqtt';
import { NatsParser } from '@ogma/platform-nats';
import { TcpParser } from '@ogma/platform-tcp';
import { RpcClientModule } from '../src/rpc/client/rpc-client.module';
import { RpcServerModule } from '../src/rpc/server/rpc-server.module';
Expand All @@ -22,7 +23,8 @@ import {
describe.each`
server | transport | options | protocol | parser
${'TCP'} | ${Transport.TCP} | ${{}} | ${'IPv4'} | ${TcpParser}
${'MQTT'} | ${Transport.MQTT} | ${{ url: 'mqtt://127.0.0.1:1883' }} | ${'mqtt'} | ${MqttParser}
${'MQTT'} | ${Transport.MQTT} | ${{ url: 'mqtt://localhost:1883' }} | ${'mqtt'} | ${MqttParser}
${'NATS'} | ${Transport.NATS} | ${{ url: 'nats://localhost:4222' }} | ${'nats'} | ${NatsParser}
`(
'$server server',
({
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"lint-staged": "^10.0.8",
"morgan": "^1.10.0",
"mqtt": "^4.0.1",
"nats": "^1.4.9",
"pino": "^6.2.0",
"prettier": "^2.0.1",
"reflect-metadata": "^0.1.13",
Expand Down
3 changes: 2 additions & 1 deletion packages/platform-mqtt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
},
"peerDependencies": {
"@ogma/nestjs-module": "^0.1.0",
"@nestjs/microservices": "^7.0.13"
"@nestjs/microservices": "^7.0.0",
"mqtt": "^4.0.0"
}
}
23 changes: 19 additions & 4 deletions packages/platform-nats/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
# `@ogma/platform-nats`

> TODO: description
The `NatsParser` parser for the `OgmaInterceptor`. This plugin class parses NATS request and response object to be able to successfully log the data about the request. For more information, check out [the @ogma/nestjs-module](../nestjs-module/README.md) documentation.

## Installation

Nothing special, standard `npm i @ogma/platform-nats` or `yarn add @ogma/platform-nats`

## Usage

```
const platformNats = require('@ogma/platform-nats');
This plugin is to be used in the `OgmaInterceptorOptions` portion of the `OgmaModule` during `forRoot` or `forRootAsync` registration. It can be used like so:

// TODO: DEMONSTRATE API
```ts
@Module(
OgmaModule.forRoot({
interceptor: {
rpc: NatsParser
}
})
)
export class AppModule {}
```

## Important Notes

Because of how NATS requests are sent and the data available in them, to get the IP address in the request log, an IP property must be sent in the payload. This is the only way to get the IP address. If an IP property is not sent, the interceptor will use an empty string. This is for the same reason as in the [@ogma/platform-mqtt](../platform-mqtt) docs.
10 changes: 8 additions & 2 deletions packages/platform-nats/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@
"url": "git+https://github.com/jmcdo29/ogma.git"
},
"scripts": {
"test": "echo \"No tests to run\""
"prebuild": "rimraf lib",
"build": "tsc -p tsconfig.build.json",
"postbuild": "mv ./lib/src/* ./lib && rmdir lib/src",
"test": "jest",
"test:cov": "jest --coverage"
},
"bugs": {
"url": "https://github.com/jmcdo29/ogma/issues"
Expand All @@ -39,6 +43,8 @@
"rxjs": "^6.5.4"
},
"peerDependencies": {
"@ogma/nestjs-module": "^0.1.0"
"@ogma/nestjs-module": "^0.1.0",
"@nestjs/microservices": "^7.0.0",
"nats": "^1.4.0"
}
}
1 change: 1 addition & 0 deletions packages/platform-nats/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './nats-interceptor.service';
40 changes: 40 additions & 0 deletions packages/platform-nats/src/nats-interceptor.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ExecutionContext, Injectable } from '@nestjs/common';
import { AbstractInterceptorService } from '@ogma/nestjs-module';

@Injectable()
export class NatsParser extends AbstractInterceptorService {
getCallPoint(context: ExecutionContext) {
const client = this.getClient(context);
return client.getSubject();
}

getCallerIp(context: ExecutionContext) {
const data = this.getData(context);
return data.ip || '';
}

getMethod() {
return 'NATS';
}

getProtocol() {
return 'nats';
}

getStatus(
context: ExecutionContext,
inColor: boolean,
error?: Error | ExecutionContext,
): string {
const status = error ? 500 : 200;
return inColor ? this.wrapInColor(status) : status.toString();
}

private getClient(context: ExecutionContext) {
return context.switchToRpc().getContext();
}

private getData(context: ExecutionContext): any {
return context.switchToRpc().getData();
}
}
77 changes: 77 additions & 0 deletions packages/platform-nats/test/nats-interceptor.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { createMock } from '@golevelup/ts-jest';
import { ExecutionContext } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { NatsParser } from '../src';
import { color } from '@ogma/logger';

describe('NatsParser', () => {
let parser: NatsParser;

beforeEach(async () => {
const modRef = await Test.createTestingModule({
providers: [NatsParser],
}).compile();
parser = modRef.get(NatsParser);
});
describe('getCallPoint', () => {
it('should get the subject from the client', () => {
const ctxMock = createMock<ExecutionContext>({
switchToRpc: () => ({
getContext: () => ({
getSubject: () => JSON.stringify({ cmd: 'message' }),
}),
}),
});
expect(parser.getCallPoint(ctxMock)).toBe(
JSON.stringify({ cmd: 'message' }),
);
});
});
describe('getCallerIp', () => {
it('should return an ip from data', () => {
const ctxMock = createMock<ExecutionContext>({
switchToRpc: () => ({
getData: () => ({
ip: '127.0.0.1',
}),
}),
});
expect(parser.getCallerIp(ctxMock)).toBe('127.0.0.1');
});
it('should return a blank string', () => {
const ctxMock = createMock<ExecutionContext>({
switchToRpc: () => ({
getData: () => ({}),
}),
});
expect(parser.getCallerIp(ctxMock)).toBe('');
});
});
describe('getMethod', () => {
it('should return "NATS"', () => {
expect(parser.getMethod()).toBe('NATS');
});
});
describe('getStatus', () => {
it('should return a 200', () => {
expect(parser.getStatus(createMock<ExecutionContext>(), false)).toBe(
'200',
);
});
it('should return a 500', () => {
expect(
parser.getStatus(createMock<ExecutionContext>(), false, new Error()),
).toBe('500');
});
it('should return a 200 in color', () => {
expect(parser.getStatus(createMock<ExecutionContext>(), true)).toBe(
color.green(200),
);
});
});
describe('getProtocol', () => {
it('should return "nats"', () => {
expect(parser.getProtocol()).toBe('nats');
});
});
});
25 changes: 25 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8766,6 +8766,14 @@ nanomatch@^1.2.9:
snapdragon "^0.8.1"
to-regex "^3.0.1"

nats@^1.4.9:
version "1.4.9"
resolved "https://registry.yarnpkg.com/nats/-/nats-1.4.9.tgz#ad86a920bd44110193edc25068c8e91a358f7a81"
integrity sha512-D0qTg2v0jir32TCJfGkxRwOba4/9vqJJmr4GtghR/J9AF6E0y1BdCeXxnD2G+hruSPnKUbF0NzaQVPbSg5fdOg==
dependencies:
nuid "^1.1.4"
ts-nkeys "^1.0.16"

natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
Expand Down Expand Up @@ -9010,6 +9018,11 @@ npmlog@^4.1.2:
gauge "~2.7.3"
set-blocking "~2.0.0"

nuid@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/nuid/-/nuid-1.1.4.tgz#6145710b6cc9ef0df7b94af09c6d750925939097"
integrity sha512-PXiYyHhGfrq8H4g5HyC8enO1lz6SBe5z6x1yx/JG4tmADzDGJVQy3l1sRf3VtEvPsN8dGn9hRFRwDKWL62x0BA==

number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
Expand Down Expand Up @@ -11562,6 +11575,13 @@ ts-morph@^7.0.0:
"@ts-morph/common" "~0.4.0"
code-block-writer "^10.1.0"

ts-nkeys@^1.0.16:
version "1.0.16"
resolved "https://registry.yarnpkg.com/ts-nkeys/-/ts-nkeys-1.0.16.tgz#b0c6e7c4f16f976c7e7ddb6982fc789a2f971248"
integrity sha512-1qrhAlavbm36wtW+7NtKOgxpzl+70NTF8xlz9mEhiA5zHMlMxjj3sEVKWm3pGZhHXE0Q3ykjrj+OSRVaYw+Dqg==
dependencies:
tweetnacl "^1.0.3"

ts-node@^8.8.1:
version "8.8.1"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.8.1.tgz#7c4d3e9ed33aa703b64b28d7f9d194768be5064d"
Expand Down Expand Up @@ -11621,6 +11641,11 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=

tweetnacl@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==

type-check@~0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
Expand Down

0 comments on commit b9136f8

Please sign in to comment.