54
This commit is contained in:
parent
0696705809
commit
6eebc7afa8
8 changed files with 127 additions and 32 deletions
|
@ -1,6 +1,6 @@
|
||||||
# angel_framework
|
# 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)
|
[![build status](https://travis-ci.org/angel-dart/framework.svg)](https://travis-ci.org/angel-dart/framework)
|
||||||
|
|
||||||
Core libraries for the Angel Framework.
|
Core libraries for the Angel Framework.
|
||||||
|
|
50
lib/src/http/anonymous_service.dart
Normal file
50
lib/src/http/anonymous_service.dart
Normal 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);
|
||||||
|
}
|
|
@ -138,7 +138,7 @@ class HookedService extends Service {
|
||||||
get(
|
get(
|
||||||
'/:id',
|
'/:id',
|
||||||
(req, res) async => await this
|
(req, res) async => await this
|
||||||
.read(req.params['id'], mergeMap([req.query, restProvider])),
|
.read(toId(req.params['id']), mergeMap([req.query, restProvider])),
|
||||||
middleware: []
|
middleware: []
|
||||||
..addAll(handlers)
|
..addAll(handlers)
|
||||||
..addAll((readMiddleware == null) ? [] : readMiddleware.handlers));
|
..addAll((readMiddleware == null) ? [] : readMiddleware.handlers));
|
||||||
|
@ -146,8 +146,8 @@ class HookedService extends Service {
|
||||||
Middleware modifyMiddleware = getAnnotation(inner.modify, Middleware);
|
Middleware modifyMiddleware = getAnnotation(inner.modify, Middleware);
|
||||||
patch(
|
patch(
|
||||||
'/:id',
|
'/:id',
|
||||||
(req, res) async => await this.modify(
|
(req, res) async => await this.modify(toId(req.params['id']), req.body,
|
||||||
req.params['id'], req.body, mergeMap([req.query, restProvider])),
|
mergeMap([req.query, restProvider])),
|
||||||
middleware: []
|
middleware: []
|
||||||
..addAll(handlers)
|
..addAll(handlers)
|
||||||
..addAll(
|
..addAll(
|
||||||
|
@ -156,8 +156,8 @@ class HookedService extends Service {
|
||||||
Middleware updateMiddleware = getAnnotation(inner.update, Middleware);
|
Middleware updateMiddleware = getAnnotation(inner.update, Middleware);
|
||||||
post(
|
post(
|
||||||
'/:id',
|
'/:id',
|
||||||
(req, res) async => await this.update(
|
(req, res) async => await this.update(toId(req.params['id']), req.body,
|
||||||
req.params['id'], req.body, mergeMap([req.query, restProvider])),
|
mergeMap([req.query, restProvider])),
|
||||||
middleware: []
|
middleware: []
|
||||||
..addAll(handlers)
|
..addAll(handlers)
|
||||||
..addAll(
|
..addAll(
|
||||||
|
@ -166,8 +166,8 @@ class HookedService extends Service {
|
||||||
Middleware removeMiddleware = getAnnotation(inner.remove, Middleware);
|
Middleware removeMiddleware = getAnnotation(inner.remove, Middleware);
|
||||||
delete(
|
delete(
|
||||||
'/:id',
|
'/:id',
|
||||||
(req, res) async => await this
|
(req, res) async => await this.remove(
|
||||||
.remove(req.params['id'], mergeMap([req.query, restProvider])),
|
toId(req.params['id']), mergeMap([req.query, restProvider])),
|
||||||
middleware: []
|
middleware: []
|
||||||
..addAll(handlers)
|
..addAll(handlers)
|
||||||
..addAll(
|
..addAll(
|
||||||
|
|
|
@ -4,6 +4,7 @@ library angel_framework.http;
|
||||||
export 'package:angel_route/angel_route.dart';
|
export 'package:angel_route/angel_route.dart';
|
||||||
export 'angel_base.dart';
|
export 'angel_base.dart';
|
||||||
export 'angel_http_exception.dart';
|
export 'angel_http_exception.dart';
|
||||||
|
export 'anonymous_service.dart';
|
||||||
export 'base_middleware.dart';
|
export 'base_middleware.dart';
|
||||||
export 'base_plugin.dart';
|
export 'base_plugin.dart';
|
||||||
export 'controller.dart';
|
export 'controller.dart';
|
||||||
|
|
|
@ -8,11 +8,25 @@ import '../defs.dart';
|
||||||
import 'angel_http_exception.dart';
|
import 'angel_http_exception.dart';
|
||||||
import 'service.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].
|
/// An in-memory [Service].
|
||||||
class MemoryService<T> extends 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))) {
|
if (!reflectType(T).isAssignableTo(reflectType(MemoryModel))) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
"MemoryServices only support classes that inherit from MemoryModel.");
|
"MemoryServices only support classes that inherit from MemoryModel.");
|
||||||
|
@ -31,19 +45,22 @@ class MemoryService<T> extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future read(id, [Map params]) async {
|
Future read(id, [Map params]) async {
|
||||||
int desiredId = int.parse(id.toString());
|
int desiredId = _getId(id);
|
||||||
if (items.containsKey(desiredId)) {
|
if (items.containsKey(desiredId)) {
|
||||||
MemoryModel found = items[desiredId];
|
MemoryModel found = items[desiredId];
|
||||||
if (found != null) {
|
if (found != null) {
|
||||||
return _makeJson(desiredId, found);
|
return _makeJson(desiredId, found);
|
||||||
} else throw new AngelHttpException.notFound();
|
} else
|
||||||
} else throw new AngelHttpException.notFound();
|
throw new AngelHttpException.notFound();
|
||||||
|
} else
|
||||||
|
throw new AngelHttpException.notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future create(data, [Map params]) async {
|
Future create(data, [Map params]) async {
|
||||||
//try {
|
//try {
|
||||||
MemoryModel created = (data is MemoryModel) ? data : god.deserializeDatum(
|
MemoryModel created = (data is MemoryModel)
|
||||||
data, outputType: T);
|
? data
|
||||||
|
: god.deserializeDatum(data, outputType: T);
|
||||||
|
|
||||||
created.id = items.length;
|
created.id = items.length;
|
||||||
items[created.id] = created;
|
items[created.id] = created;
|
||||||
|
@ -54,39 +71,50 @@ class MemoryService<T> extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future modify(id, data, [Map params]) async {
|
Future modify(id, data, [Map params]) async {
|
||||||
int desiredId = int.parse(id.toString());
|
int desiredId = _getId(id);
|
||||||
if (items.containsKey(desiredId)) {
|
if (items.containsKey(desiredId)) {
|
||||||
try {
|
try {
|
||||||
Map existing = god.serializeObject(items[desiredId]);
|
Map existing = god.serializeObject(items[desiredId]);
|
||||||
data = mergeMap([existing, data]);
|
data = mergeMap([existing, data]);
|
||||||
items[desiredId] =
|
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]);
|
return _makeJson(desiredId, items[desiredId]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new AngelHttpException.badRequest(message: 'Invalid data.');
|
throw new AngelHttpException.badRequest(message: 'Invalid data.');
|
||||||
}
|
}
|
||||||
} else throw new AngelHttpException.notFound();
|
} else
|
||||||
|
throw new AngelHttpException.notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future update(id, data, [Map params]) async {
|
Future update(id, data, [Map params]) async {
|
||||||
int desiredId = int.parse(id.toString());
|
int desiredId = _getId(id);
|
||||||
if (items.containsKey(desiredId)) {
|
if (items.containsKey(desiredId)) {
|
||||||
try {
|
try {
|
||||||
items[desiredId] =
|
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]);
|
return _makeJson(desiredId, items[desiredId]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new AngelHttpException.badRequest(message: 'Invalid data.');
|
throw new AngelHttpException.badRequest(message: 'Invalid data.');
|
||||||
}
|
}
|
||||||
} else throw new AngelHttpException.notFound();
|
} else
|
||||||
|
throw new AngelHttpException.notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future remove(id, [Map params]) async {
|
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)) {
|
if (items.containsKey(desiredId)) {
|
||||||
MemoryModel item = items[desiredId];
|
MemoryModel item = items[desiredId];
|
||||||
items[desiredId] = null;
|
items[desiredId] = null;
|
||||||
return _makeJson(desiredId, item);
|
return _makeJson(desiredId, item);
|
||||||
} else throw new AngelHttpException.notFound();
|
} else
|
||||||
|
throw new AngelHttpException.notFound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,11 @@ class Angel extends AngelBase {
|
||||||
/// Returns the parent instance of this application, if any.
|
/// Returns the parent instance of this application, if any.
|
||||||
Angel get parent => _parent;
|
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.
|
/// Always run before responses are sent.
|
||||||
///
|
///
|
||||||
/// These will only not run if an [AngelFatalError] occurs.
|
/// 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 {
|
Future<HttpServer> startServer([InternetAddress address, int port]) async {
|
||||||
var host = address ?? InternetAddress.LOOPBACK_IP_V4;
|
var host = address ?? InternetAddress.LOOPBACK_IP_V4;
|
||||||
this.httpServer = await _serverGenerator(host, port ?? 0);
|
this.httpServer = await _serverGenerator(host, port ?? 0);
|
||||||
|
await Future.wait(justBeforeStart.map(configure));
|
||||||
preprocessRoutes();
|
preprocessRoutes();
|
||||||
return httpServer..listen(handleRequest);
|
return httpServer..listen(handleRequest);
|
||||||
}
|
}
|
||||||
|
@ -139,6 +145,7 @@ class Angel extends AngelBase {
|
||||||
container.singleton(this);
|
container.singleton(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs some [handler]. Returns `true` if request execution should continue.
|
||||||
Future<bool> executeHandler(
|
Future<bool> executeHandler(
|
||||||
handler, RequestContext req, ResponseContext res) async {
|
handler, RequestContext req, ResponseContext res) async {
|
||||||
if (handler is RequestMiddleware) {
|
if (handler is RequestMiddleware) {
|
||||||
|
|
|
@ -68,6 +68,15 @@ class Service extends Routable {
|
||||||
throw new AngelHttpException.methodNotAllowed();
|
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() {
|
void addRoutes() {
|
||||||
Map restProvider = {'provider': Providers.REST};
|
Map restProvider = {'provider': Providers.REST};
|
||||||
|
|
||||||
|
@ -100,7 +109,7 @@ class Service extends Routable {
|
||||||
get(
|
get(
|
||||||
'/:id',
|
'/:id',
|
||||||
(req, res) async => await this
|
(req, res) async => await this
|
||||||
.read(req.params['id'], mergeMap([req.query, restProvider])),
|
.read(toId(req.params['id']), mergeMap([req.query, restProvider])),
|
||||||
middleware: []
|
middleware: []
|
||||||
..addAll(handlers)
|
..addAll(handlers)
|
||||||
..addAll((readMiddleware == null) ? [] : readMiddleware.handlers));
|
..addAll((readMiddleware == null) ? [] : readMiddleware.handlers));
|
||||||
|
@ -108,8 +117,8 @@ class Service extends Routable {
|
||||||
Middleware modifyMiddleware = getAnnotation(this.modify, Middleware);
|
Middleware modifyMiddleware = getAnnotation(this.modify, Middleware);
|
||||||
patch(
|
patch(
|
||||||
'/:id',
|
'/:id',
|
||||||
(req, res) async => await this.modify(
|
(req, res) async => await this.modify(toId(req.params['id']), req.body,
|
||||||
req.params['id'], req.body, mergeMap([req.query, restProvider])),
|
mergeMap([req.query, restProvider])),
|
||||||
middleware: []
|
middleware: []
|
||||||
..addAll(handlers)
|
..addAll(handlers)
|
||||||
..addAll(
|
..addAll(
|
||||||
|
@ -118,8 +127,8 @@ class Service extends Routable {
|
||||||
Middleware updateMiddleware = getAnnotation(this.update, Middleware);
|
Middleware updateMiddleware = getAnnotation(this.update, Middleware);
|
||||||
post(
|
post(
|
||||||
'/:id',
|
'/:id',
|
||||||
(req, res) async => await this.update(
|
(req, res) async => await this.update(toId(req.params['id']), req.body,
|
||||||
req.params['id'], req.body, mergeMap([req.query, restProvider])),
|
mergeMap([req.query, restProvider])),
|
||||||
middleware: []
|
middleware: []
|
||||||
..addAll(handlers)
|
..addAll(handlers)
|
||||||
..addAll(
|
..addAll(
|
||||||
|
@ -128,8 +137,8 @@ class Service extends Routable {
|
||||||
Middleware removeMiddleware = getAnnotation(this.remove, Middleware);
|
Middleware removeMiddleware = getAnnotation(this.remove, Middleware);
|
||||||
delete(
|
delete(
|
||||||
'/:id',
|
'/:id',
|
||||||
(req, res) async => await this
|
(req, res) async => await this.remove(
|
||||||
.remove(req.params['id'], mergeMap([req.query, restProvider])),
|
toId(req.params['id']), mergeMap([req.query, restProvider])),
|
||||||
middleware: []
|
middleware: []
|
||||||
..addAll(handlers)
|
..addAll(handlers)
|
||||||
..addAll(
|
..addAll(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: angel_framework
|
name: angel_framework
|
||||||
version: 1.0.0-dev.53
|
version: 1.0.0-dev.54
|
||||||
description: Core libraries for the Angel framework.
|
description: Core libraries for the Angel framework.
|
||||||
author: Tobe O <thosakwe@gmail.com>
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
homepage: https://github.com/angel-dart/angel_framework
|
homepage: https://github.com/angel-dart/angel_framework
|
||||||
|
|
Loading…
Reference in a new issue