Skip to content

Commit

Permalink
Update decorator types to be correct
Browse files Browse the repository at this point in the history
- 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 does
- Add relevant tests
- Update inline docs
- Create and export DecorationValue type
- Fixes #4524
  • Loading branch information
feedmypixel committed Aug 26, 2024
1 parent 22377ee commit 1d95f23
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 10 deletions.
7 changes: 6 additions & 1 deletion lib/types/plugin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,12 @@ export interface HandlerDecorationMethod {
}

/**
* The general case for decorators added via server.decorate.
* The general case for decorator values added via server.decorate.
*/
export type DecorationValue<T> = DecorationMethod<T> | any;

/**
* Decorator function.
*/
export type DecorationMethod<T> = (this: T, ...args: any[]) => any;

Expand Down
16 changes: 8 additions & 8 deletions lib/types/server/server.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
ServerRegisterPluginObject,
ServerRegisterPluginObjectArray,
DecorateName,
DecorationMethod,
DecorationValue,
HandlerDecorationMethod,
PluginProperties
} from '../plugin';
Expand Down Expand Up @@ -310,13 +310,13 @@ export class Server<A = ServerApplicationState> {
* [See docs](https://github.com/hapijs/hapi/blob/master/API.md#-serverdecoratetype-property-method-options)
*/
decorate(type: 'handler', property: DecorateName, method: HandlerDecorationMethod, options?: {apply?: boolean | undefined, extend?: boolean | undefined}): void;
decorate(type: 'request', property: DecorateName, method: (existing: ((...args: any[]) => any)) => (request: Request) => DecorationMethod<Request>, options: {apply: true, extend: true}): void;
decorate(type: 'request', property: DecorateName, method: (request: Request) => DecorationMethod<Request>, options: {apply: true, extend?: boolean | undefined}): void;
decorate(type: 'request', property: DecorateName, method: DecorationMethod<Request>, options?: {apply?: boolean | undefined, extend?: boolean | undefined}): void;
decorate(type: 'toolkit', property: DecorateName, method: (existing: ((...args: any[]) => any)) => DecorationMethod<ResponseToolkit>, options: {apply?: boolean | undefined, extend: true}): void;
decorate(type: 'toolkit', property: DecorateName, method: DecorationMethod<ResponseToolkit>, options?: {apply?: boolean | undefined, extend?: boolean | undefined}): void;
decorate(type: 'server', property: DecorateName, method: (existing: ((...args: any[]) => any)) => DecorationMethod<Server>, options: {apply?: boolean | undefined, extend: true}): void;
decorate(type: 'server', property: DecorateName, method: DecorationMethod<Server>, options?: {apply?: boolean | undefined, extend?: boolean | undefined}): void;
decorate(type: 'request', property: DecorateName, method: (existing: ((...args: any[]) => any)) => (request: Request) => DecorationValue<Request>, options: {apply: true, extend: true}): void;
decorate(type: 'request', property: DecorateName, method: (request: Request) => DecorationValue<Request>, options: {apply: true, extend?: boolean | undefined}): void;
decorate(type: 'request', property: DecorateName, method: DecorationValue<Request>, options?: {apply?: boolean | undefined, extend?: boolean | undefined}): void;
decorate(type: 'toolkit', property: DecorateName, method: (existing: ((...args: any[]) => any)) => DecorationValue<ResponseToolkit>, options: {apply?: boolean | undefined, extend: true}): void;
decorate(type: 'toolkit', property: DecorateName, method: DecorationValue<ResponseToolkit>, options?: {apply?: boolean | undefined, extend?: boolean | undefined}): void;
decorate(type: 'server', property: DecorateName, method: (existing: ((...args: any[]) => any)) => DecorationValue<Server>, options: {apply?: boolean | undefined, extend: true}): void;
decorate(type: 'server', property: DecorateName, method: DecorationValue<Server>, options?: {apply?: boolean | undefined, extend?: boolean | undefined}): void;

/**
* Used within a plugin to declare a required dependency on other plugins where:
Expand Down
84 changes: 83 additions & 1 deletion test/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ describe('Server', () => {

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

it('decorates request', async () => {
it('decorates request with function', async () => {

const server = Hapi.server();

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

it('decorates request with object', async () => {

const server = Hapi.server();

const customData = { id: '123' };

server.decorate('request', 'customData', customData);

server.route({
method: 'GET',
path: '/',
handler: (request) => request.customData
});

const res = await server.inject('/');
expect(res.statusCode).to.equal(200);
expect(res.result).to.equal({ id: '123' });
});

it('decorates request (apply)', async () => {

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

it('decorates request (extend) with an array', async () => {

const server = Hapi.server();

const items = ['one', 'two', 'three'];

server.decorate('request', 'items', items);
server.decorate('request', 'items', (existing) => [...existing, 'four'], { extend: true });

server.route({
method: 'GET',
path: '/',
handler: (request) => request.items
});

const res = await server.inject('/');
expect(res.statusCode).to.equal(200);
expect(res.result).to.equal([...items, 'four']);
});

it('decorates request (apply + extend)', async () => {

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

it('decorates toolkit with boolean', async () => {

const server = Hapi.server();

const isOk = true;

server.decorate('toolkit', 'isOk', isOk);

server.route({
method: 'GET',
path: '/',
handler: (request, h) => h.isOk
});

const res = await server.inject('/');
expect(res.statusCode).to.equal(200);
expect(res.result).to.equal(true);
});

it('add new handler', async () => {

const test = {
Expand Down Expand Up @@ -559,6 +617,30 @@ describe('Server', () => {
expect(res.result).to.equal('ok');
});

it('decorates server with Map', async () => {

const server = Hapi.server();

const plants = new Map();
plants.set('mango', 'Mango');
plants.set('banana', 'Banana');
plants.set('apple', 'Apple');

server.decorate('server', 'plants', plants);

server.route({
method: 'GET',
path: '/',
handler: (request) => request.server.plants
});

const res = await server.inject('/');
expect(res.statusCode).to.equal(200);
expect(res.result.get('mango')).to.equal('Mango');
expect(res.result.get('banana')).to.equal('Banana');
expect(res.result.get('apple')).to.equal('Apple');
});

it('throws on double server decoration', () => {

const server = Hapi.server();
Expand Down

0 comments on commit 1d95f23

Please sign in to comment.