Migrated angel_configuration

This commit is contained in:
thomashii 2021-04-10 19:23:57 +08:00
parent 526c761846
commit d211fc2c3d
25 changed files with 145 additions and 102 deletions

View file

@ -1,17 +1,17 @@
# 4.0.0 (NNBD)
* Changed Dart SDK requirements for all packages to ">=2.12.0 <3.0.0" to support NNBD.
* Updated pretty_logging to 3.0.0
* Updated angel_http_exception to 3.0.0
* Updated pretty_logging to 3.0.0 (0/0 tests)
* Updated angel_http_exception to 3.0.0 (0/0 tests)
* Moved angel_cli to https://github.com/dukefirehawk/cli
* Added code_buffer and updated to 2.0.0
* Added combinator and updated to 2.0.0
* Updated angel_route to 5.0.0
* Updated angel_model to 3.0.0
* Updated angel_container to 3.0.0
* Added merge_map and updated to 2.0.0
* Added mock_request and updated to 2.0.0
* Updated angel_framework to 4.0.0 (Revisit TODO, Fix failed tests)
* Updated angel_auth to 4.0.0 (In progress)
* Added code_buffer and updated to 2.0.0 (16/16 tests)
* Added combinator and updated to 2.0.0 (16/16 tests)
* Updated angel_route to 5.0.0 (35/35 tests passed)
* Updated angel_model to 3.0.0 (0/0 tests)
* Updated angel_container to 3.0.0 (55/55 tests passed)
* Added merge_map and updated to 2.0.0 (6/6 tests passed)
* Added mock_request and updated to 2.0.0 (0/0 tests)
* Updated angel_framework to 4.0.0 (146/149 tests passed)
* Updated angel_auth to 4.0.0 (22/32 test passed)
* Updated angel_configuration to 4.0.0 (In progress)
# 3.0.0 (Non NNBD)

View file

@ -41,7 +41,7 @@ class AuthToken {
DateTime? issuedAt,
Map payload = const {}}) {
this.issuedAt = issuedAt ?? DateTime.now();
this.payload.addAll(payload?.keys?.fold(
this.payload.addAll(payload.keys.fold(
{},
((out, k) => out..[k.toString()] = payload[k])
as Map<String, dynamic>? Function(

View file

@ -217,16 +217,16 @@ class AngelAuth<User> {
/// Retrieves a JWT from a request, if any was sent at all.
String? getJwt(RequestContext req) {
if (req.headers!.value("Authorization") != null) {
final authHeader = req.headers!.value("Authorization")!;
if (req.headers!.value('Authorization') != null) {
final authHeader = req.headers!.value('Authorization')!;
// Allow Basic auth to fall through
if (_rgxBearer.hasMatch(authHeader)) {
return authHeader.replaceAll(_rgxBearer, "").trim();
return authHeader.replaceAll(_rgxBearer, '').trim();
}
} else if (allowCookie &&
req.cookies!.any((cookie) => cookie.name == "token")) {
return req.cookies!.firstWhere((cookie) => cookie.name == "token").value;
req.cookies.any((cookie) => cookie.name == 'token')) {
return req.cookies.firstWhere((cookie) => cookie.name == 'token').value;
} else if (allowTokenInQuery &&
req.uri!.queryParameters['token'] is String) {
return req.uri!.queryParameters['token']?.toString();
@ -266,7 +266,7 @@ class AngelAuth<User> {
var jwt = getJwt(req);
if (jwt == null) {
var body = await req.parseBody().then((_) => req.bodyAsMap!);
var body = await req.parseBody().then((_) => req.bodyAsMap);
jwt = body['token']?.toString();
}
if (jwt == null) {

View file

@ -34,7 +34,7 @@ class User extends Model {
}
void main() {
Angel? app;
late Angel app;
late AngelHttp angelHttp;
AngelAuth<User?> auth;
http.Client? client;
@ -45,15 +45,15 @@ void main() {
hierarchicalLoggingEnabled = true;
app = Angel();
angelHttp = AngelHttp(app);
app!.use('/users', MapService());
app.use('/users', MapService());
var oldErrorHandler = app!.errorHandler;
app!.errorHandler = (e, req, res) {
app!.logger!.severe(e.message, e, e.stackTrace ?? StackTrace.current);
var oldErrorHandler = app.errorHandler;
app.errorHandler = (e, req, res) {
app.logger!.severe(e.message, e, e.stackTrace ?? StackTrace.current);
return oldErrorHandler(e, req, res);
};
app!.logger = Logger('angel_auth')
app.logger = Logger('angel_auth')
..level = Level.FINEST
..onRecord.listen((rec) {
print(rec);
@ -67,19 +67,19 @@ void main() {
}
});
await app!
await app
.findService('users')!
.create({'username': 'jdoe1', 'password': 'password'});
auth = AngelAuth<User?>();
auth.serializer = (u) => u!.id;
auth.deserializer =
(id) async => await app!.findService('users')!.read(id) as User;
(id) async => await app.findService('users')!.read(id) as User;
await app!.configure(auth.configureServer);
await app.configure(auth.configureServer);
auth.strategies['local'] = LocalAuthStrategy((username, password) async {
var users = await app!
var users = await app
.findService('users')!
.index()
.then((it) => it.map<User>((m) => User.parse(m as Map)).toList());
@ -90,7 +90,7 @@ void main() {
return Future.value(result);
});
app!.post(
app.post(
'/login',
auth.authenticate('local',
AngelAuthOptions(callback: (req, res, token) {
@ -99,7 +99,7 @@ void main() {
..close();
})));
app!.chain([
app.chain([
(req, res) {
if (!req.container!.has<User>()) {
req.container!.registerSingleton<User>(
@ -120,7 +120,7 @@ void main() {
tearDown(() async {
client!.close();
await angelHttp.close();
app = null;
//app = null;
client = null;
url = null;
});

View file

@ -21,7 +21,7 @@ Future<void> _loadYamlFile(Map map, File yamlFile, Map<String, String> env,
var out = {};
var configMap = Map.of(config as Map);
var configMap = Map.of(config);
// Check for _include
if (configMap.containsKey('_include')) {
@ -44,8 +44,8 @@ Future<void> _loadYamlFile(Map map, File yamlFile, Map<String, String> env,
}
}
for (String key in configMap.keys) {
out[key] = _applyEnv(configMap[key], env ?? {}, warn);
for (var key in configMap.keys as Iterable<String>) {
out[key] = _applyEnv(configMap[key], env, warn);
}
map.addAll(mergeMap(
@ -58,7 +58,7 @@ Future<void> _loadYamlFile(Map map, File yamlFile, Map<String, String> env,
}
}
Object _applyEnv(
Object? _applyEnv(
var v, Map<String, String> env, void Function(String msg) warn) {
if (v is String) {
if (v.startsWith(r'$') && v.length > 1) {
@ -74,10 +74,10 @@ Object _applyEnv(
return v;
}
} else if (v is Iterable) {
return v.map((x) => _applyEnv(x, env ?? {}, warn)).toList();
return v.map((x) => _applyEnv(x, env, warn)).toList();
} else if (v is Map) {
return v.keys
.fold<Map>({}, (out, k) => out..[k] = _applyEnv(v[k], env ?? {}, warn));
.fold<Map>({}, (out, k) => out..[k] = _applyEnv(v[k], env, warn));
} else {
return v;
}
@ -88,9 +88,9 @@ Object _applyEnv(
/// You can override [onWarning]; otherwise, configuration errors will throw.
Future<Map> loadStandaloneConfiguration(FileSystem fileSystem,
{String directoryPath = './config',
String overrideEnvironmentName,
String envPath,
void Function(String message) onWarning}) async {
String? overrideEnvironmentName,
String? envPath,
void Function(String message)? onWarning}) async {
var sourceDirectory = fileSystem.directory(directoryPath);
var env = dotenv.env;
var envFile = sourceDirectory.childFile(envPath ?? '.env');
@ -127,8 +127,8 @@ Future<Map> loadStandaloneConfiguration(FileSystem fileSystem,
/// You can also specify a custom [envPath] to load system configuration from.
AngelConfigurer configuration(FileSystem fileSystem,
{String directoryPath = './config',
String overrideEnvironmentName,
String envPath}) {
String? overrideEnvironmentName,
String? envPath}) {
return (Angel app) async {
var config = await loadStandaloneConfiguration(
fileSystem,

View file

@ -5,20 +5,24 @@ author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/angel_configuration
publish_to: none
environment:
sdk: ">=2.10.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'
dependencies:
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
ref: sdk-2.12.x_nnbd
path: packages/framework
dotenv: ^3.0.0-nullsafety.0
# file: ^5.0.0
merge_map: ^1.0.0
merge_map:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/merge_map
yaml: ^3.1.0
dev_dependencies:
io: ^1.0.0
# logging: ^0.11.0
pedantic: ^1.0.0
pedantic: ^1.11.0
# pretty_logging: ^1.0.0
test: ^1.15.7

View file

@ -1,3 +1,6 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.watcherExclude": {
"**/target": true
}
}

View file

@ -19,7 +19,7 @@ main() async {
(req, res) => Future.error('Throwing just because I feel like!'));
var http = AngelHttp(app);
HttpServer server = await http.startServer('127.0.0.1', 3000);
HttpServer? server = await http.startServer('127.0.0.1', 3000);
var url = 'http://${server.address.address}:${server.port}';
print('Listening at $url');
}

View file

@ -33,7 +33,7 @@ main() async {
});
app.errorHandler = (e, req, res) {
print(e.message ?? e.error ?? e);
print(e.message);
print(e.stackTrace);
return e.toJson();
};

View file

@ -44,7 +44,7 @@ serverMain(_) async {
});
app.errorHandler = (e, req, res) {
print(e.message ?? e.error ?? e);
print(e.message);
print(e.stackTrace);
};

View file

@ -22,6 +22,10 @@ abstract class Driver<
final bool useZone;
bool _closed = false;
late Server _server;
// TODO: Ugly fix
bool isServerInitialised = false;
StreamSubscription<Request>? _sub;
final log = Logger('Driver');
@ -34,16 +38,27 @@ abstract class Driver<
Uri get uri;
/// The native server running this instance.
Server get server => _server;
Server? get server {
// TODO: Ugly fix
if (isServerInitialised) {
return _server;
} else {
return null;
}
}
Future<Server> generateServer(address, int port) =>
serverGenerator(address, port);
/// Starts, and returns the server.
Future<Server> startServer([address, int port = 5000]) {
Future<Server> startServer([address, int port = 0]) {
var host = address ?? '127.0.0.1';
return generateServer(host, port).then((server) {
_server = server;
// TODO: Ugly fix
isServerInitialised = true;
return Future.wait(app.startupHooks.map(app.configure)).then((_) {
app.optimizeForProduction();
_sub = server.listen((request) {
@ -62,16 +77,23 @@ abstract class Driver<
}
/// Shuts down the underlying server.
Future<Server> close() {
Future<void> close() {
if (_closed) {
return Future.value(_server);
//return Future.value(_server);
return Future.value();
}
_closed = true;
_sub?.cancel();
return app.close().then((_) =>
Future.wait(app.shutdownHooks.map(app.configure))
.then((_) => Future.value()));
/*
return app.close().then((_) =>
Future.wait(app.shutdownHooks.map(app.configure))
.then((_) => Future.value(_server)));
*/
}
Future<RequestContextType> createRequestContext(
@ -81,7 +103,7 @@ abstract class Driver<
Request request, Response response,
[RequestContextType? correspondingRequest]);
void setHeader(Response response, String key, String? value);
void setHeader(Response response, String key, String value);
void setContentLength(Response response, int length);
@ -174,7 +196,7 @@ abstract class Driver<
if (app.logger != null) {
var error = e.error ?? e;
var trace = Trace.from(e.stackTrace ?? StackTrace.current).terse;
app.logger?.severe(e.message ?? e.toString(), error, trace);
app.logger?.severe(e.message, error, trace);
}
return handleAngelHttpException(
@ -206,7 +228,7 @@ abstract class Driver<
}
if (app.logger != null) {
app.logger?.severe(e.message ?? e.toString(), error, trace);
app.logger?.severe(e.message, error, trace);
}
return handleAngelHttpException(
@ -309,7 +331,7 @@ abstract class Driver<
//if (res.isOpen) res.close();
for (var key in res.headers.keys) {
setHeader(response, key, res.headers[key]);
setHeader(response, key, res.headers[key] ?? '');
}
setContentLength(response, res.buffer!.length);

View file

@ -8,18 +8,16 @@ const List<Type> _primitiveTypes = [String, int, num, double, Null];
///
/// Calling [ioc] also auto-serializes the result of a [handler].
RequestHandler ioc(Function handler, {Iterable<String> optional = const []}) {
InjectionRequest? injection;
RequestHandler? contained;
return (req, res) {
if (injection == null) {
if (req.app!.container != null) {
injection = preInject(handler, req.app!.container!.reflector);
if (injection != null) {
injection?.optional.addAll(optional);
contained = handleContained(handler, injection!);
}
}
RequestHandler? contained;
if (req.app?.container != null) {
InjectionRequest injection =
preInject(handler, req.app!.container!.reflector);
//if (injection != null) {
injection.optional.addAll(optional);
contained = handleContained(handler, injection);
//}
}
return req.app!.executeHandler(contained, req, res);

View file

@ -43,7 +43,7 @@ abstract class RequestContext<RawRequest> {
Map<String, dynamic> _bodyFields = {};
List _bodyList = [];
List<UploadedFile> _uploadedFiles = <UploadedFile>[];
MediaType _contentType = MediaType("text", "html");
MediaType _contentType = MediaType("text", "plain");
/// The underlying [RawRequest] provided by the driver.
RawRequest get rawRequest;

View file

@ -9,6 +9,7 @@ import 'dart:typed_data';
import 'package:angel_route/angel_route.dart';
import 'package:file/file.dart';
import 'package:http_parser/http_parser.dart';
import 'package:logging/logging.dart';
import 'package:mime/mime.dart';
import 'controller.dart';
@ -26,6 +27,8 @@ abstract class ResponseContext<RawResponse>
'server': 'angel',
});
final log = Logger('ResponseContext');
Completer? _done;
int _statusCode = 200;
@ -76,11 +79,11 @@ abstract class ResponseContext<RawResponse>
/// This response's status code.
int get statusCode => _statusCode;
set statusCode(int? value) {
set statusCode(int value) {
if (!isOpen) {
throw closed();
} else {
_statusCode = value ?? 200;
_statusCode = value; // ?? 200;
}
}
@ -202,7 +205,7 @@ abstract class ResponseContext<RawResponse>
/// based on the provided params.
///
/// See [Router]#navigate for more. :)
Future<void> redirect(url, {bool absolute = true, int? code = 302}) {
Future<void> redirect(url, {bool absolute = true, int? code}) {
if (!isOpen) throw closed();
headers
..['content-type'] = 'text/html'
@ -344,7 +347,11 @@ abstract class ResponseContext<RawResponse>
if (_done?.isCompleted == false) {
_done!.completeError(error, stackTrace);
} else if (_done == null) {
Zone.current.handleUncaughtError(error, stackTrace!);
if (stackTrace != null) {
Zone.current.handleUncaughtError(error, stackTrace);
} else {
log.warning("[ResponseContext] stackTrace is null");
}
}
}

View file

@ -135,7 +135,7 @@ class Angel extends Routable {
}
res.contentType = MediaType('text', 'html', {'charset': 'utf8'});
res.statusCode = e.statusCode;
res.statusCode = e.statusCode; // ?? 200;
res.write("<!DOCTYPE html><html><head><title>${e.message}</title>");
res.write("</head><body><h1>${e.message}</h1><ul>");

View file

@ -195,13 +195,14 @@ class Service<Id, Data> extends Routable {
/// For example, `parseId<bool>` attempts to parse the value as a [bool].
static T parseId<T>(id) {
if (id == 'null' || id == null) {
return '' as T;
//return 'null' as T;
throw ArgumentError("[Service] Null is not supported");
} else if (T == String) {
return id.toString() as T;
} else if (T == int) {
return int.parse(id.toString()) as T;
} else if (T == bool) {
return (id == true || id?.toString() == 'true') as T;
return (id == true || id.toString() == 'true') as T;
} else if (T == double) {
return double.parse(id.toString()) as T;
} else if (T == num) {

View file

@ -23,7 +23,8 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
//if (server == null) {
// throw ArgumentError("[AngelHttp] Server instance not intialised");
//}
return Uri(scheme: 'http', host: server.address.address, port: server.port);
return Uri(
scheme: 'http', host: server?.address.address, port: server?.port);
}
AngelHttp._(Angel app,
@ -67,13 +68,13 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
}
/// Use [server] instead.
@deprecated
HttpServer get httpServer {
//if (server == null) {
// throw ArgumentError("[AngelHttp] Server instance not initialised");
//}
return server;
}
//@deprecated
//HttpServer get httpServer {
//if (server == null) {
// throw ArgumentError("[AngelHttp] Server instance not initialised");
//}
// return server;
//}
Future handleRequest(HttpRequest request) =>
handleRawRequest(request, request.response);
@ -83,7 +84,7 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
response.cookies.addAll(cookies);
@override
Future<HttpServer> close() async {
Future<void> close() async {
return await super.close();
}
@ -105,12 +106,8 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
// TODO: Refactored to overcome NNBD migration error
HttpResponseContext context =
HttpResponseContext(response, app, correspondingRequest);
if (app.serializer == null) {
context.serializer = json.encode;
} else {
context.serializer = app.serializer;
}
context.serializer = (app.serializer ?? json.encode);
context.encoders.addAll(app.encoders);
return Future<HttpResponseContext>.value(context);
// return Future<HttpResponseContext>.value(
// HttpResponseContext(response, app, correspondingRequest)
@ -132,8 +129,8 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
response.headers.contentLength = length;
@override
void setHeader(HttpResponse response, String key, String? value) =>
response.headers.set(key, value!);
void setHeader(HttpResponse response, String key, String value) =>
response.headers.set(key, value);
@override
void setStatusCode(HttpResponse response, int value) =>

View file

@ -9,7 +9,7 @@ import '../core/core.dart';
/// An implementation of [RequestContext] that wraps a [HttpRequest].
class HttpRequestContext extends RequestContext<HttpRequest?> {
Container? _container;
MediaType _contentType = MediaType("text", "html");
MediaType _contentType = MediaType("text", "plain");
HttpRequest? _io;
String? _override;
String _path = '';
@ -89,7 +89,7 @@ class HttpRequestContext extends RequestContext<HttpRequest?> {
ctx.app = app;
ctx._contentType = request.headers.contentType == null
? MediaType("text", "html")
? MediaType("text", "plain")
: MediaType.parse(request.headers.contentType.toString());
ctx._override = override;

View file

@ -79,7 +79,7 @@ class AngelHttp2 extends Driver<Socket, ServerTransportStream,
}
@override
Future<SecureServerSocket> close() async {
Future<void> close() async {
await _artificial!.close();
await _http.close();
return await super.close();
@ -143,8 +143,8 @@ class AngelHttp2 extends Driver<Socket, ServerTransportStream,
@override
Uri get uri => Uri(
scheme: 'https',
host: server.address.address,
port: server.port != 443 ? server.port : null);
host: server?.address.address,
port: server?.port != 443 ? server?.port : null);
@override
void writeStringToResponse(ServerTransportStream response, String value) {

View file

@ -60,7 +60,7 @@ main() {
Future<RequestContext> acceptContentTypes(
[Iterable<String> contentTypes = const []]) {
var headerString =
contentTypes.isEmpty ? ContentType.html : contentTypes.join(',');
contentTypes.isEmpty ? ContentType.text : contentTypes.join(',');
var rq = MockHttpRequest('GET', ENDPOINT, persistentConnection: false);
rq.headers.set('accept', headerString);
rq.close();

View file

@ -98,8 +98,8 @@ void encodingTests(Angel getApp()) {
});
test('only uses one encoder', () async {
var rq = MockHttpRequest('GET', Uri.parse('/hello'))
..headers.set('accept-encoding', ['gzip', 'deflate']);
var rq = MockHttpRequest('GET', Uri.parse('/hello'));
rq.headers.set('accept-encoding', ['gzip', 'deflate']);
await rq.close();
var rs = rq.response;
await http.handleRequest(rq);

View file

@ -126,6 +126,7 @@ main() {
});
test('cannot remove all unless explicitly set', () async {
var a = "1234";
var response = await client.delete(Uri.parse('$url/todos/null'));
expect(response.statusCode, 403);
});

View file

@ -0,0 +1,5 @@
{
"files.watcherExclude": {
"**/target": true
}
}

View file

@ -1,4 +1,4 @@
import 'package:angel_http_exception/angel_http_exception.dart';
void main() =>
throw new AngelHttpException.notFound(message: "Can't find that page!");
throw AngelHttpException.notFound(message: "Can't find that page!");

5
packages/route/.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,5 @@
{
"files.watcherExclude": {
"**/target": true
}
}