Skip to content

Commit

Permalink
Enhance health check code
Browse files Browse the repository at this point in the history
  • Loading branch information
denolfe committed May 4, 2019
1 parent fe850bd commit 7cbc083
Show file tree
Hide file tree
Showing 12 changed files with 49 additions and 44 deletions.
4 changes: 2 additions & 2 deletions posts/cucumber-with-typescript/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictFunctionTypes": true, /* Enable strict checking of function enums. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
Expand All @@ -39,7 +39,7 @@
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "enums": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum ResourceHealth {
'HEALTHY',
'UNHEALTHY'
}
4 changes: 2 additions & 2 deletions posts/standardized-health-checks/health/health-indicator.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ResourceHealth } from '../types/resource-health.type';
import { ResourceHealth } from '../enums/resource-health.enum';

export abstract class HealthIndicator {
abstract name: string;
status: ResourceHealth = 'UNHEALTHY';
status: ResourceHealth = ResourceHealth.UNHEALTHY;
details: string | undefined;

abstract checkHealth(): Promise<void>;
Expand Down
7 changes: 4 additions & 3 deletions posts/standardized-health-checks/health/some-service.check.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import axios, { AxiosResponse } from 'axios';
import { HealthIndicator } from './health-indicator';
import { ResourceHealth } from '../enums/resource-health.enum';

export class SomeServiceCheck extends HealthIndicator {
name: string = 'Some Service';
Expand All @@ -11,13 +12,13 @@ export class SomeServiceCheck extends HealthIndicator {
result = await axios(pingURL);

if (result.status === 200) {
this.status = 'HEALTHY';
this.status = ResourceHealth.HEALTHY;
} else {
this.status = 'UNHEALTHY';
this.status = ResourceHealth.UNHEALTHY;
this.details = `Received status: ${result.status}`;
}
} catch (e) {
this.status = 'UNHEALTHY';
this.status = ResourceHealth.UNHEALTHY;
this.details = e.message;
console.log(`HEALTH: ${this.name} is unhealthy.`, e.message);
}
Expand Down
3 changes: 2 additions & 1 deletion posts/standardized-health-checks/routes/health.routes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SomeServiceCheck } from '../health/some-service.check';
import { HealthService } from '../services/health-service';
import { Router } from 'express';
import { ResourceHealth } from '../enums/resource-health.enum';

const healthRoutes = Router();

Expand All @@ -14,7 +15,7 @@ healthRoutes.get('/health', async (req, res) => {

const healthResults = await healthService.getHealth();

res.status(healthResults.status === 'HEALTHY' ? 200 : 503)
res.status(healthResults.status === ResourceHealth.HEALTHY ? 200 : 503)
.send({
status: healthResults.status, dependencies: healthResults.results
});
Expand Down
20 changes: 9 additions & 11 deletions posts/standardized-health-checks/services/health-service.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
import { HealthIndicator } from '../health/health-indicator';
import { ResourceHealth } from '../types/resource-health.type';
import { ResourceHealth } from '../enums/resource-health.enum';

export class HealthService {
private readonly checks: HealthIndicator[];
public overallHealth: ResourceHealth = 'HEALTHY';
public overallHealth: ResourceHealth = ResourceHealth.HEALTHY;

constructor(checks: HealthIndicator[]) {
this.checks = checks;
}

async getHealth(): Promise<HealthCheckResult> {
await Promise.all(
this.checks.map((check) => {
return check.checkHealth();
}
)
this.checks.map(check => check.checkHealth())
);

const anyUnhealthy = this.checks.some(item =>
item.status === ResourceHealth.UNHEALTHY
);

const anyUnhealthy = this.checks.some((item) => {
return item.status === 'UNHEALTHY';
});
this.overallHealth = anyUnhealthy
? 'UNHEALTHY'
: 'HEALTHY';
? ResourceHealth.UNHEALTHY
: ResourceHealth.HEALTHY;

return {
status: this.overallHealth,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import axios from 'axios';
import { ResourceHealth } from '../enums/resource-health.enum';

// Service MUST be running!
describe('Health Checks - Integration', () => {
it('should return healthy if all downstream dependencies are healthy', async () => {
const result = await axios.get('http://localhost:8080/health');
expect(result.status).toBe(200);
expect(result.data.dependencies[0].status).toEqual('HEALTHY');
expect(result.data.dependencies[0].status).toEqual(ResourceHealth.HEALTHY);
});
});
27 changes: 14 additions & 13 deletions posts/standardized-health-checks/tests/health-check.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { HealthService } from '../services/health-service';
import { MockIndicator } from './mocks/mock-indicator';
import { MockToggleIndicator } from './mocks/mock-toggle-indicator';
import { ResourceHealth } from '../enums/resource-health.enum';

describe('Health Checks', () => {
describe('Health Service', () => {
Expand All @@ -11,33 +12,33 @@ describe('Health Checks', () => {

const result = await service.getHealth();

expect(result.status).toEqual('HEALTHY');
expect(result.results[0].status).toEqual('HEALTHY');
expect(result.status).toEqual(ResourceHealth.HEALTHY);
expect(result.results[0].status).toEqual(ResourceHealth.HEALTHY);
expect(result.results[0].details).not.toBeDefined()
});

it('should evaluate health - unhealthy', async () => {
const service = new HealthService([
new MockIndicator('UNHEALTHY')
new MockIndicator(ResourceHealth.UNHEALTHY)
]);

const result = await service.getHealth();

expect(result.status).toEqual('UNHEALTHY');
expect(result.results[0].status).toEqual('UNHEALTHY');
expect(result.status).toEqual(ResourceHealth.UNHEALTHY);
expect(result.results[0].status).toEqual(ResourceHealth.UNHEALTHY);
});

it('should evaluate unhealthy if mixed health dependencies', async () => {
const service = new HealthService([
new MockIndicator(),
new MockIndicator('UNHEALTHY')
new MockIndicator(ResourceHealth.UNHEALTHY)
]);

const result = await service.getHealth();

expect(result.status).toEqual('UNHEALTHY');
expect(result.results.filter((result) => result.status === 'HEALTHY').length).toBe(1);
expect(result.results.filter((result) => result.status === 'UNHEALTHY').length).toBe(1);
expect(result.status).toEqual(ResourceHealth.UNHEALTHY);
expect(result.results.filter((result) => result.status === ResourceHealth.HEALTHY).length).toBe(1);
expect(result.results.filter((result) => result.status === ResourceHealth.UNHEALTHY).length).toBe(1);
});

it('should be able to return to healthy after being unhealthy', async () => {
Expand All @@ -46,20 +47,20 @@ describe('Health Checks', () => {
]);

let result = await service.getHealth();
expect(result.status).toEqual('UNHEALTHY');
expect(result.status).toEqual(ResourceHealth.UNHEALTHY);

result = await service.getHealth();
expect(result.status).toEqual('HEALTHY');
expect(result.status).toEqual(ResourceHealth.HEALTHY);
});

it('should return details when unhealthy', async () => {
const unhealthyDetails = 'Unable to communicate to DB';
const service = new HealthService([
new MockIndicator('UNHEALTHY', unhealthyDetails),
new MockIndicator(ResourceHealth.UNHEALTHY, unhealthyDetails),
]);

let result = await service.getHealth();
expect(result.status).toEqual('UNHEALTHY');
expect(result.status).toEqual(ResourceHealth.UNHEALTHY);
expect(result.results[0].details).toEqual(unhealthyDetails);
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { HealthIndicator } from '../../health/health-indicator';
import { ResourceHealth } from '../../types/resource-health.type';
import { ResourceHealth } from '../../enums/resource-health.enum';

export class MockIndicator extends HealthIndicator {
name = 'Mock Indicator';

constructor(health?: ResourceHealth, details?: string) {
super();
this.status = health || 'HEALTHY';
this.status = health || ResourceHealth.HEALTHY;
if (details) {
this.details = details;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { HealthIndicator } from '../../health/health-indicator';
import { ResourceHealth } from '../../types/resource-health.type';
import { ResourceHealth } from '../../enums/resource-health.enum';

export class MockToggleIndicator extends HealthIndicator {
name = 'Mock Toggle Indicator';

constructor(health?: ResourceHealth) {
super();
this.status = health || 'HEALTHY';
this.status = health || ResourceHealth.HEALTHY;
}

checkHealth(): Promise<void> {
this.status = this.status === 'HEALTHY'
? 'UNHEALTHY'
: 'HEALTHY';
this.status = this.status === ResourceHealth.HEALTHY
? ResourceHealth.UNHEALTHY
: ResourceHealth.HEALTHY;

return new Promise((resolve) => {
resolve();
});
}
}
}
4 changes: 2 additions & 2 deletions posts/standardized-health-checks/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"strictNullChecks": true , /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictFunctionTypes": true, /* Enable strict checking of function enums. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
Expand All @@ -39,7 +39,7 @@
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "enums": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
Expand Down

This file was deleted.

0 comments on commit 7cbc083

Please sign in to comment.