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