Skip to content

Commit

Permalink
feat(bootstrap): Add useGlobalPrefix option (#261)
Browse files Browse the repository at this point in the history
feat(bootstrap): Add `useGlobalPrefix` option
  • Loading branch information
BrunnerLivio authored Jul 1, 2019
2 parents 16df953 + a5f0d2e commit 69fb664
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 119 deletions.
13 changes: 13 additions & 0 deletions lib/interfaces/terminus-module-options.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ export interface TerminusEndpoint {
* The health checks which should get executed.
*/
healthIndicators: HealthIndicatorFunction[];
/**
* Prepends the global prefix to the health url
*
* @see [faq/global-prefix](Global Prefix)
*/
useGlobalPrefix?: boolean;
}

/**
Expand All @@ -36,6 +42,13 @@ export interface TerminusModuleOptions {
endpoints: TerminusEndpoint[];

logger?: TerminusLogger;

/**
* Prepends the global prefix to the health url
*
* @see [faq/global-prefix](Global Prefix)
*/
useGlobalPrefix?: boolean;
}

/**
Expand Down
128 changes: 103 additions & 25 deletions lib/terminus-bootstrap.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { TerminusBootstrapService } from './terminus-bootstrap.service';
import { HttpAdapterHost } from '@nestjs/core';
import { HttpAdapterHost, ApplicationConfig } from '@nestjs/core';
import { TerminusEndpoint, TerminusModuleOptions } from './interfaces';
import { HealthCheckError } from '@godaddy/terminus';
import { HealthCheckError, Terminus } from '@godaddy/terminus';
import { Test, TestingModule, TestingModuleBuilder } from '@nestjs/testing';
import { TERMINUS_MODULE_OPTIONS, TERMINUS_LIB } from './terminus.constants';

const httpServer = jest.fn();

Expand All @@ -11,7 +13,11 @@ const refhostMock = {
},
};

const terminus = jest.fn();
const applicationConfigMock = {
getGlobalPrefix: jest.fn().mockImplementation(() => '/health'),
};

const terminusMock = jest.fn();

const upHealthIndicator = jest
.fn()
Expand All @@ -34,14 +40,45 @@ const endpoints: TerminusEndpoint[] = [
const options: TerminusModuleOptions = { endpoints };

describe('TerminusBootstrapService', () => {
let bootstrapService: TerminusBootstrapService;
let terminus: Terminus;
let module: TestingModuleBuilder;
let context: TestingModule;
// let applicationConfig: ApplicationConfig;
// let httpAdapterHost: HttpAdapterHost;

beforeEach(async () => {
module = Test.createTestingModule({
providers: [
TerminusBootstrapService,
{
provide: TERMINUS_MODULE_OPTIONS,
useValue: options,
},
{
provide: TERMINUS_LIB,
useValue: terminusMock,
},
{
provide: HttpAdapterHost,
useValue: refhostMock,
},
{
provide: ApplicationConfig,
useValue: applicationConfigMock,
},
],
});

context = await module.compile();

bootstrapService = context.get(TerminusBootstrapService);
terminus = context.get(TERMINUS_LIB);
// applicationConfig = module.get(ApplicationConfig);
// httpAdapterHost = module.get(HttpAdapterHost);
});
describe('onApplicationBootstrap', () => {
it('should call the terminus correctly on application bootstrap', () => {
const bootstrapService = new TerminusBootstrapService(
options,
terminus,
refhostMock as HttpAdapterHost<any>,
);

expect(terminus).not.toHaveBeenCalled();

bootstrapService.onApplicationBootstrap();
Expand All @@ -55,14 +92,18 @@ describe('TerminusBootstrapService', () => {
});
});

it('should use the custom logger', () => {
it('should use the custom logger', async () => {
const logger = jest.fn();

const bootstrapService = new TerminusBootstrapService(
{ ...options, logger },
terminus,
refhostMock as HttpAdapterHost<any>,
);
const options = context.get(TERMINUS_MODULE_OPTIONS);

context = await module
.overrideProvider(TERMINUS_MODULE_OPTIONS)
.useValue({ logger, ...options })
.compile();

bootstrapService = context.get(TerminusBootstrapService);
terminus = context.get(TERMINUS_LIB);

bootstrapService.onApplicationBootstrap();

Expand All @@ -73,16 +114,6 @@ describe('TerminusBootstrapService', () => {
});
});
describe('prepareHealthChecksMap', () => {
let bootstrapService: TerminusBootstrapService;

beforeEach(() => {
bootstrapService = new TerminusBootstrapService(
options,
terminus,
refhostMock as HttpAdapterHost<any>,
);
});

it('should prepare a correct map', () => {
const map = bootstrapService.getHealthChecksMap();
expect(map['/up']).not.toBe(undefined);
Expand All @@ -106,5 +137,52 @@ describe('TerminusBootstrapService', () => {
done();
});
});

it('should prepend the global prefix when using useGlobalPrefix on TerminusOptions', async () => {
const options = context.get(TERMINUS_MODULE_OPTIONS);

context = await module
.overrideProvider(TERMINUS_MODULE_OPTIONS)
.useValue({ ...options, useGlobalPrefix: true })
.compile();

bootstrapService = context.get(TerminusBootstrapService);

const map = bootstrapService.getHealthChecksMap();
expect(map['/health/down']).toBeDefined();
expect(map['/health/up']).toBeDefined();
expect(map['/up']).toBeUndefined();
expect(map['/down']).toBeUndefined();
});

it('should prepend the global prefix when using useGlobalPrefix on TerminusEndpoint', async () => {
const options: TerminusModuleOptions = {
useGlobalPrefix: false,
endpoints: [
{
useGlobalPrefix: true,
healthIndicators: [],
url: '/up',
},
{
useGlobalPrefix: false,
healthIndicators: [],
url: 'down',
},
],
};
context = await module
.overrideProvider(TERMINUS_MODULE_OPTIONS)
.useValue(options)
.compile();

bootstrapService = context.get(TerminusBootstrapService);

const map = bootstrapService.getHealthChecksMap();
expect(map['/health/up']).toBeDefined();
expect(map['/down']).toBeDefined();
expect(map['/up']).toBeUndefined();
expect(map['down']).toBeUndefined();
});
});
});
32 changes: 24 additions & 8 deletions lib/terminus-bootstrap.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import {
HealthIndicatorFunction,
TerminusEndpoint,
} from './interfaces';
import { HttpAdapterHost } from '@nestjs/core';
import { HttpAdapterHost, ApplicationConfig } from '@nestjs/core';
import { Server } from 'http';
import { HealthCheckError, Terminus, HealthCheckMap } from '@godaddy/terminus';
import { validatePath } from '@nestjs/common/utils/shared.utils';

/**
* Bootstraps the third party Terminus library with the
Expand All @@ -29,18 +30,13 @@ export class TerminusBootstrapService implements OnApplicationBootstrap {
*/
private readonly logger = new Logger(TerminusBootstrapService.name, true);

/**
* Initializes the service
* @param options The terminus module options
* @param refHost The application reference host of NestJS which contains the http server instance
* @param terminus The terminus instance
*/
constructor(
@Inject(TERMINUS_MODULE_OPTIONS)
private readonly options: TerminusModuleOptions,
@Inject(TERMINUS_LIB)
private readonly terminus: Terminus,
private readonly refHost: HttpAdapterHost<any>,
private readonly applicationConfig: ApplicationConfig,
) {}

/**
Expand Down Expand Up @@ -123,14 +119,34 @@ export class TerminusBootstrapService implements OnApplicationBootstrap {
};
}

private validateEndpointUrl(endpoint: TerminusEndpoint): string {
const prefix = this.applicationConfig.getGlobalPrefix();

const shouldUseGlobalPrefix =
prefix &&
(endpoint.useGlobalPrefix
? endpoint.useGlobalPrefix
: this.options.useGlobalPrefix &&
endpoint.useGlobalPrefix === undefined);

let url = validatePath(endpoint.url);

if (shouldUseGlobalPrefix) {
url = validatePath(prefix) + url;
}

return url;
}

/**
* Returns the health check map using the configured health
* indicators
*/
public getHealthChecksMap(): HealthCheckMap {
return this.options.endpoints.reduce(
(healthChecks, endpoint) => {
healthChecks[endpoint.url] = this.getHealthCheckExecutor(endpoint);
const url = this.validateEndpointUrl(endpoint);
healthChecks[url] = this.getHealthCheckExecutor(endpoint);
return healthChecks;
},
{} as HealthCheckMap,
Expand Down
Loading

0 comments on commit 69fb664

Please sign in to comment.