Skip to content

Commit

Permalink
Cria Endpoint /status para lidar com Health Check das dependências do…
Browse files Browse the repository at this point in the history
… TabNews (#130)

* feat: create endpoint that check health for dependencies

* refactor: do a simple implementation of the health check, remove unnecessary files
  • Loading branch information
liverday authored Oct 18, 2021
1 parent c6fb447 commit 5cb9703
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 0 deletions.
57 changes: 57 additions & 0 deletions model/health.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import database from 'infra/database';

export default function Health() {
async function getDependencies() {
const dependenciesHandlersToCheck = [
{
name: 'database',
handler: checkDatabaseDependency
}
]

const promises = dependenciesHandlersToCheck.map(async ({ name, handler }) => {
const dependencyResult = await handler()

return {
name,
result: dependencyResult
}
})

const checkedDependencies = await Promise.all(promises);

// group dependencies by name
return checkedDependencies.reduce((accumulator, currentDependency) => {
accumulator[currentDependency.name] = currentDependency.result

return accumulator
}, { })
}

async function checkDatabaseDependency() {
let result;
try {
const openConnectionsResult = await database.query(
'SELECT sum(numbackends) as opened_connections FROM pg_stat_database'
);
const { opened_connections } = openConnectionsResult.rows[0];

result = {
status: 'healthy',
opened_connections: parseInt(opened_connections)
}
} catch (err) {
console.error('database dependency might be down: ', err);

result = {
status: 'unhealthy'
}
}

return result;
}

return {
getDependencies
}
}
42 changes: 42 additions & 0 deletions pages/api/v1/status/index.public.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import nextConnect from 'next-connect';
import { formatISO } from 'date-fns';
import { v4 as uuid } from 'uuid';
import { NotFoundError, InternalServerError } from 'errors';

import healthFactory from 'model/health';

const health = healthFactory()

export default nextConnect({
attachParams: true,
onNoMatch: onNoMatchHandler,
onError: onErrorHandler,
})
.use(injectRequestId)
.get(getHandler);

async function injectRequestId(request, response, next) {
request.id = uuid();
next();
}

async function getHandler(request, response) {
const checkedDependencies = await health.getDependencies();

return response.status(200).json({
updated_at: formatISO(Date.now()),
dependencies: checkedDependencies,
});
}

async function onNoMatchHandler(request, response) {
const errorObject = new NotFoundError({ requestId: request.id });
console.log(errorObject);
return response.status(errorObject.statusCode).json(errorObject);
}

function onErrorHandler(error, request, response) {
const errorObject = new InternalServerError({ requestId: request.id, stack: error.stack });
console.error(errorObject);
return response.status(errorObject.statusCode).json(errorObject);
}
39 changes: 39 additions & 0 deletions pages/api/v1/status/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import fetch from 'cross-fetch';
import { version as uuidVersion } from 'uuid';
import { validate as uuidValidate } from 'uuid';
import orchestratorFactory from 'tests/orchestrator.js';

const orchestrator = orchestratorFactory();

beforeAll(async () => {
await orchestrator.waitForAllServices();
await orchestrator.dropAllTables();
});

describe('[e2e] do a GET request to /api/v1/status', () => {
test('should be able to execute all health-indicators', async () => {
const serverStatusResponse = await fetch(`${orchestrator.webserverUrl}/api/v1/status`);
const serverStatusBody = await serverStatusResponse.json();

expect(serverStatusResponse.status).toEqual(200);
expect(serverStatusBody.updated_at).toBeDefined();
expect(serverStatusBody.dependencies.database).toEqual(expect.objectContaining({ status: 'healthy', opened_connections: 1 }));
});
});

describe('[e2e] do a PUT request to /api/v1/status', () => {
test('should return a 404 response', async () => {
const putStatusResponse = await fetch(`${orchestrator.webserverUrl}/api/v1/status`, {
method: 'put',
});
const putStatusBody = await putStatusResponse.json();

expect(putStatusResponse.status).toEqual(404);
expect(putStatusBody.statusCode).toEqual(404);
expect(putStatusBody.stack).toBeUndefined();
expect(uuidVersion(putStatusBody.errorId)).toEqual(4);
expect(uuidValidate(putStatusBody.errorId)).toEqual(true);
expect(uuidVersion(putStatusBody.requestId)).toEqual(4);
expect(uuidValidate(putStatusBody.requestId)).toEqual(true);
});
});

0 comments on commit 5cb9703

Please sign in to comment.