Merge pull request #9 from dukefirehawk/angel3

Changed AuthToken to have required userId field
This commit is contained in:
Thomas Hii 2021-09-29 15:51:54 +08:00 committed by GitHub
commit 37ec2f06aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 257 additions and 247 deletions

View file

@ -1,5 +1,11 @@
# Change Log # Change Log
## 4.1.1
* Changed `userId` field of `AuthToken` to String type
* Changed `serializer` return value to String type
* Changed `deserializer` input parameter to String type
## 4.1.0 ## 4.1.0
* Updated linter to `package:lints` * Updated linter to `package:lints`

View file

@ -18,9 +18,10 @@ Ensure you have read the [User Guide](https://angel3-docs.dukefirehawk.com/guide
```dart ```dart
configureServer(Angel app) async { configureServer(Angel app) async {
var auth = AngelAuth<User>(); var auth = AngelAuth<User>(
auth.serializer = ...; serializer: (user) => user.id ?? '',
auth.deserializer = ...; deserializer: (id) => fetchAUserByIdSomehow(id)
);
auth.strategies['local'] = LocalAuthStrategy(...); auth.strategies['local'] = LocalAuthStrategy(...);
// POST route to handle username+password // POST route to handle username+password

View file

@ -6,7 +6,7 @@ import 'package:angel3_framework/http.dart';
void main() async { void main() async {
var app = Angel(); var app = Angel();
var auth = AngelAuth<User>( var auth = AngelAuth<User>(
serializer: (user) => user.id, serializer: (user) => user.id ?? '',
deserializer: (id) => fetchAUserByIdSomehow(id)); deserializer: (id) => fetchAUserByIdSomehow(id));
// Middleware to decode JWT's and inject a user object... // Middleware to decode JWT's and inject a user object...
@ -31,7 +31,7 @@ class User {
String? id, username, password; String? id, username, password;
} }
Future<User> fetchAUserByIdSomehow(id) async { Future<User> fetchAUserByIdSomehow(String id) async {
// Fetch a user somehow... // Fetch a user somehow...
throw UnimplementedError(); throw UnimplementedError();
} }

View file

@ -32,41 +32,35 @@ class AuthToken {
SplayTreeMap.from({'alg': 'HS256', 'typ': 'JWT'}); SplayTreeMap.from({'alg': 'HS256', 'typ': 'JWT'});
String? ipAddress; String? ipAddress;
late DateTime issuedAt;
num lifeSpan; num lifeSpan;
var userId; String userId;
late DateTime issuedAt;
Map<String, dynamic> payload = {}; Map<String, dynamic> payload = {};
AuthToken( AuthToken(
{this.ipAddress, {this.ipAddress,
this.lifeSpan = -1, this.lifeSpan = -1,
this.userId, required this.userId,
DateTime? issuedAt, DateTime? issuedAt,
Map payload = const {}}) { Map<String, dynamic>? payload}) {
this.issuedAt = issuedAt ?? DateTime.now(); this.issuedAt = issuedAt ?? DateTime.now();
this.payload.addAll(payload.keys if (payload != null) {
.fold({}, ((out, k) => out?..[k.toString()] = payload[k])) ?? 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) => factory AuthToken.fromJson(String jsons) =>
AuthToken.fromMap(json.decode(jsons) as Map); AuthToken.fromMap(json.decode(jsons) as Map<String, dynamic>);
factory AuthToken.fromMap(Map data) { factory AuthToken.fromMap(Map<String, dynamic> data) {
return AuthToken( return AuthToken(
ipAddress: data['aud'].toString(), ipAddress: data['aud'].toString(),
lifeSpan: data['exp'] as num, lifeSpan: data['exp'] as num,
issuedAt: DateTime.parse(data['iat'].toString()), issuedAt: DateTime.parse(data['iat'].toString()),
userId: data['sub'], userId: data['sub'],
payload: data['pld'] as Map); payload: data['pld']);
} }
factory AuthToken.parse(String jwt) { factory AuthToken.parse(String jwt) {
@ -78,7 +72,8 @@ class AuthToken {
} }
var payloadString = decodeBase64(split[1]); var payloadString = decodeBase64(split[1]);
return AuthToken.fromMap(json.decode(payloadString) as Map); return AuthToken.fromMap(
json.decode(payloadString) as Map<String, dynamic>);
} }
factory AuthToken.validate(String jwt, Hmac hmac) { factory AuthToken.validate(String jwt, Hmac hmac) {
@ -100,7 +95,8 @@ class AuthToken {
message: 'JWT payload does not match hashed version.'); message: 'JWT payload does not match hashed version.');
} }
return AuthToken.fromMap(json.decode(payloadString) as Map); return AuthToken.fromMap(
json.decode(payloadString) as Map<String, dynamic>);
} }
String serialize(Hmac hmac) { String serialize(Hmac hmac) {
@ -111,7 +107,7 @@ class AuthToken {
return data + '.' + base64Url.encode(signature); return data + '.' + base64Url.encode(signature);
} }
Map toJson() { Map<String, dynamic> toJson() {
return _splayify({ return _splayify({
'iss': 'angel_auth', 'iss': 'angel_auth',
'aud': ipAddress, 'aud': ipAddress,
@ -123,7 +119,7 @@ class AuthToken {
} }
} }
SplayTreeMap _splayify(Map map) { Map<String, dynamic> _splayify(Map<String, dynamic> map) {
var data = {}; var data = {};
map.forEach((k, v) { map.forEach((k, v) {
data[k] = _splay(v); data[k] = _splay(v);
@ -131,11 +127,11 @@ SplayTreeMap _splayify(Map map) {
return SplayTreeMap.from(data); return SplayTreeMap.from(data);
} }
dynamic _splay(value) { dynamic _splay(dynamic value) {
if (value is Iterable) { if (value is Iterable) {
return value.map(_splay).toList(); return value.map(_splay).toList();
} else if (value is Map) { } else if (value is Map) {
return _splayify(value); return _splayify(value as Map<String, dynamic>);
} else { } else {
return value; return value;
} }

View file

@ -46,7 +46,7 @@ class ExternalAuthOptions {
/// * `client_id` /// * `client_id`
/// * `client_secret` /// * `client_secret`
/// * `redirect_uri` /// * `redirect_uri`
factory ExternalAuthOptions.fromMap(Map map) { factory ExternalAuthOptions.fromMap(Map<String, dynamic> map) {
var clientId = map['client_id']; var clientId = map['client_id'];
var clientSecret = map['client_secret']; var clientSecret = map['client_secret'];
if (clientId == null || clientSecret == null) { if (clientId == null || clientSecret == null) {
@ -55,8 +55,8 @@ class ExternalAuthOptions {
} }
return ExternalAuthOptions( return ExternalAuthOptions(
clientId: clientId as String, clientId: clientId,
clientSecret: clientSecret as String, clientSecret: clientSecret,
redirectUri: map['redirect_uri'], redirectUri: map['redirect_uri'],
scopes: map['scopes'] is Iterable scopes: map['scopes'] is Iterable
? ((map['scopes'] as Iterable).map((x) => x.toString())) ? ((map['scopes'] as Iterable).map((x) => x.toString()))

View file

@ -52,10 +52,10 @@ class AngelAuth<User> {
Map<String, AuthStrategy<User>> strategies = {}; Map<String, AuthStrategy<User>> strategies = {};
/// Serializes a user into a unique identifier associated only with one identity. /// Serializes a user into a unique identifier associated only with one identity.
FutureOr Function(User) serializer; FutureOr<String> Function(User) serializer;
/// Deserializes a unique identifier into its associated identity. In most cases, this is a user object or model instance. /// 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(String) deserializer;
/// Fires the result of [deserializer] whenever a user signs in to the application. /// 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;
@ -200,6 +200,7 @@ class AngelAuth<User> {
/// String getUsername(User user) => user.name /// String getUsername(User user) => user.name
/// } /// }
/// ``` /// ```
/*
@deprecated @deprecated
Future decodeJwt(RequestContext req, ResponseContext res) async { Future decodeJwt(RequestContext req, ResponseContext res) async {
if (req.method == 'POST' && req.path == reviveTokenEndpoint) { if (req.method == 'POST' && req.path == reviveTokenEndpoint) {
@ -209,6 +210,7 @@ class AngelAuth<User> {
return true; return true;
} }
} }
*/
Future<_AuthResult<User>?> _decodeJwt( Future<_AuthResult<User>?> _decodeJwt(
RequestContext req, ResponseContext res) async { RequestContext req, ResponseContext res) async {
@ -235,7 +237,7 @@ class AngelAuth<User> {
} }
} }
var user = await deserializer(token.userId as Object); var user = await deserializer(token.userId);
_apply(req, res, token, user); _apply(req, res, token, user);
return _AuthResult(user, token); return _AuthResult(user, token);
} }
@ -332,7 +334,7 @@ class AngelAuth<User> {
_addProtectedCookie(res, 'token', token.serialize(_hs256)); _addProtectedCookie(res, 'token', token.serialize(_hs256));
} }
final data = await deserializer(token.userId as Object); final data = await deserializer(token.userId);
return {'data': data, 'token': token.serialize(_hs256)}; return {'data': data, 'token': token.serialize(_hs256)};
} }
} catch (e) { } catch (e) {
@ -431,7 +433,7 @@ class AngelAuth<User> {
req.accepts('application/json')) { req.accepts('application/json')) {
var user = hasExisting var user = hasExisting
? result ? result
: await deserializer((await serializer(result)) as Object); : await deserializer(await serializer(result));
_onLogin.add(user); _onLogin.add(user);
return {'data': user, 'token': jwt}; return {'data': user, 'token': jwt};
} }
@ -458,7 +460,7 @@ class AngelAuth<User> {
/// Log a user in on-demand. /// Log a user in on-demand.
Future login(AuthToken token, RequestContext req, ResponseContext res) async { Future login(AuthToken token, RequestContext req, ResponseContext res) async {
var user = await deserializer(token.userId as Object); var user = await deserializer(token.userId);
_apply(req, res, token, user); _apply(req, res, token, user);
_onLogin.add(user); _onLogin.add(user);
@ -468,8 +470,9 @@ class AngelAuth<User> {
} }
/// Log a user in on-demand. /// Log a user in on-demand.
Future loginById(userId, RequestContext req, ResponseContext res) async { Future loginById(
var user = await deserializer(userId as Object); String userId, RequestContext req, ResponseContext res) async {
var user = await deserializer(userId);
var token = var token =
AuthToken(userId: userId, lifeSpan: _jwtLifeSpan, ipAddress: req.ip); AuthToken(userId: userId, lifeSpan: _jwtLifeSpan, ipAddress: req.ip);
_apply(req, res, token, user); _apply(req, res, token, user);

View file

@ -38,8 +38,8 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
@override @override
Future<User?> authenticate(RequestContext req, ResponseContext res, Future<User?> authenticate(RequestContext req, ResponseContext res,
[AngelAuthOptions? options_]) async { [AngelAuthOptions? options]) async {
var options = options_ ?? AngelAuthOptions(); var _options = options ?? AngelAuthOptions();
User? verificationResult; User? verificationResult;
if (allowBasic) { if (allowBasic) {
@ -88,9 +88,9 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
if (verificationResult == null || if (verificationResult == null ||
(verificationResult is Map && verificationResult.isEmpty)) { (verificationResult is Map && verificationResult.isEmpty)) {
if (options.failureRedirect != null && if (_options.failureRedirect != null &&
options.failureRedirect!.isNotEmpty) { _options.failureRedirect!.isNotEmpty) {
await res.redirect(options.failureRedirect, code: 401); await res.redirect(_options.failureRedirect, code: 401);
return null; return null;
} }

View file

@ -1,6 +1,6 @@
name: angel3_auth name: angel3_auth
description: A complete authentication plugin for Angel3. Includes support for stateless JWT tokens, Basic Auth, and more. description: A complete authentication plugin for Angel3. Includes support for stateless JWT tokens, Basic Auth, and more.
version: 4.1.0 version: 4.1.1
homepage: https://angel3-framework.web.app/ homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/auth repository: https://github.com/dukefirehawk/angel/tree/master/packages/auth
environment: environment:
@ -21,5 +21,5 @@ dev_dependencies:
test: ^1.17.4 test: ^1.17.4
lints: ^1.0.0 lints: ^1.0.0
#dependency_overrides: #dependency_overrides:
# angel3_container: # angel3_framework:
# path: ../container/angel_container # path: ../framework

View file

@ -73,7 +73,7 @@ void main() {
?.create({'username': 'jdoe1', 'password': 'password'}); ?.create({'username': 'jdoe1', 'password': 'password'});
auth = AngelAuth<User>( auth = AngelAuth<User>(
serializer: (u) => u.id, serializer: (u) => u.id ?? '',
deserializer: (id) async => deserializer: (id) async =>
await app.findService('users')?.read(id) as User); await app.findService('users')?.read(id) as User);
//auth.serializer = (u) => u.id; //auth.serializer = (u) => u.id;

View file

@ -8,7 +8,7 @@ import 'package:logging/logging.dart';
import 'package:test/test.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>>(
serializer: (user) async => 1337, deserializer: (id) async => sampleUser); serializer: (user) async => '1337', deserializer: (id) async => sampleUser);
var headers = <String, String>{'accept': 'application/json'}; var headers = <String, String>{'accept': 'application/json'};
var localOpts = AngelAuthOptions<Map<String, String>>( var localOpts = AngelAuthOptions<Map<String, String>>(
failureRedirect: '/failure', successRedirect: '/success'); failureRedirect: '/failure', successRedirect: '/success');

View file

@ -11,7 +11,7 @@ void main() {
secureCookies: true, secureCookies: true,
cookieDomain: 'SECURE', cookieDomain: 'SECURE',
jwtLifeSpan: threeDays.inMilliseconds, jwtLifeSpan: threeDays.inMilliseconds,
serializer: (u) => u, serializer: (u) => u as String,
deserializer: (u) => u); deserializer: (u) => u);
setUp(() => defaultCookie = Cookie('a', 'b')); setUp(() => defaultCookie = Cookie('a', 'b'));

View file

@ -1,5 +1,9 @@
# Change Log # Change Log
## 4.1.0
* Updated linter to `package:lints`
## 4.0.2 ## 4.0.2
* Updated example * Updated example

View file

@ -1,21 +1,29 @@
MIT License (MIT) BSD 3-Clause License
Copyright (c) 2021 dukefirehawk.com Copyright (c) 2021, dukefirehawk.com
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy Redistribution and use in source and binary forms, with or without
of this software and associated documentation files (the "Software"), to deal modification, are permitted provided that the following conditions are met:
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all 1. Redistributions of source code must retain the above copyright notice, this
copies or substantial portions of the Software. list of conditions and the following disclaimer.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2. Redistributions in binary form must reproduce the above copyright notice,
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, this list of conditions and the following disclaimer in the documentation
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE and/or other materials provided with the distribution.
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 3. Neither the name of the copyright holder nor the names of its
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE contributors may be used to endorse or promote products derived from
SOFTWARE. this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,10 +1,9 @@
# Angel3 OAuth2 Handler # Angel3 OAuth2 Handler
[![version](https://img.shields.io/badge/pub-v4.0.2-brightgreen)](https://pub.dartlang.org/packages/angel3_auth_oauth2) ![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_auth_oauth2?include_prereleases)
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety) ![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](<https://dart.dev/null-safety>)
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion) [![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/auth_oauth2/LICENSE)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/auth_oauth2/LICENSE)
Angel3 library for authenticating users with remote identity providers via OAuth2, i.e. Facebook, Google, Azure AD, etc. Angel3 library for authenticating users with remote identity providers via OAuth2, i.e. Facebook, Google, Azure AD, etc.

View file

@ -1,4 +1 @@
include: package:pedantic/analysis_options.yaml include: package:lints/recommended.yaml
analyzer:
strong-mode:
implicit-casts: false

View file

@ -48,8 +48,8 @@ void main() async {
// Set up the authenticator plugin. // Set up the authenticator plugin.
var auth = AngelAuth<User>( var auth = AngelAuth<User>(
serializer: (user) async => user.id, serializer: (user) async => user.id ?? '',
deserializer: (id) => mappedUserService.read(id.toString()), deserializer: (id) => mappedUserService.read(id),
jwtKey: 'oauth2 example secret', jwtKey: 'oauth2 example secret',
allowCookie: false); allowCookie: false);
await app.configure(auth.configureServer); await app.configure(auth.configureServer);
@ -120,7 +120,7 @@ class User extends Model {
User({this.id, this.githubId}); User({this.id, this.githubId});
static User parse(Map map) => static User parse(Map<String, dynamic> map) =>
User(id: map['id'] as String?, githubId: map['github_id'] as int?); User(id: map['id'] as String?, githubId: map['github_id'] as int?);
static Map<String, dynamic> serialize(User user) => user.toJson(); static Map<String, dynamic> serialize(User user) => user.toJson();

View file

@ -1,15 +1,19 @@
name: angel3_auth_oauth2 name: angel3_auth_oauth2
version: 4.0.2 version: 4.1.0
description: Angel3 library for authenticating users with external identity providers via OAuth2. description: Angel3 library for authenticating users with external identity providers via OAuth2.
homepage: https://angel3-framework.web.app/ homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/auth_oauth2 repository: https://github.com/dukefirehawk/angel/tree/master/packages/auth_oauth2
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
angel3_auth: ^4.0.0 angel3_auth: ^4.1.0
angel3_framework: ^4.0.0 angel3_framework: ^4.2.0
http_parser: ^4.0.0 http_parser: ^4.0.0
oauth2: ^2.0.0 oauth2: ^2.0.0
dev_dependencies: dev_dependencies:
logging: ^1.0.1 logging: ^1.0.1
pedantic: ^1.11.0 lints: ^1.0.0
#dependency_overrides:
# angel3_auth:
# path: ../auth

View file

@ -5,7 +5,7 @@ import 'dart:async';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'dart:convert'; import 'dart:convert';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:logging/logging.dart'; //import 'package:logging/logging.dart';
export 'package:angel3_http_exception/angel3_http_exception.dart'; export 'package:angel3_http_exception/angel3_http_exception.dart';
/// A function that configures an [Angel] client in some way. /// A function that configures an [Angel] client in some way.

View file

@ -15,17 +15,16 @@ dependencies:
logging: ^1.0.0 logging: ^1.0.0
dev_dependencies: dev_dependencies:
angel3_framework: ^4.2.0 angel3_framework: ^4.2.0
angel3_model: ^3.0.0 angel3_model: ^3.1.0
angel3_mock_request: ^2.0.0 angel3_mock_request: ^2.0.0
angel3_container: ^3.0.0 angel3_container: ^3.1.0
angel3_auth: ^4.0.0 angel3_auth: ^4.1.0
async: ^2.6.1 async: ^2.6.1
build_runner: ^1.12.2 build_runner: ^1.12.2
build_web_compilers: ^2.16.5 build_web_compilers: ^2.16.5
test: ^1.17.5 test: ^1.17.5
lints: ^1.0.0 lints: ^1.0.0
#dependency_overrides: #dependency_overrides:
# angel3_framework: # angel3_auth:
# path: ../framework # path: ../auth
# angel3_route:
# path: ../route

View file

@ -1,5 +1,9 @@
# Change Log # Change Log
## 3.1.1
* Updated `_ReflectedMethodMirror` to have optional `returnType` parameter
## 3.1.0 ## 3.1.0
* Updated linter to `package:lints` * Updated linter to `package:lints`

View file

@ -5,7 +5,7 @@
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion) [![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/container/angel_container/LICENSE) [![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/container/angel_container/LICENSE)
A better IoC container for Angel3, ultimately allowing Angel3 to be used without `dart:mirrors` package. An better IoC container for Angel3, ultimately allowing Angel3 to be used without `dart:mirrors` package.
```dart ```dart
import 'package:angel3_container/angel3_container.dart'; import 'package:angel3_container/angel3_container.dart';

View file

@ -15,7 +15,7 @@ class EmptyReflector extends Reflector {
@override @override
String? getName(Symbol symbol) { String? getName(Symbol symbol) {
return _symbolNames.putIfAbsent( return _symbolNames.putIfAbsent(
symbol, () => symbolRegex.firstMatch(symbol.toString())!.group(1)); symbol, () => symbolRegex.firstMatch(symbol.toString())?.group(1));
} }
@override @override
@ -75,7 +75,8 @@ class _EmptyReflectedType extends ReflectedType {
@override @override
ReflectedInstance newInstance( ReflectedInstance newInstance(
String constructorName, List positionalArguments, String constructorName, List positionalArguments,
[Map<String, dynamic>? namedArguments, List<Type>? typeArguments]) { [Map<String, dynamic> namedArguments = const {},
List<Type> typeArguments = const []]) {
throw UnsupportedError( throw UnsupportedError(
'Types reflected via an EmptyReflector cannot be instantiated.'); 'Types reflected via an EmptyReflector cannot be instantiated.');
} }
@ -113,10 +114,10 @@ class _EmptyReflectedFunction extends ReflectedFunction {
'(empty)', '(empty)',
const <ReflectedTypeParameter>[], const <ReflectedTypeParameter>[],
const <ReflectedInstance>[], const <ReflectedInstance>[],
const _EmptyReflectedType(),
const <ReflectedParameter>[], const <ReflectedParameter>[],
false, false,
false); false,
returnType: const _EmptyReflectedType());
@override @override
ReflectedInstance invoke(Invocation invocation) { ReflectedInstance invoke(Invocation invocation) {

View file

@ -179,11 +179,9 @@ class _ReflectedClassMirror extends ReflectedClass {
} }
class _ReflectedDeclarationMirror extends ReflectedDeclaration { class _ReflectedDeclarationMirror extends ReflectedDeclaration {
@override
final String name;
final dart.MethodMirror mirror; final dart.MethodMirror mirror;
_ReflectedDeclarationMirror(this.name, this.mirror) _ReflectedDeclarationMirror(String name, this.mirror)
: super(name, mirror.isStatic, null); : super(name, mirror.isStatic, null);
@override @override
@ -217,13 +215,13 @@ class _ReflectedMethodMirror extends ReflectedFunction {
mirror.metadata mirror.metadata
.map((mirror) => _ReflectedInstanceMirror(mirror)) .map((mirror) => _ReflectedInstanceMirror(mirror))
.toList(), .toList(),
!mirror.returnType.hasReflectedType
? const MirrorsReflector().reflectType(dynamic)
: const MirrorsReflector()
.reflectType(mirror.returnType.reflectedType),
mirror.parameters.map(_reflectParameter).toList(), mirror.parameters.map(_reflectParameter).toList(),
mirror.isGetter, mirror.isGetter,
mirror.isSetter); mirror.isSetter,
returnType: !mirror.returnType.hasReflectedType
? const MirrorsReflector().reflectType(dynamic)
: const MirrorsReflector()
.reflectType(mirror.returnType.reflectedType));
static ReflectedParameter _reflectParameter(dart.ParameterMirror mirror) { static ReflectedParameter _reflectParameter(dart.ParameterMirror mirror) {
return ReflectedParameter( return ReflectedParameter(

View file

@ -56,7 +56,8 @@ abstract class ReflectedType {
ReflectedInstance newInstance( ReflectedInstance newInstance(
String constructorName, List positionalArguments, String constructorName, List positionalArguments,
[Map<String, dynamic>? namedArguments, List<Type>? typeArguments]); [Map<String, dynamic> namedArguments = const {},
List<Type> typeArguments = const []]);
bool isAssignableTo(ReflectedType? other); bool isAssignableTo(ReflectedType? other);
} }
@ -113,12 +114,13 @@ abstract class ReflectedFunction {
final String name; final String name;
final List<ReflectedTypeParameter> typeParameters; final List<ReflectedTypeParameter> typeParameters;
final List<ReflectedInstance> annotations; final List<ReflectedInstance> annotations;
final ReflectedType returnType; final ReflectedType? returnType;
final List<ReflectedParameter> parameters; final List<ReflectedParameter> parameters;
final bool isGetter, isSetter; final bool isGetter, isSetter;
const ReflectedFunction(this.name, this.typeParameters, this.annotations, const ReflectedFunction(this.name, this.typeParameters, this.annotations,
this.returnType, this.parameters, this.isGetter, this.isSetter); this.parameters, this.isGetter, this.isSetter,
{this.returnType});
@override @override
int get hashCode => hashObjects([ int get hashCode => hashObjects([

View file

@ -1,5 +1,5 @@
name: angel3_container name: angel3_container
version: 3.1.0 version: 3.1.1
description: A hierarchical DI container, and pluggable backends for reflection. description: A hierarchical DI container, and pluggable backends for reflection.
homepage: https://angel3-framework.web.app/ homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/container/angel_container repository: https://github.com/dukefirehawk/angel/tree/master/packages/container/angel_container

View file

@ -1,5 +1,10 @@
# Change Log # Change Log
## 3.0.0
* Fixed NNBD issues
* All 9 test cases passed
## 3.0.0-beta.1 ## 3.0.0-beta.1
* Migrated to support Dart SDK 2.12.x NNBD * Migrated to support Dart SDK 2.12.x NNBD

View file

@ -5,4 +5,4 @@
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion) [![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/container/angel3_container_generator/LICENSE) [![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/container/angel3_container_generator/LICENSE)
A better IoC container for Angel3, ultimately allowing Angel3 to be used without `dart:mirrors`. A better IoC container generator for Angel3, ultimately allowing Angel3 to be used without `dart:mirrors`.

View file

@ -15,7 +15,7 @@ class ContainedReflectable extends Reflectable {
newInstanceCapability, newInstanceCapability,
reflectedTypeCapability, reflectedTypeCapability,
typeRelationsCapability, typeRelationsCapability,
typeCapability, typingCapability,
); );
} }
@ -163,7 +163,8 @@ class _GeneratedReflectedType extends ReflectedType {
@override @override
ReflectedInstance newInstance( ReflectedInstance newInstance(
String constructorName, List positionalArguments, String constructorName, List positionalArguments,
[Map<String, dynamic>? namedArguments, List<Type>? typeArguments]) { [Map<String, dynamic> namedArguments = const {},
List<Type> typeArguments = const []]) {
throw UnsupportedError('Cannot create a new instance of $reflectedType.'); throw UnsupportedError('Cannot create a new instance of $reflectedType.');
} }
} }
@ -178,17 +179,14 @@ class _GeneratedReflectedFunction extends ReflectedFunction {
mirror.simpleName, mirror.simpleName,
[], [],
[], [],
/*
!mirror.isRegularMethod
? null
: _GeneratedReflectedType(mirror.returnType),
*/
_GeneratedReflectedType(mirror.returnType),
mirror.parameters mirror.parameters
.map((p) => _convertParameter(p, reflector)) .map((p) => _convertParameter(p, reflector))
.toList(), .toList(),
mirror.isGetter, mirror.isGetter,
mirror.isSetter); mirror.isSetter,
returnType: !mirror.isRegularMethod
? null
: _GeneratedReflectedType(mirror.returnType));
@override @override
List<ReflectedInstance> get annotations => mirror.metadata List<ReflectedInstance> get annotations => mirror.metadata
@ -247,8 +245,10 @@ ReflectedParameter _convertParameter(
ParameterMirror mirror, Reflector reflector) { ParameterMirror mirror, Reflector reflector) {
return ReflectedParameter( return ReflectedParameter(
mirror.simpleName, mirror.simpleName,
mirror.metadata.map(reflector.reflectInstance).toList() mirror.metadata
as List<ReflectedInstance>, .map(reflector.reflectInstance)
.whereType<ReflectedInstance>()
.toList(),
reflector.reflectType(mirror.type.reflectedType)!, reflector.reflectType(mirror.type.reflectedType)!,
!mirror.isOptional, !mirror.isOptional,
mirror.isNamed); mirror.isNamed);

View file

@ -1,5 +1,5 @@
name: angel3_container_generator name: angel3_container_generator
version: 3.0.0-beta.1 version: 3.0.0
description: Codegen support for using pkg:reflectable with pkg:angel3_container. description: Codegen support for using pkg:reflectable with pkg:angel3_container.
homepage: https://angel3-framework.web.app/ homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/container/angel_container_generator repository: https://github.com/dukefirehawk/angel/tree/master/packages/container/angel_container_generator
@ -13,6 +13,6 @@ dev_dependencies:
build_test: ^2.1.3 build_test: ^2.1.3
test: ^1.17.5 test: ^1.17.5
lints: ^1.0.1 lints: ^1.0.1
#dependency_overrides: dependency_overrides:
# angel3_container: angel3_container:
# path: ../angel_container path: ../angel_container

View file

@ -57,24 +57,24 @@ void testReflector(Reflector reflector) {
var mirror = reflector.reflectFunction(returnVoidFromAFunction); var mirror = reflector.reflectFunction(returnVoidFromAFunction);
test('void return type returns dynamic', () { test('void return type returns dynamic', () {
expect(mirror.returnType, reflector.reflectType(dynamic)); expect(mirror?.returnType, reflector.reflectType(dynamic));
}); });
test('counts parameters', () { test('counts parameters', () {
expect(mirror.parameters, hasLength(1)); expect(mirror?.parameters, hasLength(1));
}); });
test('counts types parameters', () { test('counts types parameters', () {
expect(mirror.typeParameters, isEmpty); expect(mirror?.typeParameters, isEmpty);
}); });
test('correctly reflects parameter types', () { test('correctly reflects parameter types', () {
var p = mirror.parameters[0]; var p = mirror?.parameters[0];
expect(p.name, 'x'); expect(p?.name, 'x');
expect(p.isRequired, true); expect(p?.isRequired, true);
expect(p.isNamed, false); expect(p?.isNamed, false);
expect(p.annotations, isEmpty); expect(p?.annotations, isEmpty);
expect(p.type, reflector.reflectType(int)); expect(p?.type, reflector.reflectType(int));
}); });
}, skip: 'pkg:reflectable cannot reflect on closures at all (yet)'); }, skip: 'pkg:reflectable cannot reflect on closures at all (yet)');
*/ */

View file

@ -35,6 +35,6 @@ dev_dependencies:
io: ^1.0.0 io: ^1.0.0
test: ^1.17.5 test: ^1.17.5
lints: ^1.0.0 lints: ^1.0.0
dependency_overrides: #dependency_overrides:
angel3_route: # angel3_container:
path: ../route # path: ../container/angel_container

View file

@ -6,8 +6,8 @@ repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/hot
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
angel3_framework: ^4.1.0 angel3_framework: ^4.2.0
angel3_websocket: ^4.0.0 angel3_websocket: ^4.1.0
belatuk_html_builder: ^3.0.0 belatuk_html_builder: ^3.0.0
charcode: ^1.2.0 charcode: ^1.2.0
glob: ^2.0.1 glob: ^2.0.1
@ -19,3 +19,7 @@ dev_dependencies:
http: ^0.13.2 http: ^0.13.2
logging: ^1.0.1 logging: ^1.0.1
lints: ^1.0.0 lints: ^1.0.0
#dependency_overrides:
# angel3_websocket:
# path: ../websocket

View file

@ -6,7 +6,7 @@ repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/productio
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
angel3_container: ^3.0.0 angel3_container: ^3.1.0
angel3_framework: ^4.1.0 angel3_framework: ^4.1.0
belatuk_pub_sub: ^4.0.0 belatuk_pub_sub: ^4.0.0
args: ^2.1.0 args: ^2.1.0
@ -14,3 +14,6 @@ dependencies:
logging: ^1.0.1 logging: ^1.0.1
dev_dependencies: dev_dependencies:
lints: ^1.0.1 lints: ^1.0.1
#dependency_overrides:
# angel3_container:
# path: ../container/angel_container

View file

@ -17,3 +17,6 @@ dev_dependencies:
belatuk_pretty_logging: ^4.0.0 belatuk_pretty_logging: ^4.0.0
test: ^1.17.5 test: ^1.17.5
lints: ^1.0.0 lints: ^1.0.0
#dependency_overrides:
# angel3_auth:
# path: ../auth

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/shelf.iml" filepath="$PROJECT_DIR$/.idea/shelf.iml" />
</modules>
</component>
</project>

View file

@ -1,6 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All Tests (coverage)" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true">
<option name="filePath" value="$PROJECT_DIR$/test/all.dart" />
<method />
</configuration>
</component>

View file

@ -1,8 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="tests in shelf" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true" nameIsGenerated="true">
<option name="filePath" value="$PROJECT_DIR$" />
<option name="scope" value="FOLDER" />
<option name="testRunnerOptions" value="-j 4" />
<method />
</configuration>
</component>

View file

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View file

@ -7,7 +7,7 @@ environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
angel3_framework: ^4.2.0 angel3_framework: ^4.2.0
angel3_container: ^3.0.0 angel3_container: ^3.1.0
angel3_mock_request: ^2.0.0 angel3_mock_request: ^2.0.0
path: ^1.8.0 path: ^1.8.0
shelf: ^1.1.4 shelf: ^1.1.4
@ -21,3 +21,6 @@ dev_dependencies:
shelf_static: ^1.0.0 shelf_static: ^1.0.0
test: ^1.17.5 test: ^1.17.5
lints: ^1.0.0 lints: ^1.0.0
#dependency_overrides:
# angel3_container:
# path: ../container/angel_container

View file

@ -6,11 +6,14 @@ repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/sync
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
angel3_framework: ^4.1.0 angel3_framework: ^4.2.0
angel3_websocket: ^4.0.0 angel3_websocket: ^4.1.0
belatuk_pub_sub: ^4.0.0 belatuk_pub_sub: ^4.0.0
stream_channel: ^2.1.0 stream_channel: ^2.1.0
dev_dependencies: dev_dependencies:
angel3_test: ^4.0.0 angel3_test: ^4.1.0
test: ^1.17.8 test: ^1.17.5
lints: ^1.0.0 lints: ^1.0.0
#dependency_overrides:
# angel3_websocket:
# path: ../websocket

View file

@ -1,5 +1,9 @@
# Change Log # Change Log
## 4.1.1
* Fixed NNBD issues
## 4.1.0 ## 4.1.0
* Updated linter to `package:lints` * Updated linter to `package:lints`

View file

@ -6,7 +6,7 @@ import 'package:angel3_websocket/server.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {
Angel? app; Angel app;
late TestClient client; late TestClient client;
setUp(() async { setUp(() async {
@ -46,17 +46,16 @@ void main() {
create: (dynamic data, [params]) async => {'foo': 'bar'})); create: (dynamic data, [params]) async => {'foo': 'bar'}));
var ws = AngelWebSocket(app); var ws = AngelWebSocket(app);
await app!.configure(ws.configureServer); await app.configure(ws.configureServer);
app!.all('/ws', ws.handleRequest); app.all('/ws', ws.handleRequest);
app!.errorHandler = (e, req, res) => e.toJson(); app.errorHandler = (e, req, res) => e.toJson();
client = await connectTo(app!); client = await connectTo(app);
}); });
tearDown(() async { tearDown(() async {
await client.close(); await client.close();
app = null;
}); });
group('matchers', () { group('matchers', () {

View file

@ -1,5 +1,5 @@
name: angel3_test name: angel3_test
version: 4.1.0 version: 4.1.1
description: Testing utility library for the Angel3 framework. Use with package:test. description: Testing utility library for the Angel3 framework. Use with package:test.
homepage: https://angel3-framework.web.app/ homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/test repository: https://github.com/dukefirehawk/angel/tree/master/packages/test
@ -12,11 +12,14 @@ dependencies:
angel3_validate: ^4.0.0 angel3_validate: ^4.0.0
angel3_websocket: ^4.1.0 angel3_websocket: ^4.1.0
angel3_mock_request: ^2.0.0 angel3_mock_request: ^2.0.0
angel3_container: ^3.0.0 angel3_container: ^3.1.0
http: ^0.13.1 http: ^0.13.1
matcher: ^0.12.10 matcher: ^0.12.10
web_socket_channel: ^2.0.0 web_socket_channel: ^2.0.0
dev_dependencies: dev_dependencies:
test: ^1.17.5 test: ^1.17.5
lints: ^1.0.0 lints: ^1.0.0
dependency_overrides:
angel3_websocket:
path: ../websocket

View file

@ -6,7 +6,7 @@ import 'package:angel3_websocket/server.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {
Angel? app; Angel app;
late TestClient client; late TestClient client;
setUp(() async { setUp(() async {
@ -48,17 +48,16 @@ void main() {
<String, dynamic>{'foo': 'bar'})); <String, dynamic>{'foo': 'bar'}));
var ws = AngelWebSocket(app); var ws = AngelWebSocket(app);
await app!.configure(ws.configureServer); await app.configure(ws.configureServer);
app!.all('/ws', ws.handleRequest); app.all('/ws', ws.handleRequest);
app!.errorHandler = (e, req, res) => e.toJson(); app.errorHandler = (e, req, res) => e.toJson();
client = await connectTo(app!, useZone: false); client = await connectTo(app, useZone: false);
}); });
tearDown(() async { tearDown(() async {
await client.close(); await client.close();
app = null;
}); });
group('matchers', () { group('matchers', () {

View file

@ -1,5 +1,10 @@
# Change Log # Change Log
## 4.1.1
* Fixed issue with type casting
* Changed `app` parameter of `AngelWebSocket` to non-nullable
## 4.1.0 ## 4.1.0
* Updated to use `package:belatuk_merge_map` * Updated to use `package:belatuk_merge_map`

View file

@ -40,17 +40,17 @@ class AngelWebSocket {
final StreamController<WebSocketContext> _onDisconnect = final StreamController<WebSocketContext> _onDisconnect =
StreamController<WebSocketContext>.broadcast(); StreamController<WebSocketContext>.broadcast();
final Angel? app; final Angel app;
/// If this is not `true`, then all client-side service parameters will be /// If this is not `true`, then all client-side service parameters will be
/// discarded, other than `params['query']`. /// discarded, other than `params['query']`.
final bool allowClientParams; final bool allowClientParams;
/// An optional whitelist of allowed client origins, or [:null:]. /// An optional whitelist of allowed client origins, or [:null:].
final List<String>? allowedOrigins; final List<String> allowedOrigins;
/// An optional whitelist of allowed client protocols, or [:null:]. /// An optional whitelist of allowed client protocols, or [:null:].
final List<String>? allowedProtocols; final List<String> allowedProtocols;
/// If `true`, then clients can authenticate their WebSockets by sending a valid JWT. /// If `true`, then clients can authenticate their WebSockets by sending a valid JWT.
final bool allowAuth; final bool allowAuth;
@ -93,8 +93,8 @@ class AngelWebSocket {
this.synchronizationChannel, this.synchronizationChannel,
this.serializer, this.serializer,
this.deserializer, this.deserializer,
this.allowedOrigins, this.allowedOrigins = const [],
this.allowedProtocols}) { this.allowedProtocols = const []}) {
serializer ??= json.encode; serializer ??= json.encode;
deserializer ??= (params) => params; deserializer ??= (params) => params;
} }
@ -161,7 +161,7 @@ class AngelWebSocket {
if (e.service.configuration.containsKey('ws:filter')) { if (e.service.configuration.containsKey('ws:filter')) {
return e.service.configuration['ws:filter'](e, socket); return e.service.configuration['ws:filter'](e, socket);
} else if (e.params != null && e.params!.containsKey('ws:filter')) { } else if (e.params != null && e.params!.containsKey('ws:filter')) {
return e.params!['ws:filter'](e, socket); return e.params?['ws:filter'](e, socket);
} else { } else {
return true; return true;
} }
@ -175,13 +175,15 @@ class AngelWebSocket {
Future<void> batchEvent(WebSocketEvent event, Future<void> batchEvent(WebSocketEvent event,
{Function(WebSocketContext socket)? filter, bool notify = true}) async { {Function(WebSocketContext socket)? filter, bool notify = true}) async {
// Default implementation will just immediately fire events // Default implementation will just immediately fire events
_clients.forEach((client) async { for (var client in _clients) {
dynamic result = true; dynamic result = true;
if (filter != null) result = await filter(client); if (filter != null) {
result = await filter(client);
}
if (result == true) { if (result == true) {
client.channel.sink.add((serializer ?? json.encode)(event.toJson())); client.channel.sink.add((serializer ?? json.encode)(event.toJson()));
} }
}); }
if (synchronizationChannel != null && notify != false) { if (synchronizationChannel != null && notify != false) {
synchronizationChannel!.sink.add(event); synchronizationChannel!.sink.add(event);
@ -200,11 +202,11 @@ class AngelWebSocket {
return null; return null;
} }
var service = app!.findService(split[0]); var service = app.findService(split[0]);
if (service == null) { if (service == null) {
socket.sendError(AngelHttpException.notFound( socket.sendError(AngelHttpException.notFound(
message: 'No service \"${split[0]}\" exists.')); message: 'No service "${split[0]}" exists.'));
return null; return null;
} }
@ -222,7 +224,7 @@ class AngelWebSocket {
var params = mergeMap<String, dynamic>([ var params = mergeMap<String, dynamic>([
(((deserializer ?? (params) => params)(action.params)) (((deserializer ?? (params) => params)(action.params))
as Map<String, dynamic>?)!, as Map<String, dynamic>),
{ {
'provider': Providers.websocket, 'provider': Providers.websocket,
'__requestctx': socket.request, '__requestctx': socket.request,
@ -257,7 +259,7 @@ class AngelWebSocket {
data: await service.remove(action.id, params)); data: await service.remove(action.id, params));
} else { } else {
socket.sendError(AngelHttpException.methodNotAllowed( socket.sendError(AngelHttpException.methodNotAllowed(
message: 'Method Not Allowed: \"$actionName\"')); message: 'Method Not Allowed: $actionName'));
return null; return null;
} }
} catch (e, st) { } catch (e, st) {
@ -278,7 +280,7 @@ class AngelWebSocket {
AuthToken token; AuthToken token;
token = AuthToken.validate(jwt, auth.hmac); token = AuthToken.validate(jwt, auth.hmac);
var user = await auth.deserializer(token.userId as Object); var user = await auth.deserializer(token.userId);
socket.request socket.request
..container!.registerSingleton<AuthToken>(token) ..container!.registerSingleton<AuthToken>(token)
..container!.registerSingleton(user, as: user.runtimeType); ..container!.registerSingleton(user, as: user.runtimeType);
@ -359,16 +361,16 @@ class AngelWebSocket {
// Send an error // Send an error
if (e is AngelHttpException) { if (e is AngelHttpException) {
socket.sendError(e); socket.sendError(e);
app?.logger?.severe(e.message, e.error ?? e, e.stackTrace); app.logger?.severe(e.message, e.error ?? e, e.stackTrace);
} else if (sendErrors) { } else if (sendErrors) {
var err = AngelHttpException(e, var err = AngelHttpException(e,
message: e.toString(), stackTrace: st, errors: [st.toString()]); message: e.toString(), stackTrace: st, errors: [st.toString()]);
socket.sendError(err); socket.sendError(err);
app?.logger?.severe(err.message, e, st); app.logger?.severe(err.message, e, st);
} else { } else {
var err = AngelHttpException(e); var err = AngelHttpException(e);
socket.sendError(err); socket.sendError(err);
app?.logger?.severe(e.toString(), e, st); app.logger?.severe(e.toString(), e, st);
} }
} }
@ -413,7 +415,7 @@ class AngelWebSocket {
/// Handles an incoming [WebSocketContext]. /// Handles an incoming [WebSocketContext].
Future<void> handleClient(WebSocketContext socket) async { Future<void> handleClient(WebSocketContext socket) async {
var origin = socket.request.headers?.value('origin'); var origin = socket.request.headers?.value('origin');
if (allowedOrigins != null && !allowedOrigins!.contains(origin)) { if (allowedOrigins.isNotEmpty && !allowedOrigins.contains(origin)) {
throw AngelHttpException.forbidden( throw AngelHttpException.forbidden(
message: message:
'WebSocket connections are not allowed from the origin "$origin".'); 'WebSocket connections are not allowed from the origin "$origin".');
@ -479,8 +481,8 @@ class AngelWebSocket {
throw AngelHttpException.badRequest( throw AngelHttpException.badRequest(
message: 'Missing `sec-websocket-key` header.'); message: 'Missing `sec-websocket-key` header.');
} else if (protocol != null && } else if (protocol != null &&
allowedProtocols != null && allowedProtocols.isNotEmpty &&
!allowedProtocols!.contains(protocol)) { !allowedProtocols.contains(protocol)) {
throw AngelHttpException.badRequest( throw AngelHttpException.badRequest(
message: 'Disallowed `sec-websocket-protocol` header "$protocol".'); message: 'Disallowed `sec-websocket-protocol` header "$protocol".');
} else { } else {

View file

@ -1,13 +1,13 @@
name: angel3_websocket name: angel3_websocket
version: 4.1.0 version: 4.1.1
description: This library provides WebSockets support for Angel3 framework. description: This library provides WebSockets support for Angel3 framework.
homepage: https://angel3-framework.web.app/ homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/websocket repository: https://github.com/dukefirehawk/angel/tree/master/packages/websocket
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
angel3_auth: ^4.0.0 angel3_auth: ^4.1.0
angel3_client: ^4.0.0 angel3_client: ^4.1.0
angel3_framework: ^4.2.0 angel3_framework: ^4.2.0
angel3_http_exception: ^3.0.0 angel3_http_exception: ^3.0.0
belatuk_merge_map: ^3.0.0 belatuk_merge_map: ^3.0.0
@ -18,12 +18,11 @@ dependencies:
collection: ^1.15.0 collection: ^1.15.0
logging: ^1.0.1 logging: ^1.0.1
dev_dependencies: dev_dependencies:
angel3_container: ^3.0.0 angel3_container: ^3.1.0
angel3_model: ^3.0.0 angel3_model: ^3.1.0
test: ^1.17.5 test: ^1.17.5
lints: ^1.0.0 lints: ^1.0.0
#dependency_overrides: dependency_overrides:
# angel3_framework: angel3_auth:
# path: ../framework path: ../auth

View file

@ -9,7 +9,7 @@ import 'package:test/test.dart';
import 'common.dart'; import 'common.dart';
void main() { void main() {
srv.Angel? app; srv.Angel app;
late srv.AngelHttp http; late srv.AngelHttp http;
ws.WebSockets? client; ws.WebSockets? client;
srv.AngelWebSocket websockets; srv.AngelWebSocket websockets;
@ -18,17 +18,17 @@ void main() {
setUp(() async { setUp(() async {
app = srv.Angel(reflector: const MirrorsReflector()); app = srv.Angel(reflector: const MirrorsReflector());
http = srv.AngelHttp(app!, useZone: false); http = srv.AngelHttp(app, useZone: false);
websockets = srv.AngelWebSocket(app) websockets = srv.AngelWebSocket(app)
..onData.listen((data) { ..onData.listen((data) {
print('Received by server: $data'); print('Received by server: $data');
}); });
await app!.configure(websockets.configureServer); await app.configure(websockets.configureServer);
app!.all('/ws', websockets.handleRequest); app.all('/ws', websockets.handleRequest);
await app!.configure(GameController(websockets).configureServer); await app.configure(GameController(websockets).configureServer);
app!.logger = Logger('angel_auth')..onRecord.listen(print); app.logger = Logger('angel_auth')..onRecord.listen(print);
server = await http.startServer(); server = await http.startServer();
url = 'ws://${server!.address.address}:${server!.port}/ws'; url = 'ws://${server!.address.address}:${server!.port}/ws';
@ -53,7 +53,7 @@ void main() {
tearDown(() async { tearDown(() async {
await client!.close(); await client!.close();
await http.close(); await http.close();
app = null; //app = null;
client = null; client = null;
server = null; server = null;
url = null; url = null;

View file

@ -9,7 +9,7 @@ import 'package:test/test.dart';
import 'common.dart'; import 'common.dart';
void main() { void main() {
srv.Angel? app; srv.Angel app;
late srv.AngelHttp http; late srv.AngelHttp http;
ws.WebSockets? client; ws.WebSockets? client;
srv.AngelWebSocket websockets; srv.AngelWebSocket websockets;
@ -19,16 +19,16 @@ void main() {
setUp(() async { setUp(() async {
app = srv.Angel(reflector: MirrorsReflector()) app = srv.Angel(reflector: MirrorsReflector())
..use('/api/todos', TodoService()); ..use('/api/todos', TodoService());
http = srv.AngelHttp(app!, useZone: false); http = srv.AngelHttp(app, useZone: false);
websockets = srv.AngelWebSocket(app) websockets = srv.AngelWebSocket(app)
..onData.listen((data) { ..onData.listen((data) {
print('Received by server: $data'); print('Received by server: $data');
}); });
await app!.configure(websockets.configureServer); await app.configure(websockets.configureServer);
app!.all('/ws', websockets.handleRequest); app.all('/ws', websockets.handleRequest);
app!.logger = Logger('angel_auth')..onRecord.listen(print); app.logger = Logger('angel_auth')..onRecord.listen(print);
server = await http.startServer(); server = await http.startServer();
url = 'ws://${server!.address.address}:${server!.port}/ws'; url = 'ws://${server!.address.address}:${server!.port}/ws';
@ -51,7 +51,7 @@ void main() {
await client!.close(); await client!.close();
await http.server!.close(force: true); await http.server!.close(force: true);
app = null; //app = null;
client = null; client = null;
server = null; server = null;
url = null; url = null;