Fixed null safety issues
This commit is contained in:
parent
5b3c589c8e
commit
15926ff45d
42 changed files with 242 additions and 207 deletions
|
@ -11,7 +11,7 @@
|
|||
* Added merge_map and migrated to 2.0.0 (6/6 tests passed)
|
||||
* Added mock_request and migrated to 2.0.0 (0/0 tests)
|
||||
* Migrated angel_framework to 4.0.0 (146/149 tests passed)
|
||||
* Migrated angel_auth to 4.0.0 (22/32 tests passed)
|
||||
* Migrated angel_auth to 4.0.0 (21/30 tests passed)
|
||||
* Migrated angel_configuration to 4.0.0 (6/8 testspassed)
|
||||
* Migrated angel_validate to 4.0.0 (6/7 tests passed)
|
||||
* Migrated json_god to 4.0.0 (13/13 tests passed)
|
||||
|
|
|
@ -30,7 +30,7 @@ class AuthToken {
|
|||
|
||||
String? ipAddress;
|
||||
late DateTime issuedAt;
|
||||
num? lifeSpan;
|
||||
num lifeSpan;
|
||||
var userId;
|
||||
Map<String, dynamic> payload = {};
|
||||
|
||||
|
@ -41,12 +41,17 @@ class AuthToken {
|
|||
DateTime? issuedAt,
|
||||
Map payload = const {}}) {
|
||||
this.issuedAt = issuedAt ?? DateTime.now();
|
||||
this.payload.addAll(payload.keys
|
||||
.fold({}, ((out, k) => out?..[k.toString()] = payload[k])) ??
|
||||
{});
|
||||
/*
|
||||
this.payload.addAll(payload.keys.fold(
|
||||
{},
|
||||
((out, k) => out..[k.toString()] = payload[k])
|
||||
as Map<String, dynamic>? Function(
|
||||
Map<String, dynamic>?, dynamic)) ??
|
||||
{});
|
||||
*/
|
||||
}
|
||||
|
||||
factory AuthToken.fromJson(String jsons) =>
|
||||
|
@ -55,10 +60,10 @@ class AuthToken {
|
|||
factory AuthToken.fromMap(Map data) {
|
||||
return AuthToken(
|
||||
ipAddress: data['aud'].toString(),
|
||||
lifeSpan: data['exp'] as num?,
|
||||
lifeSpan: data['exp'] as num,
|
||||
issuedAt: DateTime.parse(data['iat'].toString()),
|
||||
userId: data['sub'],
|
||||
payload: data['pld'] as Map? ?? {});
|
||||
payload: data['pld'] as Map);
|
||||
}
|
||||
|
||||
factory AuthToken.parse(String jwt) {
|
||||
|
|
|
@ -5,10 +5,10 @@ import 'package:quiver/core.dart';
|
|||
/// A common class containing parsing and validation logic for third-party authentication configuration.
|
||||
class ExternalAuthOptions {
|
||||
/// The user's identifier, otherwise known as an "application id".
|
||||
final String? clientId;
|
||||
final String clientId;
|
||||
|
||||
/// The user's secret, other known as an "application secret".
|
||||
final String? clientSecret;
|
||||
final String clientSecret;
|
||||
|
||||
/// The user's redirect URI.
|
||||
final Uri redirectUri;
|
||||
|
@ -17,17 +17,11 @@ class ExternalAuthOptions {
|
|||
final Set<String> scopes;
|
||||
|
||||
ExternalAuthOptions._(
|
||||
this.clientId, this.clientSecret, this.redirectUri, this.scopes) {
|
||||
if (clientId == null) {
|
||||
throw ArgumentError.notNull('clientId');
|
||||
} else if (clientSecret == null) {
|
||||
throw ArgumentError.notNull('clientSecret');
|
||||
}
|
||||
}
|
||||
this.clientId, this.clientSecret, this.redirectUri, this.scopes);
|
||||
|
||||
factory ExternalAuthOptions(
|
||||
{required String? clientId,
|
||||
required String? clientSecret,
|
||||
{required String clientId,
|
||||
required String clientSecret,
|
||||
required redirectUri,
|
||||
Iterable<String> scopes = const []}) {
|
||||
if (redirectUri is String) {
|
||||
|
@ -50,8 +44,8 @@ class ExternalAuthOptions {
|
|||
/// * `redirect_uri`
|
||||
factory ExternalAuthOptions.fromMap(Map map) {
|
||||
return ExternalAuthOptions(
|
||||
clientId: map['client_id'] as String?,
|
||||
clientSecret: map['client_secret'] as String?,
|
||||
clientId: map['client_id'] as String,
|
||||
clientSecret: map['client_secret'] as String,
|
||||
redirectUri: map['redirect_uri'],
|
||||
scopes: map['scopes'] is Iterable
|
||||
? ((map['scopes'] as Iterable).map((x) => x.toString()))
|
||||
|
@ -72,15 +66,15 @@ class ExternalAuthOptions {
|
|||
|
||||
/// Creates a copy of this object, with the specified changes.
|
||||
ExternalAuthOptions copyWith(
|
||||
{String? clientId,
|
||||
String? clientSecret,
|
||||
{String clientId = '',
|
||||
String clientSecret = '',
|
||||
redirectUri,
|
||||
Iterable<String>? scopes}) {
|
||||
Iterable<String> scopes = const []}) {
|
||||
return ExternalAuthOptions(
|
||||
clientId: clientId ?? this.clientId,
|
||||
clientSecret: clientSecret ?? this.clientSecret,
|
||||
clientId: clientId,
|
||||
clientSecret: clientSecret,
|
||||
redirectUri: redirectUri ?? this.redirectUri,
|
||||
scopes: (scopes ??= []).followedBy(this.scopes),
|
||||
scopes: (scopes).followedBy(this.scopes),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -117,7 +111,7 @@ class ExternalAuthOptions {
|
|||
secret = clientSecret;
|
||||
} else {
|
||||
var codeUnits =
|
||||
List<int>.filled(asteriskCount ?? clientSecret!.length, $asterisk);
|
||||
List<int>.filled(asteriskCount ?? clientSecret.length, $asterisk);
|
||||
secret = String.fromCharCodes(codeUnits);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,11 +6,14 @@ import 'package:angel_framework/angel_framework.dart';
|
|||
/// [realm] defaults to `'angel_auth'`.
|
||||
RequestHandler forceBasicAuth<User>({String? realm}) {
|
||||
return (RequestContext req, ResponseContext res) async {
|
||||
if (req.container!.has<User>()) {
|
||||
return true;
|
||||
} else if (req.container!.has<Future<User>>()) {
|
||||
await req.container!.makeAsync<User>();
|
||||
return true;
|
||||
if (req.container != null) {
|
||||
var reqContainer = req.container!;
|
||||
if (reqContainer.has<User>()) {
|
||||
return true;
|
||||
} else if (reqContainer.has<Future<User>>()) {
|
||||
await reqContainer.makeAsync<User>();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
res.headers['www-authenticate'] = 'Basic realm="${realm ?? 'angel_auth'}"';
|
||||
|
@ -31,11 +34,16 @@ RequestHandler requireAuthentication<User>() {
|
|||
}
|
||||
}
|
||||
|
||||
if (req.container!.has<User>() || req.method == 'OPTIONS') {
|
||||
return true;
|
||||
} else if (req.container!.has<Future<User>>()) {
|
||||
await req.container!.makeAsync<User>();
|
||||
return true;
|
||||
if (req.container != null) {
|
||||
var reqContainer = req.container!;
|
||||
if (reqContainer.has<User>() || req.method == 'OPTIONS') {
|
||||
return true;
|
||||
} else if (reqContainer.has<Future<User>>()) {
|
||||
await reqContainer.makeAsync<User>();
|
||||
return true;
|
||||
} else {
|
||||
return _reject(res);
|
||||
}
|
||||
} else {
|
||||
return _reject(res);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math' as Math;
|
||||
import 'dart:math';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'auth_token.dart';
|
||||
|
@ -9,11 +9,11 @@ import 'strategy.dart';
|
|||
|
||||
/// Handles authentication within an Angel application.
|
||||
class AngelAuth<User> {
|
||||
Hmac? _hs256;
|
||||
int? _jwtLifeSpan;
|
||||
final StreamController<User?> _onLogin = StreamController<User>(),
|
||||
late Hmac _hs256;
|
||||
late int _jwtLifeSpan;
|
||||
final StreamController<User> _onLogin = StreamController<User>(),
|
||||
_onLogout = StreamController<User>();
|
||||
final Math.Random _random = Math.Random.secure();
|
||||
final Random _random = Random.secure();
|
||||
final RegExp _rgxBearer = RegExp(r'^Bearer');
|
||||
|
||||
/// If `true` (default), then JWT's will be stored and retrieved from a `token` cookie.
|
||||
|
@ -51,23 +51,25 @@ class AngelAuth<User> {
|
|||
FutureOr Function(User)? serializer;
|
||||
|
||||
/// Deserializes a unique identifier into its associated identity. In most cases, this is a user object or model instance.
|
||||
FutureOr<User> Function(Object?)? deserializer;
|
||||
FutureOr<User> Function(Object)? deserializer;
|
||||
|
||||
/// Fires the result of [deserializer] whenever a user signs in to the application.
|
||||
Stream<User?> get onLogin => _onLogin.stream;
|
||||
Stream<User> get onLogin => _onLogin.stream;
|
||||
|
||||
/// Fires `req.user`, which is usually the result of [deserializer], whenever a user signs out of the application.
|
||||
Stream<User?> get onLogout => _onLogout.stream;
|
||||
Stream<User> get onLogout => _onLogout.stream;
|
||||
|
||||
/// The [Hmac] being used to encode JWT's.
|
||||
Hmac? get hmac => _hs256;
|
||||
Hmac get hmac => _hs256;
|
||||
|
||||
String _randomString(
|
||||
{int length = 32,
|
||||
String validChars =
|
||||
"ABCDEFHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"}) {
|
||||
'ABCDEFHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_'}) {
|
||||
var chars = <int>[];
|
||||
while (chars.length < length) chars.add(_random.nextInt(validChars.length));
|
||||
while (chars.length < length) {
|
||||
chars.add(_random.nextInt(validChars.length));
|
||||
}
|
||||
return String.fromCharCodes(chars);
|
||||
}
|
||||
|
||||
|
@ -83,7 +85,7 @@ class AngelAuth<User> {
|
|||
this.cookieDomain,
|
||||
this.cookiePath = '/',
|
||||
this.secureCookies = true,
|
||||
this.reviveTokenEndpoint = "/auth/token"})
|
||||
this.reviveTokenEndpoint = '/auth/token'})
|
||||
: super() {
|
||||
_hs256 = Hmac(sha256, (jwtKey ?? _randomString()).codeUnits);
|
||||
_jwtLifeSpan = jwtLifeSpan?.toInt() ?? -1;
|
||||
|
@ -110,7 +112,7 @@ class AngelAuth<User> {
|
|||
app.container!
|
||||
.registerLazySingleton<Future<_AuthResult<User>>>((container) async {
|
||||
var req = container.make<RequestContext>()!;
|
||||
var res = container.make<ResponseContext>();
|
||||
var res = container.make<ResponseContext>()!;
|
||||
var result = await _decodeJwt(req, res);
|
||||
if (result != null) {
|
||||
return result;
|
||||
|
@ -148,8 +150,8 @@ class AngelAuth<User> {
|
|||
req.container!.registerSingleton<AuthToken>(token);
|
||||
}
|
||||
|
||||
if (allowCookie == true) {
|
||||
_addProtectedCookie(res!, 'token', token.serialize(_hs256!));
|
||||
if (allowCookie) {
|
||||
_addProtectedCookie(res!, 'token', token.serialize(_hs256));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,29 +187,29 @@ class AngelAuth<User> {
|
|||
}
|
||||
|
||||
Future<_AuthResult<User>?> _decodeJwt(
|
||||
RequestContext req, ResponseContext? res) async {
|
||||
RequestContext req, ResponseContext res) async {
|
||||
var jwt = getJwt(req);
|
||||
|
||||
if (jwt != null) {
|
||||
var token = AuthToken.validate(jwt, _hs256!);
|
||||
var token = AuthToken.validate(jwt, _hs256);
|
||||
|
||||
if (enforceIp) {
|
||||
if (req.ip != token.ipAddress) {
|
||||
throw AngelHttpException.forbidden(
|
||||
message: "JWT cannot be accessed from this IP address.");
|
||||
message: 'JWT cannot be accessed from this IP address.');
|
||||
}
|
||||
}
|
||||
|
||||
if (token.lifeSpan! > -1) {
|
||||
if (token.lifeSpan > -1) {
|
||||
var expiry =
|
||||
token.issuedAt.add(Duration(milliseconds: token.lifeSpan!.toInt()));
|
||||
token.issuedAt.add(Duration(milliseconds: token.lifeSpan.toInt()));
|
||||
|
||||
if (!expiry.isAfter(DateTime.now())) {
|
||||
throw AngelHttpException.forbidden(message: "Expired JWT.");
|
||||
throw AngelHttpException.forbidden(message: 'Expired JWT.');
|
||||
}
|
||||
}
|
||||
|
||||
var user = await deserializer!(token.userId);
|
||||
var user = await deserializer!(token.userId as Object);
|
||||
_apply(req, res, token, user);
|
||||
return _AuthResult(user, token);
|
||||
}
|
||||
|
@ -217,7 +219,7 @@ 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) {
|
||||
if (req.headers?.value('Authorization') != null) {
|
||||
final authHeader = req.headers!.value('Authorization')!;
|
||||
|
||||
// Allow Basic auth to fall through
|
||||
|
@ -228,7 +230,7 @@ class AngelAuth<User> {
|
|||
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) {
|
||||
req.uri?.queryParameters['token'] is String) {
|
||||
return req.uri!.queryParameters['token']?.toString();
|
||||
}
|
||||
|
||||
|
@ -248,10 +250,10 @@ class AngelAuth<User> {
|
|||
cookie.secure = true;
|
||||
}
|
||||
|
||||
if (_jwtLifeSpan! > 0) {
|
||||
cookie.maxAge ??= _jwtLifeSpan! < 0 ? -1 : _jwtLifeSpan! ~/ 1000;
|
||||
cookie.expires ??=
|
||||
DateTime.now().add(Duration(milliseconds: _jwtLifeSpan!));
|
||||
var lifeSpan = _jwtLifeSpan;
|
||||
if (lifeSpan > 0) {
|
||||
cookie.maxAge ??= lifeSpan < 0 ? -1 : lifeSpan ~/ 1000;
|
||||
cookie.expires ??= DateTime.now().add(Duration(milliseconds: lifeSpan));
|
||||
}
|
||||
|
||||
cookie.domain ??= cookieDomain;
|
||||
|
@ -270,19 +272,19 @@ class AngelAuth<User> {
|
|||
jwt = body['token']?.toString();
|
||||
}
|
||||
if (jwt == null) {
|
||||
throw AngelHttpException.forbidden(message: "No JWT provided");
|
||||
throw AngelHttpException.forbidden(message: 'No JWT provided');
|
||||
} else {
|
||||
var token = AuthToken.validate(jwt, _hs256!);
|
||||
var token = AuthToken.validate(jwt, _hs256);
|
||||
if (enforceIp) {
|
||||
if (req.ip != token.ipAddress) {
|
||||
throw AngelHttpException.forbidden(
|
||||
message: "JWT cannot be accessed from this IP address.");
|
||||
message: 'JWT cannot be accessed from this IP address.');
|
||||
}
|
||||
}
|
||||
|
||||
if (token.lifeSpan! > -1) {
|
||||
if (token.lifeSpan > -1) {
|
||||
var expiry = token.issuedAt
|
||||
.add(Duration(milliseconds: token.lifeSpan!.toInt()));
|
||||
.add(Duration(milliseconds: token.lifeSpan.toInt()));
|
||||
|
||||
if (!expiry.isAfter(DateTime.now())) {
|
||||
//print(
|
||||
|
@ -293,15 +295,15 @@ class AngelAuth<User> {
|
|||
}
|
||||
|
||||
if (allowCookie) {
|
||||
_addProtectedCookie(res, 'token', token.serialize(_hs256!));
|
||||
_addProtectedCookie(res, 'token', token.serialize(_hs256));
|
||||
}
|
||||
|
||||
final data = await deserializer!(token.userId);
|
||||
return {'data': data, 'token': token.serialize(_hs256!)};
|
||||
final data = await deserializer!(token.userId as Object);
|
||||
return {'data': data, 'token': token.serialize(_hs256)};
|
||||
}
|
||||
} catch (e) {
|
||||
if (e is AngelHttpException) rethrow;
|
||||
throw AngelHttpException.badRequest(message: "Malformed JWT");
|
||||
throw AngelHttpException.badRequest(message: 'Malformed JWT');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -347,7 +349,7 @@ class AngelAuth<User> {
|
|||
// Create JWT
|
||||
var token = AuthToken(
|
||||
userId: userId, lifeSpan: _jwtLifeSpan, ipAddress: req.ip);
|
||||
var jwt = token.serialize(_hs256!);
|
||||
var jwt = token.serialize(_hs256);
|
||||
|
||||
if (options?.tokenCallback != null) {
|
||||
if (!req.container!.has<User>()) {
|
||||
|
@ -356,7 +358,7 @@ class AngelAuth<User> {
|
|||
|
||||
var r = await options!.tokenCallback!(req, res, token, result);
|
||||
if (r != null) return r;
|
||||
jwt = token.serialize(_hs256!);
|
||||
jwt = token.serialize(_hs256);
|
||||
}
|
||||
|
||||
_apply(req, res, token, result);
|
||||
|
@ -376,7 +378,7 @@ class AngelAuth<User> {
|
|||
req.accepts('application/json')) {
|
||||
var user = hasExisting
|
||||
? result
|
||||
: await deserializer!(await serializer!(result));
|
||||
: await deserializer!((await serializer!(result)) as Object);
|
||||
_onLogin.add(user);
|
||||
return {"data": user, "token": jwt};
|
||||
}
|
||||
|
@ -402,38 +404,40 @@ class AngelAuth<User> {
|
|||
|
||||
/// Log a user in on-demand.
|
||||
Future login(AuthToken token, RequestContext req, ResponseContext res) async {
|
||||
var user = await deserializer!(token.userId);
|
||||
var user = await deserializer!(token.userId as Object);
|
||||
_apply(req, res, token, user);
|
||||
_onLogin.add(user);
|
||||
|
||||
if (allowCookie) {
|
||||
_addProtectedCookie(res, 'token', token.serialize(_hs256!));
|
||||
_addProtectedCookie(res, 'token', token.serialize(_hs256));
|
||||
}
|
||||
}
|
||||
|
||||
/// Log a user in on-demand.
|
||||
Future loginById(userId, RequestContext req, ResponseContext res) async {
|
||||
var user = await deserializer!(userId);
|
||||
var user = await deserializer!(userId as Object);
|
||||
var token =
|
||||
AuthToken(userId: userId, lifeSpan: _jwtLifeSpan, ipAddress: req.ip);
|
||||
_apply(req, res, token, user);
|
||||
_onLogin.add(user);
|
||||
|
||||
if (allowCookie) {
|
||||
_addProtectedCookie(res, 'token', token.serialize(_hs256!));
|
||||
_addProtectedCookie(res, 'token', token.serialize(_hs256));
|
||||
}
|
||||
}
|
||||
|
||||
/// Log an authenticated user out.
|
||||
RequestHandler logout([AngelAuthOptions<User>? options]) {
|
||||
return (RequestContext req, ResponseContext res) async {
|
||||
if (req.container!.has<User>()) {
|
||||
var user = req.container!.make<User>();
|
||||
_onLogout.add(user);
|
||||
if (req.container?.has<User>() == true) {
|
||||
var user = req.container?.make<User>();
|
||||
if (user != null) {
|
||||
_onLogout.add(user);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowCookie == true) {
|
||||
res.cookies.removeWhere((cookie) => cookie.name == "token");
|
||||
res.cookies.removeWhere((cookie) => cookie.name == 'token');
|
||||
_addProtectedCookie(res, 'token', '""');
|
||||
}
|
||||
|
||||
|
|
|
@ -38,10 +38,13 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
|
|||
User? verificationResult;
|
||||
|
||||
if (allowBasic) {
|
||||
var authHeader = req.headers!.value('authorization') ?? '';
|
||||
var authHeader = req.headers?.value('authorization') ?? '';
|
||||
|
||||
if (_rgxBasic.hasMatch(authHeader)) {
|
||||
var base64AuthString = _rgxBasic.firstMatch(authHeader)!.group(1)!;
|
||||
var base64AuthString = _rgxBasic.firstMatch(authHeader)?.group(1);
|
||||
if (base64AuthString == null) {
|
||||
return null;
|
||||
}
|
||||
var authString = String.fromCharCodes(base64.decode(base64AuthString));
|
||||
if (_rgxUsrPass.hasMatch(authString)) {
|
||||
Match usrPassMatch = _rgxUsrPass.firstMatch(authString)!;
|
||||
|
@ -51,7 +54,7 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
|
|||
throw AngelHttpException.badRequest(errors: [invalidMessage]);
|
||||
}
|
||||
|
||||
if (verificationResult == false || verificationResult == null) {
|
||||
if (verificationResult == null) {
|
||||
res
|
||||
..statusCode = 401
|
||||
..headers['www-authenticate'] = 'Basic realm="$realm"';
|
||||
|
@ -68,16 +71,16 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
|
|||
.parseBody()
|
||||
.then((_) => req.bodyAsMap)
|
||||
.catchError((_) => <String, dynamic>{});
|
||||
if (body != null) {
|
||||
if (_validateString(body[usernameField]?.toString()) &&
|
||||
_validateString(body[passwordField]?.toString())) {
|
||||
verificationResult = await verifier(
|
||||
body[usernameField]?.toString(), body[passwordField]?.toString());
|
||||
}
|
||||
//if (body != null) {
|
||||
if (_validateString(body[usernameField].toString()) &&
|
||||
_validateString(body[passwordField].toString())) {
|
||||
verificationResult = await verifier(
|
||||
body[usernameField].toString(), body[passwordField].toString());
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
if (verificationResult == false || verificationResult == null) {
|
||||
if (verificationResult == null) {
|
||||
if (options.failureRedirect != null &&
|
||||
options.failureRedirect!.isNotEmpty) {
|
||||
await res.redirect(options.failureRedirect, code: 401);
|
||||
|
|
|
@ -7,6 +7,7 @@ environment:
|
|||
sdk: '>=2.12.0 <3.0.0'
|
||||
dependencies:
|
||||
angel_framework:
|
||||
# path: ../framework
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x_nnbd
|
||||
|
@ -23,3 +24,7 @@ dev_dependencies:
|
|||
logging: ^1.0.0
|
||||
pedantic: ^1.11.0
|
||||
test: ^1.17.3
|
||||
|
||||
#dependency_overrides:
|
||||
# angel_container:
|
||||
# path: ../container/angel_container
|
|
@ -1,12 +1,12 @@
|
|||
import "package:angel_auth/src/auth_token.dart";
|
||||
import "package:crypto/crypto.dart";
|
||||
import "package:test/test.dart";
|
||||
import 'package:angel_auth/src/auth_token.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
main() async {
|
||||
final Hmac hmac = Hmac(sha256, "angel_auth".codeUnits);
|
||||
void main() async {
|
||||
final hmac = Hmac(sha256, 'angel_auth'.codeUnits);
|
||||
|
||||
test("sample serialization", () {
|
||||
var token = AuthToken(ipAddress: "localhost", userId: "thosakwe");
|
||||
test('sample serialization', () {
|
||||
var token = AuthToken(ipAddress: 'localhost', userId: 'thosakwe');
|
||||
var jwt = token.serialize(hmac);
|
||||
print(jwt);
|
||||
|
||||
|
@ -17,7 +17,7 @@ main() async {
|
|||
});
|
||||
|
||||
test('custom payload', () {
|
||||
var token = AuthToken(ipAddress: "localhost", userId: "thosakwe", payload: {
|
||||
var token = AuthToken(ipAddress: 'localhost', userId: 'thosakwe', payload: {
|
||||
"foo": "bar",
|
||||
"baz": {
|
||||
"one": 1,
|
||||
|
|
|
@ -70,6 +70,7 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
/* Deprecated as clientId and clientSecret cannot be null
|
||||
test('ensures id not null', () {
|
||||
expect(
|
||||
() => ExternalAuthOptions(
|
||||
|
@ -89,6 +90,7 @@ void main() {
|
|||
throwsArgumentError,
|
||||
);
|
||||
});
|
||||
*/
|
||||
});
|
||||
|
||||
group('fromMap()', () {
|
||||
|
|
|
@ -7,7 +7,7 @@ import 'package:http/http.dart' as http;
|
|||
import 'package:logging/logging.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
final AngelAuth<Map<String, String>?> auth = AngelAuth<Map<String, String>?>();
|
||||
final AngelAuth<Map<String, String>> auth = AngelAuth<Map<String, String>>();
|
||||
var headers = <String, String>{'accept': 'application/json'};
|
||||
var localOpts = AngelAuthOptions<Map<String, String>>(
|
||||
failureRedirect: '/failure', successRedirect: '/success');
|
||||
|
@ -113,12 +113,13 @@ void main() async {
|
|||
auth.strategies.clear();
|
||||
auth.strategies['local'] =
|
||||
LocalAuthStrategy(verifier, forceBasic: true, realm: 'test');
|
||||
var response = await client!.get(Uri.parse('$url/hello'), headers: {
|
||||
var response = await client?.get(Uri.parse('$url/hello'), headers: {
|
||||
'accept': 'application/json',
|
||||
'content-type': 'application/json'
|
||||
});
|
||||
print(response.headers);
|
||||
print('Body <${response.body}>');
|
||||
expect(response.headers['www-authenticate'], equals('Basic realm="test"'));
|
||||
print('Header = ${response?.headers}');
|
||||
print('Body <${response?.body}>');
|
||||
var head = response?.headers['www-authenticate'];
|
||||
expect(head, equals('Basic realm="test"'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:io';
|
|||
import 'package:angel_auth/angel_auth.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
const Duration threeDays = const Duration(days: 3);
|
||||
const Duration threeDays = Duration(days: 3);
|
||||
|
||||
void main() {
|
||||
late Cookie defaultCookie;
|
||||
|
|
|
@ -18,7 +18,7 @@ final Parser<Map<String, String>> credentials = chain<String>([
|
|||
match<String>(':'),
|
||||
string.opt(),
|
||||
]).map<Map<String, String>>(
|
||||
(r) => {'username': r.value![0], 'password': r.value![2]});
|
||||
(r) => {'username': r.value![0]!, 'password': r.value![2]!});
|
||||
|
||||
/// We can actually embed a parser within another parser.
|
||||
///
|
||||
|
|
|
@ -24,7 +24,7 @@ Parser<num> calculatorGrammar() {
|
|||
expr.space(),
|
||||
match<Null>(op).space() as Parser<num>,
|
||||
expr.space(),
|
||||
]).map((r) => f(r.value![0], r.value![2])),
|
||||
]).map((r) => f(r.value![0]!, r.value![2]!)),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ class _Advance<T> extends Parser<T> {
|
|||
_Advance(this.parser, this.amount);
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<T?> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth()).change(parser: this);
|
||||
if (result.successful) args.scanner.position += amount;
|
||||
return result;
|
||||
|
|
|
@ -21,7 +21,7 @@ class _Any<T> extends Parser<T> {
|
|||
_Any(this.parsers, this.backtrack, this.errorMessage, this.severity);
|
||||
|
||||
@override
|
||||
ParseResult<T> _parse(ParseArgs args) {
|
||||
ParseResult<T?> _parse(ParseArgs args) {
|
||||
var inactive = parsers
|
||||
.where((p) => !args.trampoline.isActive(p, args.scanner.position));
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
part of lex.src.combinator;
|
||||
|
||||
class _Cache<T> extends Parser<T> {
|
||||
final Map<int, ParseResult<T>> _cache = {};
|
||||
final Map<int, ParseResult<T?>> _cache = {};
|
||||
final Parser<T> parser;
|
||||
|
||||
_Cache(this.parser);
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<T?> __parse(ParseArgs args) {
|
||||
return _cache.putIfAbsent(args.scanner.position, () {
|
||||
return parser._parse(args.increaseDepth());
|
||||
}).change(parser: this);
|
||||
|
|
|
@ -3,9 +3,9 @@ part of lex.src.combinator;
|
|||
/// Expects to parse a sequence of [parsers].
|
||||
///
|
||||
/// If [failFast] is `true` (default), then the first failure to parse will abort the parse.
|
||||
ListParser<T> chain<T>(Iterable<Parser<T>> parsers,
|
||||
ListParser<T?> chain<T>(Iterable<Parser<T>> parsers,
|
||||
{bool failFast: true, SyntaxErrorSeverity? severity}) {
|
||||
return _Chain<T>(
|
||||
return _Chain<T?>(
|
||||
parsers, failFast != false, severity ?? SyntaxErrorSeverity.error);
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ class _Alt<T> extends Parser<T> {
|
|||
_Alt(this.parser, this.errorMessage, this.severity);
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<T?> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
return result.successful
|
||||
? result
|
||||
|
@ -33,7 +33,7 @@ class _Alt<T> extends Parser<T> {
|
|||
}
|
||||
}
|
||||
|
||||
class _Chain<T> extends ListParser<T> {
|
||||
class _Chain<T> extends ListParser<T?> {
|
||||
final Iterable<Parser<T>> parsers;
|
||||
final bool failFast;
|
||||
final SyntaxErrorSeverity severity;
|
||||
|
@ -41,9 +41,9 @@ class _Chain<T> extends ListParser<T> {
|
|||
_Chain(this.parsers, this.failFast, this.severity);
|
||||
|
||||
@override
|
||||
ParseResult<List<T>> __parse(ParseArgs args) {
|
||||
ParseResult<List<T?>?> __parse(ParseArgs args) {
|
||||
var errors = <SyntaxError>[];
|
||||
var results = <T>[];
|
||||
var results = <T?>[];
|
||||
var spans = <FileSpan>[];
|
||||
bool successful = true;
|
||||
|
||||
|
@ -61,9 +61,7 @@ class _Chain<T> extends ListParser<T> {
|
|||
successful = false;
|
||||
}
|
||||
|
||||
if (result.value != null) {
|
||||
results.add(result.value!);
|
||||
}
|
||||
results.add(result.value);
|
||||
|
||||
if (result.span != null) {
|
||||
spans.add(result.span!);
|
||||
|
@ -76,14 +74,14 @@ class _Chain<T> extends ListParser<T> {
|
|||
span = spans.reduce((a, b) => a.expand(b));
|
||||
}
|
||||
|
||||
return ParseResult<List<T>>(
|
||||
return ParseResult<List<T?>?>(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
this,
|
||||
successful,
|
||||
errors,
|
||||
span: span,
|
||||
value: List<T>.unmodifiable(results),
|
||||
value: List<T?>.unmodifiable(results),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ class _Check<T> extends Parser<T> {
|
|||
_Check(this.parser, this.matcher, this.errorMessage, this.severity);
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<T?> __parse(ParseArgs args) {
|
||||
var matchState = {};
|
||||
var result = parser._parse(args.increaseDepth()).change(parser: this);
|
||||
if (!result.successful)
|
||||
|
|
|
@ -67,9 +67,9 @@ class ParseArgs {
|
|||
|
||||
/// A parser combinator, which can parse very complicated grammars in a manageable manner.
|
||||
abstract class Parser<T> {
|
||||
ParseResult<T> __parse(ParseArgs args);
|
||||
ParseResult<T?> __parse(ParseArgs args);
|
||||
|
||||
ParseResult<T> _parse(ParseArgs args) {
|
||||
ParseResult<T?> _parse(ParseArgs args) {
|
||||
var pos = args.scanner.position;
|
||||
|
||||
if (args.trampoline.hasMemoized(this, pos))
|
||||
|
@ -86,7 +86,7 @@ abstract class Parser<T> {
|
|||
}
|
||||
|
||||
/// Parses text from a [SpanScanner].
|
||||
ParseResult<T> parse(SpanScanner scanner, [int depth = 1]) {
|
||||
ParseResult<T?> parse(SpanScanner scanner, [int depth = 1]) {
|
||||
var args = ParseArgs(Trampoline(), scanner, depth);
|
||||
return _parse(args);
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ abstract class Parser<T> {
|
|||
|
||||
// TODO: Type issue
|
||||
/// Runs the given function, which changes the returned [ParseResult] into one relating to a [U] object.
|
||||
Parser<U> change<U>(ParseResult<U> Function(ParseResult<T>) f) {
|
||||
Parser<U> change<U>(ParseResult<U?> Function(ParseResult<T?>) f) {
|
||||
return _Change<T, U>(this, f);
|
||||
}
|
||||
|
||||
|
@ -128,7 +128,7 @@ abstract class Parser<T> {
|
|||
}
|
||||
|
||||
/// Transforms the parse result using a unary function.
|
||||
Parser<U> map<U>(U Function(ParseResult<T>) f) {
|
||||
Parser<U> map<U>(U Function(ParseResult<T?>) f) {
|
||||
return _Map<T, U>(this, f);
|
||||
}
|
||||
|
||||
|
@ -192,20 +192,18 @@ abstract class Parser<T> {
|
|||
Parser<List<T>> separatedBy(Parser other) {
|
||||
var suffix = other.then(this).index(1).cast<T>();
|
||||
return this.then(suffix.star()).map((r) {
|
||||
List<dynamic>? v = r.value;
|
||||
if (v != null) {
|
||||
var preceding =
|
||||
v.isEmpty ? [] : (r.value?[0] == null ? [] : [r.value?[0]]);
|
||||
var out = List<T>.from(preceding);
|
||||
if (r.value?[1] != null) {
|
||||
r.value?[1].forEach((element) {
|
||||
out.add(element as T);
|
||||
});
|
||||
}
|
||||
return out;
|
||||
} else {
|
||||
return List<T>.empty(growable: true);
|
||||
var v = r.value;
|
||||
if (v == null || v.length < 2) {
|
||||
return [];
|
||||
}
|
||||
var preceding = v.isEmpty ? [] : (v[0] == null ? [] : [v[0]]);
|
||||
var out = List<T>.from(preceding);
|
||||
if (v[1] != null) {
|
||||
v[1].forEach((element) {
|
||||
out.add(element as T);
|
||||
});
|
||||
}
|
||||
return out;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -278,7 +276,7 @@ abstract class Parser<T> {
|
|||
Parser<T> opt({bool backtrack: true}) => _Opt(this, backtrack);
|
||||
|
||||
/// Sets the value of the [ParseResult].
|
||||
Parser<T> value(T Function(ParseResult<T>) f) {
|
||||
Parser<T> value(T Function(ParseResult<T?>) f) {
|
||||
return _Value<T>(this, f);
|
||||
}
|
||||
|
||||
|
@ -379,7 +377,7 @@ class ParseResult<T> {
|
|||
scanner,
|
||||
parser ?? this.parser,
|
||||
successful ?? this.successful,
|
||||
errors,
|
||||
errors.isNotEmpty ? errors : this.errors,
|
||||
span: span ?? this.span,
|
||||
value: value ?? this.value,
|
||||
);
|
||||
|
|
|
@ -7,8 +7,8 @@ class _Compare<T> extends ListParser<T> {
|
|||
_Compare(this.parser, this.compare);
|
||||
|
||||
@override
|
||||
ParseResult<List<T>> __parse(ParseArgs args) {
|
||||
ParseResult<List<T>> result = parser._parse(args.increaseDepth());
|
||||
ParseResult<List<T>?> __parse(ParseArgs args) {
|
||||
ParseResult<List<T>?> result = parser._parse(args.increaseDepth());
|
||||
if (!result.successful) return result;
|
||||
|
||||
result = result.change(
|
||||
|
|
|
@ -7,7 +7,7 @@ class _FoldErrors<T> extends Parser<T> {
|
|||
_FoldErrors(this.parser, this.equal);
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<T?> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth()).change(parser: this);
|
||||
var errors = result.errors.fold<List<SyntaxError>>([], (out, e) {
|
||||
if (!out.any((b) => equal(e, b))) out.add(e);
|
||||
|
|
|
@ -8,11 +8,26 @@ class _Index<T> extends Parser<T> {
|
|||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<List<T>> result = parser._parse(args.increaseDepth());
|
||||
ParseResult<List<T>?> result = parser._parse(args.increaseDepth());
|
||||
Object? value;
|
||||
|
||||
if (result.successful)
|
||||
value = index == -1 ? result.value!.last : result.value!.elementAt(index);
|
||||
if (result.successful) {
|
||||
var vList = result.value;
|
||||
if (vList == null) {
|
||||
throw ArgumentError("ParseResult is null");
|
||||
}
|
||||
if (index == -1) {
|
||||
value = vList.last;
|
||||
} else {
|
||||
if (index < vList.length) {
|
||||
//TODO: Look at this
|
||||
// print(">>>>Index: $index, Size: ${vList.length}");
|
||||
// value =
|
||||
// index == -1 ? result.value!.last : result.value!.elementAt(index);
|
||||
value = result.value!.elementAt(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ParseResult<T>(
|
||||
args.trampoline,
|
||||
|
|
|
@ -16,7 +16,7 @@ class _Longest<T> extends Parser<T> {
|
|||
_Longest(this.parsers, this.errorMessage, this.severity);
|
||||
|
||||
@override
|
||||
ParseResult<T> _parse(ParseArgs args) {
|
||||
ParseResult<T?> _parse(ParseArgs args) {
|
||||
var inactive = parsers
|
||||
.toList()
|
||||
.where((p) => !args.trampoline.isActive(p, args.scanner.position));
|
||||
|
@ -27,7 +27,7 @@ class _Longest<T> extends Parser<T> {
|
|||
|
||||
int replay = args.scanner.position;
|
||||
var errors = <SyntaxError>[];
|
||||
var results = <ParseResult<T>>[];
|
||||
var results = <ParseResult<T?>>[];
|
||||
|
||||
for (var parser in inactive) {
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
|
@ -59,10 +59,10 @@ class _Longest<T> extends Parser<T> {
|
|||
}
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<T?> __parse(ParseArgs args) {
|
||||
int replay = args.scanner.position;
|
||||
var errors = <SyntaxError>[];
|
||||
var results = <ParseResult<T>>[];
|
||||
var results = <ParseResult<T?>>[];
|
||||
|
||||
for (var parser in parsers) {
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
|
|
|
@ -2,14 +2,14 @@ part of lex.src.combinator;
|
|||
|
||||
class _Map<T, U> extends Parser<U> {
|
||||
final Parser<T> parser;
|
||||
final U Function(ParseResult<T>) f;
|
||||
final U Function(ParseResult<T?>) f;
|
||||
|
||||
_Map(this.parser, this.f);
|
||||
|
||||
@override
|
||||
ParseResult<U> __parse(ParseArgs args) {
|
||||
ParseResult<U?> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
return ParseResult<U>(
|
||||
return ParseResult<U?>(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
this,
|
||||
|
@ -34,12 +34,12 @@ class _Map<T, U> extends Parser<U> {
|
|||
|
||||
class _Change<T, U> extends Parser<U> {
|
||||
final Parser<T> parser;
|
||||
final ParseResult<U> Function(ParseResult<T>) f;
|
||||
final ParseResult<U?> Function(ParseResult<T?>) f;
|
||||
|
||||
_Change(this.parser, this.f);
|
||||
|
||||
@override
|
||||
ParseResult<U> __parse(ParseArgs args) {
|
||||
ParseResult<U?> __parse(ParseArgs args) {
|
||||
return f(parser._parse(args.increaseDepth())).change(parser: this);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ class _MaxDepth<T> extends Parser<T> {
|
|||
_MaxDepth(this.parser, this.cap);
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<T?> __parse(ParseArgs args) {
|
||||
if (args.depth > cap) {
|
||||
return ParseResult<T>(args.trampoline, args.scanner, this, false, []);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ class _Negate<T> extends Parser<T> {
|
|||
_Negate(this.parser, this.errorMessage, this.severity);
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<T?> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth()).change(parser: this);
|
||||
|
||||
if (!result.successful) {
|
||||
|
|
|
@ -7,7 +7,7 @@ class _Opt<T> extends Parser<T> {
|
|||
_Opt(this.parser, this.backtrack);
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<T?> __parse(ParseArgs args) {
|
||||
var replay = args.scanner.position;
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
|
||||
|
@ -35,9 +35,9 @@ class _ListOpt<T> extends ListParser<T> {
|
|||
_ListOpt(this.parser, this.backtrack);
|
||||
|
||||
@override
|
||||
ParseResult<List<T>> __parse(ParseArgs args) {
|
||||
ParseResult<List<T>?> __parse(ParseArgs args) {
|
||||
var replay = args.scanner.position;
|
||||
ParseResult<List<T>> result = parser._parse(args.increaseDepth());
|
||||
ParseResult<List<T>?> result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (!result.successful) args.scanner.position = replay;
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ class _Reduce<T> extends Parser<T> {
|
|||
_Reduce(this.parser, this.combine);
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<List<T>> result = parser._parse(args.increaseDepth());
|
||||
ParseResult<T?> __parse(ParseArgs args) {
|
||||
ParseResult<List<T>?> result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (!result.successful)
|
||||
return ParseResult<T>(
|
||||
|
|
|
@ -15,14 +15,14 @@ class Reference<T> extends Parser<T> {
|
|||
}
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<T?> __parse(ParseArgs args) {
|
||||
if (_parser == null)
|
||||
throw StateError('There is no parser assigned to this reference.');
|
||||
return _parser!._parse(args);
|
||||
}
|
||||
|
||||
@override
|
||||
ParseResult<T> _parse(ParseArgs args) {
|
||||
ParseResult<T?> _parse(ParseArgs args) {
|
||||
if (_parser == null)
|
||||
throw StateError('There is no parser assigned to this reference.');
|
||||
return _parser!._parse(args);
|
||||
|
|
|
@ -12,12 +12,12 @@ class _Repeat<T> extends ListParser<T> {
|
|||
this.backtrack, this.severity);
|
||||
|
||||
@override
|
||||
ParseResult<List<T>> __parse(ParseArgs args) {
|
||||
ParseResult<List<T>?> __parse(ParseArgs args) {
|
||||
var errors = <SyntaxError>[];
|
||||
var results = <T>[];
|
||||
var spans = <FileSpan>[];
|
||||
int success = 0, replay = args.scanner.position;
|
||||
ParseResult<T>? result;
|
||||
ParseResult<T?> result;
|
||||
|
||||
do {
|
||||
result = parser._parse(args.increaseDepth());
|
||||
|
@ -51,7 +51,7 @@ class _Repeat<T> extends ListParser<T> {
|
|||
} else if (success > count && exact) {
|
||||
if (backtrack) args.scanner.position = replay;
|
||||
|
||||
return ParseResult<List<T>>(args.trampoline, args.scanner, this, false, [
|
||||
return ParseResult<List<T>?>(args.trampoline, args.scanner, this, false, [
|
||||
SyntaxError(
|
||||
severity,
|
||||
tooMany,
|
||||
|
|
|
@ -10,7 +10,7 @@ class _Safe<T> extends Parser<T> {
|
|||
_Safe(this.parser, this.backtrack, this.errorMessage, this.severity);
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<T?> __parse(ParseArgs args) {
|
||||
var replay = args.scanner.position;
|
||||
|
||||
try {
|
||||
|
|
|
@ -2,12 +2,12 @@ part of lex.src.combinator;
|
|||
|
||||
class _Value<T> extends Parser<T> {
|
||||
final Parser<T> parser;
|
||||
final T Function(ParseResult<T>) f;
|
||||
final T Function(ParseResult<T?>) f;
|
||||
|
||||
_Value(this.parser, this.f);
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<T?> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth()).change(parser: this);
|
||||
return result.successful ? result.change(value: f(result)) : result;
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@ abstract class Driver<
|
|||
r.resolveAbsolute(path, method: req.method, strip: false);
|
||||
var pipeline = MiddlewarePipeline<RequestHandler>(resolved);
|
||||
return Tuple4(
|
||||
pipeline.handlers!,
|
||||
pipeline.handlers,
|
||||
resolved.fold<Map<String, dynamic>>(
|
||||
<String, dynamic>{}, (out, r) => out..addAll(r.allParams)),
|
||||
(resolved.isEmpty ? null : resolved.first.parseResult)!,
|
||||
|
@ -164,7 +164,7 @@ abstract class Driver<
|
|||
..registerSingleton<ParseResult?>(tuple.item3);
|
||||
|
||||
if (app.environment.isProduction && app.logger != null) {
|
||||
req.container!.registerSingleton<Stopwatch>(Stopwatch()..start());
|
||||
req.container?.registerSingleton<Stopwatch>(Stopwatch()..start());
|
||||
}
|
||||
|
||||
return runPipeline(it, req, res, app)
|
||||
|
|
|
@ -96,7 +96,7 @@ class HostnameRouter {
|
|||
var resolved = r.resolveAbsolute(req.path, method: req.method);
|
||||
var pipeline = MiddlewarePipeline<RequestHandler>(resolved);
|
||||
// print('Pipeline: $pipeline');
|
||||
for (var handler in pipeline.handlers!) {
|
||||
for (var handler in pipeline.handlers) {
|
||||
// print(handler);
|
||||
// Avoid stack overflow.
|
||||
if (handler == handleRequest) {
|
||||
|
|
|
@ -95,7 +95,7 @@ class Routable extends Router<RequestHandler> {
|
|||
@override
|
||||
Route<RequestHandler> addRoute(
|
||||
String method, String path, RequestHandler handler,
|
||||
{Iterable<RequestHandler>? middleware}) {
|
||||
{Iterable<RequestHandler> middleware = const {}}) {
|
||||
final handlers = <RequestHandler>[];
|
||||
// Merge @Middleware declaration, if any
|
||||
var reflector = _container?.reflector;
|
||||
|
|
|
@ -36,7 +36,7 @@ class Angel extends Routable {
|
|||
final List<Angel> _children = [];
|
||||
final Map<
|
||||
String,
|
||||
Tuple4<List, Map<String, dynamic>, ParseResult<RouteResult>?,
|
||||
Tuple4<List, Map<String, dynamic>, ParseResult<RouteResult>,
|
||||
MiddlewarePipeline>> handlerCache = HashMap();
|
||||
|
||||
Router<RequestHandler>? _flattened;
|
||||
|
@ -150,8 +150,8 @@ class Angel extends Routable {
|
|||
@override
|
||||
Route<RequestHandler> addRoute(
|
||||
String method, String path, RequestHandler handler,
|
||||
{Iterable<RequestHandler>? middleware}) {
|
||||
middleware ??= [];
|
||||
{Iterable<RequestHandler> middleware = const []}) {
|
||||
//middleware ??= [];
|
||||
if (_flattened != null) {
|
||||
logger?.warning(
|
||||
'WARNING: You added a route ($method $path) to the router, after it had been optimized.');
|
||||
|
|
|
@ -11,10 +11,10 @@ final RegExp _straySlashes = RegExp(r'(^/+)|(/+$)');
|
|||
/// A variation of the [Router] support both hash routing and push state.
|
||||
abstract class BrowserRouter<T> extends Router<T> {
|
||||
/// Fires whenever the active route changes. Fires `null` if none is selected (404).
|
||||
Stream<RoutingResult<T>> get onResolve;
|
||||
Stream<RoutingResult<T?>> get onResolve;
|
||||
|
||||
/// Fires whenever the active route changes. Fires `null` if none is selected (404).
|
||||
Stream<Route<T>> get onRoute;
|
||||
Stream<Route<T?>> get onRoute;
|
||||
|
||||
/// Set `hash` to true to use hash routing instead of push state.
|
||||
/// `listen` as `true` will call `listen` after initialization.
|
||||
|
@ -48,17 +48,17 @@ abstract class _BrowserRouterImpl<T> extends Router<T>
|
|||
implements BrowserRouter<T> {
|
||||
bool _listening = false;
|
||||
Route? _current;
|
||||
final StreamController<RoutingResult<T>> _onResolve =
|
||||
StreamController<RoutingResult<T>>();
|
||||
final StreamController<Route<T>> _onRoute = StreamController<Route<T>>();
|
||||
final StreamController<RoutingResult<T?>> _onResolve =
|
||||
StreamController<RoutingResult<T?>>();
|
||||
final StreamController<Route<T?>> _onRoute = StreamController<Route<T?>>();
|
||||
|
||||
Route? get currentRoute => _current;
|
||||
|
||||
@override
|
||||
Stream<RoutingResult<T>> get onResolve => _onResolve.stream;
|
||||
Stream<RoutingResult<T?>> get onResolve => _onResolve.stream;
|
||||
|
||||
@override
|
||||
Stream<Route<T>> get onRoute => _onRoute.stream;
|
||||
Stream<Route<T?>> get onRoute => _onRoute.stream;
|
||||
|
||||
_BrowserRouterImpl({bool listen = false}) : super() {
|
||||
if (listen != false) this.listen();
|
||||
|
@ -69,8 +69,7 @@ abstract class _BrowserRouterImpl<T> extends Router<T>
|
|||
void go(Iterable linkParams) => _goTo(navigate(linkParams));
|
||||
|
||||
@override
|
||||
Route on(String path, T handler,
|
||||
{Iterable<T> middleware = const Iterable.empty()}) =>
|
||||
Route on(String path, T handler, {Iterable<T> middleware = const []}) =>
|
||||
all(path, handler, middleware: middleware);
|
||||
|
||||
void prepareAnchors() {
|
||||
|
@ -108,7 +107,9 @@ abstract class _BrowserRouterImpl<T> extends Router<T>
|
|||
|
||||
class _HashRouter<T> extends _BrowserRouterImpl<T> {
|
||||
_HashRouter({required bool listen}) : super(listen: listen) {
|
||||
if (listen) this.listen();
|
||||
if (listen) {
|
||||
this.listen();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -6,13 +6,13 @@ class RouteGrammar {
|
|||
static final Parser<String> notSlash =
|
||||
match<String>(RegExp(notSlashRgx)).value((r) => r.span?.text ?? '');
|
||||
|
||||
static final Parser<Match?> regExp =
|
||||
match<Match?>(RegExp(r'\(([^\n)]+)\)([^/]+)?'))
|
||||
.value((r) => r.scanner.lastMatch);
|
||||
static final Parser<Match> regExp =
|
||||
match<Match>(RegExp(r'\(([^\n)]+)\)([^/]+)?'))
|
||||
.value((r) => r.scanner.lastMatch!);
|
||||
|
||||
static final Parser<Match?> parameterName =
|
||||
match<Match?>(RegExp('$notSlashRgx?' r':([A-Za-z0-9_]+)' r'([^(/\n])?'))
|
||||
.value((r) => r.scanner.lastMatch);
|
||||
static final Parser<Match> parameterName =
|
||||
match<Match>(RegExp('$notSlashRgx?' r':([A-Za-z0-9_]+)' r'([^(/\n])?'))
|
||||
.value((r) => r.scanner.lastMatch!);
|
||||
|
||||
static final Parser<ParameterSegment> parameterSegment = chain([
|
||||
parameterName,
|
||||
|
|
|
@ -4,10 +4,10 @@ import 'router.dart';
|
|||
class MiddlewarePipeline<T> {
|
||||
/// All the possible routes that matched the given path.
|
||||
final Iterable<RoutingResult<T>> routingResults;
|
||||
final List<T> _handlers = [];
|
||||
final List<T?> _handlers = [];
|
||||
|
||||
/// An ordered list of every handler delegated to handle this request.
|
||||
List<T> get handlers {
|
||||
List<T?> get handlers {
|
||||
/*
|
||||
if (_handlers != null) return _handlers;
|
||||
final handlers = <T>[];
|
||||
|
@ -19,7 +19,7 @@ class MiddlewarePipeline<T> {
|
|||
return _handlers = handlers;
|
||||
|
||||
*/
|
||||
if (_handlers.isEmpty) {
|
||||
if (_handlers.isNotEmpty) {
|
||||
return _handlers;
|
||||
}
|
||||
|
||||
|
|
|
@ -284,7 +284,7 @@ class Router<T> {
|
|||
|
||||
/// Finds the first [Route] that matches the given path,
|
||||
/// with the given method.
|
||||
bool resolve(String absolute, String relative, List<RoutingResult<T>> out,
|
||||
bool resolve(String absolute, String relative, List<RoutingResult<T?>> out,
|
||||
{String method = 'GET', bool strip = true}) {
|
||||
final cleanRelative =
|
||||
strip == false ? relative : stripStraySlashes(relative);
|
||||
|
@ -334,13 +334,13 @@ class Router<T> {
|
|||
|
||||
/// Returns the result of [resolve] with [path] passed as
|
||||
/// both `absolute` and `relative`.
|
||||
Iterable<RoutingResult<T>> resolveAbsolute(String path,
|
||||
Iterable<RoutingResult<T?>> resolveAbsolute(String path,
|
||||
{String method = 'GET', bool strip = true}) =>
|
||||
resolveAll(path, path, method: method, strip: strip);
|
||||
|
||||
/// Finds every possible [Route] that matches the given path,
|
||||
/// with the given method.
|
||||
Iterable<RoutingResult<T>> resolveAll(String absolute, String relative,
|
||||
Iterable<RoutingResult<T?>> resolveAll(String absolute, String relative,
|
||||
{String method = 'GET', bool strip = true}) {
|
||||
if (_useCache == true) {
|
||||
return _cache.putIfAbsent('$method$absolute',
|
||||
|
|
|
@ -3,7 +3,7 @@ part of angel_route.src.router;
|
|||
/// Represents a complex result of navigating to a path.
|
||||
class RoutingResult<T> {
|
||||
/// The parse result that matched the given sub-path.
|
||||
final ParseResult<RouteResult> parseResult;
|
||||
final ParseResult<RouteResult?> parseResult;
|
||||
|
||||
/// A nested instance, if a sub-path was matched.
|
||||
final Iterable<RoutingResult<T>> nested;
|
||||
|
@ -47,10 +47,10 @@ class RoutingResult<T> {
|
|||
}
|
||||
|
||||
/// All handlers on this sub-path and its children.
|
||||
List<T> get allHandlers {
|
||||
final handlers = <T>[];
|
||||
List<T?> get allHandlers {
|
||||
final handlers = <T?>[];
|
||||
|
||||
void crawl(RoutingResult<T> result) {
|
||||
void crawl(RoutingResult<T?> result) {
|
||||
handlers.addAll(result.handlers);
|
||||
|
||||
if (result.nested.isNotEmpty == true) {
|
||||
|
|
|
@ -7,6 +7,7 @@ environment:
|
|||
sdk: '>=2.12.0 <3.0.0'
|
||||
dependencies:
|
||||
combinator:
|
||||
# path: ../combinator
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x_nnbd
|
||||
|
|
Loading…
Reference in a new issue