Dependency Injection! :)
This commit is contained in:
parent
52c5112d93
commit
dfbfc4cbcf
8 changed files with 249 additions and 56 deletions
|
@ -1,12 +1,17 @@
|
||||||
library angel_framework.http.angel_base;
|
library angel_framework.http.angel_base;
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'package:container/container.dart';
|
||||||
import 'routable.dart';
|
import 'routable.dart';
|
||||||
|
|
||||||
/// A function that asynchronously generates a view from the given path and data.
|
/// A function that asynchronously generates a view from the given path and data.
|
||||||
typedef Future<String> ViewGenerator(String path, [Map data]);
|
typedef Future<String> ViewGenerator(String path, [Map data]);
|
||||||
|
|
||||||
class AngelBase extends Routable {
|
class AngelBase extends Routable {
|
||||||
|
Container _container = new Container();
|
||||||
|
/// A [Container] used to inject dependencies.
|
||||||
|
Container get container => _container;
|
||||||
|
|
||||||
/// A function that renders views.
|
/// A function that renders views.
|
||||||
///
|
///
|
||||||
/// Called by [ResponseContext]@`render`.
|
/// Called by [ResponseContext]@`render`.
|
||||||
|
|
9
lib/src/http/base_middleware.dart
Normal file
9
lib/src/http/base_middleware.dart
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
library angel_framework.http.base_middleware;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'request_context.dart';
|
||||||
|
import 'response_context.dart';
|
||||||
|
|
||||||
|
abstract class BaseMiddleware {
|
||||||
|
Future<bool> call(RequestContext req, ResponseContext res);
|
||||||
|
}
|
8
lib/src/http/base_plugin.dart
Normal file
8
lib/src/http/base_plugin.dart
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
library angel_framework.http.base_plugin;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'server.dart';
|
||||||
|
|
||||||
|
abstract class AngelPlugin {
|
||||||
|
Future call(Angel app);
|
||||||
|
}
|
|
@ -56,16 +56,26 @@ class Controller {
|
||||||
args.add(req);
|
args.add(req);
|
||||||
else if (parameter.type.reflectedType == ResponseContext)
|
else if (parameter.type.reflectedType == ResponseContext)
|
||||||
args.add(res);
|
args.add(res);
|
||||||
else {
|
else {String name = MirrorSystem.getName(parameter.simpleName);
|
||||||
String name = MirrorSystem.getName(parameter.simpleName);
|
|
||||||
var arg = req.params[name];
|
var arg = req.params[name];
|
||||||
|
|
||||||
if (arg == null &&
|
if (arg == null) {
|
||||||
!exposeMirror.reflectee.allowNull.contain(name)) {
|
if (parameter.type.reflectedType != dynamic) {
|
||||||
throw new AngelHttpException.BadRequest();
|
try {
|
||||||
}
|
arg = app.container.make(parameter.type.reflectedType);
|
||||||
|
if (arg != null) {
|
||||||
|
args.add(arg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
args.add(arg);
|
if (!exposeMirror.reflectee.allowNull.contain(name))
|
||||||
|
throw new AngelHttpException.BadRequest(message: "Missing parameter '$name'");
|
||||||
|
|
||||||
|
} else args.add(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ library angel_framework.http.server;
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math' show Random;
|
import 'dart:math' show Random;
|
||||||
|
import 'dart:mirrors';
|
||||||
import 'package:json_god/json_god.dart' as god;
|
import 'package:json_god/json_god.dart' as god;
|
||||||
import 'angel_base.dart';
|
import 'angel_base.dart';
|
||||||
import 'angel_http_exception.dart';
|
import 'angel_http_exception.dart';
|
||||||
|
@ -12,40 +13,41 @@ import 'response_context.dart';
|
||||||
import 'routable.dart';
|
import 'routable.dart';
|
||||||
import 'route.dart';
|
import 'route.dart';
|
||||||
import 'service.dart';
|
import 'service.dart';
|
||||||
|
export 'package:container/container.dart';
|
||||||
|
|
||||||
/// A function that binds an [Angel] server to an Internet address and port.
|
/// A function that binds an [Angel] server to an Internet address and port.
|
||||||
typedef Future<HttpServer> ServerGenerator(InternetAddress address, int port);
|
typedef Future<HttpServer> ServerGenerator(InternetAddress address, int port);
|
||||||
|
|
||||||
/// Handles an [AngelHttpException].
|
/// Handles an [AngelHttpException].
|
||||||
typedef Future AngelErrorHandler(AngelHttpException err, RequestContext req,
|
typedef Future AngelErrorHandler(
|
||||||
ResponseContext res);
|
AngelHttpException err, RequestContext req, ResponseContext res);
|
||||||
|
|
||||||
/// A function that configures an [AngelBase] server in some way.
|
/// A function that configures an [AngelBase] server in some way.
|
||||||
typedef Future AngelConfigurer(AngelBase app);
|
typedef Future AngelConfigurer(AngelBase app);
|
||||||
|
|
||||||
/// A powerful real-time/REST/MVC server class.
|
/// A powerful real-time/REST/MVC server class.
|
||||||
class Angel extends AngelBase {
|
class Angel extends AngelBase {
|
||||||
var _beforeProcessed = new StreamController<HttpRequest>();
|
var _afterProcessed = new StreamController<HttpRequest>.broadcast();
|
||||||
var _afterProcessed = new StreamController<HttpRequest>();
|
var _beforeProcessed = new StreamController<HttpRequest>.broadcast();
|
||||||
var _onController = new StreamController<Controller>.broadcast();
|
var _onController = new StreamController<Controller>.broadcast();
|
||||||
|
ServerGenerator _serverGenerator =
|
||||||
|
(address, port) async => await HttpServer.bind(address, port);
|
||||||
|
|
||||||
|
/// Fired after a request is processed. Always runs.
|
||||||
|
Stream<HttpRequest> get afterProcessed => _afterProcessed.stream;
|
||||||
|
|
||||||
/// Fired before a request is processed. Always runs.
|
/// Fired before a request is processed. Always runs.
|
||||||
Stream<HttpRequest> get beforeProcessed => _beforeProcessed.stream;
|
Stream<HttpRequest> get beforeProcessed => _beforeProcessed.stream;
|
||||||
|
|
||||||
/// Fired after a request is processed. Always runs.
|
|
||||||
Stream<HttpRequest> get afterProcessed => _afterProcessed.stream;
|
|
||||||
|
|
||||||
/// Fired whenever a controller is added to this instance.
|
/// Fired whenever a controller is added to this instance.
|
||||||
///
|
///
|
||||||
/// **NOTE**: This is a broadcast stream.
|
/// **NOTE**: This is a broadcast stream.
|
||||||
Stream<Controller> get onController => _onController.stream;
|
Stream<Controller> get onController => _onController.stream;
|
||||||
|
|
||||||
ServerGenerator _serverGenerator =
|
|
||||||
(address, port) async => await HttpServer.bind(address, port);
|
|
||||||
|
|
||||||
/// Default error handler, show HTML error page
|
/// Default error handler, show HTML error page
|
||||||
AngelErrorHandler _errorHandler = (AngelHttpException e, req,
|
AngelErrorHandler _errorHandler =
|
||||||
ResponseContext res) {
|
(AngelHttpException e, req, ResponseContext res) {
|
||||||
res.header(HttpHeaders.CONTENT_TYPE, ContentType.HTML.toString());
|
res.header(HttpHeaders.CONTENT_TYPE, ContentType.HTML.toString());
|
||||||
res.status(e.statusCode);
|
res.status(e.statusCode);
|
||||||
res.write("<!DOCTYPE html><html><head><title>${e.message}</title>");
|
res.write("<!DOCTYPE html><html><head><title>${e.message}</title>");
|
||||||
|
@ -69,9 +71,9 @@ class Angel extends AngelBase {
|
||||||
/// Starts the server.
|
/// Starts the server.
|
||||||
///
|
///
|
||||||
/// Returns false on failure; otherwise, returns the HttpServer.
|
/// Returns false on failure; otherwise, returns the HttpServer.
|
||||||
startServer(InternetAddress address, int port) async {
|
Future<HttpServer> startServer([InternetAddress address, int port]) async {
|
||||||
var server =
|
var server =
|
||||||
await _serverGenerator(address ?? InternetAddress.LOOPBACK_IP_V4, port);
|
await _serverGenerator(address ?? InternetAddress.LOOPBACK_IP_V4, port ?? 0);
|
||||||
this.httpServer = server;
|
this.httpServer = server;
|
||||||
|
|
||||||
server.listen(handleRequest);
|
server.listen(handleRequest);
|
||||||
|
@ -79,29 +81,42 @@ class Angel extends AngelBase {
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Loads some base dependencies into the service container.
|
||||||
|
void bootstrapContainer() {
|
||||||
|
container.singleton(this, as: AngelBase);
|
||||||
|
container.singleton(this);
|
||||||
|
|
||||||
|
if (runtimeType != Angel)
|
||||||
|
container.singleton(this, as: Angel);
|
||||||
|
}
|
||||||
|
|
||||||
Future handleRequest(HttpRequest request) async {
|
Future handleRequest(HttpRequest request) async {
|
||||||
_beforeProcessed.add(request);
|
_beforeProcessed.add(request);
|
||||||
String req_url =
|
|
||||||
request.uri.toString().replaceAll("?" + request.uri.query, "").replaceAll(
|
String requestedUrl = request.uri
|
||||||
new RegExp(r'\/+$'), '');
|
.toString()
|
||||||
if (req_url.isEmpty) req_url = '/';
|
.replaceAll("?" + request.uri.query, "")
|
||||||
|
.replaceAll(new RegExp(r'\/+$'), '');
|
||||||
|
|
||||||
|
if (requestedUrl.isEmpty) requestedUrl = '/';
|
||||||
|
|
||||||
RequestContext req = await RequestContext.from(request, {}, this, null);
|
RequestContext req = await RequestContext.from(request, {}, this, null);
|
||||||
ResponseContext res = await ResponseContext.from(request.response, this);
|
ResponseContext res = await ResponseContext.from(request.response, this);
|
||||||
|
|
||||||
bool canContinue = true;
|
bool canContinue = true;
|
||||||
|
|
||||||
var execHandler = (handler, req) async {
|
executeHandler(handler, req) async {
|
||||||
if (canContinue) {
|
if (canContinue) {
|
||||||
canContinue = await new Future.sync(() async {
|
try {
|
||||||
return _applyHandler(handler, req, res);
|
canContinue = await _applyHandler(handler, req, res);
|
||||||
}).catchError((e, [StackTrace stackTrace]) async {
|
} catch (e, stackTrace) {
|
||||||
if (e is AngelHttpException) {
|
if (e is AngelHttpException) {
|
||||||
// Special handling for AngelHttpExceptions :)
|
// Special handling for AngelHttpExceptions :)
|
||||||
try {
|
try {
|
||||||
res.status(e.statusCode);
|
res.status(e.statusCode);
|
||||||
String accept = request.headers.value(HttpHeaders.ACCEPT);
|
String accept = request.headers.value(HttpHeaders.ACCEPT);
|
||||||
if (accept == "*/*" ||
|
if (accept == "*/*" ||
|
||||||
accept.contains("application/json") ||
|
accept.contains(ContentType.JSON.mimeType) ||
|
||||||
accept.contains("application/javascript")) {
|
accept.contains("application/javascript")) {
|
||||||
res.json(e.toMap());
|
res.json(e.toMap());
|
||||||
} else {
|
} else {
|
||||||
|
@ -113,38 +128,41 @@ class Angel extends AngelBase {
|
||||||
_onError(e, stackTrace);
|
_onError(e, stackTrace);
|
||||||
canContinue = false;
|
canContinue = false;
|
||||||
return false;
|
return false;
|
||||||
});
|
}
|
||||||
} else
|
} else
|
||||||
return false;
|
return false;
|
||||||
};
|
}
|
||||||
|
|
||||||
for (var handler in before) {
|
for (var handler in before) {
|
||||||
await execHandler(handler, req);
|
await executeHandler(handler, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Route route in routes) {
|
for (Route route in routes) {
|
||||||
if (!canContinue) break;
|
if (!canContinue) break;
|
||||||
if (route.matcher.hasMatch(req_url) &&
|
|
||||||
|
if (route.matcher.hasMatch(requestedUrl) &&
|
||||||
(request.method == route.method || route.method == '*')) {
|
(request.method == route.method || route.method == '*')) {
|
||||||
req.params = route.parseParameters(req_url);
|
req.params = route.parseParameters(requestedUrl);
|
||||||
req.route = route;
|
req.route = route;
|
||||||
|
|
||||||
for (var handler in route.handlers) {
|
for (var handler in route.handlers) {
|
||||||
await execHandler(handler, req);
|
await executeHandler(handler, req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var handler in after) {
|
for (var handler in after) {
|
||||||
await execHandler(handler, req);
|
await executeHandler(handler, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
_finalizeResponse(request, res);
|
_finalizeResponse(request, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _applyHandler(handler, RequestContext req,
|
Future<bool> _applyHandler(
|
||||||
ResponseContext res) async {
|
handler, RequestContext req, ResponseContext res) async {
|
||||||
if (handler is RequestMiddleware) {
|
if (handler is RequestMiddleware) {
|
||||||
var result = await handler(req, res);
|
var result = await handler(req, res);
|
||||||
|
|
||||||
if (result is bool)
|
if (result is bool)
|
||||||
return result == true;
|
return result == true;
|
||||||
else if (result != null) {
|
else if (result != null) {
|
||||||
|
@ -157,7 +175,9 @@ class Angel extends AngelBase {
|
||||||
if (handler is RequestHandler) {
|
if (handler is RequestHandler) {
|
||||||
await handler(req, res);
|
await handler(req, res);
|
||||||
return res.isOpen;
|
return res.isOpen;
|
||||||
} else if (handler is RawRequestHandler) {
|
}
|
||||||
|
|
||||||
|
if (handler is RawRequestHandler) {
|
||||||
var result = await handler(req.underlyingRequest);
|
var result = await handler(req.underlyingRequest);
|
||||||
if (result is bool)
|
if (result is bool)
|
||||||
return result == true;
|
return result == true;
|
||||||
|
@ -166,8 +186,10 @@ class Angel extends AngelBase {
|
||||||
return false;
|
return false;
|
||||||
} else
|
} else
|
||||||
return true;
|
return true;
|
||||||
} else if (handler is Function || handler is Future) {
|
}
|
||||||
var result = await handler();
|
|
||||||
|
if (handler is Future) {
|
||||||
|
var result = await handler;
|
||||||
if (result is bool)
|
if (result is bool)
|
||||||
return result == true;
|
return result == true;
|
||||||
else if (result != null) {
|
else if (result != null) {
|
||||||
|
@ -175,14 +197,27 @@ class Angel extends AngelBase {
|
||||||
return false;
|
return false;
|
||||||
} else
|
} else
|
||||||
return true;
|
return true;
|
||||||
} else if (requestMiddleware.containsKey(handler)) {
|
|
||||||
return await _applyHandler(requestMiddleware[handler], req, res);
|
|
||||||
} else {
|
|
||||||
res.willCloseItself = true;
|
|
||||||
res.underlyingResponse.write(god.serialize(handler));
|
|
||||||
await res.underlyingResponse.close();
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (handler is Function) {
|
||||||
|
var result = await runContained(handler, req, res);
|
||||||
|
if (result is bool)
|
||||||
|
return result == true;
|
||||||
|
else if (result != null) {
|
||||||
|
res.json(result);
|
||||||
|
return false;
|
||||||
|
} else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestMiddleware.containsKey(handler)) {
|
||||||
|
return await _applyHandler(requestMiddleware[handler], req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.willCloseItself = true;
|
||||||
|
res.underlyingResponse.write(god.serialize(handler));
|
||||||
|
await res.underlyingResponse.close();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_finalizeResponse(HttpRequest request, ResponseContext res) async {
|
_finalizeResponse(HttpRequest request, ResponseContext res) async {
|
||||||
|
@ -193,7 +228,7 @@ class Angel extends AngelBase {
|
||||||
_afterProcessed.add(request);
|
_afterProcessed.add(request);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Remember: This fails silently
|
failSilently(request, res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,14 +241,47 @@ class Angel extends AngelBase {
|
||||||
return new String.fromCharCodes(codeUnits);
|
return new String.fromCharCodes(codeUnits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run a function after injecting from service container
|
||||||
|
Future runContained(Function handler, RequestContext req, ResponseContext res) async {
|
||||||
|
ClosureMirror closureMirror = reflect(handler);
|
||||||
|
List args = [];
|
||||||
|
|
||||||
|
for (ParameterMirror parameter in closureMirror.function.parameters) {
|
||||||
|
if (parameter.type.reflectedType == RequestContext)
|
||||||
|
args.add(req);
|
||||||
|
else if (parameter.type.reflectedType == ResponseContext)
|
||||||
|
args.add(res);
|
||||||
|
else {
|
||||||
|
// First, search to see if we can map this to a type
|
||||||
|
if (parameter.type.reflectedType != dynamic) {
|
||||||
|
args.add(container.make(parameter.type.reflectedType));
|
||||||
|
} else {
|
||||||
|
String name = MirrorSystem.getName(parameter.simpleName);
|
||||||
|
|
||||||
|
if (name == "req")
|
||||||
|
args.add(req);
|
||||||
|
else if (name == "res")
|
||||||
|
args.add(res);
|
||||||
|
else {
|
||||||
|
throw new Exception("Cannot resolve parameter '$name' within handler.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await closureMirror.apply(args).reflectee;
|
||||||
|
}
|
||||||
|
|
||||||
/// Applies an [AngelConfigurer] to this instance.
|
/// Applies an [AngelConfigurer] to this instance.
|
||||||
Future configure(AngelConfigurer configurer) async {
|
Future configure(AngelConfigurer configurer) async {
|
||||||
await configurer(this);
|
await configurer(this);
|
||||||
|
|
||||||
if (configurer is Controller)
|
if (configurer is Controller) _onController.add(configurer);
|
||||||
_onController.add(configurer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fallback when an error is thrown while handling a request.
|
||||||
|
void failSilently(HttpRequest request, ResponseContext res) {}
|
||||||
|
|
||||||
/// Starts the server.
|
/// Starts the server.
|
||||||
void listen({InternetAddress address, int port: 3000}) {
|
void listen({InternetAddress address, int port: 3000}) {
|
||||||
runZoned(() async {
|
runZoned(() async {
|
||||||
|
@ -242,7 +310,9 @@ class Angel extends AngelBase {
|
||||||
if (stackTrace != null) stderr.write(stackTrace.toString());
|
if (stackTrace != null) stderr.write(stackTrace.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
Angel() : super() {}
|
Angel() : super() {
|
||||||
|
bootstrapContainer();
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates an HTTPS server.
|
/// Creates an HTTPS server.
|
||||||
/// Provide paths to a certificate chain and server key (both .pem).
|
/// Provide paths to a certificate chain and server key (both .pem).
|
||||||
|
@ -251,9 +321,10 @@ class Angel extends AngelBase {
|
||||||
Angel.secure(String certificateChainPath, String serverKeyPath,
|
Angel.secure(String certificateChainPath, String serverKeyPath,
|
||||||
{String password})
|
{String password})
|
||||||
: super() {
|
: super() {
|
||||||
|
bootstrapContainer();
|
||||||
_serverGenerator = (InternetAddress address, int port) async {
|
_serverGenerator = (InternetAddress address, int port) async {
|
||||||
var certificateChain =
|
var certificateChain =
|
||||||
Platform.script.resolve('server_chain.pem').toFilePath();
|
Platform.script.resolve('server_chain.pem').toFilePath();
|
||||||
var serverKey = Platform.script.resolve('server_key.pem').toFilePath();
|
var serverKey = Platform.script.resolve('server_key.pem').toFilePath();
|
||||||
var serverContext = new SecurityContext();
|
var serverContext = new SecurityContext();
|
||||||
serverContext.useCertificateChain(certificateChain);
|
serverContext.useCertificateChain(certificateChain);
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
name: angel_framework
|
name: angel_framework
|
||||||
version: 1.0.0-dev.15
|
version: 1.0.0-dev.16
|
||||||
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
|
||||||
dependencies:
|
dependencies:
|
||||||
body_parser: ">=1.0.0-dev <2.0.0"
|
body_parser: ">=1.0.0-dev <2.0.0"
|
||||||
|
container: ">=0.1.2 <1.0.0"
|
||||||
json_god: ">=2.0.0-beta <3.0.0"
|
json_god: ">=2.0.0-beta <3.0.0"
|
||||||
merge_map: ">=1.0.0 <2.0.0"
|
merge_map: ">=1.0.0 <2.0.0"
|
||||||
mime: ">=0.9.3 <1.0.0"
|
mime: ">=0.9.3 <1.0.0"
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
http: ">= 0.11.3 < 0.12.0"
|
http: ">= 0.11.3 <0.12.0"
|
||||||
test: ">= 0.12.13 < 0.13.0"
|
test: ">= 0.12.13 <0.13.0"
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
import 'controller.dart' as controller;
|
import 'controller.dart' as controller;
|
||||||
|
import 'di.dart' as di;
|
||||||
import 'hooked.dart' as hooked;
|
import 'hooked.dart' as hooked;
|
||||||
import 'routing.dart' as routing;
|
import 'routing.dart' as routing;
|
||||||
import 'services.dart' as services;
|
import 'services.dart' as services;
|
||||||
|
@ -7,6 +8,7 @@ import 'services.dart' as services;
|
||||||
main() {
|
main() {
|
||||||
group('controller', controller.main);
|
group('controller', controller.main);
|
||||||
group('hooked', hooked.main);
|
group('hooked', hooked.main);
|
||||||
|
group('di', di.main);
|
||||||
group('routing', routing.main);
|
group('routing', routing.main);
|
||||||
group('services', services.main);
|
group('services', services.main);
|
||||||
}
|
}
|
87
test/di.dart
Normal file
87
test/di.dart
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'common.dart';
|
||||||
|
|
||||||
|
final String TEXT = "make your bed";
|
||||||
|
final String OVER = "never";
|
||||||
|
|
||||||
|
main() {
|
||||||
|
Angel app;
|
||||||
|
http.Client client;
|
||||||
|
HttpServer server;
|
||||||
|
String url;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
app = new Angel();
|
||||||
|
client = new http.Client();
|
||||||
|
|
||||||
|
// Inject some todos
|
||||||
|
app.container.singleton(new Todo(text: TEXT, over: OVER));
|
||||||
|
|
||||||
|
app.get("/errands", (Todo singleton) => singleton);
|
||||||
|
app.get("/errands3", (Errand singleton, Todo foo, RequestContext req) => singleton.text);
|
||||||
|
await app.configure(new SingletonController());
|
||||||
|
await app.configure(new ErrandController());
|
||||||
|
|
||||||
|
server = await app.startServer();
|
||||||
|
url = "http://${server.address.host}:${server.port}";
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
app = null;
|
||||||
|
url = null;
|
||||||
|
client.close();
|
||||||
|
client = null;
|
||||||
|
await server.close(force: true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("singleton in route", () async {
|
||||||
|
validateTodoSingleton(await client.get("$url/errands"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("singleton in controller", () async {
|
||||||
|
validateTodoSingleton(await client.get("$url/errands2"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("make in route", () async {
|
||||||
|
var response = await client.get("$url/errands3");
|
||||||
|
String text = await JSON.decode(response.body);
|
||||||
|
expect(text, equals(TEXT));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("make in controller", () async {
|
||||||
|
var response = await client.get("$url/errands4");
|
||||||
|
String text = await JSON.decode(response.body);
|
||||||
|
expect(text, equals(TEXT));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void validateTodoSingleton(response) {
|
||||||
|
Map todo = JSON.decode(response.body);
|
||||||
|
expect(todo.keys.length, equals(3));
|
||||||
|
expect(todo["id"], equals(null));
|
||||||
|
expect(todo["text"], equals(TEXT));
|
||||||
|
expect(todo["over"], equals(OVER));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Expose("/errands2")
|
||||||
|
class SingletonController extends Controller {
|
||||||
|
@Expose("/")
|
||||||
|
todo(Todo singleton) => singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Expose("/errands4")
|
||||||
|
class ErrandController extends Controller {
|
||||||
|
@Expose("/")
|
||||||
|
errand(Errand errand) => errand.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Errand {
|
||||||
|
Todo todo;
|
||||||
|
String get text => todo.text;
|
||||||
|
|
||||||
|
Errand(this.todo);
|
||||||
|
}
|
Loading…
Reference in a new issue