Skip to content

Commit 47f9357

Browse files
committed
Update decorator types to be correct
- The code currently allows any value to be added as a decorator, but the types do not. This work allows any value to be added to a decorator by updating the types to reflect what the code doeis - Add relevant tests - Update inline docs - Create and export DecorationValue type - Fixes #4524
1 parent 22377ee commit 47f9357

File tree

3 files changed

+97
-10
lines changed

3 files changed

+97
-10
lines changed

lib/types/plugin.d.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,12 @@ export interface HandlerDecorationMethod {
248248
}
249249

250250
/**
251-
* The general case for decorators added via server.decorate.
251+
* The general case for decorator values added via server.decorate.
252+
*/
253+
export type DecorationValue<T> = DecorationMethod<T> | any;
254+
255+
/**
256+
* Decorator function.
252257
*/
253258
export type DecorationMethod<T> = (this: T, ...args: any[]) => any;
254259

lib/types/server/server.d.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
ServerRegisterPluginObject,
1414
ServerRegisterPluginObjectArray,
1515
DecorateName,
16-
DecorationMethod,
16+
DecorationValue,
1717
HandlerDecorationMethod,
1818
PluginProperties
1919
} from '../plugin';
@@ -310,13 +310,13 @@ export class Server<A = ServerApplicationState> {
310310
* [See docs](https://github.com/hapijs/hapi/blob/master/API.md#-serverdecoratetype-property-method-options)
311311
*/
312312
decorate(type: 'handler', property: DecorateName, method: HandlerDecorationMethod, options?: {apply?: boolean | undefined, extend?: boolean | undefined}): void;
313-
decorate(type: 'request', property: DecorateName, method: (existing: ((...args: any[]) => any)) => (request: Request) => DecorationMethod<Request>, options: {apply: true, extend: true}): void;
314-
decorate(type: 'request', property: DecorateName, method: (request: Request) => DecorationMethod<Request>, options: {apply: true, extend?: boolean | undefined}): void;
315-
decorate(type: 'request', property: DecorateName, method: DecorationMethod<Request>, options?: {apply?: boolean | undefined, extend?: boolean | undefined}): void;
316-
decorate(type: 'toolkit', property: DecorateName, method: (existing: ((...args: any[]) => any)) => DecorationMethod<ResponseToolkit>, options: {apply?: boolean | undefined, extend: true}): void;
317-
decorate(type: 'toolkit', property: DecorateName, method: DecorationMethod<ResponseToolkit>, options?: {apply?: boolean | undefined, extend?: boolean | undefined}): void;
318-
decorate(type: 'server', property: DecorateName, method: (existing: ((...args: any[]) => any)) => DecorationMethod<Server>, options: {apply?: boolean | undefined, extend: true}): void;
319-
decorate(type: 'server', property: DecorateName, method: DecorationMethod<Server>, options?: {apply?: boolean | undefined, extend?: boolean | undefined}): void;
313+
decorate(type: 'request', property: DecorateName, method: (existing: ((...args: any[]) => any)) => (request: Request) => DecorationValue<Request>, options: {apply: true, extend: true}): void;
314+
decorate(type: 'request', property: DecorateName, method: (request: Request) => DecorationValue<Request>, options: {apply: true, extend?: boolean | undefined}): void;
315+
decorate(type: 'request', property: DecorateName, method: DecorationValue<Request>, options?: {apply?: boolean | undefined, extend?: boolean | undefined}): void;
316+
decorate(type: 'toolkit', property: DecorateName, method: (existing: ((...args: any[]) => any)) => DecorationValue<ResponseToolkit>, options: {apply?: boolean | undefined, extend: true}): void;
317+
decorate(type: 'toolkit', property: DecorateName, method: DecorationValue<ResponseToolkit>, options?: {apply?: boolean | undefined, extend?: boolean | undefined}): void;
318+
decorate(type: 'server', property: DecorateName, method: (existing: ((...args: any[]) => any)) => DecorationValue<Server>, options: {apply?: boolean | undefined, extend: true}): void;
319+
decorate(type: 'server', property: DecorateName, method: DecorationValue<Server>, options?: {apply?: boolean | undefined, extend?: boolean | undefined}): void;
320320

321321
/**
322322
* Used within a plugin to declare a required dependency on other plugins where:

test/server.js

+83-1
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ describe('Server', () => {
294294

295295
describe('decorate()', () => {
296296

297-
it('decorates request', async () => {
297+
it('decorates request with function', async () => {
298298

299299
const server = Hapi.server();
300300

@@ -316,6 +316,25 @@ describe('Server', () => {
316316
expect(res.result).to.match(/^.*\:.*\:.*\:.*\:.*$/);
317317
});
318318

319+
it('decorates request with object', async () => {
320+
321+
const server = Hapi.server();
322+
323+
const customData = { id: '123' };
324+
325+
server.decorate('request', 'customData', customData);
326+
327+
server.route({
328+
method: 'GET',
329+
path: '/',
330+
handler: (request) => request.customData
331+
});
332+
333+
const res = await server.inject('/');
334+
expect(res.statusCode).to.equal(200);
335+
expect(res.result).to.equal({ id: '123' });
336+
});
337+
319338
it('decorates request (apply)', async () => {
320339

321340
const server = Hapi.server();
@@ -366,6 +385,26 @@ describe('Server', () => {
366385
expect(res.result).to.match(/^.*\:.*\:.*\:.*\:.*!$/);
367386
});
368387

388+
it('decorates request (extend) with an array', async () => {
389+
390+
const server = Hapi.server();
391+
392+
const items = ['one', 'two', 'three'];
393+
394+
server.decorate('request', 'items', items);
395+
server.decorate('request', 'items', (existing) => [...existing, 'four'], { extend: true });
396+
397+
server.route({
398+
method: 'GET',
399+
path: '/',
400+
handler: (request) => request.items
401+
});
402+
403+
const res = await server.inject('/');
404+
expect(res.statusCode).to.equal(200);
405+
expect(res.result).to.equal([...items, 'four']);
406+
});
407+
369408
it('decorates request (apply + extend)', async () => {
370409

371410
const server = Hapi.server();
@@ -444,6 +483,25 @@ describe('Server', () => {
444483
expect(res.result.status).to.equal('ok');
445484
});
446485

486+
it('decorates toolkit with boolean', async () => {
487+
488+
const server = Hapi.server();
489+
490+
const isOk = true;
491+
492+
server.decorate('toolkit', 'isOk', isOk);
493+
494+
server.route({
495+
method: 'GET',
496+
path: '/',
497+
handler: (request, h) => h.isOk
498+
});
499+
500+
const res = await server.inject('/');
501+
expect(res.statusCode).to.equal(200);
502+
expect(res.result).to.equal(true);
503+
});
504+
447505
it('add new handler', async () => {
448506

449507
const test = {
@@ -559,6 +617,30 @@ describe('Server', () => {
559617
expect(res.result).to.equal('ok');
560618
});
561619

620+
it('decorates server with Map', async () => {
621+
622+
const server = Hapi.server();
623+
624+
const plants = new Map();
625+
plants.set('mango', 'Mango');
626+
plants.set('banana', 'Banana');
627+
plants.set('apple', 'Apple');
628+
629+
server.decorate('server', 'plants', plants);
630+
631+
server.route({
632+
method: 'GET',
633+
path: '/',
634+
handler: (request) => request.server.plants
635+
});
636+
637+
const res = await server.inject('/');
638+
expect(res.statusCode).to.equal(200);
639+
expect(res.result.get('mango')).to.equal('Mango');
640+
expect(res.result.get('banana')).to.equal('Banana');
641+
expect(res.result.get('apple')).to.equal('Apple');
642+
});
643+
562644
it('throws on double server decoration', () => {
563645

564646
const server = Hapi.server();

0 commit comments

Comments
 (0)