-
-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(@nestjs/terminus): Add microservice health indicator
- Loading branch information
1 parent
5ea8171
commit 7cc931a
Showing
18 changed files
with
11,530 additions
and
3,561 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { INestApplication } from '@nestjs/common'; | ||
|
||
import Axios from 'axios'; | ||
import { TerminusModuleOptions, MicroserviceHealthIndicator } from '../../lib'; | ||
import { bootstrapModule } from '../helper/bootstrap-module'; | ||
import { Transport } from '@nestjs/microservices'; | ||
|
||
describe('Microservice Health', () => { | ||
let app: INestApplication; | ||
let port: number; | ||
|
||
const getTerminusOptions = ( | ||
microservice: MicroserviceHealthIndicator, | ||
): TerminusModuleOptions => { | ||
const tcpCheck = async () => | ||
microservice.pingCheck('tcp', { | ||
transport: Transport.TCP, | ||
options: { | ||
host: '0.0.0.0', | ||
port: 8890, | ||
}, | ||
}); | ||
|
||
return { | ||
endpoints: [ | ||
{ | ||
url: '/health', | ||
healthIndicators: [tcpCheck], | ||
}, | ||
], | ||
}; | ||
}; | ||
|
||
it('should check if the microservice is available', async () => { | ||
[app, port] = await bootstrapModule( | ||
{ | ||
inject: [MicroserviceHealthIndicator], | ||
useFactory: getTerminusOptions, | ||
}, | ||
false, | ||
false, | ||
false, | ||
8890, | ||
); | ||
|
||
const response = await Axios.get(`http://0.0.0.0:${port}/health`); | ||
expect(response.status).toBe(200); | ||
expect(response.data).toEqual({ | ||
status: 'ok', | ||
info: { tcp: { status: 'up' } }, | ||
}); | ||
}); | ||
|
||
it('should throw an error if runs into timeout error', async () => { | ||
[app, port] = await bootstrapModule( | ||
{ | ||
inject: [MicroserviceHealthIndicator], | ||
useFactory: ( | ||
microservice: MicroserviceHealthIndicator, | ||
): TerminusModuleOptions => ({ | ||
endpoints: [ | ||
{ | ||
url: '/health', | ||
healthIndicators: [ | ||
async () => | ||
microservice.pingCheck('tcp', { | ||
timeout: 1, | ||
transport: Transport.TCP, | ||
options: { | ||
host: '0.0.0.0', | ||
port: 8889, | ||
}, | ||
}), | ||
], | ||
}, | ||
], | ||
}), | ||
}, | ||
false, | ||
false, | ||
false, | ||
8889, | ||
); | ||
|
||
try { | ||
await Axios.get(`http://0.0.0.0:${port}/health`, {}); | ||
} catch (error) { | ||
expect(error.response.status).toBe(503); | ||
expect(error.response.data).toEqual({ | ||
status: 'error', | ||
error: { | ||
tcp: { | ||
status: 'down', | ||
message: expect.any(String), | ||
}, | ||
}, | ||
}); | ||
} | ||
}); | ||
|
||
afterEach(async () => await app.close()); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export * from './typeorm/typeorm.health'; | ||
export * from './dns/dns.health'; | ||
export * from './mongoose/mongoose.health'; | ||
export * from './microservice/microservice.health'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { HealthIndicatorResult } from '../../interfaces'; | ||
import { HealthIndicator } from '../abstract/health-indicator'; | ||
import { HealthCheckError } from '@godaddy/terminus'; | ||
import { ClientProxyFactory, ClientOptions } from '@nestjs/microservices'; | ||
import { | ||
promiseTimeout, | ||
TimeoutError as PromiseTimeoutError, | ||
} from '../../utils'; | ||
import { TimeoutError } from '../../errors'; | ||
|
||
export type MicroserviceHealthIndicatorOptions = ClientOptions & { | ||
timeout?: number; | ||
}; | ||
|
||
/** | ||
* The MicroserviceHealthIndicator is a health indicators | ||
* which is used for health checks related to microservices | ||
*/ | ||
@Injectable() | ||
export class MicroserviceHealthIndicator extends HealthIndicator { | ||
/** | ||
* Initializes the health indicator | ||
*/ | ||
constructor() { | ||
super(); | ||
} | ||
|
||
private async pingMicroservice( | ||
options: MicroserviceHealthIndicatorOptions, | ||
): Promise<any> { | ||
const client = ClientProxyFactory.create(options); | ||
return await client.connect(); | ||
} | ||
|
||
/** | ||
* Prepares and throw a HealthCheckError | ||
* @param key The key which will be used for the result object | ||
* @param error The thrown error | ||
* @param timeout The timeout in ms | ||
* | ||
* @throws {HealthCheckError} | ||
*/ | ||
private generateError(key: string, error: Error, timeout: number) { | ||
if (!error) { | ||
return; | ||
} | ||
if (error instanceof PromiseTimeoutError) { | ||
throw new TimeoutError( | ||
timeout, | ||
this.getStatus(key, false, { | ||
message: `timeout of ${timeout}ms exceeded`, | ||
}), | ||
); | ||
} | ||
throw new HealthCheckError( | ||
error.message, | ||
this.getStatus(key, false, { | ||
message: error.message, | ||
}), | ||
); | ||
} | ||
|
||
/** | ||
* Checks if the given microservice is up | ||
* @param key The key which will be used for the result object | ||
* @param options The options of the microservice | ||
* | ||
* @throws {HealthCheckError} If the microservice is not reachable | ||
* | ||
* @example | ||
* ```TypeScript | ||
* microservice.pingCheck('tcp', { | ||
* transport: Transport.TCP, | ||
* options: { host: 'localhost', port: 3001 }, | ||
* }) | ||
* ``` | ||
*/ | ||
async pingCheck( | ||
key: string, | ||
options: MicroserviceHealthIndicatorOptions, | ||
): Promise<HealthIndicatorResult> { | ||
let isHealthy = false; | ||
const timeout = options.timeout || 1000; | ||
|
||
try { | ||
await promiseTimeout(timeout, this.pingMicroservice(options)); | ||
isHealthy = true; | ||
} catch (err) { | ||
this.generateError(key, err, timeout); | ||
} | ||
|
||
return this.getStatus(key, isHealthy); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.