-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
773fe3d
commit 2ca2495
Showing
10 changed files
with
237 additions
and
13 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
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,114 @@ | ||
import 'reflect-metadata'; | ||
|
||
import type { Request, Response } from 'express'; | ||
import HealthReadinessRequest from '../features/health-readiness/health-readiness-request'; | ||
|
||
import type Mediator from '../infrastructure/mediator'; | ||
import HealthController from './health-controller'; | ||
|
||
describe('HealthController', () => { | ||
describe('liveness', () => { | ||
describe('success', () => { | ||
let mockedResponseStatus: jest.Mock; | ||
|
||
beforeAll(() => { | ||
const controller = new HealthController({} as unknown as Mediator); | ||
|
||
const req = {}; | ||
|
||
mockedResponseStatus = jest.fn(() => ({ json: jest.fn() })); | ||
|
||
const res = { | ||
status: mockedResponseStatus | ||
}; | ||
|
||
controller.liveness( | ||
req as unknown as Request, | ||
res as unknown as Response | ||
); | ||
}); | ||
|
||
it('returns http status code ok', () => { | ||
expect(mockedResponseStatus).toHaveBeenCalledWith(200); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('readiness', () => { | ||
describe('success', () => { | ||
let mockedResponseStatus: jest.Mock; | ||
let readinessRequest: HealthReadinessRequest; | ||
|
||
beforeAll(async () => { | ||
const mockedMediatorSend: jest.Mock = jest.fn(async () => | ||
Promise.resolve(true) | ||
); | ||
|
||
const mediator = { | ||
send: mockedMediatorSend | ||
}; | ||
|
||
const controller = new HealthController( | ||
mediator as unknown as Mediator | ||
); | ||
|
||
const req = {}; | ||
|
||
mockedResponseStatus = jest.fn(() => ({ json: jest.fn() })); | ||
|
||
const res = { | ||
status: mockedResponseStatus | ||
}; | ||
|
||
await controller.readiness(req as Request, res as unknown as Response); | ||
readinessRequest = mockedMediatorSend.mock.calls[0][0]; | ||
}); | ||
|
||
it('uses mediator', () => { | ||
expect(readinessRequest).toBeInstanceOf(HealthReadinessRequest); | ||
}); | ||
|
||
it('returns http status code ok', () => { | ||
expect(mockedResponseStatus).toHaveBeenCalledWith(200); | ||
}); | ||
}); | ||
|
||
describe('fail', () => { | ||
let mockedResponseStatus: jest.Mock; | ||
let readinessRequest: HealthReadinessRequest; | ||
|
||
beforeAll(async () => { | ||
const mockedMediatorSend: jest.Mock = jest.fn(async () => | ||
Promise.resolve(false) | ||
); | ||
|
||
const mediator = { | ||
send: mockedMediatorSend | ||
}; | ||
|
||
const controller = new HealthController( | ||
mediator as unknown as Mediator | ||
); | ||
|
||
const req = {}; | ||
|
||
mockedResponseStatus = jest.fn(() => ({ json: jest.fn() })); | ||
|
||
const res = { | ||
status: mockedResponseStatus | ||
}; | ||
|
||
await controller.readiness(req as Request, res as unknown as Response); | ||
readinessRequest = mockedMediatorSend.mock.calls[0][0]; | ||
}); | ||
|
||
it('uses mediator', () => { | ||
expect(readinessRequest).toBeInstanceOf(HealthReadinessRequest); | ||
}); | ||
|
||
it('returns http status code service unavailable', () => { | ||
expect(mockedResponseStatus).toHaveBeenCalledWith(503); | ||
}); | ||
}); | ||
}); | ||
}); |
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,43 @@ | ||
import { injectable } from 'tsyringe'; | ||
|
||
import type { Request, Response } from 'express'; | ||
|
||
import Mediator from '../infrastructure/mediator'; | ||
import HealthReadinessRequest from '../features/health-readiness/health-readiness-request'; | ||
|
||
@injectable() | ||
export default class HealthController { | ||
constructor(private readonly _mediator: Mediator) {} | ||
|
||
liveness(_: Request, res: Response): void { | ||
res.status(200).json({ | ||
healthy: true, | ||
uptime: process.uptime(), | ||
timestamp: new Date().toISOString() | ||
}); | ||
} | ||
|
||
async readiness(_: Request, res: Response): Promise<void> { | ||
const request = new HealthReadinessRequest({ | ||
ts: new Date() | ||
}); | ||
|
||
const result = await this._mediator.send<boolean, HealthReadinessRequest>( | ||
request | ||
); | ||
|
||
if (!result) { | ||
res.status(503).json({ | ||
healthy: false, | ||
timestamp: new Date().toISOString() | ||
}); | ||
return; | ||
} | ||
|
||
res.status(200).json({ | ||
healthy: true, | ||
uptime: process.uptime(), | ||
timestamp: new Date().toISOString() | ||
}); | ||
} | ||
} |
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,37 @@ | ||
import { inject, injectable } from 'tsyringe'; | ||
import { Client } from 'pg'; | ||
import { Logger } from 'pino'; | ||
|
||
import handles from '../../infrastructure/handles'; | ||
import Handler from '../../infrastructure/handler'; | ||
import HealthReadinessRequest from './health-readiness-request'; | ||
|
||
@injectable() | ||
@handles(HealthReadinessRequest) | ||
export default class HealthReadinessHandler extends Handler< | ||
HealthReadinessRequest, | ||
boolean | ||
> { | ||
constructor( | ||
@inject('PGClient') private readonly _pg: Client, | ||
@inject('Logger') private readonly _logger: Logger | ||
) { | ||
super(); | ||
} | ||
|
||
async handle(request: HealthReadinessRequest): Promise<boolean> { | ||
try { | ||
await this._pg.connect(); | ||
await this._pg.end(); | ||
|
||
return true; | ||
} catch (e) { | ||
this._logger.error( | ||
{ error: e, timestamp: request.ts }, | ||
// eslint-disable-next-line i18n-text/no-en | ||
'Failed to connect to database' | ||
); | ||
return false; | ||
} | ||
} | ||
} |
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,5 @@ | ||
import Request from '../../infrastructure/request'; | ||
|
||
export default class HealthReadinessRequest extends Request { | ||
ts: Date; | ||
} |
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
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,18 @@ | ||
import type { Request, Response, Router } from 'express'; | ||
import express from 'express'; | ||
|
||
import type HealthController from '../controllers/health-controller'; | ||
|
||
export default function healthRouter(controller: HealthController): Router { | ||
const router = express.Router(); | ||
|
||
router.get('/', (req: Request, res: Response) => | ||
controller.liveness(req, res) | ||
); | ||
|
||
router.get('/readiness', async (req: Request, res: Response) => | ||
controller.readiness(req, res) | ||
); | ||
|
||
return router; | ||
} |