Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Breaking] Improve request typing #3

Merged
merged 7 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
## 0.1.1 - 2023-12-22
## 0.2.0

- `RequestManager.send` now only accepts a single generic argument, `TResponse`, which is the type of the response body. The `TRequest` type argument has been removed. The type of the Response will be inferred based on the given `Request<Response>` (#3)

## 0.1.1

- Add `registerFactory` and `registerFunction` methods to `RequestManager`.

Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ Future<void> main() async {

mediator.requests.register(MyQueryHandler());

final response = await mediator.requests
.send<Something, MyQuery>(MyQuery());
final Something response = await mediator.requests.send(MyQuery());

print(response);
}
Expand Down
37 changes: 20 additions & 17 deletions example/example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,28 @@ Future<void> main() async {
.map((event) => event.count)
.distinct()
.subscribeFunction(
(count) => print('[$CountEvent handler] received count: $count'),
(count) => print('[CountEvent handler] received count: $count'),
);

mediator.events.on<CountEvent>().subscribeFunction(
(count) => print('[Other Event Handler] received: $count'),
);

const getUserQuery = GetUserByIdQuery(123);

print('Sending $getUserQuery request');

final resp =
await mediator.requests.send<User, GetUserByIdQuery>(getUserQuery);
final resp = await mediator.requests.send(getUserQuery);

print('Got $GetUserByIdQuery response: $resp');
print('Got $getUserQuery response: $resp');

print('---');

const order66Command = MyCommand('Order 66');

print('Sending command $order66Command');

await mediator.requests.send<void, MyCommand>(order66Command);
await mediator.requests.send(order66Command);

print('Command $order66Command completed');

Expand All @@ -58,23 +61,23 @@ class CountEvent implements DomainEvent {
const CountEvent(this.count);

@override
String toString() => '$CountEvent(count: $count)';
String toString() => 'CountEvent(count: $count)';
}

class MyCommand implements Command {
final String command;
const MyCommand(this.command);

@override
String toString() => '$MyCommand(command: $command)';
String toString() => 'MyCommand(command: $command)';
}

class MyCommandHandler implements CommandHandler<MyCommand> {
@override
Future<void> handle(MyCommand request) async {
print('[$MyCommandHandler] Executing "$request"');
print('[MyCommandHandler] Executing "$request"');
await Future.delayed(const Duration(milliseconds: 500));
print('[$MyCommandHandler] "$request" completed');
print('[MyCommandHandler] "$request" completed');
}
}

Expand All @@ -83,15 +86,15 @@ class GetUserByIdQuery implements Query<User> {
const GetUserByIdQuery(this.userId);

@override
String toString() => '$GetUserByIdQuery(userId: $userId)';
String toString() => 'GetUserByIdQuery(userId: $userId)';
}

class GetUserByIdQueryHandler implements QueryHandler<User, GetUserByIdQuery> {
@override
Future<User> handle(GetUserByIdQuery request) async {
print('[$GetUserByIdQueryHandler] handeling $request');
print('[GetUserByIdQueryHandler] handeling $request');
final user = await getUserByIdAsync(request.userId);
print('[$GetUserByIdQueryHandler] got $user');
print('[GetUserByIdQueryHandler] got $user');
return user;
}
}
Expand All @@ -100,10 +103,10 @@ class LoggingBehavior implements PipelineBehavior {
@override
Future handle(request, RequestHandlerDelegate next) async {
try {
print('[$LoggingBehavior] [${request.runtimeType}] Before');
print('[LoggingBehavior] [$request] Before');
return await next();
} finally {
print('[$LoggingBehavior] [${request.runtimeType}] After');
print('[LoggingBehavior] [$request] After');
}
}
}
Expand All @@ -115,7 +118,7 @@ class LoggingEventObserver implements EventObserver {
Set<EventHandler<TEvent>> handlers,
) {
print(
'[$LoggingEventObserver] onDispatch "$event" with ${handlers.length} handlers',
'[LoggingEventObserver] onDispatch "$event" with ${handlers.length} handlers',
);
}

Expand All @@ -126,7 +129,7 @@ class LoggingEventObserver implements EventObserver {
Object error,
StackTrace stackTrace,
) {
print('[$LoggingEventObserver] onError $event -> $handler ($error)');
print('[LoggingEventObserver] onError $event -> $handler ($error)');
}

@override
Expand All @@ -143,7 +146,7 @@ class User {
const User(this.id, this.name);

@override
String toString() => '$User(id: $id, name: $name)';
String toString() => 'User(id: $id, name: $name)';
}

Future<User> getUserByIdAsync(int id) async {
Expand Down
19 changes: 11 additions & 8 deletions lib/src/request/handler/request_handler_store.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,26 @@ class RequestHandlerStore {
_handlers.remove(TRequest);
}

/// Returns the registered [RequestHandler]'s for [TRequest].
RequestHandler<TResponse, TRequest>
getHandlerFor<TResponse, TRequest extends Request<TResponse>>() {
final handler = _handlers[TRequest] ?? _handlerFactories[TRequest]?.call();
/// Returns the registered [RequestHandler]'s for [request].
RequestHandler getHandlerFor<TResponse extends Object?>(
Request<TResponse> request,
) {
final requestType = request.runtimeType;
final handler =
_handlers[requestType] ?? _handlerFactories[requestType]?.call();

assert(
handler != null,
'getHandlerFor<$TResponse, $TRequest> did not have a registered handler. '
'getHandlerFor<$TResponse, $requestType> did not have a registered handler. '
'Make sure to register the request handler first.',
);

assert(
handler is RequestHandler<TResponse, TRequest>,
handler is RequestHandler<TResponse, Request<TResponse>>,
'The registered handler is of the wrong type got $handler but was '
'expecting a type of RequestHandler<$TResponse, $TRequest>',
'expecting a type of RequestHandler<$TResponse, $requestType>',
);

return handler as RequestHandler<TResponse, TRequest>;
return handler!;
}
}
49 changes: 34 additions & 15 deletions lib/src/request/pipeline/pipeline_behavior_store.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,34 @@ import 'package:dart_mediator/src/request/pipeline/pipeline_configurator.dart';
import 'package:dart_mediator/src/request/pipeline/pipeline_behavior.dart';

class PipelineBehaviorStore implements PipelineConfigurator {
final _handlers = <PipelineBehavior<Object?, Object>>{};
final _handlerFactories = <PipelineBehaviorFactory<Object?, Object>>{};
final _handlers = <Type, List<PipelineBehavior>>{};
final _handlerFactories = <Type, List<PipelineBehaviorFactory>>{};
final _genericHandlers = <PipelineBehavior>{};
final _genericHandlerFactories = <PipelineBehaviorFactory>{};

@override
void register<TResponse extends Object?, TRequest extends Object>(
void register<TResponse extends Object?, TRequest extends Request<TResponse>>(
PipelineBehavior<TResponse, TRequest> behavior,
) {
_handlers.add(behavior);
final handlers = _handlers.putIfAbsent(
TRequest,
() => <PipelineBehavior>[],
);

handlers.add(behavior);
}

@override
void registerFactory<TResponse extends Object?, TRequest extends Object>(
void registerFactory<TResponse extends Object?,
TRequest extends Request<TResponse>>(
PipelineBehaviorFactory<TResponse, TRequest> factory,
) {
_handlerFactories.add(factory);
final handlers = _handlerFactories.putIfAbsent(
TRequest,
() => <PipelineBehaviorFactory>[],
);

handlers.add(factory);
}

@override
Expand All @@ -38,29 +49,37 @@ class PipelineBehaviorStore implements PipelineConfigurator {

@override
void unregister(PipelineBehavior behavior) {
_handlers.remove(behavior);
for (final handlers in _handlers.values) {
handlers.remove(behavior);
}
_genericHandlers.remove(behavior);
}

@override
void unregisterFactory(PipelineBehaviorFactory factory) {
_handlerFactories.remove(factory);
for (final handlers in _handlerFactories.values) {
handlers.remove(factory);
}
_genericHandlerFactories.remove(factory);
}

/// Returns all [PipelineBehavior]'s that match.
List<PipelineBehavior> getPipelines<TResponse extends Object?,
TRequest extends Request<TResponse>>() {
final handlerFactories = _handlerFactories
.whereType<PipelineBehaviorFactory<TResponse, TRequest>>()
.map((factory) => factory());
List<PipelineBehavior> getPipelines(
Request request,
) {
final requestType = request.runtimeType;

final handlerFactories =
_handlerFactories[requestType]?.map((factory) => factory());

final genericFactories =
_genericHandlerFactories.map((factory) => factory());

final handlers = _handlers[requestType];

return [
..._handlers.whereType<PipelineBehavior<TResponse, TRequest>>(),
...handlerFactories,
if (handlers != null) ...handlers,
if (handlerFactories != null) ...handlerFactories,
..._genericHandlers,
...genericFactories,
];
Expand Down
6 changes: 4 additions & 2 deletions lib/src/request/pipeline/pipeline_configurator.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:dart_mediator/src/request/pipeline/pipeline_behavior.dart';
import 'package:dart_mediator/src/request/request.dart';

/// Factory to create a [PipelineBehavior].
typedef PipelineBehaviorFactory<TRequest, TResponse>
Expand All @@ -9,15 +10,16 @@ abstract interface class PipelineConfigurator {
///
/// When using a generic [PipelineBehavior] the [registerGeneric] should be
/// used instead.
void register<TResponse extends Object?, TRequest extends Object>(
void register<TResponse extends Object?, TRequest extends Request<TResponse>>(
PipelineBehavior<TResponse, TRequest> behavior,
);

/// Registers the [factory].
///
/// When using a generic [PipelineBehavior] the [registerGenericFactory] should
/// be used instead.
void registerFactory<TResponse extends Object?, TRequest extends Object>(
void registerFactory<TResponse extends Object?,
TRequest extends Request<TResponse>>(
PipelineBehaviorFactory<TResponse, TRequest> factory,
);

Expand Down
17 changes: 9 additions & 8 deletions lib/src/request/request_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,13 @@ class RequestManager {
/// This request can be wrapped by [PipelineBehavior]'s see [pipeline].
///
/// This will return [TResponse].
Future<TResponse>
send<TResponse extends Object?, TRequest extends Request<TResponse>>(
TRequest request,
Future<TResponse> send<TResponse extends Object?>(
Request<TResponse> request,
) async {
final handler = _requestHandlerStore.getHandlerFor<TResponse, TRequest>();
final handler = _requestHandlerStore.getHandlerFor(request)
as RequestHandler<TResponse, Request<TResponse>>;

final pipelines =
_pipelineBehaviorStore.getPipelines<TResponse, TRequest>();
final pipelines = _pipelineBehaviorStore.getPipelines(request);

FutureOr<TResponse> handle() => handler.handle(request);

Expand All @@ -73,11 +72,13 @@ class RequestManager {
(next, pipeline) => () => pipeline.handle(request, next),
);

final response = await executionPlan();
final futureOrResult = executionPlan();
final response =
futureOrResult is Future ? await futureOrResult : futureOrResult;

assert(
response is TResponse,
'$TRequest expected a return type of $TResponse but '
'$request expected a return type of $TResponse but '
'got one of type ${response.runtimeType}. '
'One of the registered pipelines is not correctly returning the '
'`next()` call. Pipelines used: $pipelines',
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: dart_mediator
description: >
A simple yet highly configurable Mediator implementation
that allows sending requests and publishing events.
version: 0.1.1
version: 0.2.0
repository: https://github.com/MatthiWare/mediator.dart

environment:
Expand Down
3 changes: 1 addition & 2 deletions test/integration/choreography_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ void main() {
),
);

final stock = await mediator.requests
.send<Map<String, int>, GetInventoryQuery>(GetInventoryQuery());
final stock = await mediator.requests.send(GetInventoryQuery());

expect(stock, {
'mouse': 8,
Expand Down
Loading