This commit is contained in:
thosakwe 2017-02-01 16:43:18 -05:00
parent 0696705809
commit 6eebc7afa8
8 changed files with 127 additions and 32 deletions

View file

@ -1,6 +1,6 @@
# angel_framework
[![pub 1.0.0-dev.53](https://img.shields.io/badge/pub-1.0.0--dev.53-red.svg)](https://pub.dartlang.org/packages/angel_framework)
[![pub 1.0.0-dev.54](https://img.shields.io/badge/pub-1.0.0--dev.54-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.

View file

@ -0,0 +1,50 @@
import 'dart:async';
import 'service.dart';
/// An easy helper class to create one-off services without having to create an entire class.
///
/// Well-suited for testing.
class AnonymousService extends Service {
Function _index, _read, _create, _modify, _update, _remove;
AnonymousService(
{Future<List> index([Map params]),
Future read(id, [Map params]),
Future create(data, [Map params]),
Future modify(id, data, [Map params]),
Future update(id, data, [Map params]),
Future remove(id, [Map params])})
: super() {
_index = index;
_read = read;
_create = create;
_modify = modify;
_update = update;
_remove = remove;
}
@override
index([Map params]) => _index != null ? _index(params) : super.index(params);
@override
read(id, [Map params]) =>
_read != null ? _read(id, params) : super.read(id, params);
@override
create(data, [Map params]) =>
_create != null ? _create(data, params) : super.create(data, params);
@override
modify(id, data, [Map params]) => _modify != null
? _modify(id, data, params)
: super.modify(id, data, params);
@override
update(id, data, [Map params]) => _update != null
? _update(id, data, params)
: super.update(id, data, params);
@override
remove(id, [Map params]) =>
_remove != null ? _remove(id, params) : super.remove(id, params);
}

View file

@ -138,7 +138,7 @@ class HookedService extends Service {
get(
'/:id',
(req, res) async => await this
.read(req.params['id'], mergeMap([req.query, restProvider])),
.read(toId(req.params['id']), mergeMap([req.query, restProvider])),
middleware: []
..addAll(handlers)
..addAll((readMiddleware == null) ? [] : readMiddleware.handlers));
@ -146,8 +146,8 @@ class HookedService extends Service {
Middleware modifyMiddleware = getAnnotation(inner.modify, Middleware);
patch(
'/:id',
(req, res) async => await this.modify(
req.params['id'], req.body, mergeMap([req.query, restProvider])),
(req, res) async => await this.modify(toId(req.params['id']), req.body,
mergeMap([req.query, restProvider])),
middleware: []
..addAll(handlers)
..addAll(
@ -156,8 +156,8 @@ class HookedService extends Service {
Middleware updateMiddleware = getAnnotation(inner.update, Middleware);
post(
'/:id',
(req, res) async => await this.update(
req.params['id'], req.body, mergeMap([req.query, restProvider])),
(req, res) async => await this.update(toId(req.params['id']), req.body,
mergeMap([req.query, restProvider])),
middleware: []
..addAll(handlers)
..addAll(
@ -166,8 +166,8 @@ class HookedService extends Service {
Middleware removeMiddleware = getAnnotation(inner.remove, Middleware);
delete(
'/:id',
(req, res) async => await this
.remove(req.params['id'], mergeMap([req.query, restProvider])),
(req, res) async => await this.remove(
toId(req.params['id']), mergeMap([req.query, restProvider])),
middleware: []
..addAll(handlers)
..addAll(

View file

@ -4,6 +4,7 @@ library angel_framework.http;
export 'package:angel_route/angel_route.dart';
export 'angel_base.dart';
export 'angel_http_exception.dart';
export 'anonymous_service.dart';
export 'base_middleware.dart';
export 'base_plugin.dart';
export 'controller.dart';

View file

@ -8,11 +8,25 @@ import '../defs.dart';
import 'angel_http_exception.dart';
import 'service.dart';
int _getId(id) {
try {
return int.parse(id.toString());
} catch (e) {
throw new AngelHttpException.badRequest(message: 'Invalid ID.');
}
}
/// An in-memory [Service].
class MemoryService<T> extends Service {
Map <int, MemoryModel> items = {};
/// If set to `true`, clients can remove all items by passing a `null` `id` to `remove`.
///
/// `false` by default.
final bool allowRemoveAll;
MemoryService() :super() {
//// The data contained in this service.
final Map<int, MemoryModel> items = {};
MemoryService({this.allowRemoveAll: false}) : super() {
if (!reflectType(T).isAssignableTo(reflectType(MemoryModel))) {
throw new Exception(
"MemoryServices only support classes that inherit from MemoryModel.");
@ -31,19 +45,22 @@ class MemoryService<T> extends Service {
}
Future read(id, [Map params]) async {
int desiredId = int.parse(id.toString());
int desiredId = _getId(id);
if (items.containsKey(desiredId)) {
MemoryModel found = items[desiredId];
if (found != null) {
return _makeJson(desiredId, found);
} else throw new AngelHttpException.notFound();
} else throw new AngelHttpException.notFound();
} else
throw new AngelHttpException.notFound();
} else
throw new AngelHttpException.notFound();
}
Future create(data, [Map params]) async {
//try {
MemoryModel created = (data is MemoryModel) ? data : god.deserializeDatum(
data, outputType: T);
MemoryModel created = (data is MemoryModel)
? data
: god.deserializeDatum(data, outputType: T);
created.id = items.length;
items[created.id] = created;
@ -54,39 +71,50 @@ class MemoryService<T> extends Service {
}
Future modify(id, data, [Map params]) async {
int desiredId = int.parse(id.toString());
int desiredId = _getId(id);
if (items.containsKey(desiredId)) {
try {
Map existing = god.serializeObject(items[desiredId]);
data = mergeMap([existing, data]);
items[desiredId] =
(data is Map) ? god.deserializeDatum(data, outputType: T) : data;
(data is Map) ? god.deserializeDatum(data, outputType: T) : data;
return _makeJson(desiredId, items[desiredId]);
} catch (e) {
throw new AngelHttpException.badRequest(message: 'Invalid data.');
}
} else throw new AngelHttpException.notFound();
} else
throw new AngelHttpException.notFound();
}
Future update(id, data, [Map params]) async {
int desiredId = int.parse(id.toString());
int desiredId = _getId(id);
if (items.containsKey(desiredId)) {
try {
items[desiredId] =
(data is Map) ? god.deserializeDatum(data, outputType: T) : data;
(data is Map) ? god.deserializeDatum(data, outputType: T) : data;
return _makeJson(desiredId, items[desiredId]);
} catch (e) {
throw new AngelHttpException.badRequest(message: 'Invalid data.');
}
} else throw new AngelHttpException.notFound();
} else
throw new AngelHttpException.notFound();
}
Future remove(id, [Map params]) async {
int desiredId = int.parse(id.toString());
if (id == null ||
id == 'null' &&
(allowRemoveAll == true ||
params?.containsKey('provider') != true)) {
items.clear();
return {};
}
int desiredId = _getId(id);
if (items.containsKey(desiredId)) {
MemoryModel item = items[desiredId];
items[desiredId] = null;
return _makeJson(desiredId, item);
} else throw new AngelHttpException.notFound();
} else
throw new AngelHttpException.notFound();
}
}
}

View file

@ -70,6 +70,11 @@ class Angel extends AngelBase {
/// Returns the parent instance of this application, if any.
Angel get parent => _parent;
/// Plug-ins to be called right before server startup.
///
/// If the server is never started, they will never be called.
final List<AngelConfigurer> justBeforeStart = [];
/// Always run before responses are sent.
///
/// These will only not run if an [AngelFatalError] occurs.
@ -126,6 +131,7 @@ class Angel extends AngelBase {
Future<HttpServer> startServer([InternetAddress address, int port]) async {
var host = address ?? InternetAddress.LOOPBACK_IP_V4;
this.httpServer = await _serverGenerator(host, port ?? 0);
await Future.wait(justBeforeStart.map(configure));
preprocessRoutes();
return httpServer..listen(handleRequest);
}
@ -139,6 +145,7 @@ class Angel extends AngelBase {
container.singleton(this);
}
/// Runs some [handler]. Returns `true` if request execution should continue.
Future<bool> executeHandler(
handler, RequestContext req, ResponseContext res) async {
if (handler is RequestMiddleware) {

View file

@ -68,6 +68,15 @@ class Service extends Routable {
throw new AngelHttpException.methodNotAllowed();
}
/// Transforms an [id] string into one acceptable by a service.
toId(String id) {
if (id == 'null' || id == null)
return null;
else
return id;
}
/// Generates RESTful routes pointing to this class's methods.
void addRoutes() {
Map restProvider = {'provider': Providers.REST};
@ -100,7 +109,7 @@ class Service extends Routable {
get(
'/:id',
(req, res) async => await this
.read(req.params['id'], mergeMap([req.query, restProvider])),
.read(toId(req.params['id']), mergeMap([req.query, restProvider])),
middleware: []
..addAll(handlers)
..addAll((readMiddleware == null) ? [] : readMiddleware.handlers));
@ -108,8 +117,8 @@ class Service extends Routable {
Middleware modifyMiddleware = getAnnotation(this.modify, Middleware);
patch(
'/:id',
(req, res) async => await this.modify(
req.params['id'], req.body, mergeMap([req.query, restProvider])),
(req, res) async => await this.modify(toId(req.params['id']), req.body,
mergeMap([req.query, restProvider])),
middleware: []
..addAll(handlers)
..addAll(
@ -118,8 +127,8 @@ class Service extends Routable {
Middleware updateMiddleware = getAnnotation(this.update, Middleware);
post(
'/:id',
(req, res) async => await this.update(
req.params['id'], req.body, mergeMap([req.query, restProvider])),
(req, res) async => await this.update(toId(req.params['id']), req.body,
mergeMap([req.query, restProvider])),
middleware: []
..addAll(handlers)
..addAll(
@ -128,8 +137,8 @@ class Service extends Routable {
Middleware removeMiddleware = getAnnotation(this.remove, Middleware);
delete(
'/:id',
(req, res) async => await this
.remove(req.params['id'], mergeMap([req.query, restProvider])),
(req, res) async => await this.remove(
toId(req.params['id']), mergeMap([req.query, restProvider])),
middleware: []
..addAll(handlers)
..addAll(

View file

@ -1,5 +1,5 @@
name: angel_framework
version: 1.0.0-dev.53
version: 1.0.0-dev.54
description: Core libraries for the Angel framework.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/angel_framework