Hooks
This commit is contained in:
parent
315e0bca7a
commit
6cc9967b3e
7 changed files with 111 additions and 46 deletions
|
@ -31,12 +31,12 @@ class AngelHttpException extends _AngelHttpExceptionBase {
|
|||
/// Throws a 500 Internal Server Error.
|
||||
/// Set includeRealException to true to print include the actual exception along with
|
||||
/// this error. Useful flag for development vs. production.
|
||||
AngelHttpException(Exception exception,
|
||||
{bool includeRealException: false, StackTrace stackTrace}) :super.base() {
|
||||
AngelHttpException(error,
|
||||
{bool includeRealError: false, StackTrace stackTrace}) :super.base() {
|
||||
statusCode = 500;
|
||||
message = "500 Internal Server Error";
|
||||
if (includeRealException) {
|
||||
errors.add(exception.toString());
|
||||
if (includeRealError) {
|
||||
errors.add(error.toString());
|
||||
if (stackTrace != null) {
|
||||
errors.add(stackTrace.toString());
|
||||
}
|
||||
|
|
|
@ -63,6 +63,7 @@ class Angel extends Routable {
|
|||
if (e is AngelHttpException) {
|
||||
// Special handling for AngelHttpExceptions :)
|
||||
try {
|
||||
res.status(e.statusCode);
|
||||
String accept = request.headers.value(HttpHeaders.ACCEPT);
|
||||
if (accept == "*/*" ||
|
||||
accept.contains("application/json") ||
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
part of angel_framework.http;
|
||||
|
||||
class Providers {
|
||||
final String via;
|
||||
|
||||
const Providers._base(String this.via);
|
||||
|
||||
static final Providers SERVER = const Providers._base('server_side');
|
||||
static final Providers REST = const Providers._base('rest');
|
||||
static final Providers WEBSOCKET = const Providers._base('websocket');
|
||||
}
|
||||
|
||||
/// A data store exposed to the Internet.
|
||||
class Service extends Routable {
|
||||
/// The [Angel] app powering this service.
|
||||
|
@ -16,17 +26,17 @@ class Service extends Routable {
|
|||
}
|
||||
|
||||
/// Creates a resource.
|
||||
Future create(Map data, [Map params]) {
|
||||
Future create(data, [Map params]) {
|
||||
throw new AngelHttpException.MethodNotAllowed();
|
||||
}
|
||||
|
||||
/// Modifies a resource.
|
||||
Future modify(id, Map data, [Map params]) {
|
||||
Future modify(id, data, [Map params]) {
|
||||
throw new AngelHttpException.MethodNotAllowed();
|
||||
}
|
||||
|
||||
/// Overwrites a resource.
|
||||
Future update(id, Map data, [Map params]) {
|
||||
Future update(id, data, [Map params]) {
|
||||
throw new AngelHttpException.MethodNotAllowed();
|
||||
}
|
||||
|
||||
|
@ -36,19 +46,24 @@ class Service extends Routable {
|
|||
}
|
||||
|
||||
Service() : super() {
|
||||
get('/', (req, res) async => await this.index(req.query));
|
||||
Map restProvider = {'provider': Providers.REST};
|
||||
|
||||
post('/', (req, res) async => await this.create(req.body));
|
||||
get('/', (req, res) async => await this.index(
|
||||
mergeMap([req.query, restProvider])));
|
||||
|
||||
post('/', (req, res) async => await this.create(
|
||||
mergeMap([req.body, restProvider])));
|
||||
|
||||
get('/:id', (req, res) async =>
|
||||
await this.read(req.params['id'], req.query));
|
||||
await this.read(req.params['id'], mergeMap([req.query, restProvider])));
|
||||
|
||||
patch('/:id', (req, res) async => await this.modify(
|
||||
req.params['id'], req.body));
|
||||
req.params['id'], mergeMap([req.body, restProvider])));
|
||||
|
||||
post('/:id', (req, res) async => await this.update(
|
||||
req.params['id'], req.body));
|
||||
req.params['id'], mergeMap([req.body, restProvider])));
|
||||
|
||||
delete('/:id', (req, res) async => await this.remove(req.params['id'], req.query));
|
||||
delete('/:id', (req, res) async => await this.remove(
|
||||
req.params['id'], mergeMap([req.query, restProvider])));
|
||||
}
|
||||
}
|
|
@ -1,25 +1,44 @@
|
|||
part of angel_framework.http;
|
||||
|
||||
/// Wraps another service in a service that fires events on actions.
|
||||
/// Wraps another service in a service that broadcasts events on actions.
|
||||
class HookedService extends Service {
|
||||
StreamController<List> _onIndexed = new StreamController<List>();
|
||||
StreamController _onRead = new StreamController();
|
||||
StreamController _onCreated = new StreamController();
|
||||
StreamController _onModified = new StreamController();
|
||||
StreamController _onUpdated = new StreamController();
|
||||
StreamController _onRemoved = new StreamController();
|
||||
StreamController<HookedServiceEvent> _beforeIndexed = new StreamController<HookedServiceEvent>.broadcast();
|
||||
StreamController<HookedServiceEvent> _beforeRead = new StreamController.broadcast();
|
||||
StreamController<HookedServiceEvent> _beforeCreated = new StreamController.broadcast();
|
||||
StreamController<HookedServiceEvent> _beforeModified = new StreamController.broadcast();
|
||||
StreamController<HookedServiceEvent> _beforeUpdated = new StreamController.broadcast();
|
||||
StreamController<HookedServiceEvent> _beforeRemoved = new StreamController.broadcast();
|
||||
|
||||
Stream<List> get onIndexed => _onIndexed.stream;
|
||||
Stream<HookedServiceEvent> get beforeIndexed => _beforeIndexed.stream;
|
||||
|
||||
Stream get onRead => _onRead.stream;
|
||||
Stream<HookedServiceEvent> get beforeRead => _beforeRead.stream;
|
||||
|
||||
Stream get onCreated => _onCreated.stream;
|
||||
Stream<HookedServiceEvent> get beforeCreated => _beforeCreated.stream;
|
||||
|
||||
Stream get onModified => _onModified.stream;
|
||||
Stream<HookedServiceEvent> get beforeModified => _beforeModified.stream;
|
||||
|
||||
Stream get onUpdated => _onUpdated.stream;
|
||||
Stream<HookedServiceEvent> get beforeUpdated => _beforeUpdated.stream;
|
||||
|
||||
Stream get onRemoved => _onRemoved.stream;
|
||||
Stream<HookedServiceEvent> get beforeRemoved => _beforeRemoved.stream;
|
||||
|
||||
StreamController<HookedServiceEvent> _afterIndexed = new StreamController<HookedServiceEvent>.broadcast();
|
||||
StreamController<HookedServiceEvent> _afterRead = new StreamController<HookedServiceEvent>.broadcast();
|
||||
StreamController<HookedServiceEvent> _afterCreated = new StreamController<HookedServiceEvent>.broadcast();
|
||||
StreamController<HookedServiceEvent> _afterModified = new StreamController<HookedServiceEvent>.broadcast();
|
||||
StreamController<HookedServiceEvent> _afterUpdated = new StreamController<HookedServiceEvent>.broadcast();
|
||||
StreamController<HookedServiceEvent> _afterRemoved = new StreamController<HookedServiceEvent>.broadcast();
|
||||
|
||||
Stream<HookedServiceEvent> get afterIndexed => _afterIndexed.stream;
|
||||
|
||||
Stream<HookedServiceEvent> get afterRead => _afterRead.stream;
|
||||
|
||||
Stream<HookedServiceEvent> get afterCreated => _afterCreated.stream;
|
||||
|
||||
Stream<HookedServiceEvent> get afterModified => _afterModified.stream;
|
||||
|
||||
Stream<HookedServiceEvent> get afterUpdated => _afterUpdated.stream;
|
||||
|
||||
Stream<HookedServiceEvent> get afterRemoved => _afterRemoved.stream;
|
||||
|
||||
final Service inner;
|
||||
|
||||
|
@ -28,16 +47,26 @@ class HookedService extends Service {
|
|||
|
||||
@override
|
||||
Future<List> index([Map params]) async {
|
||||
List indexed = await inner.index(params);
|
||||
_onIndexed.add(indexed);
|
||||
return indexed;
|
||||
HookedServiceEvent before = new HookedServiceEvent._base(inner, params: params);
|
||||
_beforeIndexed.add(before);
|
||||
|
||||
if (before._canceled) {
|
||||
HookedServiceEvent after = new HookedServiceEvent._base(inner, params: params, result: before.result);
|
||||
_afterIndexed.add(after);
|
||||
return before.result;
|
||||
}
|
||||
|
||||
List result = await inner.index(params);
|
||||
HookedServiceEvent after = new HookedServiceEvent._base(inner, params: params, result: result);
|
||||
_afterIndexed.add(after);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Future read(id, [Map params]) async {
|
||||
var retrieved = await inner.read(id, params);
|
||||
_onRead.add(retrieved);
|
||||
_afterRead.add(retrieved);
|
||||
return retrieved;
|
||||
}
|
||||
|
||||
|
@ -45,14 +74,14 @@ class HookedService extends Service {
|
|||
@override
|
||||
Future create(data, [Map params]) async {
|
||||
var created = await inner.create(data, params);
|
||||
_onCreated.add(created);
|
||||
_afterCreated.add(created);
|
||||
return created;
|
||||
}
|
||||
|
||||
@override
|
||||
Future modify(id, data, [Map params]) async {
|
||||
var modified = await inner.modify(id, data, params);
|
||||
_onUpdated.add(modified);
|
||||
_afterUpdated.add(modified);
|
||||
return modified;
|
||||
}
|
||||
|
||||
|
@ -60,14 +89,36 @@ class HookedService extends Service {
|
|||
@override
|
||||
Future update(id, data, [Map params]) async {
|
||||
var updated = await inner.update(id, data, params);
|
||||
_onUpdated.add(updated);
|
||||
_afterUpdated.add(updated);
|
||||
return updated;
|
||||
}
|
||||
|
||||
@override
|
||||
Future remove(id, [Map params]) async {
|
||||
var removed = await inner.remove(id, params);
|
||||
_onRemoved.add(removed);
|
||||
_afterRemoved.add(removed);
|
||||
return removed;
|
||||
}
|
||||
}
|
||||
|
||||
/// Fired when a hooked service is invoked.
|
||||
class HookedServiceEvent {
|
||||
/// Use this to end processing of an event.
|
||||
void cancel(result) {
|
||||
_canceled = true;
|
||||
_result = result;
|
||||
}
|
||||
|
||||
bool _canceled = false;
|
||||
var id;
|
||||
var data;
|
||||
Map params;
|
||||
var _result;
|
||||
get result => _result;
|
||||
/// The inner service whose method was hooked.
|
||||
Service service;
|
||||
|
||||
HookedServiceEvent._base(Service this.service, {this.id, this.data, Map this.params: const{}, result}) {
|
||||
_result = result;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ class MemoryService<T> extends Service {
|
|||
God god = new God();
|
||||
Map <int, T> items = {};
|
||||
|
||||
makeJson(int index, T t) {
|
||||
_makeJson(int index, T t) {
|
||||
if (T == null || T == Map)
|
||||
return mergeMap([god.serializeToMap(t), {'id': index}]);
|
||||
else
|
||||
|
@ -15,7 +15,7 @@ class MemoryService<T> extends Service {
|
|||
Future<List> index([Map params]) async {
|
||||
return items.keys
|
||||
.where((index) => items[index] != null)
|
||||
.map((index) => makeJson(index, items[index]))
|
||||
.map((index) => _makeJson(index, items[index]))
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ class MemoryService<T> extends Service {
|
|||
if (items.containsKey(desiredId)) {
|
||||
T found = items[desiredId];
|
||||
if (found != null) {
|
||||
return makeJson(desiredId, found);
|
||||
return _makeJson(desiredId, found);
|
||||
} else throw new AngelHttpException.NotFound();
|
||||
} else throw new AngelHttpException.NotFound();
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class MemoryService<T> extends Service {
|
|||
items[items.length] =
|
||||
(data is Map) ? god.deserializeFromMap(data, T) : data;
|
||||
T created = items[items.length - 1];
|
||||
return makeJson(items.length - 1, created);
|
||||
return _makeJson(items.length - 1, created);
|
||||
} catch (e) {
|
||||
throw new AngelHttpException.BadRequest(message: 'Invalid data.');
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ class MemoryService<T> extends Service {
|
|||
data = mergeMap([existing, data]);
|
||||
items[desiredId] =
|
||||
(data is Map) ? god.deserializeFromMap(data, T) : data;
|
||||
return makeJson(desiredId, items[desiredId]);
|
||||
return _makeJson(desiredId, items[desiredId]);
|
||||
} catch (e) {
|
||||
throw new AngelHttpException.BadRequest(message: 'Invalid data.');
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ class MemoryService<T> extends Service {
|
|||
try {
|
||||
items[desiredId] =
|
||||
(data is Map) ? god.deserializeFromMap(data, T) : data;
|
||||
return makeJson(desiredId, items[desiredId]);
|
||||
return _makeJson(desiredId, items[desiredId]);
|
||||
} catch (e) {
|
||||
throw new AngelHttpException.BadRequest(message: 'Invalid data.');
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ class MemoryService<T> extends Service {
|
|||
if (items.containsKey(desiredId)) {
|
||||
T item = items[desiredId];
|
||||
items[desiredId] = null;
|
||||
return makeJson(desiredId, item);
|
||||
return _makeJson(desiredId, item);
|
||||
} else throw new AngelHttpException.NotFound();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: angel_framework
|
||||
version: 0.0.0-dev.17
|
||||
version: 0.0.0-dev.18
|
||||
description: Core libraries for the Angel framework.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/angel_framework
|
||||
|
@ -7,7 +7,7 @@ dependencies:
|
|||
body_parser: ">=1.0.0-dev <2.0.0"
|
||||
json_god: ">=1.0.0 <2.0.0"
|
||||
merge_map: ">=1.0.0 <2.0.0"
|
||||
mime: ">=0.9.3 <0.10.0"
|
||||
mime: ^0.9.3
|
||||
dev_dependencies:
|
||||
http: ">= 0.11.3 < 0.12.0"
|
||||
test: ">= 0.12.13 < 0.13.0"
|
|
@ -20,7 +20,6 @@ main() {
|
|||
});
|
||||
|
||||
test('can use app.properties like members', () {
|
||||
/*
|
||||
angel.properties['hello'] = 'world';
|
||||
angel.properties['foo'] = () => 'bar';
|
||||
angel.properties['Foo'] = new Foo('bar');
|
||||
|
@ -28,7 +27,6 @@ main() {
|
|||
expect(angel.hello, equals('world'));
|
||||
expect(angel.foo(), equals('bar'));
|
||||
expect(angel.Foo.name, equals('bar'));
|
||||
*/
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue