Migrated angel_client to 4.0.0
This commit is contained in:
parent
5e9172aca9
commit
037e82d699
13 changed files with 220 additions and 200 deletions
|
@ -15,7 +15,7 @@
|
||||||
* Updated angel_configuration to 4.0.0 (6/8 test passed)
|
* Updated angel_configuration to 4.0.0 (6/8 test passed)
|
||||||
* Updated angel_validate to 4.0.0 (6/7 test passed)
|
* Updated angel_validate to 4.0.0 (6/7 test passed)
|
||||||
* Updated json_god to 4.0.0 (13/13 test passed)
|
* Updated json_god to 4.0.0 (13/13 test passed)
|
||||||
* Updated angel_client to 3.0.0 (in progress)
|
* Updated angel_client to 4.0.0 (6/13 test passed)
|
||||||
* Updated angel_websocket to 3.0.0 (in progress)
|
* Updated angel_websocket to 3.0.0 (in progress)
|
||||||
* Updated test to 3.0.0 (in progress)
|
* Updated test to 3.0.0 (in progress)
|
||||||
* Updated jael to 3.0.0 (in progress)
|
* Updated jael to 3.0.0 (in progress)
|
||||||
|
|
|
@ -6,16 +6,16 @@ Future doSomething(Angel app) async {
|
||||||
.service<String, Map<String, dynamic>>('api/users')
|
.service<String, Map<String, dynamic>>('api/users')
|
||||||
.map(User.fromMap, User.toMap);
|
.map(User.fromMap, User.toMap);
|
||||||
|
|
||||||
var users = await userService.index();
|
var users = await (userService.index() as FutureOr<List<User>>);
|
||||||
print('Name: ${users.first.name}');
|
print('Name: ${users.first.name}');
|
||||||
}
|
}
|
||||||
|
|
||||||
class User {
|
class User {
|
||||||
final String name;
|
final String? name;
|
||||||
|
|
||||||
User({this.name});
|
User({this.name});
|
||||||
|
|
||||||
static User fromMap(Map data) => User(name: data['name'] as String);
|
static User fromMap(Map data) => User(name: data['name'] as String?);
|
||||||
|
|
||||||
static Map<String, String> toMap(User user) => {'name': user.name};
|
static Map<String, String?> toMap(User user) => {'name': user.name};
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,13 @@ export 'package:angel_http_exception/angel_http_exception.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
/// A function that configures an [Angel] client in some way.
|
/// A function that configures an [Angel] client in some way.
|
||||||
typedef FutureOr<void> AngelConfigurer(Angel app);
|
typedef AngelConfigurer = FutureOr<void> Function(Angel app);
|
||||||
|
|
||||||
/// A function that deserializes data received from the server.
|
/// A function that deserializes data received from the server.
|
||||||
///
|
///
|
||||||
/// This is only really necessary in the browser, where `json_god`
|
/// This is only really necessary in the browser, where `json_god`
|
||||||
/// doesn't work.
|
/// doesn't work.
|
||||||
typedef T AngelDeserializer<T>(x);
|
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 {
|
||||||
|
@ -23,7 +23,7 @@ abstract class Angel extends http.BaseClient {
|
||||||
/// that is automatically attached to every request sent.
|
/// that is automatically attached to every request sent.
|
||||||
///
|
///
|
||||||
/// This is designed with `package:angel_auth` in mind.
|
/// This is designed with `package:angel_auth` in mind.
|
||||||
String authToken;
|
String? authToken;
|
||||||
|
|
||||||
/// The root URL at which the target server.
|
/// The root URL at which the target server.
|
||||||
final Uri baseUrl;
|
final Uri baseUrl;
|
||||||
|
@ -46,7 +46,7 @@ abstract class Angel extends http.BaseClient {
|
||||||
///
|
///
|
||||||
/// The given [credentials] are sent to server as-is; the request body is sent as JSON.
|
/// The given [credentials] are sent to server as-is; the request body is sent as JSON.
|
||||||
Future<AngelAuthResult> authenticate(
|
Future<AngelAuthResult> authenticate(
|
||||||
{@required String type,
|
{required String type,
|
||||||
credentials,
|
credentials,
|
||||||
String authEndpoint = '/auth',
|
String authEndpoint = '/auth',
|
||||||
@deprecated String reviveEndpoint = '/auth/token'});
|
@deprecated String reviveEndpoint = '/auth/token'});
|
||||||
|
@ -85,57 +85,59 @@ abstract class Angel extends http.BaseClient {
|
||||||
/// You can pass a custom [deserializer], which is typically necessary in cases where
|
/// You can pass a custom [deserializer], which is typically necessary in cases where
|
||||||
/// `dart:mirrors` does not exist.
|
/// `dart:mirrors` does not exist.
|
||||||
Service<Id, Data> service<Id, Data>(String path,
|
Service<Id, Data> service<Id, Data>(String path,
|
||||||
{@deprecated Type type, AngelDeserializer<Data> deserializer});
|
{@deprecated Type? type, AngelDeserializer<Data>? deserializer});
|
||||||
|
|
||||||
//@override
|
//@override
|
||||||
//Future<http.Response> delete(url, {Map<String, String> headers});
|
//Future<http.Response> delete(url, {Map<String, String> headers});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> get(url, {Map<String, String> headers});
|
Future<http.Response> get(url, {Map<String, String>? headers});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> head(url, {Map<String, String> headers});
|
Future<http.Response> head(url, {Map<String, String>? headers});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> patch(url,
|
Future<http.Response> patch(url,
|
||||||
{body, Map<String, String> headers, Encoding encoding});
|
{body, Map<String, String>? headers, Encoding? encoding});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> post(url,
|
Future<http.Response> post(url,
|
||||||
{body, Map<String, String> headers, Encoding encoding});
|
{body, Map<String, String>? headers, Encoding? encoding});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> put(url,
|
Future<http.Response> put(url,
|
||||||
{body, Map<String, String> headers, Encoding encoding});
|
{body, Map<String, String>? headers, Encoding? encoding});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the result of authentication with an Angel server.
|
/// Represents the result of authentication with an Angel server.
|
||||||
class AngelAuthResult {
|
class AngelAuthResult {
|
||||||
String _token;
|
String? _token;
|
||||||
final Map<String, dynamic> data = {};
|
final Map<String, dynamic> data = {};
|
||||||
|
|
||||||
/// The JSON Web token that was sent with this response.
|
/// The JSON Web token that was sent with this response.
|
||||||
String get token => _token;
|
String? get token => _token;
|
||||||
|
|
||||||
AngelAuthResult({String token, Map<String, dynamic> data = const {}}) {
|
AngelAuthResult({String? token, Map<String, dynamic> data = const {}}) {
|
||||||
_token = token;
|
_token = token;
|
||||||
this.data.addAll(data ?? {});
|
this.data.addAll(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to deserialize a response from a [Map].
|
/// Attempts to deserialize a response from a [Map].
|
||||||
factory AngelAuthResult.fromMap(Map data) {
|
factory AngelAuthResult.fromMap(Map? data) {
|
||||||
final result = AngelAuthResult();
|
final result = AngelAuthResult();
|
||||||
|
|
||||||
if (data is Map && data.containsKey('token') && data['token'] is String)
|
if (data is Map && data.containsKey('token') && data['token'] is String) {
|
||||||
result._token = data['token'].toString();
|
result._token = data['token'].toString();
|
||||||
|
}
|
||||||
|
|
||||||
if (data is Map)
|
if (data is Map) {
|
||||||
result.data.addAll((data['data'] as Map<String, dynamic>) ?? {});
|
result.data.addAll((data['data'] as Map<String, dynamic>?) ?? {});
|
||||||
|
}
|
||||||
|
|
||||||
if (result.token == null) {
|
if (result.token == null) {
|
||||||
throw FormatException(
|
throw FormatException(
|
||||||
'The required "token" field was not present in the given data.');
|
'The required "token" field was not present in the given data.');
|
||||||
} else if (data['data'] is! Map) {
|
} else if (data!['data'] is! Map) {
|
||||||
throw FormatException(
|
throw FormatException(
|
||||||
'The required "data" field in the given data was not a map; instead, it was ${data['data']}.');
|
'The required "data" field in the given data was not a map; instead, it was ${data['data']}.');
|
||||||
}
|
}
|
||||||
|
@ -145,7 +147,7 @@ class AngelAuthResult {
|
||||||
|
|
||||||
/// Attempts to deserialize a response from a [String].
|
/// Attempts to deserialize a response from a [String].
|
||||||
factory AngelAuthResult.fromJson(String s) =>
|
factory AngelAuthResult.fromJson(String s) =>
|
||||||
AngelAuthResult.fromMap(json.decode(s) as Map);
|
AngelAuthResult.fromMap(json.decode(s) as Map?);
|
||||||
|
|
||||||
/// Converts this instance into a JSON-friendly representation.
|
/// Converts this instance into a JSON-friendly representation.
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
|
@ -179,22 +181,22 @@ abstract class Service<Id, Data> {
|
||||||
Future close();
|
Future close();
|
||||||
|
|
||||||
/// Retrieves all resources.
|
/// Retrieves all resources.
|
||||||
Future<List<Data>> index([Map<String, dynamic> params]);
|
Future<List<Data>?> index([Map<String, dynamic>? params]);
|
||||||
|
|
||||||
/// Retrieves the desired resource.
|
/// Retrieves the desired resource.
|
||||||
Future<Data> read(Id id, [Map<String, dynamic> params]);
|
Future<Data> read(Id id, [Map<String, dynamic>? params]);
|
||||||
|
|
||||||
/// Creates a resource.
|
/// Creates a resource.
|
||||||
Future<Data> create(Data data, [Map<String, dynamic> params]);
|
Future<Data> create(Data data, [Map<String, dynamic>? params]);
|
||||||
|
|
||||||
/// Modifies a resource.
|
/// Modifies a resource.
|
||||||
Future<Data> modify(Id id, Data data, [Map<String, dynamic> params]);
|
Future<Data> modify(Id id, Data data, [Map<String, dynamic>? params]);
|
||||||
|
|
||||||
/// Overwrites a resource.
|
/// Overwrites a resource.
|
||||||
Future<Data> update(Id id, Data data, [Map<String, dynamic> params]);
|
Future<Data> update(Id id, Data data, [Map<String, dynamic>? params]);
|
||||||
|
|
||||||
/// Removes the given resource.
|
/// Removes the given resource.
|
||||||
Future<Data> remove(Id id, [Map<String, dynamic> params]);
|
Future<Data> remove(Id id, [Map<String, dynamic>? params]);
|
||||||
|
|
||||||
/// Creates a [Service] that wraps over this one, and maps input and output using two converter functions.
|
/// Creates a [Service] that wraps over this one, and maps input and output using two converter functions.
|
||||||
///
|
///
|
||||||
|
@ -218,17 +220,17 @@ class _MappedService<Id, Data, U> extends Service<Id, U> {
|
||||||
Future close() => Future.value();
|
Future close() => Future.value();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<U> create(U data, [Map<String, dynamic> params]) {
|
Future<U> create(U data, [Map<String, dynamic>? params]) {
|
||||||
return inner.create(decoder(data)).then(encoder);
|
return inner.create(decoder(data)).then(encoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<U>> index([Map<String, dynamic> params]) {
|
Future<List<U>> index([Map<String, dynamic>? params]) {
|
||||||
return inner.index(params).then((l) => l.map(encoder).toList());
|
return inner.index(params).then((l) => l!.map(encoder).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<U> modify(Id id, U data, [Map<String, dynamic> params]) {
|
Future<U> modify(Id id, U data, [Map<String, dynamic>? params]) {
|
||||||
return inner.modify(id, decoder(data), params).then(encoder);
|
return inner.modify(id, decoder(data), params).then(encoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,17 +254,17 @@ class _MappedService<Id, Data, U> extends Service<Id, U> {
|
||||||
Stream<U> get onUpdated => inner.onUpdated.map(encoder);
|
Stream<U> get onUpdated => inner.onUpdated.map(encoder);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<U> read(Id id, [Map<String, dynamic> params]) {
|
Future<U> read(Id id, [Map<String, dynamic>? params]) {
|
||||||
return inner.read(id, params).then(encoder);
|
return inner.read(id, params).then(encoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<U> remove(Id id, [Map<String, dynamic> params]) {
|
Future<U> remove(Id id, [Map<String, dynamic>? params]) {
|
||||||
return inner.remove(id, params).then(encoder);
|
return inner.remove(id, params).then(encoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<U> update(Id id, U data, [Map<String, dynamic> params]) {
|
Future<U> update(Id id, U data, [Map<String, dynamic>? params]) {
|
||||||
return inner.update(id, decoder(data), params).then(encoder);
|
return inner.update(id, decoder(data), params).then(encoder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,9 +277,9 @@ class ServiceList<Id, Data> extends DelegatingList<Data> {
|
||||||
/// A function used to compare the ID's two items for equality.
|
/// A function used to compare the ID's two items for equality.
|
||||||
///
|
///
|
||||||
/// Defaults to comparing the [idField] of `Map` instances.
|
/// Defaults to comparing the [idField] of `Map` instances.
|
||||||
Equality<Data> get equality => _equality;
|
Equality<Data>? get equality => _equality;
|
||||||
|
|
||||||
Equality<Data> _equality;
|
Equality<Data>? _equality;
|
||||||
|
|
||||||
final Service<Id, Data> service;
|
final Service<Id, Data> service;
|
||||||
|
|
||||||
|
@ -285,15 +287,16 @@ class ServiceList<Id, Data> extends DelegatingList<Data> {
|
||||||
|
|
||||||
final List<StreamSubscription> _subs = [];
|
final List<StreamSubscription> _subs = [];
|
||||||
|
|
||||||
ServiceList(this.service, {this.idField = 'id', Equality<Data> equality})
|
ServiceList(this.service, {this.idField = 'id', Equality<Data>? equality})
|
||||||
: super([]) {
|
: super([]) {
|
||||||
_equality = equality;
|
_equality = equality;
|
||||||
_equality ??= EqualityBy<Data, Id>((map) {
|
_equality ??= EqualityBy<Data, Id?>((map) {
|
||||||
if (map is Map)
|
if (map is Map) {
|
||||||
return map[idField ?? 'id'] as Id;
|
return map[idField] as Id?;
|
||||||
else
|
} else {
|
||||||
throw UnsupportedError(
|
throw UnsupportedError(
|
||||||
'ServiceList only knows how to find the id from a Map object. Provide a custom `Equality` in your call to the constructor.');
|
'ServiceList only knows how to find the id from a Map object. Provide a custom `Equality` in your call to the constructor.');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Index
|
// Index
|
||||||
_subs.add(service.onIndexed.where(_notNull).listen((data) {
|
_subs.add(service.onIndexed.where(_notNull).listen((data) {
|
||||||
|
@ -310,15 +313,17 @@ class ServiceList<Id, Data> extends DelegatingList<Data> {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Modified/Updated
|
// Modified/Updated
|
||||||
handleModified(Data item) {
|
void handleModified(Data item) {
|
||||||
var indices = <int>[];
|
var indices = <int>[];
|
||||||
|
|
||||||
for (int i = 0; i < length; i++) {
|
for (var i = 0; i < length; i++) {
|
||||||
if (_equality.equals(item, this[i])) indices.add(i);
|
if (_equality!.equals(item, this[i])) indices.add(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (indices.isNotEmpty) {
|
if (indices.isNotEmpty) {
|
||||||
for (var i in indices) this[i] = item;
|
for (var i in indices) {
|
||||||
|
this[i] = item;
|
||||||
|
}
|
||||||
|
|
||||||
_onChange.add(this);
|
_onChange.add(this);
|
||||||
}
|
}
|
||||||
|
@ -331,7 +336,7 @@ class ServiceList<Id, Data> extends DelegatingList<Data> {
|
||||||
|
|
||||||
// Removed
|
// Removed
|
||||||
_subs.add(service.onRemoved.where(_notNull).listen((item) {
|
_subs.add(service.onRemoved.where(_notNull).listen((item) {
|
||||||
removeWhere((x) => _equality.equals(item, x));
|
removeWhere((x) => _equality!.equals(item, x));
|
||||||
_onChange.add(this);
|
_onChange.add(this);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ const Map<String, String> _writeHeaders = {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
};
|
};
|
||||||
|
|
||||||
Map<String, String> _buildQuery(Map<String, dynamic> params) {
|
Map<String, String>? _buildQuery(Map<String, dynamic>? params) {
|
||||||
return params?.map((k, v) => MapEntry(k, v.toString()));
|
return params?.map((k, v) => MapEntry(k, v.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ bool _invalid(http.Response response) =>
|
||||||
response.statusCode >= 300;
|
response.statusCode >= 300;
|
||||||
|
|
||||||
AngelHttpException failure(http.Response response,
|
AngelHttpException failure(http.Response response,
|
||||||
{error, String message, StackTrace stack}) {
|
{error, String? message, StackTrace? stack}) {
|
||||||
try {
|
try {
|
||||||
var v = json.decode(response.body);
|
var v = json.decode(response.body);
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ abstract class BaseAngelClient extends Angel {
|
||||||
final StreamController<AngelAuthResult> _onAuthenticated =
|
final StreamController<AngelAuthResult> _onAuthenticated =
|
||||||
StreamController<AngelAuthResult>();
|
StreamController<AngelAuthResult>();
|
||||||
final List<Service> _services = [];
|
final List<Service> _services = [];
|
||||||
final http.BaseClient client;
|
final http.BaseClient? client;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<AngelAuthResult> get onAuthenticated => _onAuthenticated.stream;
|
Stream<AngelAuthResult> get onAuthenticated => _onAuthenticated.stream;
|
||||||
|
@ -61,7 +61,7 @@ abstract class BaseAngelClient extends Angel {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<AngelAuthResult> authenticate(
|
Future<AngelAuthResult> authenticate(
|
||||||
{String type,
|
{String? type,
|
||||||
credentials,
|
credentials,
|
||||||
String authEndpoint = '/auth',
|
String authEndpoint = '/auth',
|
||||||
@deprecated String reviveEndpoint = '/auth/token'}) async {
|
@deprecated String reviveEndpoint = '/auth/token'}) async {
|
||||||
|
@ -92,14 +92,12 @@ abstract class BaseAngelClient extends Angel {
|
||||||
//var v = json.decode(response.body);
|
//var v = json.decode(response.body);
|
||||||
var v = jsonDecode(response.body);
|
var v = jsonDecode(response.body);
|
||||||
|
|
||||||
if (v is! Map ||
|
if (v is! Map || !v.containsKey('data') || !v.containsKey('token')) {
|
||||||
!(v as Map).containsKey('data') ||
|
|
||||||
!(v as Map).containsKey('token')) {
|
|
||||||
throw AngelHttpException.notAuthenticated(
|
throw AngelHttpException.notAuthenticated(
|
||||||
message: "Auth endpoint '$url' did not return a proper response.");
|
message: "Auth endpoint '$url' did not return a proper response.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var r = AngelAuthResult.fromMap(v as Map);
|
var r = AngelAuthResult.fromMap(v);
|
||||||
_onAuthenticated.add(r);
|
_onAuthenticated.add(r);
|
||||||
return r;
|
return r;
|
||||||
} on AngelHttpException {
|
} on AngelHttpException {
|
||||||
|
@ -111,7 +109,7 @@ abstract class BaseAngelClient extends Angel {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
client.close();
|
client!.close();
|
||||||
await _onAuthenticated.close();
|
await _onAuthenticated.close();
|
||||||
await Future.wait(_services.map((s) => s.close())).then((_) {
|
await Future.wait(_services.map((s) => s.close())).then((_) {
|
||||||
_services.clear();
|
_services.clear();
|
||||||
|
@ -128,13 +126,13 @@ abstract class BaseAngelClient extends Angel {
|
||||||
if (authToken?.isNotEmpty == true) {
|
if (authToken?.isNotEmpty == true) {
|
||||||
request.headers['authorization'] ??= 'Bearer $authToken';
|
request.headers['authorization'] ??= 'Bearer $authToken';
|
||||||
}
|
}
|
||||||
return client.send(request);
|
return client!.send(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a non-streaming [Request] and returns a non-streaming [Response].
|
/// Sends a non-streaming [Request] and returns a non-streaming [Response].
|
||||||
Future<http.Response> sendUnstreamed(
|
Future<http.Response> sendUnstreamed(
|
||||||
String method, url, Map<String, String> headers,
|
String method, url, Map<String, String>? headers,
|
||||||
[body, Encoding encoding]) async {
|
[body, Encoding? encoding]) async {
|
||||||
var request =
|
var request =
|
||||||
http.Request(method, url is Uri ? url : Uri.parse(url.toString()));
|
http.Request(method, url is Uri ? url : Uri.parse(url.toString()));
|
||||||
|
|
||||||
|
@ -160,12 +158,12 @@ abstract class BaseAngelClient extends Angel {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Service<Id, Data> service<Id, Data>(String path,
|
Service<Id, Data> service<Id, Data>(String path,
|
||||||
{Type type, AngelDeserializer<Data> deserializer}) {
|
{Type? type, AngelDeserializer<Data>? deserializer}) {
|
||||||
var url = baseUrl.replace(path: p.join(baseUrl.path, path));
|
var url = baseUrl.replace(path: p.join(baseUrl.path, path));
|
||||||
var s = BaseAngelService<Id, Data>(client, this, url,
|
var s = BaseAngelService<Id, Data>(client, this, url,
|
||||||
deserializer: deserializer);
|
deserializer: deserializer);
|
||||||
_services.add(s);
|
_services.add(s);
|
||||||
return s;
|
return s as Service<Id, Data>;
|
||||||
}
|
}
|
||||||
|
|
||||||
Uri _join(url) {
|
Uri _join(url) {
|
||||||
|
@ -180,65 +178,65 @@ abstract class BaseAngelClient extends Angel {
|
||||||
//}
|
//}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> get(url, {Map<String, String> headers}) async {
|
Future<http.Response> get(url, {Map<String, String>? headers}) async {
|
||||||
return sendUnstreamed('GET', _join(url), headers);
|
return sendUnstreamed('GET', _join(url), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> head(url, {Map<String, String> headers}) async {
|
Future<http.Response> head(url, {Map<String, String>? headers}) async {
|
||||||
return sendUnstreamed('HEAD', _join(url), headers);
|
return sendUnstreamed('HEAD', _join(url), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> patch(url,
|
Future<http.Response> patch(url,
|
||||||
{body, Map<String, String> headers, Encoding encoding}) async {
|
{body, Map<String, String>? headers, Encoding? encoding}) async {
|
||||||
return sendUnstreamed('PATCH', _join(url), headers, body, encoding);
|
return sendUnstreamed('PATCH', _join(url), headers, body, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> post(url,
|
Future<http.Response> post(url,
|
||||||
{body, Map<String, String> headers, Encoding encoding}) async {
|
{body, Map<String, String>? headers, Encoding? encoding}) async {
|
||||||
return sendUnstreamed('POST', _join(url), headers, body, encoding);
|
return sendUnstreamed('POST', _join(url), headers, body, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> put(url,
|
Future<http.Response> put(url,
|
||||||
{body, Map<String, String> headers, Encoding encoding}) async {
|
{body, Map<String, String>? headers, Encoding? encoding}) async {
|
||||||
return sendUnstreamed('PUT', _join(url), headers, body, encoding);
|
return sendUnstreamed('PUT', _join(url), headers, body, encoding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BaseAngelService<Id, Data> extends Service<Id, Data> {
|
class BaseAngelService<Id, Data> extends Service<Id, Data?> {
|
||||||
@override
|
@override
|
||||||
final BaseAngelClient app;
|
final BaseAngelClient app;
|
||||||
final Uri baseUrl;
|
final Uri baseUrl;
|
||||||
final http.BaseClient client;
|
final http.BaseClient? client;
|
||||||
final AngelDeserializer<Data> deserializer;
|
final AngelDeserializer<Data>? deserializer;
|
||||||
|
|
||||||
final StreamController<List<Data>> _onIndexed = StreamController();
|
final StreamController<List<Data?>> _onIndexed = StreamController();
|
||||||
final StreamController<Data> _onRead = StreamController(),
|
final StreamController<Data?> _onRead = StreamController(),
|
||||||
_onCreated = StreamController(),
|
_onCreated = StreamController(),
|
||||||
_onModified = StreamController(),
|
_onModified = StreamController(),
|
||||||
_onUpdated = StreamController(),
|
_onUpdated = StreamController(),
|
||||||
_onRemoved = StreamController();
|
_onRemoved = StreamController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<List<Data>> get onIndexed => _onIndexed.stream;
|
Stream<List<Data?>> get onIndexed => _onIndexed.stream;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<Data> get onRead => _onRead.stream;
|
Stream<Data?> get onRead => _onRead.stream;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<Data> get onCreated => _onCreated.stream;
|
Stream<Data?> get onCreated => _onCreated.stream;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<Data> get onModified => _onModified.stream;
|
Stream<Data?> get onModified => _onModified.stream;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<Data> get onUpdated => _onUpdated.stream;
|
Stream<Data?> get onUpdated => _onUpdated.stream;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<Data> get onRemoved => _onRemoved.stream;
|
Stream<Data?> get onRemoved => _onRemoved.stream;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future close() async {
|
Future close() async {
|
||||||
|
@ -257,8 +255,8 @@ class BaseAngelService<Id, Data> extends Service<Id, Data> {
|
||||||
@deprecated
|
@deprecated
|
||||||
String get basePath => baseUrl.toString();
|
String get basePath => baseUrl.toString();
|
||||||
|
|
||||||
Data deserialize(x) {
|
Data? deserialize(x) {
|
||||||
return deserializer != null ? deserializer(x) : x as Data;
|
return deserializer != null ? deserializer!(x) : x as Data?;
|
||||||
}
|
}
|
||||||
|
|
||||||
String makeBody(x) {
|
String makeBody(x) {
|
||||||
|
@ -267,15 +265,15 @@ class BaseAngelService<Id, Data> extends Service<Id, Data> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<http.StreamedResponse> send(http.BaseRequest request) {
|
Future<http.StreamedResponse> send(http.BaseRequest request) {
|
||||||
if (app.authToken != null && app.authToken.isNotEmpty) {
|
if (app.authToken != null && app.authToken!.isNotEmpty) {
|
||||||
request.headers['Authorization'] = 'Bearer ${app.authToken}';
|
request.headers['Authorization'] = 'Bearer ${app.authToken}';
|
||||||
}
|
}
|
||||||
|
|
||||||
return client.send(request);
|
return client!.send(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Data>> index([Map<String, dynamic> params]) async {
|
Future<List<Data?>?> index([Map<String, dynamic>? params]) async {
|
||||||
var url = baseUrl.replace(queryParameters: _buildQuery(params));
|
var url = baseUrl.replace(queryParameters: _buildQuery(params));
|
||||||
var response = await app.sendUnstreamed('GET', url, _readHeaders);
|
var response = await app.sendUnstreamed('GET', url, _readHeaders);
|
||||||
|
|
||||||
|
@ -304,7 +302,7 @@ class BaseAngelService<Id, Data> extends Service<Id, Data> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Data> read(id, [Map<String, dynamic> params]) async {
|
Future<Data?> read(id, [Map<String, dynamic>? params]) async {
|
||||||
var url = baseUrl.replace(
|
var url = baseUrl.replace(
|
||||||
path: p.join(baseUrl.path, id.toString()),
|
path: p.join(baseUrl.path, id.toString()),
|
||||||
queryParameters: _buildQuery(params));
|
queryParameters: _buildQuery(params));
|
||||||
|
@ -313,54 +311,58 @@ class BaseAngelService<Id, Data> extends Service<Id, Data> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (_invalid(response)) {
|
if (_invalid(response)) {
|
||||||
if (_onRead.hasListener)
|
if (_onRead.hasListener) {
|
||||||
_onRead.addError(failure(response));
|
_onRead.addError(failure(response));
|
||||||
else
|
} else {
|
||||||
throw failure(response);
|
throw failure(response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var r = deserialize(json.decode(response.body));
|
var r = deserialize(json.decode(response.body));
|
||||||
_onRead.add(r);
|
_onRead.add(r);
|
||||||
return r;
|
return r;
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
if (_onRead.hasListener)
|
if (_onRead.hasListener) {
|
||||||
_onRead.addError(e, st);
|
_onRead.addError(e, st);
|
||||||
else
|
} else {
|
||||||
throw failure(response, error: e, stack: st);
|
throw failure(response, error: e, stack: st);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Data> create(data, [Map<String, dynamic> params]) async {
|
Future<Data?> create(data, [Map<String, dynamic>? params]) async {
|
||||||
var url = baseUrl.replace(queryParameters: _buildQuery(params));
|
var url = baseUrl.replace(queryParameters: _buildQuery(params));
|
||||||
var response =
|
var response =
|
||||||
await app.sendUnstreamed('POST', url, _writeHeaders, makeBody(data));
|
await app.sendUnstreamed('POST', url, _writeHeaders, makeBody(data));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (_invalid(response)) {
|
if (_invalid(response)) {
|
||||||
if (_onCreated.hasListener)
|
if (_onCreated.hasListener) {
|
||||||
_onCreated.addError(failure(response));
|
_onCreated.addError(failure(response));
|
||||||
else
|
} else {
|
||||||
throw failure(response);
|
throw failure(response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var r = deserialize(json.decode(response.body));
|
var r = deserialize(json.decode(response.body));
|
||||||
_onCreated.add(r);
|
_onCreated.add(r);
|
||||||
return r;
|
return r;
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
if (_onCreated.hasListener)
|
if (_onCreated.hasListener) {
|
||||||
_onCreated.addError(e, st);
|
_onCreated.addError(e, st);
|
||||||
else
|
} else {
|
||||||
throw failure(response, error: e, stack: st);
|
throw failure(response, error: e, stack: st);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Data> modify(id, data, [Map<String, dynamic> params]) async {
|
Future<Data?> modify(id, data, [Map<String, dynamic>? params]) async {
|
||||||
var url = baseUrl.replace(
|
var url = baseUrl.replace(
|
||||||
path: p.join(baseUrl.path, id.toString()),
|
path: p.join(baseUrl.path, id.toString()),
|
||||||
queryParameters: _buildQuery(params));
|
queryParameters: _buildQuery(params));
|
||||||
|
@ -370,27 +372,29 @@ class BaseAngelService<Id, Data> extends Service<Id, Data> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (_invalid(response)) {
|
if (_invalid(response)) {
|
||||||
if (_onModified.hasListener)
|
if (_onModified.hasListener) {
|
||||||
_onModified.addError(failure(response));
|
_onModified.addError(failure(response));
|
||||||
else
|
} else {
|
||||||
throw failure(response);
|
throw failure(response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var r = deserialize(json.decode(response.body));
|
var r = deserialize(json.decode(response.body));
|
||||||
_onModified.add(r);
|
_onModified.add(r);
|
||||||
return r;
|
return r;
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
if (_onModified.hasListener)
|
if (_onModified.hasListener) {
|
||||||
_onModified.addError(e, st);
|
_onModified.addError(e, st);
|
||||||
else
|
} else {
|
||||||
throw failure(response, error: e, stack: st);
|
throw failure(response, error: e, stack: st);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Data> update(id, data, [Map<String, dynamic> params]) async {
|
Future<Data?> update(id, data, [Map<String, dynamic>? params]) async {
|
||||||
var url = baseUrl.replace(
|
var url = baseUrl.replace(
|
||||||
path: p.join(baseUrl.path, id.toString()),
|
path: p.join(baseUrl.path, id.toString()),
|
||||||
queryParameters: _buildQuery(params));
|
queryParameters: _buildQuery(params));
|
||||||
|
@ -400,27 +404,29 @@ class BaseAngelService<Id, Data> extends Service<Id, Data> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (_invalid(response)) {
|
if (_invalid(response)) {
|
||||||
if (_onUpdated.hasListener)
|
if (_onUpdated.hasListener) {
|
||||||
_onUpdated.addError(failure(response));
|
_onUpdated.addError(failure(response));
|
||||||
else
|
} else {
|
||||||
throw failure(response);
|
throw failure(response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var r = deserialize(json.decode(response.body));
|
var r = deserialize(json.decode(response.body));
|
||||||
_onUpdated.add(r);
|
_onUpdated.add(r);
|
||||||
return r;
|
return r;
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
if (_onUpdated.hasListener)
|
if (_onUpdated.hasListener) {
|
||||||
_onUpdated.addError(e, st);
|
_onUpdated.addError(e, st);
|
||||||
else
|
} else {
|
||||||
throw failure(response, error: e, stack: st);
|
throw failure(response, error: e, stack: st);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Data> remove(id, [Map<String, dynamic> params]) async {
|
Future<Data?> remove(id, [Map<String, dynamic>? params]) async {
|
||||||
var url = baseUrl.replace(
|
var url = baseUrl.replace(
|
||||||
path: p.join(baseUrl.path, id.toString()),
|
path: p.join(baseUrl.path, id.toString()),
|
||||||
queryParameters: _buildQuery(params));
|
queryParameters: _buildQuery(params));
|
||||||
|
@ -429,20 +435,22 @@ class BaseAngelService<Id, Data> extends Service<Id, Data> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (_invalid(response)) {
|
if (_invalid(response)) {
|
||||||
if (_onRemoved.hasListener)
|
if (_onRemoved.hasListener) {
|
||||||
_onRemoved.addError(failure(response));
|
_onRemoved.addError(failure(response));
|
||||||
else
|
} else {
|
||||||
throw failure(response);
|
throw failure(response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var r = deserialize(json.decode(response.body));
|
var r = deserialize(json.decode(response.body));
|
||||||
_onRemoved.add(r);
|
_onRemoved.add(r);
|
||||||
return r;
|
return r;
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
if (_onRemoved.hasListener)
|
if (_onRemoved.hasListener) {
|
||||||
_onRemoved.addError(e, st);
|
_onRemoved.addError(e, st);
|
||||||
else
|
} else {
|
||||||
throw failure(response, error: e, stack: st);
|
throw failure(response, error: e, stack: st);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -13,20 +13,21 @@ export 'angel_client.dart';
|
||||||
|
|
||||||
/// Queries an Angel server via REST.
|
/// Queries an Angel server via REST.
|
||||||
class Rest extends BaseAngelClient {
|
class Rest extends BaseAngelClient {
|
||||||
Rest(String basePath) : super(new http.BrowserClient(), basePath);
|
Rest(String basePath) : super(http.BrowserClient(), basePath);
|
||||||
|
|
||||||
|
@override
|
||||||
Future<AngelAuthResult> authenticate(
|
Future<AngelAuthResult> authenticate(
|
||||||
{String type,
|
{String? type,
|
||||||
credentials,
|
credentials,
|
||||||
String authEndpoint = '/auth',
|
String authEndpoint = '/auth',
|
||||||
@deprecated String reviveEndpoint = '/auth/token'}) async {
|
@deprecated String reviveEndpoint = '/auth/token'}) async {
|
||||||
if (type == null || type == 'token') {
|
if (type == null || type == 'token') {
|
||||||
if (!window.localStorage.containsKey('token')) {
|
if (!window.localStorage.containsKey('token')) {
|
||||||
throw new Exception(
|
throw Exception(
|
||||||
'Cannot revive token from localStorage - there is none.');
|
'Cannot revive token from localStorage - there is none.');
|
||||||
}
|
}
|
||||||
|
|
||||||
var token = json.decode(window.localStorage['token']);
|
var token = json.decode(window.localStorage['token']!);
|
||||||
credentials ??= {'token': token};
|
credentials ??= {'token': token};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,33 +40,34 @@ class Rest extends BaseAngelClient {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<String> authenticateViaPopup(String url,
|
Stream<String> authenticateViaPopup(String url,
|
||||||
{String eventName = 'token', String errorMessage}) {
|
{String eventName = 'token', String? errorMessage}) {
|
||||||
var ctrl = new StreamController<String>();
|
var ctrl = StreamController<String>();
|
||||||
var wnd = window.open(url, 'angel_client_auth_popup');
|
var wnd = window.open(url, 'angel_client_auth_popup');
|
||||||
|
|
||||||
Timer t;
|
Timer t;
|
||||||
StreamSubscription sub;
|
StreamSubscription? sub;
|
||||||
t = new Timer.periodic(new Duration(milliseconds: 500), (timer) {
|
t = Timer.periodic(Duration(milliseconds: 500), (timer) {
|
||||||
if (!ctrl.isClosed) {
|
if (!ctrl.isClosed) {
|
||||||
if (wnd.closed) {
|
if (wnd.closed!) {
|
||||||
ctrl.addError(new AngelHttpException.notAuthenticated(
|
ctrl.addError(AngelHttpException.notAuthenticated(
|
||||||
message:
|
message:
|
||||||
errorMessage ?? 'Authentication via popup window failed.'));
|
errorMessage ?? 'Authentication via popup window failed.'));
|
||||||
ctrl.close();
|
ctrl.close();
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
sub?.cancel();
|
sub?.cancel();
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
sub = window.on[eventName ?? 'token'].listen((Event ev) {
|
sub = window.on[eventName].listen((Event ev) {
|
||||||
var e = ev as CustomEvent;
|
var e = ev as CustomEvent;
|
||||||
if (!ctrl.isClosed) {
|
if (!ctrl.isClosed) {
|
||||||
ctrl.add(e.detail.toString());
|
ctrl.add(e.detail.toString());
|
||||||
t.cancel();
|
t.cancel();
|
||||||
ctrl.close();
|
ctrl.close();
|
||||||
sub.cancel();
|
sub!.cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,12 @@ export 'angel_client.dart';
|
||||||
|
|
||||||
/// Queries an Angel server via REST.
|
/// Queries an Angel server via REST.
|
||||||
class Rest extends BaseAngelClient {
|
class Rest extends BaseAngelClient {
|
||||||
Rest(String basePath) : super(new http.Client() as http.BaseClient, basePath);
|
Rest(String basePath) : super(http.Client() as http.BaseClient, basePath);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<String> authenticateViaPopup(String url,
|
Stream<String> authenticateViaPopup(String url,
|
||||||
{String eventName = 'token'}) {
|
{String eventName = 'token'}) {
|
||||||
throw new UnimplementedError(
|
throw UnimplementedError(
|
||||||
'Opening popup windows is not supported in the `flutter` client.');
|
'Opening popup windows is not supported in the `flutter` client.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,11 @@ class Rest extends BaseAngelClient {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Service<Id, Data> service<Id, Data>(String path,
|
Service<Id, Data> service<Id, Data>(String path,
|
||||||
{Type type, AngelDeserializer deserializer}) {
|
{Type? type, AngelDeserializer? deserializer}) {
|
||||||
var url = baseUrl.replace(path: p.join(baseUrl.path, path));
|
var url = baseUrl.replace(path: p.join(baseUrl.path, path));
|
||||||
var s = RestService<Id, Data>(client, this, url, type);
|
var s = RestService<Id, Data>(client, this, url, type);
|
||||||
_services.add(s);
|
_services.add(s);
|
||||||
return s;
|
return s as Service<Id, Data>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -42,21 +42,21 @@ 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 Type type;
|
final Type? type;
|
||||||
|
|
||||||
RestService(http.BaseClient client, BaseAngelClient app, url, this.type)
|
RestService(http.BaseClient? client, BaseAngelClient app, url, this.type)
|
||||||
: super(client, app, url);
|
: super(client, app, url);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Data deserialize(x) {
|
Data? deserialize(x) {
|
||||||
print(x);
|
print(x);
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
return x.runtimeType == type
|
return x.runtimeType == type
|
||||||
? x as Data
|
? x as Data?
|
||||||
: god.deserializeDatum(x, outputType: type) as Data;
|
: god.deserializeDatum(x, outputType: type) as Data?;
|
||||||
}
|
}
|
||||||
|
|
||||||
return x as Data;
|
return x as Data?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,41 +1,45 @@
|
||||||
name: angel_client
|
name: angel_client
|
||||||
version: 3.0.0
|
version: 4.0.0
|
||||||
description: Support for querying Angel servers in the browser, Flutter, and command-line.
|
description: Support for querying Angel servers in the browser, Flutter, and command-line.
|
||||||
author: Tobe O <thosakwe@gmail.com>
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
homepage: https://github.com/angel-dart/angel_client
|
homepage: https://github.com/angel-dart/angel_client
|
||||||
publish_to: none
|
publish_to: none
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.10.0 <3.0.0"
|
sdk: '>=2.12.0 <3.0.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
angel_http_exception:
|
angel_http_exception:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/dukefirehawk/angel.git
|
url: https://github.com/dukefirehawk/angel.git
|
||||||
ref: sdk-2.12.x
|
ref: sdk-2.12.x_nnbd
|
||||||
path: packages/http_exception
|
path: packages/http_exception
|
||||||
collection: ^1.0.0
|
collection: ^1.15.0
|
||||||
http: ^0.13.0
|
http: ^0.13.1
|
||||||
json_god:
|
json_god:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/dukefirehawk/angel.git
|
url: https://github.com/dukefirehawk/angel.git
|
||||||
ref: sdk-2.12.x
|
ref: sdk-2.12.x_nnbd
|
||||||
path: packages/json_god
|
path: packages/json_god
|
||||||
#dart_json_mapper: ^1.7.0
|
#dart_json_mapper: ^1.7.0
|
||||||
meta: ^1.0.0
|
meta: ^1.3.0
|
||||||
path: ^1.0.0
|
path: ^1.8.0
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
angel_framework:
|
angel_framework:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/dukefirehawk/angel.git
|
url: https://github.com/dukefirehawk/angel.git
|
||||||
ref: sdk-2.12.x
|
ref: sdk-2.12.x_nnbd
|
||||||
path: packages/framework
|
path: packages/framework
|
||||||
angel_model:
|
angel_model:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/dukefirehawk/angel.git
|
url: https://github.com/dukefirehawk/angel.git
|
||||||
ref: sdk-2.12.x
|
ref: sdk-2.12.x_nnbd
|
||||||
path: packages/model
|
path: packages/model
|
||||||
async: ^2.0.0
|
async: ^2.5.0
|
||||||
build_runner: ^1.0.0
|
build_runner: ^1.12.2
|
||||||
build_web_compilers: ^2.12.2
|
build_web_compilers: ^2.16.5
|
||||||
mock_request: ^1.0.0
|
mock_request:
|
||||||
|
git:
|
||||||
|
url: https://github.com/dukefirehawk/angel.git
|
||||||
|
ref: sdk-2.12.x_nnbd
|
||||||
|
path: packages/mock_request
|
||||||
pedantic: ^1.11.0
|
pedantic: ^1.11.0
|
||||||
test: ^1.16.5
|
test: ^1.16.8
|
||||||
|
|
|
@ -9,75 +9,75 @@ void main() {
|
||||||
test('sets method,body,headers,path', () async {
|
test('sets method,body,headers,path', () async {
|
||||||
await app.post(Uri.parse('/post'),
|
await app.post(Uri.parse('/post'),
|
||||||
headers: {'method': 'post'}, body: 'post');
|
headers: {'method': 'post'}, body: 'post');
|
||||||
expect(app.client.spec.method, 'POST');
|
expect(app.client.spec!.method, 'POST');
|
||||||
expect(app.client.spec.path, '/post');
|
expect(app.client.spec!.path, '/post');
|
||||||
expect(app.client.spec.headers['method'], 'post');
|
expect(app.client.spec!.headers['method'], 'post');
|
||||||
expect(await read(app.client.spec.request.finalize()), 'post');
|
expect(await read(app.client.spec!.request.finalize()), 'post');
|
||||||
});
|
});
|
||||||
|
|
||||||
group('service methods', () {
|
group('service methods', () {
|
||||||
test('index', () async {
|
test('index', () async {
|
||||||
await todoService.index();
|
await todoService.index();
|
||||||
expect(app.client.spec.method, 'GET');
|
expect(app.client.spec!.method, 'GET');
|
||||||
expect(app.client.spec.path, '/api/todos');
|
expect(app.client.spec!.path, '/api/todos');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('read', () async {
|
test('read', () async {
|
||||||
await todoService.read('sleep');
|
await todoService.read('sleep');
|
||||||
expect(app.client.spec.method, 'GET');
|
expect(app.client.spec!.method, 'GET');
|
||||||
expect(app.client.spec.path, '/api/todos/sleep');
|
expect(app.client.spec!.path, '/api/todos/sleep');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('create', () async {
|
test('create', () async {
|
||||||
await todoService.create({});
|
await todoService.create({});
|
||||||
expect(app.client.spec.method, 'POST');
|
expect(app.client.spec!.method, 'POST');
|
||||||
expect(app.client.spec.headers['content-type'],
|
expect(app.client.spec!.headers['content-type'],
|
||||||
startsWith('application/json'));
|
startsWith('application/json'));
|
||||||
expect(app.client.spec.path, '/api/todos');
|
expect(app.client.spec!.path, '/api/todos');
|
||||||
expect(await read(app.client.spec.request.finalize()), '{}');
|
expect(await read(app.client.spec!.request.finalize()), '{}');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('modify', () async {
|
test('modify', () async {
|
||||||
await todoService.modify('sleep', {});
|
await todoService.modify('sleep', {});
|
||||||
expect(app.client.spec.method, 'PATCH');
|
expect(app.client.spec!.method, 'PATCH');
|
||||||
expect(app.client.spec.headers['content-type'],
|
expect(app.client.spec!.headers['content-type'],
|
||||||
startsWith('application/json'));
|
startsWith('application/json'));
|
||||||
expect(app.client.spec.path, '/api/todos/sleep');
|
expect(app.client.spec!.path, '/api/todos/sleep');
|
||||||
expect(await read(app.client.spec.request.finalize()), '{}');
|
expect(await read(app.client.spec!.request.finalize()), '{}');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('update', () async {
|
test('update', () async {
|
||||||
await todoService.update('sleep', {});
|
await todoService.update('sleep', {});
|
||||||
expect(app.client.spec.method, 'POST');
|
expect(app.client.spec!.method, 'POST');
|
||||||
expect(app.client.spec.headers['content-type'],
|
expect(app.client.spec!.headers['content-type'],
|
||||||
startsWith('application/json'));
|
startsWith('application/json'));
|
||||||
expect(app.client.spec.path, '/api/todos/sleep');
|
expect(app.client.spec!.path, '/api/todos/sleep');
|
||||||
expect(await read(app.client.spec.request.finalize()), '{}');
|
expect(await read(app.client.spec!.request.finalize()), '{}');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('remove', () async {
|
test('remove', () async {
|
||||||
await todoService.remove('sleep');
|
await todoService.remove('sleep');
|
||||||
expect(app.client.spec.method, 'DELETE');
|
expect(app.client.spec!.method, 'DELETE');
|
||||||
expect(app.client.spec.path, '/api/todos/sleep');
|
expect(app.client.spec!.path, '/api/todos/sleep');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('authentication', () {
|
group('authentication', () {
|
||||||
test('no type defaults to token', () async {
|
test('no type defaults to token', () async {
|
||||||
await app.authenticate(credentials: '<jwt>');
|
await app.authenticate(credentials: '<jwt>');
|
||||||
expect(app.client.spec.path, '/auth/token');
|
expect(app.client.spec!.path, '/auth/token');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sets type', () async {
|
test('sets type', () async {
|
||||||
await app.authenticate(type: 'local');
|
await app.authenticate(type: 'local');
|
||||||
expect(app.client.spec.path, '/auth/local');
|
expect(app.client.spec!.path, '/auth/local');
|
||||||
});
|
});
|
||||||
|
|
||||||
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'});
|
||||||
expect(
|
expect(
|
||||||
await read(app.client.spec.request.finalize()),
|
await read(app.client.spec!.request.finalize()),
|
||||||
json.encode({'username': 'password'}),
|
json.encode({'username': 'password'}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,24 +10,25 @@ Future<String> read(Stream<List<int>> stream) =>
|
||||||
|
|
||||||
class MockAngel extends BaseAngelClient {
|
class MockAngel extends BaseAngelClient {
|
||||||
@override
|
@override
|
||||||
final SpecClient client = new SpecClient();
|
final SpecClient client = SpecClient();
|
||||||
|
|
||||||
MockAngel() : super(null, 'http://localhost:3000');
|
MockAngel() : super(null, 'http://localhost:3000');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
authenticateViaPopup(String url, {String eventName = 'token'}) {
|
Stream<String> authenticateViaPopup(String url,
|
||||||
throw new UnsupportedError('Nope');
|
{String eventName = 'token'}) {
|
||||||
|
throw UnsupportedError('Nope');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpecClient extends http.BaseClient {
|
class SpecClient extends http.BaseClient {
|
||||||
Spec _spec;
|
Spec? _spec;
|
||||||
|
|
||||||
Spec get spec => _spec;
|
Spec? get spec => _spec;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
send(http.BaseRequest request) {
|
Future<http.StreamedResponse> send(http.BaseRequest request) {
|
||||||
_spec = new Spec(request, request.method, request.url.path, request.headers,
|
_spec = Spec(request, request.method, request.url.path, request.headers,
|
||||||
request.contentLength);
|
request.contentLength);
|
||||||
dynamic data = {'text': 'Clean your room!', 'completed': true};
|
dynamic data = {'text': 'Clean your room!', 'completed': true};
|
||||||
|
|
||||||
|
@ -40,8 +41,8 @@ class SpecClient extends http.BaseClient {
|
||||||
data = [data];
|
data = [data];
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Future<http.StreamedResponse>.value(new http.StreamedResponse(
|
return Future<http.StreamedResponse>.value(http.StreamedResponse(
|
||||||
new Stream<List<int>>.fromIterable([utf8.encode(json.encode(data))]),
|
Stream<List<int>>.fromIterable([utf8.encode(json.encode(data))]),
|
||||||
200,
|
200,
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
|
@ -54,7 +55,7 @@ class Spec {
|
||||||
final http.BaseRequest request;
|
final http.BaseRequest request;
|
||||||
final String method, path;
|
final String method, path;
|
||||||
final Map<String, String> headers;
|
final Map<String, String> headers;
|
||||||
final int contentLength;
|
final int? contentLength;
|
||||||
|
|
||||||
Spec(this.request, this.method, this.path, this.headers, this.contentLength);
|
Spec(this.request, this.method, this.path, this.headers, this.contentLength);
|
||||||
|
|
||||||
|
|
|
@ -6,22 +6,22 @@ import 'package:angel_framework/http.dart' as s;
|
||||||
import 'package:pedantic/pedantic.dart';
|
import 'package:pedantic/pedantic.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
main() {
|
void main() {
|
||||||
HttpServer server;
|
late HttpServer server;
|
||||||
c.Angel app;
|
late c.Angel app;
|
||||||
c.ServiceList list;
|
late c.ServiceList list;
|
||||||
StreamQueue queue;
|
late StreamQueue queue;
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
var serverApp = new s.Angel();
|
var serverApp = s.Angel();
|
||||||
var http = new s.AngelHttp(serverApp);
|
var http = s.AngelHttp(serverApp);
|
||||||
serverApp.use('/api/todos', new s.MapService(autoIdAndDateFields: false));
|
serverApp.use('/api/todos', s.MapService(autoIdAndDateFields: false));
|
||||||
|
|
||||||
server = await http.startServer();
|
server = await http.startServer();
|
||||||
var uri = 'http://${server.address.address}:${server.port}';
|
var uri = 'http://${server.address.address}:${server.port}';
|
||||||
app = new c.Rest(uri);
|
app = c.Rest(uri);
|
||||||
list = new c.ServiceList(app.service('api/todos'));
|
list = c.ServiceList(app.service('api/todos'));
|
||||||
queue = new StreamQueue(list.onChange);
|
queue = StreamQueue(list.onChange);
|
||||||
});
|
});
|
||||||
|
|
||||||
tearDown(() async {
|
tearDown(() async {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import 'package:angel_model/angel_model.dart';
|
import 'package:angel_model/angel_model.dart';
|
||||||
|
|
||||||
class Postcard extends Model {
|
class Postcard extends Model {
|
||||||
String location;
|
String? location;
|
||||||
String message;
|
String? message;
|
||||||
|
|
||||||
Postcard({String id, this.location, this.message}) {
|
Postcard({String? id, this.location, this.message}) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
factory Postcard.fromJson(Map data) => new Postcard(
|
factory Postcard.fromJson(Map data) => Postcard(
|
||||||
id: data['id'].toString(),
|
id: data['id'].toString(),
|
||||||
location: data['location'].toString(),
|
location: data['location'].toString(),
|
||||||
message: data['message'].toString());
|
message: data['message'].toString());
|
||||||
|
|
|
@ -2,7 +2,7 @@ import 'dart:html';
|
||||||
import 'package:angel_client/browser.dart';
|
import 'package:angel_client/browser.dart';
|
||||||
|
|
||||||
/// Dummy app to ensure client works with DDC.
|
/// Dummy app to ensure client works with DDC.
|
||||||
main() {
|
void main() {
|
||||||
var app = new Rest(window.location.origin);
|
var app = Rest(window.location.origin);
|
||||||
window.alert(app.baseUrl.toString());
|
window.alert(app.baseUrl.toString());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue