Skip to content

Commit

Permalink
Implement Mediator.send
Browse files Browse the repository at this point in the history
  • Loading branch information
ycanardeau committed Jul 3, 2024
1 parent 72216eb commit ab3b8d1
Show file tree
Hide file tree
Showing 9 changed files with 412 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,21 @@ export function tryAddServiceDescriptor(
export function tryAddServiceDescriptorIterable(
collection: IServiceCollection,
descriptor: ServiceDescriptor,
): void;
export function tryAddServiceDescriptorIterable(
collection: IServiceCollection,
descriptors: ServiceDescriptor[],
): void;
export function tryAddServiceDescriptorIterable(
collection: IServiceCollection,
descriptorOrDescriptors: ServiceDescriptor | ServiceDescriptor[],
): void {
// TODO
tryAddServiceDescriptor(collection, descriptor);
if (descriptorOrDescriptors instanceof Array) {
for (const descriptor of descriptorOrDescriptors) {
tryAddServiceDescriptor(collection, descriptor);
}
} else {
// TODO
tryAddServiceDescriptor(collection, descriptorOrDescriptors);
}
}
9 changes: 9 additions & 0 deletions packages/third-party.mediatr/src/mediatr/IPipelineBehavior.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type RequestHandlerDelegate<TResponse> = () => Promise<TResponse>;

// https://github.com/jbogard/MediatR/blob/e611b1bcc00b10244810abeea2f7c62f155beb5e/src/MediatR/IPipelineBehavior.cs#L20
export interface IPipelineBehavior<TRequest, TResponse> {
handle(
request: TRequest,
next: RequestHandlerDelegate<TResponse>,
): Promise<TResponse>;
}
19 changes: 18 additions & 1 deletion packages/third-party.mediatr/src/mediatr/Mediatr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,17 @@ import {
NotificationHandlerWrapper,
NotificationHandlerWrapperImpl,
} from './wrappers/NotificationHandlerWrapper';
import {
RequestHandlerBase,
RequestHandlerWrapperImpl,
} from './wrappers/RequestHandlerWrapper';

// https://github.com/jbogard/MediatR/blob/f4de8196adafd37faff274ce819ada93a3d7531b/src/MediatR/Mediator.cs#L16
export class Mediator implements IMediator {
private readonly requestHandlers = new Map<
Ctor<IRequest<unknown>>,
RequestHandlerBase
>();
private readonly notificationHandlers = new Map<
Ctor<INotification>,
NotificationHandlerWrapper
Expand All @@ -26,7 +34,16 @@ export class Mediator implements IMediator {
) {}

send<TResponse>(request: IRequest<TResponse>): Promise<TResponse> {
throw new Error('Method not implemented.');
const handler = getOrAdd(
this.requestHandlers,
request.constructor as Ctor<IRequest<TResponse>>,
(requestCtor) => {
const wrapper = new RequestHandlerWrapperImpl(requestCtor);
return wrapper as RequestHandlerBase;
},
);

return handler.handle(request, this.serviceProvider);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { Ctor } from '@yohira/base';
import { ServiceLifetime } from '@yohira/extensions.dependency-injection.abstractions';
import {
ServiceDescriptor,
ServiceLifetime,
} from '@yohira/extensions.dependency-injection.abstractions';

import { INotificationPublisher } from '../INotificationPublisher';
import { Mediator } from '../Mediatr';
import { ForOfAwaitPublisher } from '../notification-publishers/ForOfAwaitPublisher';

// https://github.com/jbogard/MediatR/blob/f4de8196adafd37faff274ce819ada93a3d7531b/src/MediatR/MicrosoftExtensionsDI/MediatrServiceConfiguration.cs#L9
// https://github.com/jbogard/MediatR/blob/8b1ee39fe29fd12c8042d22f9fd1a969b5fcbc16/src/MediatR/MicrosoftExtensionsDI/MediatrServiceConfiguration.cs#L12
export class MediatRServiceConfig {
/**
* Mediator implementation type to register. Default is {@link Mediator}
Expand All @@ -23,4 +26,12 @@ export class MediatRServiceConfig {
* Service lifetime to register services under. Default value is {@link ServiceLifetime.Transient}
*/
lifetime: ServiceLifetime = ServiceLifetime.Transient;
/**
* List of request pre processors to register in specific order
*/
requestPreProcessorsToRegister: ServiceDescriptor[] = [];
/**
* List of request post processors to register in specific order
*/
requestPostProcessorsToRegister: ServiceDescriptor[] = [];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {
IPipelineBehavior,
RequestHandlerDelegate,
} from '../IPipelineBehavior';

// https://github.com/jbogard/MediatR/blob/e611b1bcc00b10244810abeea2f7c62f155beb5e/src/MediatR/Pipeline/RequestPostProcessorBehavior.cs#L12
/**
* Behavior for executing all <see cref="IRequestPostProcessor{TRequest,TResponse}"/> instances after handling the request
*/
export class RequestPostProcessorBehavior<TRequest, TResponse>
implements IPipelineBehavior<TRequest, TResponse>
{
handle(
request: TRequest,
next: RequestHandlerDelegate<TResponse>,
): Promise<TResponse> {
throw new Error('Method not implemented.');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {
IPipelineBehavior,
RequestHandlerDelegate,
} from '../IPipelineBehavior';

// https://github.com/jbogard/MediatR/blob/e611b1bcc00b10244810abeea2f7c62f155beb5e/src/MediatR/Pipeline/RequestPreProcessorBehavior.cs#L12
/**
* Behavior for executing all <see cref="IRequestPreProcessor{TRequest}"/> instances before handling a request
*/
export class RequestPreProcessorBehavior<TRequest, TResponse>
implements IPipelineBehavior<TRequest, TResponse>
{
handle(
request: TRequest,
next: RequestHandlerDelegate<TResponse>,
): Promise<TResponse> {
throw new Error('Method not implemented.');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import {
ServiceDescriptor,
ServiceLifetime,
tryAddServiceDescriptor,
tryAddServiceDescriptorIterable,
} from '@yohira/extensions.dependency-injection.abstractions';

import { IMediator } from '../IMediator';
import { INotificationPublisher } from '../INotificationPublisher';
import { MediatRServiceConfig } from '../extensions-di/MediatRServiceConfig';
import { RequestPostProcessorBehavior } from '../pipeline/RequestPostProcessorBehavior';
import { RequestPreProcessorBehavior } from '../pipeline/RequestPreProcessorBehavior';

// https://github.com/jbogard/MediatR/blob/43fb46f39020ab4880fefe75fa2315351f347742/src/MediatR/Registration/ServiceRegistrar.cs#L300
export function addRequiredServices(
Expand Down Expand Up @@ -38,4 +41,38 @@ export function addRequiredServices(
);

tryAddServiceDescriptor(services, notificationPublisherServiceDescriptor);

// TODO

if (serviceConfig.requestPreProcessorsToRegister.length > 0) {
tryAddServiceDescriptorIterable(
services,
ServiceDescriptor.fromCtor(
ServiceLifetime.Transient,
Symbol.for('IPipelineBehavior<>'),
RequestPreProcessorBehavior,
),
);
tryAddServiceDescriptorIterable(
services,
serviceConfig.requestPreProcessorsToRegister,
);
}

if (serviceConfig.requestPostProcessorsToRegister.length > 0) {
tryAddServiceDescriptorIterable(
services,
ServiceDescriptor.fromCtor(
ServiceLifetime.Transient,
Symbol.for('IPipelineBehavior<>'),
RequestPostProcessorBehavior,
),
);
tryAddServiceDescriptorIterable(
services,
serviceConfig.requestPostProcessorsToRegister,
);
}

// TODO
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Ctor, IServiceProvider } from '@yohira/base';
import {
getRequiredService,
getServices,
} from '@yohira/extensions.dependency-injection.abstractions';

import { IRequest } from '../../mediatr.contracts/IRequest';
import {
IPipelineBehavior,
RequestHandlerDelegate,
} from '../IPipelineBehavior';
import { IRequestHandler } from '../IRequestHandler';

// https://github.com/jbogard/MediatR/blob/761fb0b1b420f5a8c2cb4a751617dce7ab9c3fe3/src/MediatR/Wrappers/RequestHandlerWrapper.cs#L9
export abstract class RequestHandlerBase {
abstract handle(
request: object,
serviceProvider: IServiceProvider,
): Promise<any /* TODO */>;
}

// https://github.com/jbogard/MediatR/blob/761fb0b1b420f5a8c2cb4a751617dce7ab9c3fe3/src/MediatR/Wrappers/RequestHandlerWrapper.cs#L15
export abstract class RequestHandlerWrapper<
TResponse,
> extends RequestHandlerBase {
abstract handle(
request: IRequest<TResponse>,
serviceProvider: IServiceProvider,
): Promise<TResponse>;
}

// https://github.com/jbogard/MediatR/blob/761fb0b1b420f5a8c2cb4a751617dce7ab9c3fe3/src/MediatR/Wrappers/RequestHandlerWrapper.cs#L27
export class RequestHandlerWrapperImpl<
TRequest extends IRequest<TResponse>,
TResponse,
> extends RequestHandlerWrapper<TResponse> {
constructor(private readonly requestCtor: Ctor<TRequest>) {
super();
}

handle(
request: IRequest<TResponse>,
serviceProvider: IServiceProvider,
): Promise<TResponse> {
const handler = (): Promise<TResponse> =>
getRequiredService<IRequestHandler<TRequest, TResponse>>(
serviceProvider,
Symbol.for(`IRequestHandler<${this.requestCtor.name}>`),
).handle(request as TRequest);

return getServices<IPipelineBehavior<TRequest, TResponse>>(
serviceProvider,
Symbol.for(`IPipelineBehavior<${this.requestCtor.name}>`),
)
.reverse()
.reduce(
(next, pipeline) => () =>
pipeline.handle(request as TRequest, next),
handler as RequestHandlerDelegate<TResponse>,
)();
}
}
Loading

0 comments on commit ab3b8d1

Please sign in to comment.