Skip to content

Commit

Permalink
feat(microservices): add extras param to pattern decorators
Browse files Browse the repository at this point in the history
Add optional "extras" parameter for MessagePattern and
EventPattern decorators. Allow embedding extra params into
each message handler method and improve an experience of
creating custom microservice transports
  • Loading branch information
EugeneKorshenko committed Jan 16, 2022
1 parent b01afec commit 26e9ed6
Show file tree
Hide file tree
Showing 11 changed files with 96 additions and 42 deletions.
1 change: 1 addition & 0 deletions packages/microservices/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const SUBSCRIBE = 'subscribe';
export const CANCEL_EVENT = 'cancelled';

export const PATTERN_METADATA = 'microservices:pattern';
export const PATTERN_EXTRAS_METADATA = 'microservices:pattern_extras';
export const TRANSPORT_METADATA = 'microservices:transport';
export const CLIENT_CONFIGURATION_METADATA = 'microservices:client';
export const PATTERN_HANDLER_METADATA = 'microservices:handler_type';
Expand Down
3 changes: 3 additions & 0 deletions packages/microservices/decorators/event-pattern.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
PATTERN_HANDLER_METADATA,
PATTERN_METADATA,
TRANSPORT_METADATA,
PATTERN_EXTRAS_METADATA,
} from '../constants';
import { PatternHandler } from '../enums/pattern-handler.enum';
import { Transport } from '../enums';
Expand All @@ -12,6 +13,7 @@ import { Transport } from '../enums';
export const EventPattern = <T = string>(
metadata?: T,
transport?: Transport,
extras?: Record<string, any>,
): MethodDecorator => {
return (
target: object,
Expand All @@ -25,6 +27,7 @@ export const EventPattern = <T = string>(
descriptor.value,
);
Reflect.defineMetadata(TRANSPORT_METADATA, transport, descriptor.value);
Reflect.defineMetadata(PATTERN_EXTRAS_METADATA, extras, descriptor.value);
return descriptor;
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
PATTERN_HANDLER_METADATA,
PATTERN_METADATA,
TRANSPORT_METADATA,
PATTERN_EXTRAS_METADATA,
} from '../constants';
import { PatternHandler } from '../enums/pattern-handler.enum';
import { PatternMetadata } from '../interfaces/pattern-metadata.interface';
Expand All @@ -20,6 +21,7 @@ export enum GrpcMethodStreamingType {
export const MessagePattern = <T = PatternMetadata | string>(
metadata?: T,
transport?: Transport,
extras?: Record<string, any>,
): MethodDecorator => {
return (
target: object,
Expand All @@ -33,6 +35,7 @@ export const MessagePattern = <T = PatternMetadata | string>(
descriptor.value,
);
Reflect.defineMetadata(TRANSPORT_METADATA, transport, descriptor.value);
Reflect.defineMetadata(PATTERN_EXTRAS_METADATA, extras, descriptor.value);
return descriptor;
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export interface MessageHandler<TInput = any, TContext = any, TResult = any> {
(data: TInput, ctx?: TContext): Promise<Observable<TResult>>;
next?: (data: TInput, ctx?: TContext) => Promise<Observable<TResult>>;
isEventHandler?: boolean;
extras?: Record<string, any>;
}
4 changes: 4 additions & 0 deletions packages/microservices/listener-metadata-explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
PATTERN_HANDLER_METADATA,
PATTERN_METADATA,
TRANSPORT_METADATA,
PATTERN_EXTRAS_METADATA,
} from './constants';
import { Transport } from './enums';
import { PatternHandler } from './enums/pattern-handler.enum';
Expand All @@ -24,6 +25,7 @@ export interface EventOrMessageListenerDefinition {
isEventHandler: boolean;
targetCallback: (...args: any[]) => any;
transport?: Transport;
extras?: Record<string, any>;
}

export interface MessageRequestProperties {
Expand Down Expand Up @@ -58,11 +60,13 @@ export class ListenerMetadataExplorer {
}
const pattern = Reflect.getMetadata(PATTERN_METADATA, targetCallback);
const transport = Reflect.getMetadata(TRANSPORT_METADATA, targetCallback);
const extras = Reflect.getMetadata(PATTERN_EXTRAS_METADATA, targetCallback);
return {
methodKey,
targetCallback,
pattern,
transport,
extras,
isEventHandler: handlerType === PatternHandler.EVENT,
};
}
Expand Down
83 changes: 45 additions & 38 deletions packages/microservices/listeners-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,48 +71,55 @@ export class ListenersController {
isUndefined(server.transportId) ||
transport === server.transportId,
)
.forEach(({ pattern, targetCallback, methodKey, isEventHandler }) => {
if (isStatic) {
const proxy = this.contextCreator.create(
instance as object,
targetCallback,
.forEach(
({ pattern, targetCallback, methodKey, extras, isEventHandler }) => {
if (isStatic) {
const proxy = this.contextCreator.create(
instance as object,
targetCallback,
moduleKey,
methodKey,
STATIC_CONTEXT,
undefined,
defaultCallMetadata,
);
if (isEventHandler) {
const eventHandler: MessageHandler = (...args: unknown[]) => {
const originalArgs = args;
const [dataOrContextHost] = originalArgs;
if (dataOrContextHost instanceof RequestContextHost) {
args = args.slice(1, args.length);
}
const originalReturnValue = proxy(...args);
const returnedValueWrapper = eventHandler.next?.(
...(originalArgs as Parameters<MessageHandler>),
);
returnedValueWrapper?.then(returnedValue =>
this.connectIfStream(returnedValue as Observable<unknown>),
);
return originalReturnValue;
};
return server.addHandler(
pattern,
eventHandler,
isEventHandler,
extras,
);
} else {
return server.addHandler(pattern, proxy, isEventHandler, extras);
}
}
const asyncHandler = this.createRequestScopedHandler(
instanceWrapper,
pattern,
moduleRef,
moduleKey,
methodKey,
STATIC_CONTEXT,
undefined,
defaultCallMetadata,
);
if (isEventHandler) {
const eventHandler: MessageHandler = (...args: unknown[]) => {
const originalArgs = args;
const [dataOrContextHost] = originalArgs;
if (dataOrContextHost instanceof RequestContextHost) {
args = args.slice(1, args.length);
}
const originalReturnValue = proxy(...args);
const returnedValueWrapper = eventHandler.next?.(
...(originalArgs as Parameters<MessageHandler>),
);
returnedValueWrapper?.then(returnedValue =>
this.connectIfStream(returnedValue as Observable<unknown>),
);
return originalReturnValue;
};
return server.addHandler(pattern, eventHandler, isEventHandler);
} else {
return server.addHandler(pattern, proxy, isEventHandler);
}
}
const asyncHandler = this.createRequestScopedHandler(
instanceWrapper,
pattern,
moduleRef,
moduleKey,
methodKey,
defaultCallMetadata,
);
server.addHandler(pattern, asyncHandler, isEventHandler);
});
server.addHandler(pattern, asyncHandler, isEventHandler, extras);
},
);
}

public assignClientsToProperties(instance: Controller | Injectable) {
Expand Down
2 changes: 2 additions & 0 deletions packages/microservices/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,11 @@ export abstract class Server {
pattern: any,
callback: MessageHandler,
isEventHandler = false,
extras: Record<string, any> = {},
) {
const normalizedPattern = this.normalizePattern(pattern);
callback.isEventHandler = isEventHandler;
callback.extras = extras;

if (this.messageHandlers.has(normalizedPattern) && isEventHandler) {
const headRef = this.messageHandlers.get(normalizedPattern);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { expect } from 'chai';
import { PATTERN_METADATA } from '../../constants';
import { PATTERN_METADATA, PATTERN_EXTRAS_METADATA } from '../../constants';
import { EventPattern } from '../../decorators/event-pattern.decorator';

describe('@EventPattern', () => {
const pattern = { role: 'test' };
const extras = { param: 'value' };
class TestComponent {
@EventPattern(pattern)
@EventPattern(pattern, undefined, extras)
public static test() {}
}
it(`should enhance method with ${PATTERN_METADATA} metadata`, () => {
const metadata = Reflect.getMetadata(PATTERN_METADATA, TestComponent.test);
expect(metadata).to.be.eql(pattern);
});
it(`should enhance method with ${PATTERN_EXTRAS_METADATA} metadata`, () => {
const metadata = Reflect.getMetadata(
PATTERN_EXTRAS_METADATA,
TestComponent.test,
);
expect(metadata).to.be.deep.equal(extras);
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from 'chai';
import { PATTERN_METADATA } from '../../constants';
import { PATTERN_METADATA, PATTERN_EXTRAS_METADATA } from '../../constants';
import {
GrpcMethod,
GrpcMethodStreamingType,
Expand All @@ -10,14 +10,22 @@ import {

describe('@MessagePattern', () => {
const pattern = { role: 'test' };
const extras = { param: 'value' };
class TestComponent {
@MessagePattern(pattern)
@MessagePattern(pattern, undefined, extras)
public static test() {}
}
it(`should enhance method with ${PATTERN_METADATA} metadata`, () => {
const metadata = Reflect.getMetadata(PATTERN_METADATA, TestComponent.test);
expect(metadata).to.be.eql(pattern);
});
it(`should enhance method with ${PATTERN_EXTRAS_METADATA} metadata`, () => {
const metadata = Reflect.getMetadata(
PATTERN_EXTRAS_METADATA,
TestComponent.test,
);
expect(metadata).to.be.deep.equal(extras);
});
});

describe('@GrpcMethod', () => {
Expand Down
16 changes: 16 additions & 0 deletions packages/microservices/test/listeners-controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,22 @@ describe('ListenersController', () => {
instance.registerPatternHandlers(new InstanceWrapper(), serverTCP, '');
expect(addSpyTCP.calledTwice).to.be.true;
});
it(`should call "addHandler" method of server with extras data`, () => {
const serverHandlers = [
{ pattern: 'test', targetCallback: 'tt', extras: { param: 'value' } },
];
explorer.expects('explore').returns(serverHandlers);
instance.registerPatternHandlers(new InstanceWrapper(), serverTCP, '');
expect(addSpyTCP.calledOnce).to.be.true;
expect(
addSpyTCP.calledWith(
sinon.match.any,
sinon.match.any,
sinon.match.any,
sinon.match({ param: 'value' }),
),
).to.be.true;
});
describe('when request scoped', () => {
it(`should call "addHandler" with deferred proxy`, () => {
explorer.expects('explore').returns(handlers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ describe('ListenerMetadataExplorer', () => {
'targetCallback',
'pattern',
'transport',
'extras',
]);
expect(metadata.pattern).to.eql(pattern);
});
Expand Down

0 comments on commit 26e9ed6

Please sign in to comment.