Skip to content

Commit 9286403

Browse files
authored
[Breaking] Improve request typing (#3)
* Infer type from given request * Update changelog * Update readme * Add external integration tests * Bump `0.2.0` * Avoid minification * Add additional event handler
1 parent 383af62 commit 9286403

16 files changed

+260
-96
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
## 0.1.1 - 2023-12-22
1+
## 0.2.0
2+
3+
- `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)
4+
5+
## 0.1.1
26

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

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ Future<void> main() async {
6060
6161
mediator.requests.register(MyQueryHandler());
6262
63-
final response = await mediator.requests
64-
.send<Something, MyQuery>(MyQuery());
63+
final Something response = await mediator.requests.send(MyQuery());
6564
6665
print(response);
6766
}

example/example.dart

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,28 @@ Future<void> main() async {
2020
.map((event) => event.count)
2121
.distinct()
2222
.subscribeFunction(
23-
(count) => print('[$CountEvent handler] received count: $count'),
23+
(count) => print('[CountEvent handler] received count: $count'),
24+
);
25+
26+
mediator.events.on<CountEvent>().subscribeFunction(
27+
(count) => print('[Other Event Handler] received: $count'),
2428
);
2529

2630
const getUserQuery = GetUserByIdQuery(123);
2731

2832
print('Sending $getUserQuery request');
2933

30-
final resp =
31-
await mediator.requests.send<User, GetUserByIdQuery>(getUserQuery);
34+
final resp = await mediator.requests.send(getUserQuery);
3235

33-
print('Got $GetUserByIdQuery response: $resp');
36+
print('Got $getUserQuery response: $resp');
3437

3538
print('---');
3639

3740
const order66Command = MyCommand('Order 66');
3841

3942
print('Sending command $order66Command');
4043

41-
await mediator.requests.send<void, MyCommand>(order66Command);
44+
await mediator.requests.send(order66Command);
4245

4346
print('Command $order66Command completed');
4447

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

6063
@override
61-
String toString() => '$CountEvent(count: $count)';
64+
String toString() => 'CountEvent(count: $count)';
6265
}
6366

6467
class MyCommand implements Command {
6568
final String command;
6669
const MyCommand(this.command);
6770

6871
@override
69-
String toString() => '$MyCommand(command: $command)';
72+
String toString() => 'MyCommand(command: $command)';
7073
}
7174

7275
class MyCommandHandler implements CommandHandler<MyCommand> {
7376
@override
7477
Future<void> handle(MyCommand request) async {
75-
print('[$MyCommandHandler] Executing "$request"');
78+
print('[MyCommandHandler] Executing "$request"');
7679
await Future.delayed(const Duration(milliseconds: 500));
77-
print('[$MyCommandHandler] "$request" completed');
80+
print('[MyCommandHandler] "$request" completed');
7881
}
7982
}
8083

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

8588
@override
86-
String toString() => '$GetUserByIdQuery(userId: $userId)';
89+
String toString() => 'GetUserByIdQuery(userId: $userId)';
8790
}
8891

8992
class GetUserByIdQueryHandler implements QueryHandler<User, GetUserByIdQuery> {
9093
@override
9194
Future<User> handle(GetUserByIdQuery request) async {
92-
print('[$GetUserByIdQueryHandler] handeling $request');
95+
print('[GetUserByIdQueryHandler] handeling $request');
9396
final user = await getUserByIdAsync(request.userId);
94-
print('[$GetUserByIdQueryHandler] got $user');
97+
print('[GetUserByIdQueryHandler] got $user');
9598
return user;
9699
}
97100
}
@@ -100,10 +103,10 @@ class LoggingBehavior implements PipelineBehavior {
100103
@override
101104
Future handle(request, RequestHandlerDelegate next) async {
102105
try {
103-
print('[$LoggingBehavior] [${request.runtimeType}] Before');
106+
print('[LoggingBehavior] [$request] Before');
104107
return await next();
105108
} finally {
106-
print('[$LoggingBehavior] [${request.runtimeType}] After');
109+
print('[LoggingBehavior] [$request] After');
107110
}
108111
}
109112
}
@@ -115,7 +118,7 @@ class LoggingEventObserver implements EventObserver {
115118
Set<EventHandler<TEvent>> handlers,
116119
) {
117120
print(
118-
'[$LoggingEventObserver] onDispatch "$event" with ${handlers.length} handlers',
121+
'[LoggingEventObserver] onDispatch "$event" with ${handlers.length} handlers',
119122
);
120123
}
121124

@@ -126,7 +129,7 @@ class LoggingEventObserver implements EventObserver {
126129
Object error,
127130
StackTrace stackTrace,
128131
) {
129-
print('[$LoggingEventObserver] onError $event -> $handler ($error)');
132+
print('[LoggingEventObserver] onError $event -> $handler ($error)');
130133
}
131134

132135
@override
@@ -143,7 +146,7 @@ class User {
143146
const User(this.id, this.name);
144147

145148
@override
146-
String toString() => '$User(id: $id, name: $name)';
149+
String toString() => 'User(id: $id, name: $name)';
147150
}
148151

149152
Future<User> getUserByIdAsync(int id) async {

lib/src/request/handler/request_handler_store.dart

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,23 +51,26 @@ class RequestHandlerStore {
5151
_handlers.remove(TRequest);
5252
}
5353

54-
/// Returns the registered [RequestHandler]'s for [TRequest].
55-
RequestHandler<TResponse, TRequest>
56-
getHandlerFor<TResponse, TRequest extends Request<TResponse>>() {
57-
final handler = _handlers[TRequest] ?? _handlerFactories[TRequest]?.call();
54+
/// Returns the registered [RequestHandler]'s for [request].
55+
RequestHandler getHandlerFor<TResponse extends Object?>(
56+
Request<TResponse> request,
57+
) {
58+
final requestType = request.runtimeType;
59+
final handler =
60+
_handlers[requestType] ?? _handlerFactories[requestType]?.call();
5861

5962
assert(
6063
handler != null,
61-
'getHandlerFor<$TResponse, $TRequest> did not have a registered handler. '
64+
'getHandlerFor<$TResponse, $requestType> did not have a registered handler. '
6265
'Make sure to register the request handler first.',
6366
);
6467

6568
assert(
66-
handler is RequestHandler<TResponse, TRequest>,
69+
handler is RequestHandler<TResponse, Request<TResponse>>,
6770
'The registered handler is of the wrong type got $handler but was '
68-
'expecting a type of RequestHandler<$TResponse, $TRequest>',
71+
'expecting a type of RequestHandler<$TResponse, $requestType>',
6972
);
7073

71-
return handler as RequestHandler<TResponse, TRequest>;
74+
return handler!;
7275
}
7376
}

lib/src/request/pipeline/pipeline_behavior_store.dart

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,34 @@ import 'package:dart_mediator/src/request/pipeline/pipeline_configurator.dart';
33
import 'package:dart_mediator/src/request/pipeline/pipeline_behavior.dart';
44

55
class PipelineBehaviorStore implements PipelineConfigurator {
6-
final _handlers = <PipelineBehavior<Object?, Object>>{};
7-
final _handlerFactories = <PipelineBehaviorFactory<Object?, Object>>{};
6+
final _handlers = <Type, List<PipelineBehavior>>{};
7+
final _handlerFactories = <Type, List<PipelineBehaviorFactory>>{};
88
final _genericHandlers = <PipelineBehavior>{};
99
final _genericHandlerFactories = <PipelineBehaviorFactory>{};
1010

1111
@override
12-
void register<TResponse extends Object?, TRequest extends Object>(
12+
void register<TResponse extends Object?, TRequest extends Request<TResponse>>(
1313
PipelineBehavior<TResponse, TRequest> behavior,
1414
) {
15-
_handlers.add(behavior);
15+
final handlers = _handlers.putIfAbsent(
16+
TRequest,
17+
() => <PipelineBehavior>[],
18+
);
19+
20+
handlers.add(behavior);
1621
}
1722

1823
@override
19-
void registerFactory<TResponse extends Object?, TRequest extends Object>(
24+
void registerFactory<TResponse extends Object?,
25+
TRequest extends Request<TResponse>>(
2026
PipelineBehaviorFactory<TResponse, TRequest> factory,
2127
) {
22-
_handlerFactories.add(factory);
28+
final handlers = _handlerFactories.putIfAbsent(
29+
TRequest,
30+
() => <PipelineBehaviorFactory>[],
31+
);
32+
33+
handlers.add(factory);
2334
}
2435

2536
@override
@@ -38,29 +49,37 @@ class PipelineBehaviorStore implements PipelineConfigurator {
3849

3950
@override
4051
void unregister(PipelineBehavior behavior) {
41-
_handlers.remove(behavior);
52+
for (final handlers in _handlers.values) {
53+
handlers.remove(behavior);
54+
}
4255
_genericHandlers.remove(behavior);
4356
}
4457

4558
@override
4659
void unregisterFactory(PipelineBehaviorFactory factory) {
47-
_handlerFactories.remove(factory);
60+
for (final handlers in _handlerFactories.values) {
61+
handlers.remove(factory);
62+
}
4863
_genericHandlerFactories.remove(factory);
4964
}
5065

5166
/// Returns all [PipelineBehavior]'s that match.
52-
List<PipelineBehavior> getPipelines<TResponse extends Object?,
53-
TRequest extends Request<TResponse>>() {
54-
final handlerFactories = _handlerFactories
55-
.whereType<PipelineBehaviorFactory<TResponse, TRequest>>()
56-
.map((factory) => factory());
67+
List<PipelineBehavior> getPipelines(
68+
Request request,
69+
) {
70+
final requestType = request.runtimeType;
71+
72+
final handlerFactories =
73+
_handlerFactories[requestType]?.map((factory) => factory());
5774

5875
final genericFactories =
5976
_genericHandlerFactories.map((factory) => factory());
6077

78+
final handlers = _handlers[requestType];
79+
6180
return [
62-
..._handlers.whereType<PipelineBehavior<TResponse, TRequest>>(),
63-
...handlerFactories,
81+
if (handlers != null) ...handlers,
82+
if (handlerFactories != null) ...handlerFactories,
6483
..._genericHandlers,
6584
...genericFactories,
6685
];

lib/src/request/pipeline/pipeline_configurator.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:dart_mediator/src/request/pipeline/pipeline_behavior.dart';
2+
import 'package:dart_mediator/src/request/request.dart';
23

34
/// Factory to create a [PipelineBehavior].
45
typedef PipelineBehaviorFactory<TRequest, TResponse>
@@ -9,15 +10,16 @@ abstract interface class PipelineConfigurator {
910
///
1011
/// When using a generic [PipelineBehavior] the [registerGeneric] should be
1112
/// used instead.
12-
void register<TResponse extends Object?, TRequest extends Object>(
13+
void register<TResponse extends Object?, TRequest extends Request<TResponse>>(
1314
PipelineBehavior<TResponse, TRequest> behavior,
1415
);
1516

1617
/// Registers the [factory].
1718
///
1819
/// When using a generic [PipelineBehavior] the [registerGenericFactory] should
1920
/// be used instead.
20-
void registerFactory<TResponse extends Object?, TRequest extends Object>(
21+
void registerFactory<TResponse extends Object?,
22+
TRequest extends Request<TResponse>>(
2123
PipelineBehaviorFactory<TResponse, TRequest> factory,
2224
);
2325

lib/src/request/request_manager.dart

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,13 @@ class RequestManager {
5757
/// This request can be wrapped by [PipelineBehavior]'s see [pipeline].
5858
///
5959
/// This will return [TResponse].
60-
Future<TResponse>
61-
send<TResponse extends Object?, TRequest extends Request<TResponse>>(
62-
TRequest request,
60+
Future<TResponse> send<TResponse extends Object?>(
61+
Request<TResponse> request,
6362
) async {
64-
final handler = _requestHandlerStore.getHandlerFor<TResponse, TRequest>();
63+
final handler = _requestHandlerStore.getHandlerFor(request)
64+
as RequestHandler<TResponse, Request<TResponse>>;
6565

66-
final pipelines =
67-
_pipelineBehaviorStore.getPipelines<TResponse, TRequest>();
66+
final pipelines = _pipelineBehaviorStore.getPipelines(request);
6867

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

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

76-
final response = await executionPlan();
75+
final futureOrResult = executionPlan();
76+
final response =
77+
futureOrResult is Future ? await futureOrResult : futureOrResult;
7778

7879
assert(
7980
response is TResponse,
80-
'$TRequest expected a return type of $TResponse but '
81+
'$request expected a return type of $TResponse but '
8182
'got one of type ${response.runtimeType}. '
8283
'One of the registered pipelines is not correctly returning the '
8384
'`next()` call. Pipelines used: $pipelines',

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: dart_mediator
22
description: >
33
A simple yet highly configurable Mediator implementation
44
that allows sending requests and publishing events.
5-
version: 0.1.1
5+
version: 0.2.0
66
repository: https://github.com/MatthiWare/mediator.dart
77

88
environment:

test/integration/choreography_test.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ void main() {
4444
),
4545
);
4646

47-
final stock = await mediator.requests
48-
.send<Map<String, int>, GetInventoryQuery>(GetInventoryQuery());
47+
final stock = await mediator.requests.send(GetInventoryQuery());
4948

5049
expect(stock, {
5150
'mouse': 8,

0 commit comments

Comments
 (0)