Updated client
This commit is contained in:
parent
6dbc1f5b9a
commit
38cc17e381
8 changed files with 85 additions and 5 deletions
|
@ -1,6 +1,11 @@
|
||||||
|
|
||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## 4.0.2
|
||||||
|
|
||||||
|
* Added logging
|
||||||
|
* Added unit test for authentication
|
||||||
|
|
||||||
## 4.0.1
|
## 4.0.1
|
||||||
|
|
||||||
* Updated README
|
* Updated README
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
# Angel3 Client
|
# Angel3 Client
|
||||||
|
|
||||||
[![version](https://img.shields.io/badge/pub-v4.0.1-brightgreen)](https://pub.dartlang.org/packages/angel3_client)
|
[![version](https://img.shields.io/badge/pub-v4.0.2-brightgreen)](https://pub.dartlang.org/packages/angel3_client)
|
||||||
[![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/angel3/packages/client/LICENSE)
|
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/client/LICENSE)
|
||||||
|
|
||||||
A browser, mobile and command line based client that supports querying Angel3 servers
|
A browser, mobile and command line based client that supports querying Angel3 backend.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|
|
@ -5,6 +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';
|
||||||
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.
|
||||||
|
@ -18,6 +19,8 @@ typedef AngelDeserializer<T> = T? Function(dynamic x);
|
||||||
|
|
||||||
/// Represents an Angel server that we are querying.
|
/// Represents an Angel server that we are querying.
|
||||||
abstract class Angel extends http.BaseClient {
|
abstract class Angel extends http.BaseClient {
|
||||||
|
final _log = Logger('Angel');
|
||||||
|
|
||||||
/// A mutable member. When this is set, it holds a JSON Web Token
|
/// A mutable member. When this is set, it holds a JSON Web Token
|
||||||
/// that is automatically attached to every request sent.
|
/// that is automatically attached to every request sent.
|
||||||
///
|
///
|
||||||
|
|
|
@ -8,6 +8,7 @@ import 'package:http/src/request.dart' as http;
|
||||||
import 'package:http/src/response.dart' as http;
|
import 'package:http/src/response.dart' as http;
|
||||||
import 'package:http/src/streamed_response.dart' as http;
|
import 'package:http/src/streamed_response.dart' as http;
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
import 'angel3_client.dart';
|
import 'angel3_client.dart';
|
||||||
|
|
||||||
const Map<String, String> _readHeaders = {'Accept': 'application/json'};
|
const Map<String, String> _readHeaders = {'Accept': 'application/json'};
|
||||||
|
@ -47,6 +48,7 @@ AngelHttpException failure(http.Response response,
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class BaseAngelClient extends Angel {
|
abstract class BaseAngelClient extends Angel {
|
||||||
|
final _log = Logger('BaseAngelClient');
|
||||||
final StreamController<AngelAuthResult> _onAuthenticated =
|
final StreamController<AngelAuthResult> _onAuthenticated =
|
||||||
StreamController<AngelAuthResult>();
|
StreamController<AngelAuthResult>();
|
||||||
final List<Service> _services = [];
|
final List<Service> _services = [];
|
||||||
|
@ -89,6 +91,8 @@ abstract class BaseAngelClient extends Angel {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//var v = json.decode(response.body);
|
//var v = json.decode(response.body);
|
||||||
|
_log.info(response.headers);
|
||||||
|
|
||||||
var v = jsonDecode(response.body);
|
var v = jsonDecode(response.body);
|
||||||
|
|
||||||
if (v is! Map || !v.containsKey('data') || !v.containsKey('token')) {
|
if (v is! Map || !v.containsKey('data') || !v.containsKey('token')) {
|
||||||
|
@ -102,6 +106,7 @@ abstract class BaseAngelClient extends Angel {
|
||||||
} on AngelHttpException {
|
} on AngelHttpException {
|
||||||
rethrow;
|
rethrow;
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
|
_log.severe('Authentication failed');
|
||||||
throw failure(response, error: e, stack: st);
|
throw failure(response, error: e, stack: st);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,6 +152,7 @@ abstract class BaseAngelClient extends Angel {
|
||||||
request.bodyFields =
|
request.bodyFields =
|
||||||
body.map((k, v) => MapEntry(k, v is String ? v : v.toString()));
|
body.map((k, v) => MapEntry(k, v is String ? v : v.toString()));
|
||||||
} else {
|
} else {
|
||||||
|
_log.severe('Body is not a String, List<int>, or Map<String, String>');
|
||||||
throw ArgumentError.value(body, 'body',
|
throw ArgumentError.value(body, 'body',
|
||||||
'must be a String, List<int>, or Map<String, String>.');
|
'must be a String, List<int>, or Map<String, String>.');
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,14 @@ import 'dart:async';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:angel3_json_god/angel3_json_god.dart' as god;
|
import 'package:angel3_json_god/angel3_json_god.dart' as god;
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
import 'angel3_client.dart';
|
import 'angel3_client.dart';
|
||||||
import 'base_angel_client.dart';
|
import 'base_angel_client.dart';
|
||||||
export 'angel3_client.dart';
|
export 'angel3_client.dart';
|
||||||
|
|
||||||
/// Queries an Angel server via REST.
|
/// Queries an Angel server via REST.
|
||||||
class Rest extends BaseAngelClient {
|
class Rest extends BaseAngelClient {
|
||||||
|
//final _log = Logger('REST');
|
||||||
final List<Service> _services = [];
|
final List<Service> _services = [];
|
||||||
|
|
||||||
Rest(String path) : super(http.Client() as http.BaseClient, path);
|
Rest(String path) : super(http.Client() as http.BaseClient, path);
|
||||||
|
@ -42,6 +44,8 @@ class Rest extends BaseAngelClient {
|
||||||
|
|
||||||
/// Queries an Angel service via REST.
|
/// Queries an Angel service via REST.
|
||||||
class RestService<Id, Data> extends BaseAngelService<Id, Data> {
|
class RestService<Id, Data> extends BaseAngelService<Id, Data> {
|
||||||
|
final _log = Logger('RestService');
|
||||||
|
|
||||||
final Type? type;
|
final Type? type;
|
||||||
|
|
||||||
RestService(http.BaseClient client, BaseAngelClient app, url, this.type)
|
RestService(http.BaseClient client, BaseAngelClient app, url, this.type)
|
||||||
|
@ -49,7 +53,7 @@ class RestService<Id, Data> extends BaseAngelService<Id, Data> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Data? deserialize(x) {
|
Data? deserialize(x) {
|
||||||
print(x);
|
_log.info(x);
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
return x.runtimeType == type
|
return x.runtimeType == type
|
||||||
? x as Data?
|
? x as Data?
|
||||||
|
@ -61,7 +65,7 @@ class RestService<Id, Data> extends BaseAngelService<Id, Data> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String makeBody(x) {
|
String makeBody(x) {
|
||||||
print(x);
|
_log.info(x);
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
return super.makeBody(god.serializeObject(x));
|
return super.makeBody(god.serializeObject(x));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: angel3_client
|
name: angel3_client
|
||||||
version: 4.0.1
|
version: 4.0.2
|
||||||
description: A browser, mobile and command line based client that supports querying Angel3 servers
|
description: A browser, mobile and command line based client that supports querying Angel3 servers
|
||||||
homepage: https://angel3-framework.web.app/
|
homepage: https://angel3-framework.web.app/
|
||||||
repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/client
|
repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/client
|
||||||
|
@ -12,11 +12,13 @@ dependencies:
|
||||||
http: ^0.13.1
|
http: ^0.13.1
|
||||||
meta: ^1.3.0
|
meta: ^1.3.0
|
||||||
path: ^1.8.0
|
path: ^1.8.0
|
||||||
|
logging: ^1.0.0
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
angel3_framework: ^4.0.0
|
angel3_framework: ^4.0.0
|
||||||
angel3_model: ^3.0.0
|
angel3_model: ^3.0.0
|
||||||
angel3_mock_request: ^2.0.0
|
angel3_mock_request: ^2.0.0
|
||||||
angel3_container: ^3.0.0
|
angel3_container: ^3.0.0
|
||||||
|
angel3_auth: ^4.0.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
|
||||||
|
|
|
@ -76,6 +76,7 @@ void main() {
|
||||||
test('credentials send right body', () async {
|
test('credentials send right body', () async {
|
||||||
await app
|
await app
|
||||||
.authenticate(type: 'local', credentials: {'username': 'password'});
|
.authenticate(type: 'local', credentials: {'username': 'password'});
|
||||||
|
print(app.client.spec?.headers);
|
||||||
expect(
|
expect(
|
||||||
await read(app.client.spec!.request.finalize()),
|
await read(app.client.spec!.request.finalize()),
|
||||||
json.encode({'username': 'password'}),
|
json.encode({'username': 'password'}),
|
||||||
|
|
59
packages/client/test/auth_test.dart
Normal file
59
packages/client/test/auth_test.dart
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import 'package:angel3_auth/angel3_auth.dart';
|
||||||
|
import 'package:angel3_client/io.dart' as c;
|
||||||
|
import 'package:angel3_framework/angel3_framework.dart';
|
||||||
|
import 'package:angel3_framework/http.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
const Map<String, String> USER = {'username': 'foo', 'password': 'bar'};
|
||||||
|
var localOpts = AngelAuthOptions<Map<String, String>>(canRespondWithJson: true);
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late Angel app;
|
||||||
|
late AngelHttp http;
|
||||||
|
late c.Angel client;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
app = Angel();
|
||||||
|
http = AngelHttp(app, useZone: false);
|
||||||
|
var auth = AngelAuth(
|
||||||
|
serializer: (_) async => 'baz', deserializer: (_) async => USER);
|
||||||
|
|
||||||
|
auth.strategies['local'] = LocalAuthStrategy(
|
||||||
|
(username, password) async {
|
||||||
|
if (username == 'foo' && password == 'bar') {
|
||||||
|
return USER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
app.post('/auth/local', auth.authenticate('local', localOpts));
|
||||||
|
|
||||||
|
await app.configure(auth.configureServer);
|
||||||
|
|
||||||
|
app.logger = Logger('auth_test')
|
||||||
|
..onRecord.listen((rec) {
|
||||||
|
print(
|
||||||
|
'${rec.time}: ${rec.level.name}: ${rec.loggerName}: ${rec.message}');
|
||||||
|
});
|
||||||
|
|
||||||
|
var server = await http.startServer();
|
||||||
|
|
||||||
|
client = c.Rest('http://${server.address.address}:${server.port}');
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
http.close();
|
||||||
|
client.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('auth event fires', () async {
|
||||||
|
var localAuth = await client.authenticate(type: 'local', credentials: USER);
|
||||||
|
print('JWT: ${localAuth.token}');
|
||||||
|
print('Data: ${localAuth.data}');
|
||||||
|
|
||||||
|
expect(localAuth.data, USER);
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue