2.0.0
This commit is contained in:
parent
45a702b79f
commit
d61aa82607
16 changed files with 161 additions and 282 deletions
1
.dart_tool/pub/bin/sdk-version
Normal file
1
.dart_tool/pub/bin/sdk-version
Normal file
|
@ -0,0 +1 @@
|
|||
2.0.0
|
BIN
.dart_tool/pub/bin/test/test.dart.snapshot.dart2
Normal file
BIN
.dart_tool/pub/bin/test/test.dart.snapshot.dart2
Normal file
Binary file not shown.
|
@ -1,2 +1,5 @@
|
|||
# 2.0.0
|
||||
* Removed `supportShelf`.
|
||||
|
||||
# 1.2.0
|
||||
* Upgraded for `>=1.1.0` compatibility.
|
90
README.md
90
README.md
|
@ -3,7 +3,6 @@
|
|||
[![build status](https://travis-ci.org/angel-dart/shelf.svg)](https://travis-ci.org/angel-dart/shelf)
|
||||
|
||||
Shelf interop with Angel. This package lets you run `package:shelf` handlers via a custom adapter.
|
||||
It also includes a plug-in that configures Angel to *natively* run `shelf` response handlers.
|
||||
|
||||
Use the code in this repo to embed existing shelf apps into
|
||||
your Angel applications. This way, you can migrate legacy applications without
|
||||
|
@ -12,15 +11,14 @@ having to rewrite your business logic.
|
|||
This will make it easy to layer your API over a production application,
|
||||
rather than having to port code.
|
||||
|
||||
* [Usage](#usage)
|
||||
* [embedShelf](#embedshelf)
|
||||
* [Communicating with Angel](#communicating-with-angel-with-embedshelf)
|
||||
* [supportShelf](#supportshelf)
|
||||
* [Communicating with Angel](#communicating-with-angel-with-supportshelf)
|
||||
- [Usage](#usage)
|
||||
- [embedShelf](#embedshelf)
|
||||
- [Communicating with Angel](#communicating-with-angel-with-embedshelf)
|
||||
|
||||
# Usage
|
||||
|
||||
## embedShelf
|
||||
|
||||
This is a compliant `shelf` adapter that acts as an Angel request handler. You can use it as a middleware,
|
||||
or attach it to individual routes.
|
||||
|
||||
|
@ -32,84 +30,42 @@ import 'package:shelf/shelf.dart' as shelf;
|
|||
import 'api/api.dart';
|
||||
|
||||
main() async {
|
||||
final app = new Angel();
|
||||
|
||||
var app = new Angel();
|
||||
var http = new AngelHttp(app);
|
||||
|
||||
// Angel routes on top
|
||||
await app.configure(new ApiController());
|
||||
|
||||
await app.mountController<ApiController>();
|
||||
|
||||
// Re-route all other traffic to an
|
||||
// existing application.
|
||||
app.after.add(embedShelf(
|
||||
app.fallback(embedShelf(
|
||||
new shelf.Pipeline()
|
||||
.addMiddleware(shelf.logRequests())
|
||||
.addHandler(_echoRequest)
|
||||
));
|
||||
|
||||
// Only on a specific route
|
||||
app.get('/shelf', handler);
|
||||
|
||||
await app.startServer(InternetAddress.LOOPBACK_IP_V4, 3000);
|
||||
|
||||
// Or, only on a specific route:
|
||||
app.get('/shelf', wrappedShelfHandler);
|
||||
|
||||
await http.startServer(InternetAddress.loopbackIPV4, 3000);
|
||||
print(http.uri);
|
||||
}
|
||||
```
|
||||
|
||||
### Communicating with Angel with embedShelf
|
||||
|
||||
You can communicate with Angel:
|
||||
|
||||
```dart
|
||||
handleRequest(shelf.Request request) {
|
||||
// Access original Angel request...
|
||||
var req = request.context['angel_shelf.request'] as RequestContext;
|
||||
|
||||
|
||||
// ... And then interact with it.
|
||||
req.inject('from_shelf', new Foo());
|
||||
|
||||
// `req.properties` are also available.
|
||||
var props = request.context['angel_shelf.properties'] as Map;
|
||||
|
||||
req.container.registerNamedSingleton<Foo>('from_shelf', new Foo());
|
||||
|
||||
// `req.container` is also available.
|
||||
var container = request.context['angel_shelf.container'] as Container;
|
||||
container.make<Truck>().drive();
|
||||
}
|
||||
```
|
||||
|
||||
## supportShelf
|
||||
This plug-in takes advantage of Angel's middleware system and dependency injection to patch a server
|
||||
to run `shelf` request handlers as though they were Angel request handlers. Hooray for integration!
|
||||
|
||||
You'll want to run this before adding any other response finalizers that depend on
|
||||
the response content being effectively final, i.e. GZIP compression.
|
||||
|
||||
**NOTE**: Do not inject a `shelf.Request` into your request under the name `req`. If you do,
|
||||
Angel will automatically inject a `RequestContext` instead.
|
||||
|
||||
```dart
|
||||
configureServer(Angel app) async {
|
||||
// Return a shelf Response
|
||||
app.get('/shelf', (shelf.Request request) => new shelf.Response.ok('Yay!'));
|
||||
|
||||
// Return an arbitrary value.
|
||||
//
|
||||
// This will be serialized by Angel as per usual.
|
||||
app.get('/json', (shelf.Request request) => {'foo': 'bar'});
|
||||
|
||||
// You can still throw Angel exceptions.
|
||||
//
|
||||
// Don't be fooled: just because this is a shelf handler, doesn't mean
|
||||
// it's not an Angel response handler too. ;)
|
||||
app.get('/error', (shelf.Request request) {
|
||||
throw new AngelHttpException.forbidden();
|
||||
});
|
||||
|
||||
// Make it all happen!
|
||||
await app.configure(supportShelf());
|
||||
}
|
||||
```
|
||||
|
||||
### Communicating with Angel with supportShelf
|
||||
The following keys will be present in the shelf request's context:
|
||||
* `angel_shelf.request` - Original RequestContext
|
||||
* `angel_shelf.response` - Original ResponseContext
|
||||
* `angel_shelf.properties` - Original RequestContext's properties
|
||||
|
||||
If the original `RequestContext` contains a Map named `shelf_context` in its `properties`,
|
||||
then it will be merged into the shelf request's context.
|
||||
|
||||
If the handler returns a `shelf.Response`, then it will be present in `ResponseContext.properties`
|
||||
as `shelf_response`.
|
|
@ -1,2 +1,3 @@
|
|||
analyzer:
|
||||
strong-mode: true
|
||||
strong-mode:
|
||||
implicit-casts: false
|
|
@ -1,26 +0,0 @@
|
|||
import 'dart:io';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_shelf/angel_shelf.dart';
|
||||
import 'package:shelf_proxy/shelf_proxy.dart';
|
||||
|
||||
main() async {
|
||||
var app = new Angel();
|
||||
|
||||
// `shelf` request handler
|
||||
var shelfHandler = proxyHandler('https://www.dartlang.org');
|
||||
|
||||
// Use `embedShelf` to adapt a `shelf` handler for use within Angel.
|
||||
var angelHandler = embedShelf(shelfHandler);
|
||||
|
||||
// A normal Angel route.
|
||||
app.get('/angel', (req, ResponseContext res) {
|
||||
res.write('Hooray for `package:angel_shelf`!');
|
||||
res.end(); // End execution of handlers, so we don't proxy to dartlang.org when we don't need to.
|
||||
});
|
||||
|
||||
// Proxy any other request through to dartlang.org
|
||||
app.use(angelHandler);
|
||||
|
||||
var server = await app.startServer(InternetAddress.LOOPBACK_IP_V4, 8080);
|
||||
print('Proxying at http://${server.address.host}:${server.port}');
|
||||
}
|
29
example/main.dart
Normal file
29
example/main.dart
Normal file
|
@ -0,0 +1,29 @@
|
|||
import 'dart:io';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_framework/http.dart';
|
||||
import 'package:angel_shelf/angel_shelf.dart';
|
||||
import 'package:shelf_static/shelf_static.dart';
|
||||
|
||||
main() async {
|
||||
var app = new Angel();
|
||||
var http = new AngelHttp(app);
|
||||
|
||||
// `shelf` request handler
|
||||
var shelfHandler = createStaticHandler('.',
|
||||
defaultDocument: 'index.html', listDirectories: true);
|
||||
|
||||
// Use `embedShelf` to adapt a `shelf` handler for use within Angel.
|
||||
var wrappedHandler = embedShelf(shelfHandler);
|
||||
|
||||
// A normal Angel route.
|
||||
app.get('/angel', (req, ResponseContext res) {
|
||||
res.write('Hooray for `package:angel_shelf`!');
|
||||
return false; // End execution of handlers, so we don't proxy to dartlang.org when we don't need to.
|
||||
});
|
||||
|
||||
// Proxy any other request through to the static file handler
|
||||
app.fallback(wrappedHandler);
|
||||
|
||||
await http.startServer(InternetAddress.loopbackIPv4, 8080);
|
||||
print('Proxying at ${http.uri}');
|
||||
}
|
|
@ -1,3 +1,2 @@
|
|||
export 'src/convert.dart';
|
||||
export 'src/embed_shelf.dart';
|
||||
export 'src/support_shelf.dart';
|
|
@ -1,50 +1,91 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_framework/http.dart';
|
||||
import 'package:angel_framework/http2.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:shelf/shelf.dart' as shelf;
|
||||
import 'package:stream_channel/stream_channel.dart';
|
||||
|
||||
/// Creates a [shelf.Request] analogous to the input [request].
|
||||
/// Creates a [shelf.Request] analogous to the input [req].
|
||||
///
|
||||
/// The new request's `context` will contain [request.properties] as `angel_shelf.properties`, as well as
|
||||
/// The new request's `context` will contain [req.container] as `angel_shelf.container`, as well as
|
||||
/// the provided [context], if any.
|
||||
///
|
||||
/// The context will also have the original request available as `angel_shelf.request`.
|
||||
///
|
||||
/// If you want to read the request body, you *must* `storeOriginalBuffer` to `true`
|
||||
/// If you want to read the request body, you *must* set `keepRawRequestBuffers` to `true`
|
||||
/// on your application instance.
|
||||
Future<shelf.Request> convertRequest(RequestContext request,
|
||||
Future<shelf.Request> convertRequest(RequestContext req, ResponseContext res,
|
||||
{String handlerPath, Map<String, Object> context}) async {
|
||||
var app = req.app;
|
||||
var headers = <String, String>{};
|
||||
request.headers.forEach((k, v) {
|
||||
req.headers.forEach((k, v) {
|
||||
headers[k] = v.join(',');
|
||||
});
|
||||
|
||||
headers.remove(HttpHeaders.TRANSFER_ENCODING);
|
||||
headers.remove(HttpHeaders.transferEncodingHeader);
|
||||
|
||||
void onHijack(
|
||||
void hijack(Stream<List<int>> stream, StreamSink<List<int>> sink)) {
|
||||
request.io.response.detachSocket(writeHeaders: false).then((socket) {
|
||||
return request.lazyOriginalBuffer().then((buf) {
|
||||
var ctrl = new StreamController<List<int>>()..add(buf ?? []);
|
||||
socket.listen(ctrl.add, onError: ctrl.addError, onDone: ctrl.close);
|
||||
hijack(socket, socket);
|
||||
void Function(void Function(StreamChannel<List<int>>)) onHijack;
|
||||
String protocolVersion;
|
||||
Uri requestedUri;
|
||||
|
||||
if (req is HttpRequestContext && res is HttpResponseContext) {
|
||||
protocolVersion = req.rawRequest.protocolVersion;
|
||||
requestedUri = req.rawRequest.requestedUri;
|
||||
|
||||
onHijack = (void hijack(StreamChannel<List<int>> channel)) {
|
||||
new Future(() async {
|
||||
var rs = res.detach();
|
||||
var socket = await rs.detachSocket(writeHeaders: false);
|
||||
var ctrl = new StreamChannelController<List<int>>();
|
||||
var body = await req.parseRawRequestBuffer() ?? [];
|
||||
ctrl.local.sink.add(body ?? []);
|
||||
socket.listen(ctrl.local.sink.add,
|
||||
onError: ctrl.local.sink.addError, onDone: ctrl.local.sink.close);
|
||||
ctrl.local.stream.pipe(socket);
|
||||
hijack(ctrl.foreign);
|
||||
}).catchError((e, st) {
|
||||
app.logger?.severe('An error occurred while hijacking a shelf request',
|
||||
e, st as StackTrace);
|
||||
});
|
||||
}).catchError((e, st) {
|
||||
stderr.writeln('An error occurred while hijacking a shelf request: $e');
|
||||
stderr.writeln(st);
|
||||
});
|
||||
};
|
||||
} else if (req is Http2RequestContext && res is Http2ResponseContext) {
|
||||
protocolVersion = '2.0';
|
||||
requestedUri = req.uri;
|
||||
|
||||
onHijack = (void hijack(StreamChannel<List<int>> channel)) {
|
||||
new Future(() async {
|
||||
var rs = await res.detach();
|
||||
var ctrl = new StreamChannelController<List<int>>();
|
||||
var body = await req.parseRawRequestBuffer() ?? [];
|
||||
ctrl.local.sink.add(body ?? []);
|
||||
ctrl.local.stream.listen(rs.sendData, onDone: rs.terminate);
|
||||
hijack(ctrl.foreign);
|
||||
}).catchError((e, st) {
|
||||
stderr.writeln('An error occurred while hijacking a shelf request: $e');
|
||||
stderr.writeln(st);
|
||||
});
|
||||
};
|
||||
} else {
|
||||
throw new UnsupportedError(
|
||||
'`embedShelf` is only supported for HTTP and HTTP2 requests in Angel.');
|
||||
}
|
||||
|
||||
return new shelf.Request(request.method, request.io.requestedUri,
|
||||
protocolVersion: request.io.protocolVersion,
|
||||
var url = req.uri;
|
||||
|
||||
if (p.isAbsolute(url.path)) {
|
||||
url = url.replace(path: url.path.substring(1));
|
||||
}
|
||||
|
||||
return new shelf.Request(req.method, requestedUri,
|
||||
protocolVersion: protocolVersion,
|
||||
headers: headers,
|
||||
handlerPath: handlerPath,
|
||||
url: new Uri(
|
||||
path: request.io.requestedUri.path.substring(1),
|
||||
query: request.io.requestedUri.query),
|
||||
body: (await request.lazyOriginalBuffer()) ?? [],
|
||||
context: {'angel_shelf.request': request}
|
||||
..addAll({'angel_shelf.properties': request.properties})
|
||||
url: url,
|
||||
body: (await req.parseRawRequestBuffer()) ?? [],
|
||||
context: {'angel_shelf.request': req}
|
||||
..addAll({'angel_shelf.container': req.container})
|
||||
..addAll(context ?? {}),
|
||||
onHijack: onHijack);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ RequestHandler embedShelf(shelf.Handler handler,
|
|||
Map<String, Object> context,
|
||||
bool throwOnNullResponse: false}) {
|
||||
return (RequestContext req, ResponseContext res) async {
|
||||
var shelfRequest =
|
||||
await convertRequest(req, handlerPath: handlerPath, context: context);
|
||||
var shelfRequest = await convertRequest(req, res,
|
||||
handlerPath: handlerPath, context: context);
|
||||
try {
|
||||
var result = await handler(shelfRequest);
|
||||
if (result is! shelf.Response && result != null) return result;
|
||||
|
@ -25,10 +25,7 @@ RequestHandler embedShelf(shelf.Handler handler,
|
|||
await mergeShelfResponse(result, res);
|
||||
return false;
|
||||
} on shelf.HijackException {
|
||||
// On hijack...
|
||||
res
|
||||
..willCloseItself = true
|
||||
..end();
|
||||
// On hijack, do nothing, because the hijack handlers already call res.detach();
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:shelf/shelf.dart' as shelf;
|
||||
import 'convert.dart';
|
||||
|
||||
/// Configures a server instance to natively run shelf request handlers.
|
||||
///
|
||||
/// To pass a context to the generated shelf request, add a Map
|
||||
/// to `req.properties`, named `shelf_context`.
|
||||
///
|
||||
/// Two additional keys will be present in the `shelf` request context:
|
||||
/// * `angel_shelf.request` - The Angel [RequestContext].
|
||||
/// * `angel_shelf.response` - The Angel [ResponseContext].
|
||||
AngelConfigurer supportShelf() {
|
||||
return (Angel app) async {
|
||||
app.use((RequestContext req, ResponseContext res) async {
|
||||
// Inject shelf.Request ;)
|
||||
req.inject(
|
||||
shelf.Request,
|
||||
await convertRequest(req,
|
||||
context: {'angel_shelf.response': res}
|
||||
..addAll(req.properties['shelf_context'] ?? {})));
|
||||
|
||||
// Override serializer to support returning shelf responses
|
||||
var oldSerializer = res.serializer;
|
||||
res.serializer = (val) {
|
||||
if (val is! shelf.Response) return oldSerializer(val);
|
||||
res.properties['shelf_response'] = val;
|
||||
mergeShelfResponse(val, res);
|
||||
return ''; // Write nothing
|
||||
};
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
/*
|
||||
// Merge shelf response if necessary
|
||||
app.responseFinalizers.add((RequestContext req, ResponseContext res) async {
|
||||
if (res.properties.containsKey('shelf_response')) {
|
||||
var shelfResponse = res.properties['shelf_response'] as shelf.Response;
|
||||
await mergeShelfResponse(shelfResponse, res);
|
||||
}
|
||||
});
|
||||
*/
|
||||
};
|
||||
}
|
19
pubspec.yaml
19
pubspec.yaml
|
@ -1,15 +1,16 @@
|
|||
author: Tobe O <thosakwe@gmail.com>
|
||||
description: Shelf interop with Angel.
|
||||
description: Shelf interop with Angel. Use this to wrap existing server code.
|
||||
homepage: https://github.com/angel-dart/shelf
|
||||
name: angel_shelf
|
||||
version: 1.2.0
|
||||
version: 2.0.0
|
||||
dependencies:
|
||||
angel_framework: ">=1.1.0-alpha <2.0.0"
|
||||
shelf: ^0.6.0
|
||||
angel_framework: ^2.0.0-alpha
|
||||
path: ^1.0.0
|
||||
shelf: ^0.7.0
|
||||
stream_channel: ^1.0.0
|
||||
dev_dependencies:
|
||||
angel_test: ^1.1.0
|
||||
console: ^2.2.4
|
||||
shelf_proxy: ^0.1.0
|
||||
test: ^0.12.0
|
||||
angel_test: ^2.0.0-alpha
|
||||
shelf_static: ^0.2.8
|
||||
test: ^1.0.0
|
||||
environment:
|
||||
sdk: ">=1.19.0"
|
||||
sdk: ">=2.0.0-dev <3.0.0"
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'embed_shelf_test.dart' as embed_shelf;
|
||||
import 'support_shelf_test.dart' as support_shelf;
|
||||
|
||||
main() {
|
||||
group('embed_shelf', embed_shelf.main);
|
||||
group('support_shelf', support_shelf.main);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:angel_client/io.dart' as c;
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_framework/http.dart';
|
||||
import 'package:angel_shelf/angel_shelf.dart';
|
||||
import 'package:angel_test/angel_test.dart';
|
||||
import 'package:charcode/charcode.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:shelf/shelf.dart' as shelf;
|
||||
import 'package:stream_channel/stream_channel.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'pretty_logging.dart';
|
||||
|
||||
main() {
|
||||
c.Angel client;
|
||||
|
@ -19,16 +19,17 @@ main() {
|
|||
setUp(() async {
|
||||
var handler = new shelf.Pipeline().addHandler((shelf.Request request) {
|
||||
if (request.url.path == 'two')
|
||||
return 2;
|
||||
return new shelf.Response(200, body: json.encode(2));
|
||||
else if (request.url.path == 'error')
|
||||
throw new AngelHttpException.notFound();
|
||||
else if (request.url.path == 'status')
|
||||
return new shelf.Response.notModified(headers: {'foo': 'bar'});
|
||||
else if (request.url.path == 'hijack') {
|
||||
request.hijack((Stream<List<int>> stream, StreamSink<List<int>> sink) {
|
||||
sink.add(UTF8.encode('HTTP/1.1 200 OK\r\n'));
|
||||
request.hijack((StreamChannel<List<int>> channel) {
|
||||
var sink = channel.sink;
|
||||
sink.add(utf8.encode('HTTP/1.1 200 OK\r\n'));
|
||||
sink.add([$lf]);
|
||||
sink.add(UTF8.encode(JSON.encode({'error': 'crime'})));
|
||||
sink.add(utf8.encode(json.encode({'error': 'crime'})));
|
||||
sink.close();
|
||||
});
|
||||
} else if (request.url.path == 'throw')
|
||||
|
@ -37,14 +38,19 @@ main() {
|
|||
return new shelf.Response.ok('Request for "${request.url}"');
|
||||
});
|
||||
|
||||
var app = new Angel()..lazyParseBodies = true;
|
||||
app.get('/angel', 'Angel');
|
||||
app.use(embedShelf(handler, throwOnNullResponse: true));
|
||||
app.logger = new Logger.detached('angel')..onRecord.listen(prettyLog);
|
||||
var app = new Angel();
|
||||
var http = new AngelHttp(app);
|
||||
app.get('/angel', (req, res) => 'Angel');
|
||||
app.fallback(embedShelf(handler, throwOnNullResponse: true));
|
||||
app.logger = new Logger.detached('angel_shelf')
|
||||
..onRecord.listen((rec) {
|
||||
stdout.writeln(rec);
|
||||
if (rec.error != null) stdout.writeln(rec.error);
|
||||
if (rec.stackTrace != null) stdout.writeln(rec.stackTrace);
|
||||
});
|
||||
|
||||
server = await app.startServer(InternetAddress.LOOPBACK_IP_V4, 0);
|
||||
client =
|
||||
new c.Rest(url = 'http://${server.address.address}:${server.port}');
|
||||
server = await http.startServer(InternetAddress.loopbackIPv4, 0);
|
||||
client = new c.Rest(url = http.uri.toString());
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
|
@ -54,7 +60,7 @@ main() {
|
|||
|
||||
test('expose angel side', () async {
|
||||
var response = await client.get('/angel');
|
||||
expect(JSON.decode(response.body), equals('Angel'));
|
||||
expect(json.decode(response.body), equals('Angel'));
|
||||
});
|
||||
|
||||
test('expose shelf side', () async {
|
||||
|
@ -73,15 +79,15 @@ main() {
|
|||
var client = new HttpClient();
|
||||
var rq = await client.openUrl('GET', Uri.parse('$url/hijack'));
|
||||
var rs = await rq.close();
|
||||
var body = await rs.transform(UTF8.decoder).join();
|
||||
var body = await rs.transform(utf8.decoder).join();
|
||||
print('Response: $body');
|
||||
expect(JSON.decode(body), {'error': 'crime'});
|
||||
expect(json.decode(body), {'error': 'crime'});
|
||||
} on HttpException catch (e, st) {
|
||||
print('HTTP Exception: ' + e.message);
|
||||
print(st);
|
||||
rethrow;
|
||||
}
|
||||
});
|
||||
}, skip: '');
|
||||
|
||||
test('shelf can set status code', () async {
|
||||
var response = await client.get('/status');
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
import 'package:console/console.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
|
||||
/// Prints the contents of a [LogRecord] with pretty colors.
|
||||
void prettyLog(LogRecord record) {
|
||||
var pen = new TextPen();
|
||||
chooseLogColor(pen.reset(), record.level);
|
||||
pen(record.toString());
|
||||
|
||||
if (record.error != null)
|
||||
pen(record.error.toString());
|
||||
if (record.stackTrace != null)
|
||||
pen(new Chain.forTrace(record.stackTrace).terse.toString());
|
||||
|
||||
pen();
|
||||
}
|
||||
|
||||
/// Chooses a color based on the logger [level].
|
||||
void chooseLogColor(TextPen pen, Level level) {
|
||||
if (level == Level.SHOUT)
|
||||
pen.darkRed();
|
||||
else if (level == Level.SEVERE)
|
||||
pen.red();
|
||||
else if (level == Level.WARNING)
|
||||
pen.yellow();
|
||||
else if (level == Level.INFO)
|
||||
pen.magenta();
|
||||
else if (level == Level.FINER)
|
||||
pen.blue();
|
||||
else if (level == Level.FINEST)
|
||||
pen.darkBlue();
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_shelf/angel_shelf.dart';
|
||||
import 'package:angel_test/angel_test.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:shelf/shelf.dart' as shelf;
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'pretty_logging.dart';
|
||||
|
||||
main() {
|
||||
TestClient client;
|
||||
|
||||
setUp(() async {
|
||||
var app = new Angel()..lazyParseBodies = true;
|
||||
await app.configure(supportShelf());
|
||||
|
||||
app.get('/inject', (shelf.Request request) {
|
||||
print('URL of injected request: ${request.url.path}');
|
||||
return {'inject': request.url.path == 'inject'};
|
||||
});
|
||||
|
||||
app.get('/hello', (shelf.Request request) {
|
||||
return new shelf.Response.ok('world');
|
||||
});
|
||||
|
||||
app.logger = new Logger.detached('angel')..onRecord.listen(prettyLog);
|
||||
client = await connectTo(app);
|
||||
});
|
||||
|
||||
tearDown(() => client.close());
|
||||
|
||||
test('injected into request', () async {
|
||||
var response = await client.get('/inject', headers: {'accept': 'application/json'});
|
||||
print('Response: ${response.body}');
|
||||
expect(response, isJson({'inject': true}));
|
||||
});
|
||||
|
||||
test('can return shelf response', () {
|
||||
return Chain.capture(() async {
|
||||
var response = await client.get('/hello', headers: {'accept': 'application/json'});
|
||||
print('Response: ${response.body}');
|
||||
expect(response, hasBody('world'));
|
||||
}, onError: (e, chain) {
|
||||
print(e);
|
||||
print(chain.terse);
|
||||
expect(0, 1);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue