Skip to content

Commit b80998e

Browse files
feat: add optional middleware to workers (#33)
* add optional middleware to handlers * move route middleware assignment to route definition
1 parent 414a388 commit b80998e

File tree

12 files changed

+251
-81
lines changed

12 files changed

+251
-81
lines changed

lib/src/api/bucket.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ class Bucket {
5555

5656
/// Create a blob event subscription triggered on the [blobEventType] filtered by files that match the [keyPrefixFilter].
5757
Future<void> on(BlobEventType blobEventType, String keyPrefixFilter,
58-
FileEventHandler handler) async {
58+
FileEventHandler handler,
59+
{List<FileEventHandler> middlewares = const []}) async {
5960
// Create the request to register the Storage listener with the membrane
6061
final eventType = switch (blobEventType) {
6162
BlobEventType.write => $p.BlobEventType.Created,
@@ -68,7 +69,10 @@ class Bucket {
6869
blobEventType: eventType,
6970
);
7071

71-
var worker = FileEventWorker(registrationRequest, handler, this,
72+
final composedHandler =
73+
composeMiddleware([...middlewares, handler], FileEventContext.fromCtx);
74+
75+
var worker = FileEventWorker(registrationRequest, composedHandler, this,
7276
client: _storageListenerClient);
7377

7478
await worker.start();

lib/src/context/blobevent.dart

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,27 @@ enum BlobEventType { write, delete }
55
/// The context of a Blob event request/response.
66
class BlobEventContext
77
extends TriggerContext<BlobEventRequest, BlobEventResponse> {
8-
BlobEventContext(super.id, super.req, super.resp);
8+
late BlobEventHandler _nextHandler;
9+
10+
BlobEventContext(super.id, super.req, super.resp,
11+
{next = _defaultHandler<BlobEventContext>}) {
12+
_nextHandler = next;
13+
}
914

1015
/// Create a Blob Event context from a server message.
1116
BlobEventContext.fromRequest($bp.ServerMessage msg)
12-
: this(msg.id, BlobEventRequest(msg.blobEventRequest.blobEvent.key),
13-
BlobEventResponse());
17+
: this(
18+
msg.id,
19+
BlobEventRequest(msg.blobEventRequest.blobEvent.key),
20+
BlobEventResponse(),
21+
);
22+
23+
BlobEventContext.fromCtx(BlobEventContext ctx, BlobEventHandler next)
24+
: this(ctx.id, ctx.req, ctx.res, next: next);
25+
26+
Future<BlobEventContext> next() async {
27+
return await _nextHandler(this);
28+
}
1429

1530
/// Converts the context to a gRPC client response.
1631
$bp.ClientMessage toResponse() {
@@ -21,14 +36,28 @@ class BlobEventContext
2136
/// The context of a Blob event request/response.
2237
class FileEventContext
2338
extends TriggerContext<FileEventRequest, BlobEventResponse> {
24-
FileEventContext(super.id, super.req, super.resp);
39+
late FileEventHandler _nextHandler;
40+
41+
FileEventContext(super.id, super.req, super.resp,
42+
{next = _defaultHandler<FileEventContext>}) {
43+
_nextHandler = next;
44+
}
2545

2646
/// Create a Blob Event context from a server message.
2747
FileEventContext.fromRequest($bp.ServerMessage msg, Bucket bucket)
2848
: this(
29-
msg.id,
30-
FileEventRequest(bucket.file(msg.blobEventRequest.blobEvent.key)),
31-
BlobEventResponse());
49+
msg.id,
50+
FileEventRequest(bucket.file(msg.blobEventRequest.blobEvent.key)),
51+
BlobEventResponse(),
52+
);
53+
54+
/// Call the next middleware in the middleware chain
55+
FileEventContext.fromCtx(FileEventContext ctx, FileEventHandler next)
56+
: this(ctx.id, ctx.req, ctx.res, next: next);
57+
58+
Future<FileEventContext> next() async {
59+
return await _nextHandler(this);
60+
}
3261

3362
/// Converts the context to a gRPC client response.
3463
$bp.ClientMessage toResponse() {

lib/src/context/http.dart

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ part of './common.dart';
22

33
/// The context of a HTTP request/response triggered by a request to an API.
44
class HttpContext extends TriggerContext<HttpRequest, HttpResponse> {
5-
HttpContext(super.id, super.req, super.resp);
5+
late HttpHandler _nextHandler;
6+
7+
HttpContext(super.id, super.req, super.resp,
8+
{next = _defaultHandler<HttpContext>}) {
9+
_nextHandler = next;
10+
}
611

712
/// Create a HTTP context from a server message.
813
HttpContext.fromRequest($ap.ServerMessage msg)
@@ -21,6 +26,14 @@ class HttpContext extends TriggerContext<HttpRequest, HttpResponse> {
2126
HttpResponse(),
2227
);
2328

29+
HttpContext.fromCtx(HttpContext ctx, HttpHandler next)
30+
: this(ctx.id, ctx.req, ctx.res, next: next);
31+
32+
/// Call the next middleware in the middleware chain
33+
Future<HttpContext> next() async {
34+
return await _nextHandler(this);
35+
}
36+
2437
/// Converts the context to a gRPC client response.
2538
$ap.ClientMessage toResponse() {
2639
return $ap.ClientMessage(id: id, httpResponse: res.toWire());

lib/src/context/interval.dart

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,28 @@ part of './common.dart';
33
/// The context for a scheduled interval request/response.
44
class IntervalContext
55
extends TriggerContext<IntervalRequest, IntervalResponse> {
6-
IntervalContext(super.id, super.req, super.resp);
6+
late IntervalHandler _nextHandler;
7+
8+
IntervalContext(super.id, super.req, super.resp,
9+
{next = _defaultHandler<IntervalContext>}) {
10+
_nextHandler = next;
11+
}
712

813
/// Create an Interval context from a server message.
914
IntervalContext.fromRequest($sp.ServerMessage msg)
1015
: this(
11-
msg.id,
12-
IntervalRequest(scheduleName: msg.intervalRequest.scheduleName),
13-
IntervalResponse());
16+
msg.id,
17+
IntervalRequest(scheduleName: msg.intervalRequest.scheduleName),
18+
IntervalResponse(),
19+
);
20+
21+
IntervalContext.fromCtx(IntervalContext ctx, IntervalHandler next)
22+
: this(ctx.id, ctx.req, ctx.res, next: next);
23+
24+
/// Call the next middleware in the middleware chain
25+
Future<IntervalContext> next() async {
26+
return await _nextHandler(this);
27+
}
1428

1529
/// Converts the context to a gRPC client response.
1630
$sp.ClientMessage toResponse() {

lib/src/context/message.dart

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,33 @@ part of './common.dart';
22

33
/// The context for a topic message for a subscription.
44
class MessageContext extends TriggerContext<MessageRequest, MessageResponse> {
5-
MessageContext(super.id, super.req, super.resp);
5+
late MessageHandler _nextHandler;
6+
7+
MessageContext(super.id, super.req, super.resp,
8+
{next = _defaultHandler<MessageContext>}) {
9+
_nextHandler = next;
10+
}
611

712
/// Create an Event context from a server message.
813
factory MessageContext.fromRequest($ep.ServerMessage msg) {
914
var payload = Proto.mapFromStruct(msg.messageRequest.message.structPayload);
1015

1116
return MessageContext(
12-
msg.id,
13-
MessageRequest(
14-
msg.messageRequest.topicName,
15-
payload,
16-
),
17-
MessageResponse());
17+
msg.id,
18+
MessageRequest(
19+
msg.messageRequest.topicName,
20+
payload,
21+
),
22+
MessageResponse(),
23+
);
24+
}
25+
26+
MessageContext.fromCtx(MessageContext ctx, MessageHandler next)
27+
: this(ctx.id, ctx.req, ctx.res, next: next);
28+
29+
/// Call the next middleware in the middleware chain
30+
Future<MessageContext> next() async {
31+
return await _nextHandler(this);
1832
}
1933

2034
/// Converts the context to a gRPC client response.

lib/src/context/middleware.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,17 @@ typedef MessageHandler = Handler<MessageContext>;
88
typedef BlobEventHandler = Handler<BlobEventContext>;
99
typedef FileEventHandler = Handler<FileEventContext>;
1010
typedef WebsocketHandler = Handler<WebsocketContext>;
11+
12+
Future<T> _defaultHandler<T extends TriggerContext>(T ctx) async => ctx;
13+
14+
Handler<T> composeMiddleware<T extends TriggerContext>(
15+
List<Handler<T>> handlers,
16+
T Function(T ctx, Handler<T> next) converter) =>
17+
(T ctx) async {
18+
final Handler<T> composedHandler = handlers.reversed
19+
.fold((ctx) async => ctx, (nextHandler, currHandler) {
20+
return (ctx) => currHandler(converter(ctx, nextHandler));
21+
});
22+
23+
return await composedHandler(ctx);
24+
};

lib/src/context/websocket.dart

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ enum WebsocketEvent { connect, disconnect, message }
55
/// Base context for a websocket based request/response.
66
class WebsocketContext
77
extends TriggerContext<WebsocketRequest, WebsocketResponse> {
8-
WebsocketContext(super.id, super.req, super.resp);
8+
late WebsocketHandler _nextHandler;
9+
10+
WebsocketContext(super.id, super.req, super.resp,
11+
{next = _defaultHandler<WebsocketContext>}) {
12+
_nextHandler = next;
13+
}
914

1015
factory WebsocketContext.fromRequest($wp.ServerMessage msg) {
1116
var eventType = WebsocketEvent.connect;
@@ -27,14 +32,23 @@ class WebsocketContext
2732
}
2833

2934
return WebsocketContext(
30-
msg.id,
31-
WebsocketRequest(
32-
msg.websocketEventRequest.socketName,
33-
msg.websocketEventRequest.connectionId,
34-
eventType,
35-
queryParams,
36-
message),
37-
WebsocketResponse());
35+
msg.id,
36+
WebsocketRequest(
37+
msg.websocketEventRequest.socketName,
38+
msg.websocketEventRequest.connectionId,
39+
eventType,
40+
queryParams,
41+
message),
42+
WebsocketResponse(),
43+
);
44+
}
45+
46+
WebsocketContext.fromCtx(WebsocketContext ctx, WebsocketHandler next)
47+
: this(ctx.id, ctx.req, ctx.res, next: next);
48+
49+
/// Call the next middleware in the middleware chain
50+
Future<WebsocketContext> next() async {
51+
return await _nextHandler(this);
3852
}
3953

4054
$wp.ClientMessage toResponse() {

0 commit comments

Comments
 (0)