Socket
Socket
Sign inDemoInstall

koa-actuator

Package Overview
Dependencies
2
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.1.0 to 0.2.0

49

index.js

@@ -5,3 +5,4 @@ const compose = require('koa-compose');

const appRootPath = require('app-root-path');
const package_json = require(appRootPath + path.sep +'package.json');
const package_json = require(appRootPath + path.sep + 'package.json');
const assert = require('assert');

@@ -15,12 +16,38 @@ const HEALTH_PATH = '/health';

/**
* Writes {status: 'UP'} to response body if request path is /health
* Writes health checks results and aggregated status to response body if request path is /health
*/
//TODO: add a callback function
async function health(ctx, next) {
if (HEALTH_PATH == ctx.path)
ctx.body = {status: 'UP'};
else
await next();
function health(checks, options) {
Object.keys(checks).forEach(name => assert(typeof(checks[name]) === 'function', `'${name}' check must be a function`));
const timeout = options.checkTimeout || 5000;
return async function healthMiddleware(ctx, next) {
if (HEALTH_PATH === ctx.path) {
const health = {status: 'UP'};
for (const checkName of Object.keys(checks)) {
const checkResult = await runCheck(checks[checkName], timeout);
health[checkName] = checkResult;
if (checkResult && checkResult.status === 'DOWN') {
health.status = checkResult.status;
}
}
ctx.status = health.status === 'UP' ? 200 : 503;
ctx.body = health;
} else {
await next();
}
}
}
async function runCheck(check, timeout) {
try {
return await Promise.race([
check(),
new Promise((resolve, reject) => setTimeout(() => reject(new Error('Check timed out')), timeout))
]);
} catch (e) {
return {status: 'DOWN', error: e && (e.message || e.toString())};
}
}
/**

@@ -73,3 +100,3 @@ * Exposes application and resources information. E.g. name, version.

heap: memory.heapTotal,
"heap.used": memory.heapUsed,
'heap.used': memory.heapUsed,
resources: {

@@ -88,2 +115,4 @@ memory: memory,

module.exports = compose([health, env, info, metrics]);
module.exports = (healthChecks = {}, options = {}) => {
return compose([health(healthChecks, options), env, info, metrics]);
};
{
"name": "koa-actuator",
"version": "0.1.0",
"version": "0.2.0",
"description": "Healthcheck and monitoring middleware for koa inspired by java spring's actuators",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -16,5 +16,5 @@ # koa-actuator

const app = new Koa();
...
app.use(actuator);
...
//...
app.use(actuator());
//...
app.listen(3000);

@@ -29,5 +29,31 @@ ```

### /health
Performs health checks and returns the results:
```
{"status":"UP"}
{
"status": "UP",
"db": {
"status": "UP",
"freeConnections": 10
},
"redis": {
"status": "UP",
"usedMemory": "52m",
"uptimeDays": 16
}
}
```
The statuses of the health checks are aggregated in a root-level `status` field. If at least one check yields `DOWN` status, the aggregated status will become `DOWN`. Health checks can be defined on actuator construction:
```js
const healthChecks = {
db: async () => {
return {
status: (await isDbAlive()) ? 'UP' : 'DOWN',
freeConnections: await freeDbConnectionsLeft()
}
},
//...
};
app.use(actuator(healthChecks));
```
A check can return an object of an arbitrary structure. If the returned structure contains `status` field, it will be counted in the aggregated status.

@@ -34,0 +60,0 @@ ### /env

const actuator = require('.');
const Koa = require('koa');
const supertest = require('supertest');
const request = require('supertest');
const assert = require('chai').assert;
const app = new Koa();
describe('Koa actuator', () => {
describe('koa-actuator', () => {
let request;
describe('/health', () => {
it('should return 200 and status UP if no checks defined', (done) => {
//arrange
const app = new Koa();
app.use(actuator());
before(() => {
app.use(actuator);
request = supertest.agent(app.listen());
});
//act & assert
request(app.callback())
.get('/health')
.expect(200)
.expect({status: 'UP'}, done);
});
describe('/health', () => {
it('GET should return 200 and status UP', (done) => {
request
it('should return 200 and status UP if sync and async checks pass', (done) => {
//arrange
const app = new Koa();
app.use(actuator({
db: () => Promise.resolve({status: 'UP', freeConnections: 10}),
redis: () => ({status: 'UP', usedMemory: '52m', uptimeDays: 16})
}));
//act & assert
request(app.callback())
.get('/health')
.expect(200)
.expect(/UP/, done);
.expect({
status: 'UP',
db: {status: 'UP', freeConnections: 10},
redis: {status: 'UP', usedMemory: '52m', uptimeDays: 16}
}, done);
});
it('should return 503 and status DOWN if any check fails', (done) => {
//arrange
const app = new Koa();
app.use(actuator({
db: () => Promise.resolve({status: 'UP', freeConnections: 10}),
redis: () => Promise.resolve({status: 'DOWN', usedMemory: '52m', uptimeDays: 16})
}));
//act & assert
request(app.callback())
.get('/health')
.expect(503)
.expect({
status: 'DOWN',
db: {status: 'UP', freeConnections: 10},
redis: {status: 'DOWN', usedMemory: '52m', uptimeDays: 16}
}, done);
});
it('should return 503 and status DOWN if a sync check throws exception', (done) => {
//arrange
const app = new Koa();
app.use(actuator({
db: () => { throw new Error('unexpected error'); },
redis: () => Promise.resolve({status: 'UP', usedMemory: '52m', uptimeDays: 16})
}));
//act & assert
request(app.callback())
.get('/health')
.expect(503)
.expect({
status: 'DOWN',
db: {status: 'DOWN', error: 'unexpected error'},
redis: {status: 'UP', usedMemory: '52m', uptimeDays: 16}
}, done);
});
it('should return 503 and status DOWN if an async check rejects promise', (done) => {
//arrange
const app = new Koa();
app.use(actuator({
db: () => Promise.resolve({status: 'UP', freeConnections: 10}),
redis: () => Promise.reject('unexpected async error')
}));
//act & assert
request(app.callback())
.get('/health')
.expect(503)
.expect({
status: 'DOWN',
db: {status: 'UP', freeConnections: 10},
redis: {status: 'DOWN', error: 'unexpected async error'}
}, done);
});
it('should return 503 and status DOWN if an async check times out', (done) => {
//arrange
const app = new Koa();
const options = {checkTimeout: 100};
app.use(actuator({
db: () => new Promise((resolve, reject) => setTimeout(() => resolve({status: 'UP', freeConnections: 10}), 3000))
}, options));
//act & assert
request(app.callback())
.get('/health')
.expect(503)
.expect({
status: 'DOWN',
db: {status: 'DOWN', error: 'Check timed out'},
}, done);
});
});
describe('/info', () => {
it('GET should return 200 and version', (done) => {
request
it('should return 200 and version', (done) => {
//arrange
const app = new Koa();
app.use(actuator());
//act & assert
request(app.callback())
.get('/info')

@@ -39,8 +135,10 @@ .expect(200)

describe('/env', () => {
it('GET should return 200 and the list of environment variables', (done) => {
it('should return 200 and the list of environment variables', (done) => {
//arrange
process.env.TEST_VAR = 'test';
const app = new Koa();
app.use(actuator());
//act & assert
request
request(app.callback())
.get('/env')

@@ -60,5 +158,7 @@ .expect(200)

process.env.PASSWORD = 'test-password';
const app = new Koa();
app.use(actuator());
//act & assert
request
request(app.callback())
.get('/env')

@@ -78,4 +178,8 @@ .expect(200)

it('should return 200 and show some service info (like uptime, heap usage etc)', (done) => {
//arrange
const app = new Koa();
app.use(actuator());
//act & assert
request
request(app.callback())
.get('/metrics')

@@ -94,2 +198,2 @@ .expect(200)

});
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc