Skip to content

Instantly share code, notes, and snippets.

@luo3house
Last active February 21, 2024 01:48
Show Gist options
  • Save luo3house/4696f7c3994312d40aeea986c079214c to your computer and use it in GitHub Desktop.
Save luo3house/4696f7c3994312d40aeea986c079214c to your computer and use it in GitHub Desktop.
Interceptor Pattern
typedef Middleware<C> = dynamic Function(C context, Function() next);
class Interceptor<C> {
static _noop() {}
final List<Middleware<C>> middlewares = List.empty(growable: true);
Future<void> call<T>(C context, [Function() fn = _noop]) async {
if (middlewares.isEmpty) return fn();
return middlewares.reduce((pre, cur) {
return (context, next) => pre(context, () => cur(context, next));
})(context, fn);
}
Interceptor<C> use(Middleware<C> middleware) {
middlewares.add(middleware);
return this;
}
}
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
typedef Ctx = Map<String, dynamic>;
void main() {
test("empty", () async {
var text = "";
await Interceptor<String>().call("luo", () async => text = "luo");
expect(text, "luo");
});
test("basic", () async {
var text = "";
final interceptor = Interceptor();
interceptor.use((context, next) async {
text = "luo1";
next();
}).use((context, next) async {
text = "luo2";
next();
}).use((context, next) async {
text = "luo3";
next();
});
interceptor.call(null, () async => text = "Hello $text");
expect(text, "Hello luo3");
});
test("auth case", () async {
final interceptor = Interceptor<Ctx>();
authMiddleware(context, next) async {
if (!context.containsKey('authorization')) {
context["status"] = 401;
return;
}
context["user"] = "User Luo";
return next();
}
cacheMiddleware(context, next) async {
await next();
context["cache-control"] = "no-cache";
}
emptyMiddleware(context, next) async {
await next();
}
interceptor.use(authMiddleware).use(cacheMiddleware).use(emptyMiddleware);
Map<String, dynamic> context;
// no auth
context = <String, dynamic>{"req": "Greeting"};
interceptor.call(context, () async {
final user = context["user"];
final req = context["req"];
context["status"] = 200;
context["rsp"] = "Hi $user, You're sending: $req";
});
expect(context["status"], 401);
expect(context["rsp"], null);
expect(context["user"], null);
// with auth
context = <String, dynamic>{"req": "Greeting", "authorization": "******"};
interceptor.call(context, () async {
final user = context["user"];
final req = context["req"];
context["status"] = 200;
context["rsp"] = "Hi $user, You're sending: $req";
});
expect(context["status"], 200);
expect(context["rsp"], "Hi User Luo, You're sending: Greeting");
expect(context["user"], "User Luo");
});
test("the mixed return value", () async {
final interceptor = Interceptor<ValueNotifier<int>>();
interceptor.use((context, next) async {
await Future.delayed(const Duration(milliseconds: 20));
context.value |= 0x01;
await next();
});
interceptor.use((context, next) {
context.value |= 0x02;
return next();
});
final context = ValueNotifier(0);
await interceptor.call(context);
expect(context.value, 0x03);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment