diff --git a/README.md b/README.md index d7cd98d9..a6efbe79 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # angel_framework -[![pub 1.0.0-dev.50](https://img.shields.io/badge/pub-1.0.0--dev.50-red.svg)](https://pub.dartlang.org/packages/angel_framework) +[![pub 1.0.0-dev.51](https://img.shields.io/badge/pub-1.0.0--dev.51-red.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) Core libraries for the Angel Framework. diff --git a/lib/hooks.dart b/lib/hooks.dart new file mode 100644 index 00000000..7ed483c9 --- /dev/null +++ b/lib/hooks.dart @@ -0,0 +1,21 @@ +/// Easy helper hooks. +library angel_framework.hooks; + +import 'package:json_god/json_god.dart' as god; +import 'angel_framework.dart'; + +/// Transforms `e.data` into JSON-friendly data, i.e. a Map. +HookedServiceEventListener toJson() { + return (HookedServiceEvent e) { + if (e.data != null && e.data is! Map) e.data = god.serializeObject(e.data); + }; +} + +/// Transforms `e.data` into an instance of the given [type], +/// if it is not already. +HookedServiceEventListener toType(Type type) { + return (HookedServiceEvent e) { + if (e.data != null && e.data.runtimeType != type) + e.data = god.deserializeDatum(e.data, outputType: type); + }; +} diff --git a/lib/src/http/angel_http_exception.dart b/lib/src/http/angel_http_exception.dart index ebc35257..54cb14a0 100644 --- a/lib/src/http/angel_http_exception.dart +++ b/lib/src/http/angel_http_exception.dart @@ -58,18 +58,18 @@ class AngelHttpException implements Exception { /// Throws a 400 Bad Request error, including an optional arrray of (validation?) /// errors you specify. factory AngelHttpException.badRequest( - {String message: '400 Bad Request', List errors: const []}) => + {String message: '400 Bad Request', List errors: const []}) => new AngelHttpException(null, message: message, errors: errors, statusCode: 400); /// Throws a 401 Not Authenticated error. factory AngelHttpException.notAuthenticated( - {String message: '401 Not Authenticated'}) => + {String message: '401 Not Authenticated'}) => new AngelHttpException(null, message: message, statusCode: 401); /// Throws a 402 Payment Required error. factory AngelHttpException.paymentRequired( - {String message: '402 Payment Required'}) => + {String message: '402 Payment Required'}) => new AngelHttpException(null, message: message, statusCode: 402); /// Throws a 403 Forbidden error. @@ -82,12 +82,12 @@ class AngelHttpException implements Exception { /// Throws a 405 Method Not Allowed error. factory AngelHttpException.methodNotAllowed( - {String message: '405 Method Not Allowed'}) => + {String message: '405 Method Not Allowed'}) => new AngelHttpException(null, message: message, statusCode: 405); /// Throws a 406 Not Acceptable error. factory AngelHttpException.notAcceptable( - {String message: '406 Not Acceptable'}) => + {String message: '406 Not Acceptable'}) => new AngelHttpException(null, message: message, statusCode: 406); /// Throws a 408 Timeout error. @@ -100,83 +100,84 @@ class AngelHttpException implements Exception { /// Throws a 422 Not Processable error. factory AngelHttpException.notProcessable( - {String message: '422 Not Processable'}) => + {String message: '422 Not Processable'}) => new AngelHttpException(null, message: message, statusCode: 422); /// Throws a 501 Not Implemented error. factory AngelHttpException.notImplemented( - {String message: '501 Not Implemented'}) => + {String message: '501 Not Implemented'}) => new AngelHttpException(null, message: message, statusCode: 501); /// Throws a 503 Unavailable error. factory AngelHttpException.unavailable({String message: '503 Unavailable'}) => new AngelHttpException(null, message: message, statusCode: 503); - /// Throws a 400 Bad Request error, including an optional arrray of (validation?) - /// errors you specify. - @Deprecated('Use lowercase constructor instead') + /// DEPRECATED: Use lowercase constructor instead. + @deprecated factory AngelHttpException.BadRequest( {String message: '400 Bad Request', List errors: const []}) => new AngelHttpException(null, message: message, errors: errors, statusCode: 400); - /// Throws a 401 Not Authenticated error. - @Deprecated('Use lowercase constructor instead') + /// DEPRECATED: Use lowercase constructor instead. + @deprecated factory AngelHttpException.NotAuthenticated( {String message: '401 Not Authenticated'}) => new AngelHttpException(null, message: message, statusCode: 401); - /// Throws a 402 Payment Required error. - @Deprecated('Use lowercase constructor instead') + /// DEPRECATED: Use lowercase constructor instead. + @deprecated factory AngelHttpException.PaymentRequired( {String message: '402 Payment Required'}) => new AngelHttpException(null, message: message, statusCode: 402); - /// Throws a 403 Forbidden error. - @Deprecated('Use lowercase constructor instead') + /// DEPRECATED: Use lowercase constructor instead. + @deprecated factory AngelHttpException.Forbidden({String message: '403 Forbidden'}) => new AngelHttpException(null, message: message, statusCode: 403); - /// Throws a 404 Not Found error. - @Deprecated('Use lowercase constructor instead') + /// DEPRECATED: Use lowercase constructor instead. + @deprecated factory AngelHttpException.NotFound({String message: '404 Not Found'}) => new AngelHttpException(null, message: message, statusCode: 404); - /// Throws a 405 Method Not Allowed error. + @deprecated + + /// DEPRECATED: Use lowercase constructor instead. factory AngelHttpException.MethodNotAllowed( {String message: '405 Method Not Allowed'}) => new AngelHttpException(null, message: message, statusCode: 405); - /// Throws a 406 Not Acceptable error. - @Deprecated('Use lowercase constructor instead') + /// DEPRECATED: Use lowercase constructor instead. + @deprecated factory AngelHttpException.NotAcceptable( {String message: '406 Not Acceptable'}) => new AngelHttpException(null, message: message, statusCode: 406); - /// Throws a 408 Timeout error. - @Deprecated('Use lowercase constructor instead') + /// DEPRECATED: Use lowercase constructor instead. + @deprecated factory AngelHttpException.MethodTimeout({String message: '408 Timeout'}) => new AngelHttpException(null, message: message, statusCode: 408); - /// Throws a 409 Conflict error. - @Deprecated('Use lowercase constructor instead') + /// DEPRECATED: Use lowercase constructor instead. + @deprecated factory AngelHttpException.Conflict({String message: '409 Conflict'}) => new AngelHttpException(null, message: message, statusCode: 409); - /// Throws a 422 Not Processable error. - @Deprecated('Use lowercase constructor instead') + /// DEPRECATED: Use lowercase constructor instead. + @deprecated factory AngelHttpException.NotProcessable( {String message: '422 Not Processable'}) => new AngelHttpException(null, message: message, statusCode: 422); - /// Throws a 501 Not Implemented error. - @Deprecated('Use lowercase constructor instead') + /// DEPRECATED: Use lowercase constructor instead. + @deprecated factory AngelHttpException.NotImplemented( {String message: '501 Not Implemented'}) => new AngelHttpException(null, message: message, statusCode: 501); - /// Throws a 503 Unavailable error. - @Deprecated('Use lowercase constructor instead') + /// DEPRECATED: Use lowercase constructor instead. + @deprecated factory AngelHttpException.Unavailable({String message: '503 Unavailable'}) => new AngelHttpException(null, message: message, statusCode: 503); } diff --git a/lib/src/http/hooked_service.dart b/lib/src/http/hooked_service.dart index 780f8a50..73eb5fa9 100644 --- a/lib/src/http/hooked_service.dart +++ b/lib/src/http/hooked_service.dart @@ -176,7 +176,54 @@ class HookedService extends Service { addHooks(); } - /// Runs the [listener] before every service method; + /// Runs the [listener] before every service method specified. + void before( + Iterable eventNames, HookedServiceEventListener listener) { + eventNames.map((name) { + switch (name) { + case HookedServiceEvent.INDEXED: + return beforeIndexed; + case HookedServiceEvent.READ: + return beforeRead; + case HookedServiceEvent.CREATED: + return beforeCreated; + case HookedServiceEvent.MODIFIED: + return beforeModified; + case HookedServiceEvent.UPDATED: + return beforeUpdated; + case HookedServiceEvent.REMOVED: + return beforeRemoved; + default: + throw new ArgumentError('Invalid service method: ${name}'); + } + }).forEach((HookedServiceEventDispatcher dispatcher) => + dispatcher.listen(listener)); + } + + /// Runs the [listener] after every service method specified. + void after(Iterable eventNames, HookedServiceEventListener listener) { + eventNames.map((name) { + switch (name) { + case HookedServiceEvent.INDEXED: + return afterIndexed; + case HookedServiceEvent.READ: + return afterRead; + case HookedServiceEvent.CREATED: + return afterCreated; + case HookedServiceEvent.MODIFIED: + return afterModified; + case HookedServiceEvent.UPDATED: + return afterUpdated; + case HookedServiceEvent.REMOVED: + return afterRemoved; + default: + throw new ArgumentError('Invalid service method: ${name}'); + } + }).forEach((HookedServiceEventDispatcher dispatcher) => + dispatcher.listen(listener)); + } + + /// Runs the [listener] before every service method. void beforeAll(HookedServiceEventListener listener) { beforeIndexed.listen(listener); beforeRead.listen(listener); @@ -207,12 +254,12 @@ class HookedService extends Service { Future index([Map _params]) async { var params = _stripReq(_params); HookedServiceEvent before = await beforeIndexed._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(false, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.INDEXED, params: params)); if (before._canceled) { HookedServiceEvent after = await beforeIndexed._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(true, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.INDEXED, params: params, result: before.result)); return after.result; @@ -220,7 +267,7 @@ class HookedService extends Service { List result = await inner.index(params); HookedServiceEvent after = await afterIndexed._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(true, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.INDEXED, params: params, result: result)); return after.result; @@ -230,13 +277,13 @@ class HookedService extends Service { Future read(id, [Map _params]) async { var params = _stripReq(_params); HookedServiceEvent before = await beforeRead._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(false, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.READ, id: id, params: params)); if (before._canceled) { HookedServiceEvent after = await afterRead._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(true, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.READ, id: id, params: params, result: before.result)); return after.result; @@ -244,7 +291,7 @@ class HookedService extends Service { var result = await inner.read(id, params); HookedServiceEvent after = await afterRead._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(true, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.READ, id: id, params: params, result: result)); return after.result; @@ -254,13 +301,13 @@ class HookedService extends Service { Future create(data, [Map _params]) async { var params = _stripReq(_params); HookedServiceEvent before = await beforeCreated._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(false, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.CREATED, data: data, params: params)); if (before._canceled) { HookedServiceEvent after = await afterCreated._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(true, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.CREATED, data: data, params: params, result: before.result)); return after.result; @@ -268,7 +315,7 @@ class HookedService extends Service { var result = await inner.create(data, params); HookedServiceEvent after = await afterCreated._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(true, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.CREATED, data: data, params: params, result: result)); return after.result; @@ -278,13 +325,13 @@ class HookedService extends Service { Future modify(id, data, [Map _params]) async { var params = _stripReq(_params); HookedServiceEvent before = await beforeModified._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(false, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.MODIFIED, id: id, data: data, params: params)); if (before._canceled) { HookedServiceEvent after = await afterModified._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(true, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.MODIFIED, id: id, data: data, params: params, result: before.result)); return after.result; @@ -292,7 +339,7 @@ class HookedService extends Service { var result = await inner.modify(id, data, params); HookedServiceEvent after = await afterModified._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(true, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.MODIFIED, id: id, data: data, params: params, result: result)); return after.result; @@ -302,13 +349,13 @@ class HookedService extends Service { Future update(id, data, [Map _params]) async { var params = _stripReq(_params); HookedServiceEvent before = await beforeUpdated._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(false, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.UPDATED, id: id, data: data, params: params)); if (before._canceled) { HookedServiceEvent after = await afterUpdated._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(true, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.UPDATED, id: id, data: data, params: params, result: before.result)); return after.result; @@ -316,7 +363,7 @@ class HookedService extends Service { var result = await inner.update(id, data, params); HookedServiceEvent after = await afterUpdated._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(true, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.UPDATED, id: id, data: data, params: params, result: result)); return after.result; @@ -326,13 +373,13 @@ class HookedService extends Service { Future remove(id, [Map _params]) async { var params = _stripReq(_params); HookedServiceEvent before = await beforeRemoved._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(false, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.REMOVED, id: id, params: params)); if (before._canceled) { HookedServiceEvent after = await afterRemoved._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(true, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.REMOVED, id: id, params: params, result: before.result)); return after.result; @@ -340,7 +387,7 @@ class HookedService extends Service { var result = await inner.remove(id, params); HookedServiceEvent after = await afterRemoved._emit( - new HookedServiceEvent._base(_getRequest(_params), + new HookedServiceEvent._base(true, _getRequest(_params), _getResponse(_params), inner, HookedServiceEvent.REMOVED, id: id, params: params, result: result)); return after.result; @@ -375,7 +422,7 @@ class HookedService extends Service { throw new ArgumentError("Invalid service event name: '$eventName'"); } - var ev = new HookedServiceEvent._base(null, null, this, eventName); + var ev = new HookedServiceEvent._base(true, null, null, this, eventName); if (callback != null) await callback(ev); return await dispatcher._emit(ev); } @@ -399,6 +446,7 @@ class HookedServiceEvent { bool _canceled = false; String _eventName; var _id; + bool _isAfter; var data; Map _params; var _result; @@ -409,6 +457,10 @@ class HookedServiceEvent { get id => _id; + bool get isAfter => _isAfter == true; + + bool get isBefore => !isAfter; + Map get params => _params; RequestContext get request => _request; @@ -420,8 +472,8 @@ class HookedServiceEvent { /// The inner service whose method was hooked. Service service; - HookedServiceEvent._base(this._request, this._response, Service this.service, - String this._eventName, + HookedServiceEvent._base(this._isAfter, this._request, this._response, + Service this.service, String this._eventName, {id, this.data, Map params, result}) { _id = id; _params = params ?? {}; diff --git a/lib/src/http/service.dart b/lib/src/http/service.dart index 9d129a2d..5512e7b5 100644 --- a/lib/src/http/service.dart +++ b/lib/src/http/service.dart @@ -40,32 +40,32 @@ class Service extends Routable { /// Retrieves all resources. Future index([Map params]) { - throw new AngelHttpException.MethodNotAllowed(); + throw new AngelHttpException.methodNotAllowed(); } /// Retrieves the desired resource. Future read(id, [Map params]) { - throw new AngelHttpException.MethodNotAllowed(); + throw new AngelHttpException.methodNotAllowed(); } /// Creates a resource. Future create(data, [Map params]) { - throw new AngelHttpException.MethodNotAllowed(); + throw new AngelHttpException.methodNotAllowed(); } /// Modifies a resource. Future modify(id, data, [Map params]) { - throw new AngelHttpException.MethodNotAllowed(); + throw new AngelHttpException.methodNotAllowed(); } /// Overwrites a resource. Future update(id, data, [Map params]) { - throw new AngelHttpException.MethodNotAllowed(); + throw new AngelHttpException.methodNotAllowed(); } /// Removes the given resource. Future remove(id, [Map params]) { - throw new AngelHttpException.MethodNotAllowed(); + throw new AngelHttpException.methodNotAllowed(); } void addRoutes() { diff --git a/pubspec.yaml b/pubspec.yaml index b03e9043..8ebee790 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_framework -version: 1.0.0-dev.50 +version: 1.0.0-dev.51 description: Core libraries for the Angel framework. author: Tobe O homepage: https://github.com/angel-dart/angel_framework