From 38cca24a5668da7157c4e6dfccff09380a945205 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 17 Jan 2025 18:36:20 +0000 Subject: [PATCH 1/9] rework request :onBefore and :onAfter hook --- packages/pharaoh/lib/src/core_impl.dart | 13 ++++-- .../lib/src/middleware/session_mw.dart | 22 +++++----- packages/pharaoh/lib/src/router/router.dart | 12 +++++- .../lib/src/router/router_contract.dart | 2 + .../lib/src/router/router_handler.dart | 20 ++++++---- packages/pharaoh/lib/src/view/view.dart | 40 ++++++++++--------- 6 files changed, 67 insertions(+), 42 deletions(-) diff --git a/packages/pharaoh/lib/src/core_impl.dart b/packages/pharaoh/lib/src/core_impl.dart index 5770b61c..22d8e9a8 100644 --- a/packages/pharaoh/lib/src/core_impl.dart +++ b/packages/pharaoh/lib/src/core_impl.dart @@ -9,7 +9,7 @@ class $PharaohImpl extends RouterContract static ViewEngine? viewEngine_; - final List _preResponseHooks = [ + final List _requestHooks = [ sessionPreResponseHook, viewRenderHook, ]; @@ -114,10 +114,14 @@ class $PharaohImpl extends RouterContract req.params.addAll(routeResult.params); } + for (final hook in _requestHooks.whereNot((e) => e.onBefore == null)) { + reqRes = await hook.onBefore!.call(reqRes.req, reqRes.res); + } + reqRes = await executeHandlers(resolvedHandlers, reqRes); - for (final job in _preResponseHooks) { - reqRes = await Future.microtask(() => job(reqRes)); + for (final hook in _requestHooks.whereNot((e) => e.onAfter == null)) { + reqRes = await hook.onAfter!.call(reqRes.req, reqRes.res); } if (!reqRes.res.ended) { @@ -194,6 +198,9 @@ class $PharaohImpl extends RouterContract @override void onError(OnErrorCallback errorCb) => _onErrorCb = errorCb; + + @override + void addRequestHook(RequestHook hook) => _requestHooks.add(hook); } // ignore: constant_identifier_names diff --git a/packages/pharaoh/lib/src/middleware/session_mw.dart b/packages/pharaoh/lib/src/middleware/session_mw.dart index c96c35dc..a7350b45 100644 --- a/packages/pharaoh/lib/src/middleware/session_mw.dart +++ b/packages/pharaoh/lib/src/middleware/session_mw.dart @@ -92,15 +92,17 @@ Middleware sessionMdw({ }; } -final ReqResHook sessionPreResponseHook = (ReqRes reqRes) async { - var req = reqRes.req, res = reqRes.res; - final session = req.session; - if (session == null) return reqRes; +final sessionPreResponseHook = RequestHook( + onAfter: (req, res) async { + final session = req.session; + final reqRes = (req: req, res: res); + if (session == null) return reqRes; - if (session.saveUninitialized || session.resave || session.modified) { - await session.save(); - res = res.withCookie(session.cookie!); - } + if (session.saveUninitialized || session.resave || session.modified) { + await session.save(); + res = res.withCookie(session.cookie!); + } - return (req: req, res: res); -}; + return (req: req, res: res); + }, +); diff --git a/packages/pharaoh/lib/src/router/router.dart b/packages/pharaoh/lib/src/router/router.dart index f506e6fd..068d4610 100644 --- a/packages/pharaoh/lib/src/router/router.dart +++ b/packages/pharaoh/lib/src/router/router.dart @@ -92,8 +92,11 @@ class GroupRouter extends RouterContract { } @override - GroupRouter on(String path, Middleware func, - {HTTPMethod method = HTTPMethod.ALL}) { + GroupRouter on( + String path, + Middleware func, { + HTTPMethod method = HTTPMethod.ALL, + }) { if (method == HTTPMethod.ALL) path = '$path/*'; _pendingRouteIntents.add((method, (path: path, handler: func))); return this; @@ -109,4 +112,9 @@ class GroupRouter extends RouterContract { } } } + + @override + void addRequestHook(RequestHook hook) { + // TODO: implement addRequestHook + } } diff --git a/packages/pharaoh/lib/src/router/router_contract.dart b/packages/pharaoh/lib/src/router/router_contract.dart index a5cda71c..9dd9d085 100644 --- a/packages/pharaoh/lib/src/router/router_contract.dart +++ b/packages/pharaoh/lib/src/router/router_contract.dart @@ -21,5 +21,7 @@ abstract class RouterContract { void use(Middleware middleware); + void addRequestHook(RequestHook hook); + void on(String path, Middleware hdler, {HTTPMethod method = HTTPMethod.ALL}); } diff --git a/packages/pharaoh/lib/src/router/router_handler.dart b/packages/pharaoh/lib/src/router/router_handler.dart index 11223cc9..5ffb6214 100644 --- a/packages/pharaoh/lib/src/router/router_handler.dart +++ b/packages/pharaoh/lib/src/router/router_handler.dart @@ -6,7 +6,11 @@ import '../utils/exceptions.dart'; typedef ReqRes = ({Request req, Response res}); -typedef ReqResHook = FutureOr Function(ReqRes reqRes); +class RequestHook { + final FutureOr Function(Request req, Response res)? onBefore; + final FutureOr Function(Request req, Response res)? onAfter; + const RequestHook({this.onAfter, this.onBefore}); +} typedef NextFunction = dynamic Function([dynamic result, Next? chain]); @@ -19,13 +23,13 @@ typedef Middleware = FutureOr Function( ); extension ReqResExtension on ReqRes { - ReqRes merge(dynamic val) { - if (val == null) return this; - if (val is Request) return (req: val, res: this.res); - if (val is Response) return (req: this.req, res: val); - if (val is ReqRes) return val; - throw PharaohException.value('Invalid Type used on merge', val); - } + ReqRes merge(dynamic val) => switch (val) { + ReqRes() => val, + Response() => (req: this.req, res: val), + Request() => (req: val, res: this.res), + null => this, + _ => throw PharaohException.value('Invalid Type used on merge', val) + }; } extension MiddlewareChainExtension on Middleware { diff --git a/packages/pharaoh/lib/src/view/view.dart b/packages/pharaoh/lib/src/view/view.dart index 25c9ca13..4661bba6 100644 --- a/packages/pharaoh/lib/src/view/view.dart +++ b/packages/pharaoh/lib/src/view/view.dart @@ -19,22 +19,24 @@ class ViewRenderData { const ViewRenderData(this.name, this.data); } -final ReqResHook viewRenderHook = (ReqRes reqRes) async { - var res = reqRes.res; - final viewData = res.viewToRender; - if (viewData == null) return reqRes; - - final viewEngine = $PharaohImpl.viewEngine_; - if (viewEngine == null) throw PharaohException('No view engine found'); - - try { - final result = await Isolate.run( - () => viewEngine.render(viewData.name, viewData.data), - ); - res = res.end()..body = shelf.ShelfBody(result); - } catch (e) { - throw PharaohException.value('Failed to render view ${viewData.name}', e); - } - - return reqRes.merge(res); -}; +final viewRenderHook = RequestHook( + onAfter: (req, res) async { + final viewData = res.viewToRender; + final reqRes = (req: req, res: res); + if (viewData == null) return reqRes; + + final viewEngine = $PharaohImpl.viewEngine_; + if (viewEngine == null) throw PharaohException('No view engine found'); + + try { + final result = await Isolate.run( + () => viewEngine.render(viewData.name, viewData.data), + ); + res = res.end()..body = shelf.ShelfBody(result); + } catch (e) { + throw PharaohException.value('Failed to render view ${viewData.name}', e); + } + + return reqRes.merge(res); + }, +); From cb2fc9d1c525313dd3390e9de06879ce34fde467 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 17 Jan 2025 18:49:36 +0000 Subject: [PATCH 2/9] _ --- packages/pharaoh/lib/src/core_impl.dart | 5 +-- packages/pharaoh/lib/src/http/request.dart | 3 +- .../pharaoh/lib/src/http/request_impl.dart | 2 +- .../lib/src/middleware/request_logger.dart | 33 ++++++++++++------- .../lib/src/middleware/session_mw.dart | 1 - .../pharaoh/test/http/res.status_test.dart | 6 ++-- 6 files changed, 29 insertions(+), 21 deletions(-) diff --git a/packages/pharaoh/lib/src/core_impl.dart b/packages/pharaoh/lib/src/core_impl.dart index 22d8e9a8..b831d843 100644 --- a/packages/pharaoh/lib/src/core_impl.dart +++ b/packages/pharaoh/lib/src/core_impl.dart @@ -119,15 +119,12 @@ class $PharaohImpl extends RouterContract } reqRes = await executeHandlers(resolvedHandlers, reqRes); + if (!reqRes.res.ended) reqRes = reqRes.merge(routeNotFound()); for (final hook in _requestHooks.whereNot((e) => e.onAfter == null)) { reqRes = await hook.onAfter!.call(reqRes.req, reqRes.res); } - if (!reqRes.res.ended) { - return reqRes.merge(routeNotFound()); - } - return reqRes; } diff --git a/packages/pharaoh/lib/src/http/request.dart b/packages/pharaoh/lib/src/http/request.dart index d77d8d8d..8912cec5 100644 --- a/packages/pharaoh/lib/src/http/request.dart +++ b/packages/pharaoh/lib/src/http/request.dart @@ -15,8 +15,7 @@ class RequestContext { /// cookies & session static const String cookies = '$phar.cookies'; static const String signedCookies = '$phar.signedcookies'; - static const String session = '$phar.session.cookie'; - static const String sessionId = '$phar.session.id'; + static const String session = '$phar.session'; } HTTPMethod getHttpMethod(HttpRequest req) => switch (req.method) { diff --git a/packages/pharaoh/lib/src/http/request_impl.dart b/packages/pharaoh/lib/src/http/request_impl.dart index 63912822..bdd45e94 100644 --- a/packages/pharaoh/lib/src/http/request_impl.dart +++ b/packages/pharaoh/lib/src/http/request_impl.dart @@ -71,7 +71,7 @@ class _$RequestImpl extends Request { Session? get session => _context[RequestContext.session]; @override - String? get sessionId => _context[RequestContext.sessionId]; + String? get sessionId => session?.id; @override Object? operator [](String name) => _context[name]; diff --git a/packages/pharaoh/lib/src/middleware/request_logger.dart b/packages/pharaoh/lib/src/middleware/request_logger.dart index 583d4ca6..e783ac0f 100644 --- a/packages/pharaoh/lib/src/middleware/request_logger.dart +++ b/packages/pharaoh/lib/src/middleware/request_logger.dart @@ -1,14 +1,25 @@ -import '../http/request.dart'; -import '../http/response.dart'; +import 'dart:io'; + import '../router/router_handler.dart'; -logRequests(Request req, Response res, NextFunction next) { - final logLines = """ +final logRequestHook = RequestHook( + onBefore: (req, res) async { + req['startTime'] = DateTime.now(); + return (req: req, res: res); + }, + onAfter: (req, res) async { + final startTime = req['startTime'] as DateTime; + final elapsedTime = DateTime.now().difference(startTime).inMilliseconds; + + final logLines = """ +Request: ${req.method.name} ${req.path} +Content-Type: ${req.mimeType} +Status Code: ${res.statusCode} +Elapsed Time: ${"$elapsedTime ms"} ------------------------------------------------------- -Path: ${req.path} -Method: ${req.method.name} -Content-Type ${req.mimeType} --------------------------------------------------------\n"""; - print(logLines); - next(); -} +"""; + stdout.writeln(logLines); + + return (req: req, res: res); + }, +); diff --git a/packages/pharaoh/lib/src/middleware/session_mw.dart b/packages/pharaoh/lib/src/middleware/session_mw.dart index a7350b45..4bc1971a 100644 --- a/packages/pharaoh/lib/src/middleware/session_mw.dart +++ b/packages/pharaoh/lib/src/middleware/session_mw.dart @@ -61,7 +61,6 @@ Middleware sessionMdw({ return (req, res, next) async { nextWithSession(Session session) { - req[RequestContext.sessionId] = session.id; req[RequestContext.session] = session.._withStore(sessionStore); return next((req: req, res: res)); } diff --git a/packages/pharaoh/test/http/res.status_test.dart b/packages/pharaoh/test/http/res.status_test.dart index a47c8b04..b458bda2 100644 --- a/packages/pharaoh/test/http/res.status_test.dart +++ b/packages/pharaoh/test/http/res.status_test.dart @@ -45,8 +45,10 @@ void main() { try { await (await request(app)).get('/').test(); } catch (e) { - expect((e as StateError).message, - 'Response has no Location header for redirect'); + expect( + (e as dynamic).message, + 'Server response has no Location header for redirect', + ); } }); }); From 8449f366c56a356f4b1b79724c78a208369c057f Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 17 Jan 2025 18:53:21 +0000 Subject: [PATCH 3/9] _ --- packages/pharaoh/lib/src/core_impl.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pharaoh/lib/src/core_impl.dart b/packages/pharaoh/lib/src/core_impl.dart index b831d843..e0d75afe 100644 --- a/packages/pharaoh/lib/src/core_impl.dart +++ b/packages/pharaoh/lib/src/core_impl.dart @@ -115,7 +115,7 @@ class $PharaohImpl extends RouterContract } for (final hook in _requestHooks.whereNot((e) => e.onBefore == null)) { - reqRes = await hook.onBefore!.call(reqRes.req, reqRes.res); + reqRes = await hook.onBefore!.call(req, reqRes.res); } reqRes = await executeHandlers(resolvedHandlers, reqRes); @@ -129,7 +129,7 @@ class $PharaohImpl extends RouterContract } Future forward(HttpRequest request, Response res_) async { - var coding = res_.headers['transfer-encoding']; + var coding = res_.headers[HttpHeaders.transferEncodingHeader]; final statusCode = res_.statusCode; request.response.statusCode = statusCode; From 41a088618d413e1f0112d7cbe449d8be52608d86 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 17 Jan 2025 18:55:32 +0000 Subject: [PATCH 4/9] _ --- packages/pharaoh/lib/src/middleware/request_logger.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pharaoh/lib/src/middleware/request_logger.dart b/packages/pharaoh/lib/src/middleware/request_logger.dart index e783ac0f..3d48d799 100644 --- a/packages/pharaoh/lib/src/middleware/request_logger.dart +++ b/packages/pharaoh/lib/src/middleware/request_logger.dart @@ -15,7 +15,7 @@ final logRequestHook = RequestHook( Request: ${req.method.name} ${req.path} Content-Type: ${req.mimeType} Status Code: ${res.statusCode} -Elapsed Time: ${"$elapsedTime ms"} +Elapsed Time: ${elapsedTime} ms ------------------------------------------------------- """; stdout.writeln(logLines); From 3b7039ddeb4c17542a379ecc637f9bc9b6175ad9 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 17 Jan 2025 19:01:57 +0000 Subject: [PATCH 5/9] _ --- pharaoh_examples/lib/middleware/index.dart | 2 +- pharaoh_examples/lib/route_groups/index.dart | 3 +-- pharaoh_examples/lib/serve_files_2/index.dart | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pharaoh_examples/lib/middleware/index.dart b/pharaoh_examples/lib/middleware/index.dart index cdb72106..913686b2 100644 --- a/pharaoh_examples/lib/middleware/index.dart +++ b/pharaoh_examples/lib/middleware/index.dart @@ -3,7 +3,7 @@ import 'package:pharaoh/pharaoh.dart'; final app = Pharaoh(); void main() async { - app.use(logRequests); + app.addRequestHook(logRequestHook); app.get('/', (req, res) => res.ok("Hurray 🚀")); diff --git a/pharaoh_examples/lib/route_groups/index.dart b/pharaoh_examples/lib/route_groups/index.dart index 124a6e50..4bcf8770 100644 --- a/pharaoh_examples/lib/route_groups/index.dart +++ b/pharaoh_examples/lib/route_groups/index.dart @@ -1,6 +1,6 @@ import 'package:pharaoh/pharaoh.dart'; -final app = Pharaoh(); +final app = Pharaoh()..addRequestHook(logRequestHook); void main() async { final guestRouter = Pharaoh.router @@ -9,7 +9,6 @@ void main() async { ..put('/yoo', (req, res) => res.json({"pookey": "reyrey"})); final adminRouter = Pharaoh.router - ..use(logRequests) ..get('/user', (req, res) => res.json({"chima": "happy"})) ..put('/hello', (req, res) => res.json({"name": "chima"})) ..post('/say-hello', (req, res) => res.notFound()) diff --git a/pharaoh_examples/lib/serve_files_2/index.dart b/pharaoh_examples/lib/serve_files_2/index.dart index bb67067a..03d4daf6 100644 --- a/pharaoh_examples/lib/serve_files_2/index.dart +++ b/pharaoh_examples/lib/serve_files_2/index.dart @@ -12,7 +12,7 @@ final serveStatic = createStaticHandler( final cors = corsHeaders(); void main() async { - app.use(logRequests); + app.addRequestHook(logRequestHook); app.use(useShelfMiddleware(cors)); From 81402018f712269586eb4bfce63e876b28acc696 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Wed, 22 Jan 2025 18:46:07 +0000 Subject: [PATCH 6/9] improvements to spanner --- .../lib/src/parametric/definition.dart | 6 + packages/spanner/lib/src/route/action.dart | 26 +++- packages/spanner/lib/src/tree/node.dart | 7 +- packages/spanner/lib/src/tree/tree.dart | 111 +++++++++--------- 4 files changed, 92 insertions(+), 58 deletions(-) diff --git a/packages/spanner/lib/src/parametric/definition.dart b/packages/spanner/lib/src/parametric/definition.dart index 08b5f954..9d07b03e 100644 --- a/packages/spanner/lib/src/parametric/definition.dart +++ b/packages/spanner/lib/src/parametric/definition.dart @@ -44,6 +44,9 @@ abstract class ParameterDefinition implements HandlerStore { List collector, { bool caseSentive = false, }); + + @override + Object get owner => this; } class SingleParameterDefn extends ParameterDefinition with HandlerStoreMixin { @@ -151,6 +154,9 @@ class CompositeParameterDefinition extends ParameterDefinition _maybeTerminalPart.addRoute(method, handler); } + @override + void offsetIndex(int index) => _maybeTerminalPart.offsetIndex(index); + @override IndexedValue? getHandler(HTTPMethod method) { return _maybeTerminalPart.getHandler(method); diff --git a/packages/spanner/lib/src/route/action.dart b/packages/spanner/lib/src/route/action.dart index c6e250bc..2ce5e30d 100644 --- a/packages/spanner/lib/src/route/action.dart +++ b/packages/spanner/lib/src/route/action.dart @@ -4,15 +4,19 @@ typedef Indexed = ({int index, T value}); typedef IndexedValue = Indexed; -abstract interface class HandlerStore { +abstract interface class HandlerStore { IndexedValue? getHandler(HTTPMethod method); Iterable get methods; + void offsetIndex(int index); + bool hasMethod(HTTPMethod method); void addRoute(HTTPMethod method, IndexedValue handler); void addMiddleware(IndexedValue handler); + + Owner get owner; } mixin HandlerStoreMixin implements HandlerStore { @@ -22,6 +26,26 @@ mixin HandlerStoreMixin implements HandlerStore { null, ); + @override + void offsetIndex(int index) { + for (final middleware in middlewares.indexed) { + middlewares[middleware.$1] = ( + index: middleware.$2.index + index, + value: middleware.$2.value, + ); + } + + // Offset the indices of request handlers + for (int i = 0; i < requestHandlers.length; i++) { + if (requestHandlers[i] != null) { + requestHandlers[i] = ( + index: requestHandlers[i]!.index + index, + value: requestHandlers[i]!.value, + ); + } + } + } + @override Iterable get methods => HTTPMethod.values.where(hasMethod); diff --git a/packages/spanner/lib/src/tree/node.dart b/packages/spanner/lib/src/tree/node.dart index 71ed9d18..36ec2e9d 100644 --- a/packages/spanner/lib/src/tree/node.dart +++ b/packages/spanner/lib/src/tree/node.dart @@ -98,6 +98,9 @@ abstract class Node with HandlerStoreMixin { } String get routes => _getRoutes('/', this); + + @override + Object get owner => this; } class StaticNode extends Node { @@ -110,7 +113,7 @@ class StaticNode extends Node { } class ParametricNode extends Node { - static final String key = '<:>'; + static const String key = '<:>'; final Map> _definitionsMap; @@ -203,7 +206,7 @@ class ParametricNode extends Node { } class WildcardNode extends StaticNode { - static final String key = '*'; + static const String key = '*'; WildcardNode() : super(WildcardNode.key); diff --git a/packages/spanner/lib/src/tree/tree.dart b/packages/spanner/lib/src/tree/tree.dart index 77c7f548..599876d9 100644 --- a/packages/spanner/lib/src/tree/tree.dart +++ b/packages/spanner/lib/src/tree/tree.dart @@ -11,14 +11,14 @@ const BASE_PATH = '/'; enum HTTPMethod { GET, HEAD, POST, PUT, DELETE, ALL, PATCH, OPTIONS, TRACE } class RouterConfig { + final String rootPath; final bool caseSensitive; final bool ignoreTrailingSlash; - final bool ignoreDuplicateSlashes; const RouterConfig({ + this.rootPath = BASE_PATH, this.caseSensitive = true, this.ignoreTrailingSlash = true, - this.ignoreDuplicateSlashes = true, }); } @@ -32,7 +32,8 @@ class Spanner { int get _nextIndex => _currentIndex + 1; - Spanner({this.config = const RouterConfig()}) : _root = StaticNode(BASE_PATH); + Spanner({this.config = const RouterConfig()}) + : _root = StaticNode(config.rootPath); void addRoute(HTTPMethod method, String path, T handler) { _on(method, path).addRoute(method, ( @@ -52,20 +53,28 @@ class Spanner { _currentIndex = _nextIndex; } + void attachNode(Node node) { + if (node is! StaticNode) { + throw UnsupportedError('Only Static Nodes are supported for now'); + } + + root.addChildAndReturn(node.route, node..offsetIndex(_currentIndex)); + + _currentIndex = _nextIndex; + } + HandlerStore _on(HTTPMethod method, String path) { - path = _cleanPath(path); + final pathSegments = getRoutePathSegments(path); Node rootNode = _root; - if (path == BASE_PATH) { + if (pathSegments.isEmpty) { return rootNode; - } else if (path == WildcardNode.key) { + } else if (pathSegments[0] == WildcardNode.key) { return rootNode.wildcardNode ?? rootNode.addChildAndReturn(WildcardNode.key, WildcardNode()); } - final pathSegments = path.split('/'); - for (int i = 0; i < pathSegments.length; i += 2) { final firstPart = pathSegments[i]; final secondPart = @@ -165,9 +174,7 @@ class Spanner { } RouteResult? lookup(HTTPMethod method, dynamic route) { - var path = route is Uri ? route.path : route.toString(); - if (path.startsWith(BASE_PATH)) path = path.substring(1); - if (path.endsWith(BASE_PATH)) path = path.substring(0, path.length - 1); + final pathSegments = getRoutePathSegments(route); final resolvedParams = []; final resolvedHandlers = [...root.middlewares]; @@ -175,26 +182,24 @@ class Spanner { getResults(IndexedValue? handler) => handler != null ? (resolvedHandlers..add(handler)) : resolvedHandlers; - Node rootNode = _root; - - if (path.isEmpty) { + if (pathSegments.isEmpty) { return RouteResult( resolvedParams, - getResults(rootNode.getHandler(method)), + getResults(_root.getHandler(method)), ); } + Node rootNode = _root; + /// keep track of last wildcard we encounter along route. We'll resort to this /// incase we don't find the route we were looking for. var wildcardNode = rootNode.wildcardNode; - final routeSegments = route is Uri ? route.pathSegments : path.split('/'); - - for (int i = 0; i < routeSegments.length; i++) { - final currPart = routeSegments[i]; + for (int i = 0; i < pathSegments.length; i++) { + final currPart = pathSegments[i]; final routePart = config.caseSensitive ? currPart : currPart.toLowerCase(); - final isLastPart = i == (routeSegments.length - 1); + final isLastPart = i == (pathSegments.length - 1); final parametricNode = rootNode.paramNode; final childNode = rootNode.maybeChild(routePart) ?? @@ -202,26 +207,31 @@ class Spanner { wildcardNode = childNode?.wildcardNode ?? wildcardNode; - if (childNode == null && parametricNode == null && wildcardNode == null) { - return RouteResult(resolvedParams, getResults(null)); + if (childNode == null && parametricNode == null) { + if (wildcardNode == null) { + return RouteResult(resolvedParams, getResults(null)); + } + + return RouteResult( + resolvedParams, + getResults(wildcardNode.getHandler(method)), + actual: wildcardNode, + ); } - // set root node as current child - rootNode = childNode ?? parametricNode ?? rootNode; + rootNode = (childNode ?? parametricNode)!; final definition = parametricNode?.findMatchingDefinition( method, routePart, terminal: isLastPart, caseSensitive: config.caseSensitive, - nextPart: isLastPart ? null : routeSegments[i + 1], + nextPart: isLastPart ? null : pathSegments[i + 1], ); - /// If we don't find no matching Static path or a Parametric Node, OR - /// we don't find a matching path or a matching definition, then + /// If we don't find a matching path or a matching definition, then /// use wildcard if we have any registered - if ((childNode == null && parametricNode == null) || - (childNode == null && definition == null)) { + if (childNode == null && definition == null) { if (wildcardNode != null) rootNode = wildcardNode; break; } @@ -246,35 +256,26 @@ class Spanner { } } - if (!rootNode.terminal) { - return RouteResult(resolvedParams, getResults(null), actual: null); - } + return !rootNode.terminal + ? RouteResult(resolvedParams, getResults(null), actual: null) + : RouteResult( + resolvedParams, + getResults(rootNode.getHandler(method)), + actual: rootNode, + ); + } - final handler = rootNode.getHandler(method); - if (handler == null && wildcardNode != null) { - return RouteResult( - resolvedParams, - getResults(wildcardNode.getHandler(method)), - actual: wildcardNode, - ); - } + List getRoutePathSegments(dynamic route) { + if (route is Uri) return route.pathSegments; + if (route == BASE_PATH) return const []; + if (route == WildcardNode.key) return const [WildcardNode.key]; - return RouteResult(resolvedParams, getResults(handler), actual: rootNode); - } + var path = route.toString(); + if (path.isEmpty) return const []; - String _cleanPath(String path) { - if ([BASE_PATH, WildcardNode.key].contains(path)) return path; - if (!path.startsWith(BASE_PATH)) { - throw ArgumentError.value( - path, null, 'Route registration must start with `/`'); - } - if (config.ignoreDuplicateSlashes) { - path = path.replaceAll(RegExp(r'/+'), '/'); - } - if (config.ignoreTrailingSlash) { - path = path.replaceAll(RegExp(r'/+$'), ''); - } - return path.substring(1); + if (path.startsWith(BASE_PATH)) path = path.substring(1); + if (path.endsWith(BASE_PATH)) path = path.substring(0, path.length - 1); + return path.split('/'); } } From 6af5c4c44526a301b59673535ab93968134f0e27 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Wed, 22 Jan 2025 19:43:05 +0000 Subject: [PATCH 7/9] fix implementation for re-attaching nodes back into pharaoh tree --- .../acceptance/request_handling_test.dart | 9 ++--- .../lib/src/parametric/definition.dart | 3 -- packages/spanner/lib/src/route/action.dart | 2 -- packages/spanner/lib/src/tree/node.dart | 3 -- packages/spanner/lib/src/tree/tree.dart | 34 ++++++++++++++++--- 5 files changed, 34 insertions(+), 17 deletions(-) diff --git a/packages/pharaoh/test/acceptance/request_handling_test.dart b/packages/pharaoh/test/acceptance/request_handling_test.dart index b6173e55..0ef158c1 100644 --- a/packages/pharaoh/test/acceptance/request_handling_test.dart +++ b/packages/pharaoh/test/acceptance/request_handling_test.dart @@ -130,12 +130,10 @@ void main() { test('should execute route groups', () async { final app = Pharaoh() - ..get( - '/users/', - (req, res) => res.json(req.params), - ); + ..get('/users/', (req, res) => res.json(req.params)); final router = Pharaoh.router + ..use((req, res, next) => next(res.header('admin', '1'))) ..get('/', (req, res) => res.ok('Group working')) ..delete('/say-hello', (req, res) => res.ok('Hello World')); @@ -144,17 +142,20 @@ void main() { await (await request(app)) .get('/users/chima') .expectStatus(200) + .expectHeader('admin', isNull) .expectBody({'userId': 'chima'}).test(); await (await request(app)) .get('/api/v1') .expectStatus(200) + .expectHeader('admin', '1') .expectBody('Group working') .test(); await (await request(app)) .delete('/api/v1/say-hello') .expectStatus(200) + .expectHeader('admin', '1') .expectBody('Hello World') .test(); }); diff --git a/packages/spanner/lib/src/parametric/definition.dart b/packages/spanner/lib/src/parametric/definition.dart index 9d07b03e..1b894418 100644 --- a/packages/spanner/lib/src/parametric/definition.dart +++ b/packages/spanner/lib/src/parametric/definition.dart @@ -44,9 +44,6 @@ abstract class ParameterDefinition implements HandlerStore { List collector, { bool caseSentive = false, }); - - @override - Object get owner => this; } class SingleParameterDefn extends ParameterDefinition with HandlerStoreMixin { diff --git a/packages/spanner/lib/src/route/action.dart b/packages/spanner/lib/src/route/action.dart index 2ce5e30d..75afa319 100644 --- a/packages/spanner/lib/src/route/action.dart +++ b/packages/spanner/lib/src/route/action.dart @@ -15,8 +15,6 @@ abstract interface class HandlerStore { void addRoute(HTTPMethod method, IndexedValue handler); void addMiddleware(IndexedValue handler); - - Owner get owner; } mixin HandlerStoreMixin implements HandlerStore { diff --git a/packages/spanner/lib/src/tree/node.dart b/packages/spanner/lib/src/tree/node.dart index 36ec2e9d..2b691362 100644 --- a/packages/spanner/lib/src/tree/node.dart +++ b/packages/spanner/lib/src/tree/node.dart @@ -98,9 +98,6 @@ abstract class Node with HandlerStoreMixin { } String get routes => _getRoutes('/', this); - - @override - Object get owner => this; } class StaticNode extends Node { diff --git a/packages/spanner/lib/src/tree/tree.dart b/packages/spanner/lib/src/tree/tree.dart index 599876d9..01d398ff 100644 --- a/packages/spanner/lib/src/tree/tree.dart +++ b/packages/spanner/lib/src/tree/tree.dart @@ -53,14 +53,38 @@ class Spanner { _currentIndex = _nextIndex; } - void attachNode(Node node) { - if (node is! StaticNode) { - throw UnsupportedError('Only Static Nodes are supported for now'); + void attachNode(String path, Node node) { + final pathSegments = getRoutePathSegments(path); + if (pathSegments.isEmpty) { + root.addChildAndReturn(BASE_PATH, node); } - root.addChildAndReturn(node.route, node..offsetIndex(_currentIndex)); + Node rootNode = root; - _currentIndex = _nextIndex; + final totalLength = pathSegments.length; + for (int i = 0; i < totalLength; i++) { + final routePart = pathSegments[i]; + final isLastPart = i == totalLength - 1; + + final maybeChild = rootNode.maybeChild(routePart); + if (isLastPart) { + if (maybeChild != null) { + throw ArgumentError.value(path, null, 'Route entry already exists'); + } + + rootNode = rootNode.addChildAndReturn( + routePart, + node..offsetIndex(_currentIndex), + ); + + _currentIndex = _nextIndex; + break; + } + + if (maybeChild == null) { + rootNode = rootNode.addChildAndReturn(routePart, StaticNode(routePart)); + } + } } HandlerStore _on(HTTPMethod method, String path) { From 7a6950945c41c38b69f0384fe3af268f9d4fc9b2 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Wed, 22 Jan 2025 19:45:10 +0000 Subject: [PATCH 8/9] rework pharaoh exports --- packages/pharaoh/lib/pharaoh.dart | 4 +- packages/pharaoh/lib/pharaoh_next.dart | 11 +- .../lib/src/_next/_core/core_impl.dart | 6 +- packages/pharaoh/lib/src/_next/http.dart | 2 +- packages/pharaoh/lib/src/_next/router.dart | 2 +- packages/pharaoh/lib/src/core.dart | 55 -------- packages/pharaoh/lib/src/http/request.dart | 1 - .../src/{core_impl.dart => http/router.dart} | 87 ++++++++++--- .../router/router_contract.dart} | 26 +++- .../src/{ => http}/router/router_handler.dart | 8 +- .../lib/src/middleware/body_parser.dart | 2 +- .../lib/src/middleware/cookie_parser.dart | 2 +- .../lib/src/middleware/request_logger.dart | 2 +- .../lib/src/middleware/session_mw.dart | 2 +- packages/pharaoh/lib/src/router/router.dart | 120 ------------------ .../lib/src/router/router_contract.dart | 27 ---- .../lib/src/shelf_interop/adapter.dart | 2 +- packages/pharaoh/lib/src/view/view.dart | 6 +- .../pharaoh/test/http/res.render_test.dart | 10 +- .../test/router/router_group_test.dart | 8 +- pharaoh_examples/lib/middleware/index.dart | 2 +- pharaoh_examples/lib/route_groups/index.dart | 2 +- pharaoh_examples/lib/serve_files_2/index.dart | 2 +- 23 files changed, 121 insertions(+), 268 deletions(-) delete mode 100644 packages/pharaoh/lib/src/core.dart rename packages/pharaoh/lib/src/{core_impl.dart => http/router.dart} (77%) rename packages/pharaoh/lib/src/{router/router_mixin.dart => http/router/router_contract.dart} (73%) rename packages/pharaoh/lib/src/{ => http}/router/router_handler.dart (93%) delete mode 100644 packages/pharaoh/lib/src/router/router.dart delete mode 100644 packages/pharaoh/lib/src/router/router_contract.dart diff --git a/packages/pharaoh/lib/pharaoh.dart b/packages/pharaoh/lib/pharaoh.dart index 5cc8df29..7f985397 100644 --- a/packages/pharaoh/lib/pharaoh.dart +++ b/packages/pharaoh/lib/pharaoh.dart @@ -1,17 +1,17 @@ library; -export 'src/core.dart' hide $PharaohImpl; export 'src/view/view.dart'; export 'src/http/cookie.dart'; export 'src/http/request.dart'; export 'src/http/response.dart'; export 'src/utils/utils.dart'; export 'src/utils/exceptions.dart'; -export 'src/router/router_handler.dart'; +export 'src/http/router.dart'; export 'src/middleware/session_mw.dart'; export 'src/middleware/body_parser.dart'; export 'src/middleware/cookie_parser.dart'; export 'src/middleware/request_logger.dart'; +export 'package:spanner/spanner.dart' show HTTPMethod; // shelf export 'src/shelf_interop/adapter.dart'; diff --git a/packages/pharaoh/lib/pharaoh_next.dart b/packages/pharaoh/lib/pharaoh_next.dart index fc17aa97..4f8c8bd0 100644 --- a/packages/pharaoh/lib/pharaoh_next.dart +++ b/packages/pharaoh/lib/pharaoh_next.dart @@ -1,10 +1,5 @@ -export 'src/_next/validation.dart'; -export 'src/_next/router.dart'; +export 'pharaoh.dart'; export 'src/_next/core.dart'; export 'src/_next/http.dart'; -export 'src/core.dart'; - -export 'src/http/response.dart'; -export 'src/http/request.dart'; -export 'src/router/router_handler.dart'; -export 'package:spanner/spanner.dart' show HTTPMethod; +export 'src/_next/router.dart'; +export 'src/_next/validation.dart'; diff --git a/packages/pharaoh/lib/src/_next/_core/core_impl.dart b/packages/pharaoh/lib/src/_next/_core/core_impl.dart index 3b097969..b6ac958c 100644 --- a/packages/pharaoh/lib/src/_next/_core/core_impl.dart +++ b/packages/pharaoh/lib/src/_next/_core/core_impl.dart @@ -38,9 +38,9 @@ class _PharaohNextImpl implements Application { int get port => config.port; Pharaoh _createPharaohInstance({OnErrorCallback? onException}) { - final pharaoh = Pharaoh() - ..useSpanner(_spanner) - ..viewEngine = _viewEngine; + final pharaoh = Pharaoh()..useSpanner(_spanner); + Pharaoh.viewEngine = _viewEngine; + if (onException != null) pharaoh.onError(onException); return pharaoh; } diff --git a/packages/pharaoh/lib/src/_next/http.dart b/packages/pharaoh/lib/src/_next/http.dart index 3387c7ab..0c60583b 100644 --- a/packages/pharaoh/lib/src/_next/http.dart +++ b/packages/pharaoh/lib/src/_next/http.dart @@ -5,7 +5,7 @@ import 'dart:io'; import '../http/request.dart'; import '../http/response.dart'; import '../middleware/session_mw.dart'; -import '../router/router_handler.dart'; +import '../http/router.dart'; import 'core.dart'; @inject diff --git a/packages/pharaoh/lib/src/_next/router.dart b/packages/pharaoh/lib/src/_next/router.dart index 232c10fd..33ab43a2 100644 --- a/packages/pharaoh/lib/src/_next/router.dart +++ b/packages/pharaoh/lib/src/_next/router.dart @@ -9,7 +9,7 @@ import 'package:grammer/grammer.dart'; import 'package:meta/meta.dart'; import '../http/request.dart'; import '../http/response.dart'; -import '../router/router_handler.dart'; +import '../http/router.dart'; import 'validation.dart'; import 'core.dart'; diff --git a/packages/pharaoh/lib/src/core.dart b/packages/pharaoh/lib/src/core.dart deleted file mode 100644 index 6d465f25..00000000 --- a/packages/pharaoh/lib/src/core.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:collection/collection.dart'; -import 'package:http_parser/http_parser.dart'; -import 'package:meta/meta.dart'; -import 'package:spanner/spanner.dart'; - -import 'http/request.dart'; -import 'http/response.dart'; -import 'middleware/session_mw.dart'; -import 'router/router_contract.dart'; -import 'router/router_handler.dart'; -import 'router/router_mixin.dart'; -import 'router/router.dart'; -import 'view/view.dart'; - -import 'middleware/body_parser.dart'; -import 'utils/exceptions.dart'; -import 'shelf_interop/shelf.dart' as shelf; - -part 'core_impl.dart'; - -typedef PharaohError = ({Object exception, StackTrace trace}); - -typedef OnErrorCallback = FutureOr Function( - PharaohError error, - Request req, - Response res, -); - -abstract class Pharaoh implements RouterContract { - static RouterContract get router => GroupRouter(); - - factory Pharaoh() => $PharaohImpl(); - - ViewEngine? get viewEngine; - - void onError(OnErrorCallback onError); - - set viewEngine(ViewEngine? engine); - - void useSpanner(Spanner spanner); - - Uri get uri; - - Pharaoh group(String path, RouterContract router); - - Future listen({int port = 3000}); - - @visibleForTesting - void handleRequest(HttpRequest httpReq); - - Future shutdown(); -} diff --git a/packages/pharaoh/lib/src/http/request.dart b/packages/pharaoh/lib/src/http/request.dart index 8912cec5..fbdcbb04 100644 --- a/packages/pharaoh/lib/src/http/request.dart +++ b/packages/pharaoh/lib/src/http/request.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'package:http_parser/http_parser.dart'; import 'package:pharaoh/pharaoh.dart'; -import 'package:spanner/spanner.dart'; import 'message.dart'; diff --git a/packages/pharaoh/lib/src/core_impl.dart b/packages/pharaoh/lib/src/http/router.dart similarity index 77% rename from packages/pharaoh/lib/src/core_impl.dart rename to packages/pharaoh/lib/src/http/router.dart index e0d75afe..330ba536 100644 --- a/packages/pharaoh/lib/src/core_impl.dart +++ b/packages/pharaoh/lib/src/http/router.dart @@ -1,20 +1,70 @@ -part of 'core.dart'; +import 'dart:async'; +import 'dart:io'; -class $PharaohImpl extends RouterContract +import 'package:collection/collection.dart'; +import 'package:http_parser/http_parser.dart'; +import 'package:meta/meta.dart'; +import 'package:spanner/spanner.dart'; +import 'package:spanner/src/tree/tree.dart' show BASE_PATH; + +import '../middleware/body_parser.dart'; +import '../middleware/session_mw.dart'; +import '../shelf_interop/shelf.dart' as shelf; +import '../utils/exceptions.dart'; +import '../view/view.dart'; + +import 'request.dart'; +import 'response.dart'; + +part 'router/router_contract.dart'; +part 'router/router_handler.dart'; + +typedef PharaohError = ({Object exception, StackTrace trace}); + +typedef OnErrorCallback = FutureOr Function( + PharaohError error, + Request req, + Response res, +); + +abstract class Pharaoh implements RouterContract { + static _$GroupRouter get router => _$GroupRouter(); + + factory Pharaoh() => _$PharaohImpl(); + + static ViewEngine? viewEngine; + + void onError(OnErrorCallback onError); + + void useSpanner(Spanner spanner); + + void useRequestHook(RequestHook hook); + + void group(String path, _$GroupRouter router); + + Uri get uri; + + Future listen({int port = 3000}); + + @visibleForTesting + void handleRequest(HttpRequest httpReq); + + Future shutdown(); +} + +class _$PharaohImpl extends RouterContract with RouteDefinitionMixin implements Pharaoh { late final HttpServer _server; OnErrorCallback? _onErrorCb; - static ViewEngine? viewEngine_; - final List _requestHooks = [ sessionPreResponseHook, viewRenderHook, ]; - $PharaohImpl() { + _$PharaohImpl() { useSpanner(Spanner()); use(bodyParser); } @@ -42,15 +92,6 @@ class $PharaohImpl extends RouterContract ); } - @override - Pharaoh group(final String path, final RouterContract router) { - if (router is! GroupRouter) { - throw PharaohException.value('Router is not an instance of GroupRouter'); - } - router.commit(path, spanner); - return this; - } - @override Future listen({int port = 3000}) async { _server = await HttpServer.bind(InternetAddress.anyIPv4, port, shared: true) @@ -188,17 +229,21 @@ class $PharaohImpl extends RouterContract Future shutdown() async => _server.close(); @override - ViewEngine? get viewEngine => viewEngine_; - - @override - set viewEngine(ViewEngine? engine) => viewEngine_ = engine; + void onError(OnErrorCallback errorCb) => _onErrorCb = errorCb; @override - void onError(OnErrorCallback errorCb) => _onErrorCb = errorCb; + void useRequestHook(RequestHook hook) => _requestHooks.add(hook); @override - void addRequestHook(RequestHook hook) => _requestHooks.add(hook); + void group(String path, _$GroupRouter router) { + spanner.attachNode(path, router.spanner.root); + } } -// ignore: constant_identifier_names const _XPoweredByHeader = 'X-Powered-By'; + +class _$GroupRouter extends RouterContract with RouteDefinitionMixin { + _$GroupRouter() { + useSpanner(Spanner()); + } +} diff --git a/packages/pharaoh/lib/src/router/router_mixin.dart b/packages/pharaoh/lib/src/http/router/router_contract.dart similarity index 73% rename from packages/pharaoh/lib/src/router/router_mixin.dart rename to packages/pharaoh/lib/src/http/router/router_contract.dart index 3187d8e4..1138c372 100644 --- a/packages/pharaoh/lib/src/router/router_mixin.dart +++ b/packages/pharaoh/lib/src/http/router/router_contract.dart @@ -1,8 +1,26 @@ -import 'package:spanner/spanner.dart'; -import 'package:spanner/src/tree/tree.dart' show BASE_PATH; +part of '../router.dart'; -import 'router_contract.dart'; -import 'router_handler.dart'; +sealed class RouterContract { + void get(String path, RequestHandler hdler); + + void post(String path, RequestHandler hdler); + + void put(String path, RequestHandler hdler); + + void delete(String path, RequestHandler hdler); + + void head(String path, RequestHandler hdler); + + void patch(String path, RequestHandler hdler); + + void options(String path, RequestHandler hdler); + + void trace(String path, RequestHandler hdler); + + void use(Middleware middleware); + + void on(String path, Middleware hdler, {HTTPMethod method = HTTPMethod.ALL}); +} mixin RouteDefinitionMixin on RouterContract { late Spanner spanner; diff --git a/packages/pharaoh/lib/src/router/router_handler.dart b/packages/pharaoh/lib/src/http/router/router_handler.dart similarity index 93% rename from packages/pharaoh/lib/src/router/router_handler.dart rename to packages/pharaoh/lib/src/http/router/router_handler.dart index 5ffb6214..15bf20ba 100644 --- a/packages/pharaoh/lib/src/router/router_handler.dart +++ b/packages/pharaoh/lib/src/http/router/router_handler.dart @@ -1,12 +1,8 @@ -import 'dart:async'; - -import '../http/request.dart'; -import '../http/response.dart'; -import '../utils/exceptions.dart'; +part of '../router.dart'; typedef ReqRes = ({Request req, Response res}); -class RequestHook { +final class RequestHook { final FutureOr Function(Request req, Response res)? onBefore; final FutureOr Function(Request req, Response res)? onAfter; const RequestHook({this.onAfter, this.onBefore}); diff --git a/packages/pharaoh/lib/src/middleware/body_parser.dart b/packages/pharaoh/lib/src/middleware/body_parser.dart index 2bb6ee39..54744707 100644 --- a/packages/pharaoh/lib/src/middleware/body_parser.dart +++ b/packages/pharaoh/lib/src/middleware/body_parser.dart @@ -5,7 +5,7 @@ import 'package:mime/mime.dart'; import '../http/request.dart'; import '../http/response.dart'; -import '../router/router_handler.dart'; +import '../http/router.dart'; sealed class MimeType { static const String multiPartForm = 'multipart/form-data'; diff --git a/packages/pharaoh/lib/src/middleware/cookie_parser.dart b/packages/pharaoh/lib/src/middleware/cookie_parser.dart index 6db38c3c..30d5097f 100644 --- a/packages/pharaoh/lib/src/middleware/cookie_parser.dart +++ b/packages/pharaoh/lib/src/middleware/cookie_parser.dart @@ -4,7 +4,7 @@ import 'package:pharaoh/src/utils/utils.dart'; import '../http/cookie.dart'; import '../http/request.dart'; -import '../router/router_handler.dart'; +import '../http/router.dart'; Middleware cookieParser({CookieOpts opts = const CookieOpts()}) { opts.validate(); diff --git a/packages/pharaoh/lib/src/middleware/request_logger.dart b/packages/pharaoh/lib/src/middleware/request_logger.dart index 3d48d799..5be4bec4 100644 --- a/packages/pharaoh/lib/src/middleware/request_logger.dart +++ b/packages/pharaoh/lib/src/middleware/request_logger.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import '../router/router_handler.dart'; +import '../http/router.dart'; final logRequestHook = RequestHook( onBefore: (req, res) async { diff --git a/packages/pharaoh/lib/src/middleware/session_mw.dart b/packages/pharaoh/lib/src/middleware/session_mw.dart index 4bc1971a..28f045f0 100644 --- a/packages/pharaoh/lib/src/middleware/session_mw.dart +++ b/packages/pharaoh/lib/src/middleware/session_mw.dart @@ -7,7 +7,7 @@ import 'package:uuid/uuid.dart'; import '../http/cookie.dart'; import '../http/request.dart'; -import '../router/router_handler.dart'; +import '../http/router.dart'; import '../utils/utils.dart'; part '../http/session.dart'; diff --git a/packages/pharaoh/lib/src/router/router.dart b/packages/pharaoh/lib/src/router/router.dart deleted file mode 100644 index 068d4610..00000000 --- a/packages/pharaoh/lib/src/router/router.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'package:spanner/spanner.dart'; - -import 'router_contract.dart'; -import 'router_handler.dart'; - -typedef _PendingRouteIntent = ( - HTTPMethod method, - ({String path, Middleware handler}) -); - -class GroupRouter extends RouterContract { - final List<_PendingRouteIntent> _pendingRouteIntents = []; - - List<_PendingRouteIntent> get routes => _pendingRouteIntents; - - @override - GroupRouter delete(String path, RequestHandler hdler) { - _pendingRouteIntents.add(( - HTTPMethod.DELETE, - (path: path, handler: useRequestHandler(hdler)), - )); - return this; - } - - @override - GroupRouter get(String path, RequestHandler hdler) { - _pendingRouteIntents.add(( - HTTPMethod.GET, - (path: path, handler: useRequestHandler(hdler)), - )); - return this; - } - - @override - GroupRouter head(String path, RequestHandler hdler) { - _pendingRouteIntents.add(( - HTTPMethod.HEAD, - (path: path, handler: useRequestHandler(hdler)), - )); - return this; - } - - @override - GroupRouter options(String path, RequestHandler hdler) { - _pendingRouteIntents.add(( - HTTPMethod.OPTIONS, - (path: path, handler: useRequestHandler(hdler)), - )); - return this; - } - - @override - GroupRouter patch(String path, RequestHandler hdler) { - _pendingRouteIntents.add(( - HTTPMethod.PATCH, - (path: path, handler: useRequestHandler(hdler)), - )); - return this; - } - - @override - GroupRouter post(String path, RequestHandler hdler) { - _pendingRouteIntents.add(( - HTTPMethod.POST, - (path: path, handler: useRequestHandler(hdler)), - )); - return this; - } - - @override - GroupRouter put(String path, RequestHandler hdler) { - _pendingRouteIntents.add(( - HTTPMethod.PUT, - (path: path, handler: useRequestHandler(hdler)), - )); - return this; - } - - @override - GroupRouter trace(String path, RequestHandler hdler) { - _pendingRouteIntents.add(( - HTTPMethod.TRACE, - (path: path, handler: useRequestHandler(hdler)), - )); - return this; - } - - @override - GroupRouter use(Middleware mdw) { - _pendingRouteIntents.add((HTTPMethod.ALL, (path: '*', handler: mdw))); - return this; - } - - @override - GroupRouter on( - String path, - Middleware func, { - HTTPMethod method = HTTPMethod.ALL, - }) { - if (method == HTTPMethod.ALL) path = '$path/*'; - _pendingRouteIntents.add((method, (path: path, handler: func))); - return this; - } - - void commit(String prefix, Spanner spanner) { - for (final intent in _pendingRouteIntents) { - final handler = intent.$2.handler; - if (intent.$1 == HTTPMethod.ALL) { - spanner.addMiddleware(prefix, handler); - } else { - spanner.addRoute(intent.$1, '$prefix${intent.$2.path}', handler); - } - } - } - - @override - void addRequestHook(RequestHook hook) { - // TODO: implement addRequestHook - } -} diff --git a/packages/pharaoh/lib/src/router/router_contract.dart b/packages/pharaoh/lib/src/router/router_contract.dart deleted file mode 100644 index 9dd9d085..00000000 --- a/packages/pharaoh/lib/src/router/router_contract.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:spanner/spanner.dart'; - -import 'router_handler.dart'; - -abstract class RouterContract { - void get(String path, RequestHandler hdler); - - void post(String path, RequestHandler hdler); - - void put(String path, RequestHandler hdler); - - void delete(String path, RequestHandler hdler); - - void head(String path, RequestHandler hdler); - - void patch(String path, RequestHandler hdler); - - void options(String path, RequestHandler hdler); - - void trace(String path, RequestHandler hdler); - - void use(Middleware middleware); - - void addRequestHook(RequestHook hook); - - void on(String path, Middleware hdler, {HTTPMethod method = HTTPMethod.ALL}); -} diff --git a/packages/pharaoh/lib/src/shelf_interop/adapter.dart b/packages/pharaoh/lib/src/shelf_interop/adapter.dart index e555e159..7fd1f094 100644 --- a/packages/pharaoh/lib/src/shelf_interop/adapter.dart +++ b/packages/pharaoh/lib/src/shelf_interop/adapter.dart @@ -2,7 +2,7 @@ import 'dart:async'; import '../http/request.dart'; import '../http/response.dart'; -import '../router/router_handler.dart'; +import '../http/router.dart'; import '../utils/exceptions.dart'; import 'shelf.dart' as shelf; diff --git a/packages/pharaoh/lib/src/view/view.dart b/packages/pharaoh/lib/src/view/view.dart index 4661bba6..d218eee3 100644 --- a/packages/pharaoh/lib/src/view/view.dart +++ b/packages/pharaoh/lib/src/view/view.dart @@ -1,9 +1,7 @@ import 'dart:async'; import 'dart:isolate'; -import '../core.dart'; - -import '../router/router_handler.dart'; +import '../http/router.dart'; import '../utils/exceptions.dart'; import '../shelf_interop/shelf.dart' as shelf; @@ -25,7 +23,7 @@ final viewRenderHook = RequestHook( final reqRes = (req: req, res: res); if (viewData == null) return reqRes; - final viewEngine = $PharaohImpl.viewEngine_; + final viewEngine = Pharaoh.viewEngine; if (viewEngine == null) throw PharaohException('No view engine found'); try { diff --git a/packages/pharaoh/test/http/res.render_test.dart b/packages/pharaoh/test/http/res.render_test.dart index f8de03f1..fd859f70 100644 --- a/packages/pharaoh/test/http/res.render_test.dart +++ b/packages/pharaoh/test/http/res.render_test.dart @@ -24,7 +24,10 @@ class TestViewEngine extends ViewEngine { void main() { late Pharaoh app; - setUp(() async => app = Pharaoh()..viewEngine = TestViewEngine()); + setUp(() { + Pharaoh.viewEngine = TestViewEngine(); + app = Pharaoh(); + }); group('res.render', () { test('should render template', () async { @@ -68,9 +71,8 @@ void main() { }); test('should err when no view engine', () async { - app = app - ..viewEngine = null - ..get('/', (req, res) => res.render('products')); + Pharaoh.viewEngine = null; + app = app..get('/', (req, res) => res.render('products')); await (await request(app)) .get('/') diff --git a/packages/pharaoh/test/router/router_group_test.dart b/packages/pharaoh/test/router/router_group_test.dart index 7c36cfed..3b7c1e6f 100644 --- a/packages/pharaoh/test/router/router_group_test.dart +++ b/packages/pharaoh/test/router/router_group_test.dart @@ -11,19 +11,21 @@ void main() { ..post('/hello', (req, res) => res.json(req.body)); app.group('/admin', adminRouter); - await (await request(app)) + final appTester = await request(app); + + await appTester .post('/', {'_': 'Hello World 🚀'}) .expectBody({"_": "Hello World 🚀"}) .expectStatus(200) .test(); - await (await request(app)) + await appTester .post('/admin/hello', {'_': 'Hello World 🚀'}) .expectBody({"_": "Hello World 🚀"}) .expectStatus(200) .test(); - await (await request(app)) + await appTester .get('/admin') .expectBody('Holy Moly 🚀') .expectStatus(200) diff --git a/pharaoh_examples/lib/middleware/index.dart b/pharaoh_examples/lib/middleware/index.dart index 913686b2..a43ab2b6 100644 --- a/pharaoh_examples/lib/middleware/index.dart +++ b/pharaoh_examples/lib/middleware/index.dart @@ -3,7 +3,7 @@ import 'package:pharaoh/pharaoh.dart'; final app = Pharaoh(); void main() async { - app.addRequestHook(logRequestHook); + app.useRequestHook(logRequestHook); app.get('/', (req, res) => res.ok("Hurray 🚀")); diff --git a/pharaoh_examples/lib/route_groups/index.dart b/pharaoh_examples/lib/route_groups/index.dart index 4bcf8770..aa464b58 100644 --- a/pharaoh_examples/lib/route_groups/index.dart +++ b/pharaoh_examples/lib/route_groups/index.dart @@ -1,6 +1,6 @@ import 'package:pharaoh/pharaoh.dart'; -final app = Pharaoh()..addRequestHook(logRequestHook); +final app = Pharaoh()..useRequestHook(logRequestHook); void main() async { final guestRouter = Pharaoh.router diff --git a/pharaoh_examples/lib/serve_files_2/index.dart b/pharaoh_examples/lib/serve_files_2/index.dart index 03d4daf6..2b23c845 100644 --- a/pharaoh_examples/lib/serve_files_2/index.dart +++ b/pharaoh_examples/lib/serve_files_2/index.dart @@ -12,7 +12,7 @@ final serveStatic = createStaticHandler( final cors = corsHeaders(); void main() async { - app.addRequestHook(logRequestHook); + app.useRequestHook(logRequestHook); app.use(useShelfMiddleware(cors)); From 201b9a49e8a3595ec9aa41beb4a3da96e406629b Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Wed, 22 Jan 2025 19:46:39 +0000 Subject: [PATCH 9/9] _ --- packages/pharaoh/lib/pharaoh.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/pharaoh/lib/pharaoh.dart b/packages/pharaoh/lib/pharaoh.dart index 7f985397..e863d89f 100644 --- a/packages/pharaoh/lib/pharaoh.dart +++ b/packages/pharaoh/lib/pharaoh.dart @@ -4,15 +4,17 @@ export 'src/view/view.dart'; export 'src/http/cookie.dart'; export 'src/http/request.dart'; export 'src/http/response.dart'; +export 'src/http/router.dart'; + +export 'src/shelf_interop/adapter.dart'; +export 'src/shelf_interop/shelf.dart' show ShelfBody; + export 'src/utils/utils.dart'; export 'src/utils/exceptions.dart'; -export 'src/http/router.dart'; + export 'src/middleware/session_mw.dart'; export 'src/middleware/body_parser.dart'; export 'src/middleware/cookie_parser.dart'; export 'src/middleware/request_logger.dart'; -export 'package:spanner/spanner.dart' show HTTPMethod; -// shelf -export 'src/shelf_interop/adapter.dart'; -export 'src/shelf_interop/shelf.dart' show ShelfBody; +export 'package:spanner/spanner.dart' show HTTPMethod;