diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml index 11a357af..fd29c844 100644 --- a/.idea/libraries/Dart_Packages.xml +++ b/.idea/libraries/Dart_Packages.xml @@ -5,14 +5,14 @@ - - @@ -26,21 +26,21 @@ - - - @@ -58,10 +58,17 @@ + + + + + + - @@ -89,7 +96,14 @@ - + + + + + + @@ -110,7 +124,7 @@ - @@ -138,7 +152,7 @@ - @@ -173,7 +187,7 @@ - @@ -215,7 +229,7 @@ - @@ -226,6 +240,13 @@ + + + + + + @@ -264,7 +285,7 @@ - @@ -278,14 +299,14 @@ - - @@ -296,10 +317,17 @@ + + + + + + - @@ -331,6 +359,20 @@ + + + + + + + + + + + + @@ -341,53 +383,59 @@ - - + + - - - + + + - + + - + + - + - + - + - + + - + - - + + - + + + + diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml new file mode 100644 index 00000000..a1c33e62 --- /dev/null +++ b/.idea/libraries/Dart_SDK.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/All_Tests.xml b/.idea/runConfigurations/All_Tests.xml index ac11209e..c753608c 100644 --- a/.idea/runConfigurations/All_Tests.xml +++ b/.idea/runConfigurations/All_Tests.xml @@ -2,6 +2,7 @@ \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 721e5e7e..3127f8de 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,8 +2,16 @@ + + + + + + + + @@ -24,24 +32,27 @@ - - + + - - - - - + + + - - + + - - - + + + + + + + + @@ -49,8 +60,8 @@ - - + + @@ -59,39 +70,31 @@ - - + + - - + + - - + + - - + + - - - - - - - - - - - - - + + + + + @@ -112,10 +115,8 @@ - - - - + + @@ -139,6 +140,9 @@ BEFORE _onError _fatalErrorStream.add + throw + upa + transform @@ -153,13 +157,15 @@ @@ -170,8 +176,8 @@ DEFINITION_ORDER - @@ -193,6 +199,8 @@ + + @@ -275,8 +283,6 @@ - - @@ -291,13 +297,32 @@ - + + + + + + + + + + + @@ -332,7 +357,7 @@ - + @@ -352,11 +377,11 @@ - - - - - + + + + + @@ -376,6 +401,7 @@ + 1481237183504 @@ -423,17 +449,17 @@ - - + + - - + + - - + + - - + + @@ -455,7 +481,7 @@ - @@ -467,29 +493,29 @@ - + - - + - + - + + + + - - @@ -517,7 +543,7 @@ - + @@ -584,7 +610,6 @@ - @@ -627,9 +652,7 @@ - - - + @@ -662,7 +685,6 @@ - @@ -712,9 +734,7 @@ - - - + @@ -751,9 +771,7 @@ - - - + @@ -786,16 +804,6 @@ - - - - - - - - - - @@ -816,26 +824,7 @@ - - - - - - - - - - - - - - - - - - - - + @@ -843,7 +832,7 @@ - + @@ -851,7 +840,7 @@ - + @@ -877,10 +866,8 @@ - - - - + + @@ -896,16 +883,76 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + diff --git a/README.md b/README.md index abb1495e..c605acd2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # angel_framework -[![pub 1.0.0+1](https://img.shields.io/badge/pub-1.0.0+1-brightgreen.svg)](https://pub.dartlang.org/packages/angel_framework) +[![pub 1.0.1](https://img.shields.io/badge/pub-1.0.1-brightgreen.svg)](https://pub.dartlang.org/packages/angel_framework) [![build status](https://travis-ci.org/angel-dart/framework.svg)](https://travis-ci.org/angel-dart/framework) A high-powered HTTP server with support for dependency injection, sophisticated routing and more. diff --git a/lib/hooks.dart b/lib/hooks.dart index 684abc6b..71aa40d2 100644 --- a/lib/hooks.dart +++ b/lib/hooks.dart @@ -36,21 +36,75 @@ AngelConfigurer hookAllServices(callback(Service service)) { HookedServiceEventListener toJson() => transform(god.serializeObject); /// Mutates `e.data` or `e.result` using the given [transformer]. -HookedServiceEventListener transform(transformer(obj)) { - normalize(obj) { +/// +/// You can optionally provide a [condition], which can be: +/// * A [Providers] instance, or String, to run only on certain clients +/// * The type [Providers], in which case the transformer will run on every client, but *not* on server-side events. +/// * A function: if the function returns `true` (sync or async, doesn't matter), +/// then the transformer will run. If not, the event will be skipped. +/// * An [Iterable] of the above three. +/// +/// A provided function must take a [HookedServiceEvent] as its only parameter. +HookedServiceEventListener transform(transformer(obj), [condition]) { + Iterable cond = condition is Iterable ? condition : [condition]; + + _condition(HookedServiceEvent e, condition) async { + if (condition is Function) + return await condition(e); + else if (condition == Providers) + return true; + else { + if (e.params?.containsKey('provider') == true) { + var provider = e.params['provider'] as Providers; + if (condition is Providers) + return condition == provider; + else + return condition.toString() == provider.via; + } else { + return false; + } + } + } + + normalize(HookedServiceEvent e, obj) async { + bool transform = true; + + for (var c in cond) { + var r = await _condition(e, c); + + if (r != true) { + transform = false; + break; + } + } + + if (transform != true) { + if (obj == null) + return null; + else if (obj is Iterable) + return obj.toList(); + else + return obj; + } + if (obj == null) return null; - else if (obj is Iterable) - return obj.map(normalize).toList(); - else + else if (obj is Iterable) { + var r = []; + + for (var o in obj) { + r.add(await normalize(e, o)); + } + + return r; + } else return transformer(obj); } - return (HookedServiceEvent e) { + return (HookedServiceEvent e) async { if (e.isBefore) { - e.data = normalize(e.data); - } else if (e.isAfter) - e.result = normalize(e.result); + e.data = await normalize(e, e.data); + } else if (e.isAfter) e.result = await normalize(e, e.result); }; } @@ -77,8 +131,6 @@ HookedServiceEventListener toType(Type type) { /// Only applies to the client-side. HookedServiceEventListener remove(key, [remover(key, obj)]) { return (HookedServiceEvent e) async { - if (!e.isAfter) throw new StateError("'remove' only works on after hooks."); - _remover(key, obj) { if (remover != null) return remover(key, obj); @@ -95,7 +147,7 @@ HookedServiceEventListener remove(key, [remover(key, obj)]) { reflect(obj).setField(new Symbol(key), null); return obj; } catch (e) { - throw new ArgumentError("Cannot remove key 'key' from $obj."); + throw new ArgumentError("Cannot remove key '$key' from $obj."); } } } @@ -206,10 +258,18 @@ HookedServiceEventListener addCreatedAt({ }; } +/// Typo: Use [addUpdatedAt] instead. +@deprecated +HookedServiceEventListener addUpatedAt({ + assign(obj, String now), + String key, +}) => + addUpdatedAt(assign: assign, key: key); + /// Serializes the current time to `e.data` or `e.result`. /// You can provide an [assign] function to set the property on your object, and skip reflection./// /// Default key: `createdAt` -HookedServiceEventListener addUpatedAt({ +HookedServiceEventListener addUpdatedAt({ assign(obj, String now), String key, }) { diff --git a/lib/src/http/map_service.dart b/lib/src/http/map_service.dart index d06fbf73..a4b3909c 100644 --- a/lib/src/http/map_service.dart +++ b/lib/src/http/map_service.dart @@ -52,9 +52,11 @@ class MapService extends Service { throw new AngelHttpException.badRequest( message: 'MapService does not support `create` with ${data.runtimeType}.'); + var now = new DateTime.now(); var result = data ..['id'] = items.length.toString() - ..['createdAt'] = new DateTime.now(); + ..['createdAt'] = now + ..['updatedAt'] = now; items.add(result); return result; } @@ -97,6 +99,14 @@ class MapService extends Service { @override Future remove(id, [Map params]) async { + if (id == null || + id == 'null' && + (allowRemoveAll == true || + params?.containsKey('provider') != true)) { + items.clear(); + return {}; + } + var result = await read(id, params); if (items.remove(result)) diff --git a/lib/src/http/response_context.dart b/lib/src/http/response_context.dart index e666bf8a..01b2efdc 100644 --- a/lib/src/http/response_context.dart +++ b/lib/src/http/response_context.dart @@ -181,8 +181,10 @@ class ResponseContext extends Extensible { /// See [Router]#navigate for more. :) void redirect(url, {bool absolute: true, int code: 302}) { // if (!_isOpen) throw _closed(); - headers[HttpHeaders.LOCATION] = - url is String ? url : app.navigate(url, absolute: absolute); + headers + ..[HttpHeaders.CONTENT_TYPE] = ContentType.HTML.toString() + ..[HttpHeaders.LOCATION] = + url is String ? url : app.navigate(url, absolute: absolute); statusCode = code ?? 302; write(''' diff --git a/lib/src/http/server.dart b/lib/src/http/server.dart index 093569e9..f32fe3a9 100644 --- a/lib/src/http/server.dart +++ b/lib/src/http/server.dart @@ -252,70 +252,64 @@ class Angel extends AngelBase { _serializer = serializer; } - /// Runs some [handler]. Returns `true` if request execution should continue. - Future executeHandler( + Future getHandlerResult( handler, RequestContext req, ResponseContext res) async { if (handler is RequestMiddleware) { var result = await handler(req, res); - if (result is bool) - return result == true; - else if (result is RequestHandler) - return await executeHandler(result, req, res); - else if (result != null) { - res.serialize(result, - contentType: res.headers[HttpHeaders.CONTENT_TYPE] ?? - ContentType.JSON.mimeType); - return false; - } else - return res.isOpen; + if (result is RequestHandler) + return await getHandlerResult(result, req, res); + else + return result; } if (handler is RequestHandler) { var result = await handler(req, res); if (result is RequestHandler) - return await executeHandler(result, req, res); - return res.isOpen; + return await getHandlerResult(result, req, res); + else + return result; } if (handler is Future) { var result = await handler; - if (result is bool) - return result == true; - else if (result is RequestHandler) - return await executeHandler(result, req, res); - else if (result != null) { - res.serialize(result, - contentType: res.headers[HttpHeaders.CONTENT_TYPE] ?? - ContentType.JSON.mimeType); - return false; - } else - return true; + if (result is RequestHandler) + return await getHandlerResult(result, req, res); + else + return result; } if (handler is Function) { var result = await runContained(handler, req, res); - if (result is bool) - return result == true; - else if (result is RequestHandler) - return await executeHandler(result, req, res); - else if (result != null) { - res.serialize(result, - contentType: res.headers[HttpHeaders.CONTENT_TYPE] ?? - ContentType.JSON.mimeType); - return false; - } else - return true; + if (result is RequestHandler) + return await getHandlerResult(result, req, res); + else + return result; } if (requestMiddleware.containsKey(handler)) { - return await executeHandler(requestMiddleware[handler], req, res); + return await getHandlerResult(requestMiddleware[handler], req, res); } - res.serialize(handler, - contentType: - res.headers[HttpHeaders.CONTENT_TYPE] ?? ContentType.JSON.mimeType); - return false; + return handler; + } + + /// Runs some [handler]. Returns `true` if request execution should continue. + Future executeHandler( + handler, RequestContext req, ResponseContext res) async { + var result = await getHandlerResult(handler, req, res); + + if (result is Future) { + return await executeHandler(await result, req, res); + } else if (result is bool) { + return result; + } else if (result != null) { + res.serialize(result, + contentType: res.headers[HttpHeaders.CONTENT_TYPE] ?? + ContentType.JSON.mimeType); + return false; + } else + return res.isOpen; } Future createRequestContext(HttpRequest request) { diff --git a/pubspec.yaml b/pubspec.yaml index 207b8c5f..a1990838 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_framework -version: 1.0.0+1 +version: 1.0.1 description: A high-powered HTTP server with DI, routing and more. author: Tobe O homepage: https://github.com/angel-dart/angel_framework