From c16b369de8fa676cbf1178a77f14e625b3543629 Mon Sep 17 00:00:00 2001 From: thomashii Date: Fri, 9 Jul 2021 22:19:16 +0800 Subject: [PATCH] Fixed client --- packages/client/CHANGELOG.md | 52 +++++++++++++++------ packages/client/README.md | 32 +++++++------ packages/client/lib/base_angel_client.dart | 54 +++++++++++++--------- packages/client/lib/io.dart | 2 +- packages/client/pubspec.yaml | 8 ++-- packages/client/test/common.dart | 2 +- packages/client/test/list_test.dart | 5 +- packages/framework/test/services_test.dart | 2 - 8 files changed, 98 insertions(+), 59 deletions(-) diff --git a/packages/client/CHANGELOG.md b/packages/client/CHANGELOG.md index 7fc15e9f..d88f8072 100644 --- a/packages/client/CHANGELOG.md +++ b/packages/client/CHANGELOG.md @@ -1,51 +1,73 @@ +# Change Log + +## 4.0.1 + +* Updated README +* Fixed NNBD issues +* Fixed path issue on Windows +* All 13 unit tests passed + +## 4.0.0 -# 4.0.0 * Migrated to support Dart SDK 2.12.x NNBD -# 3.0.0 +## 3.0.0 + * Migrated to work with Dart SDK 2.12.x Non NNBD -# 2.0.2 +## 2.0.2 + * `_join` previously discarded quer parameters, etc. * Allow any `Map` as body, not just `Map`. -# 2.0.1 +## 2.0.1 + * Change `BaseAngelClient` constructor to accept `dynamic` instead of `String` for `baseUrl. -# 2.0.0 +## 2.0.0 + * Deprecate `basePath` in favor of `baseUrl`. * `Angel` now extends `http.Client`. * Deprecate `auth_types`. -# 2.0.0-alpha.2 +## 2.0.0-alpha.2 + * Make Service `index` always return `List`. * Add `Service.map`. -# 2.0.0-alpha.1 +## 2.0.0-alpha.1 + * Refactor `params` to `Map`. -# 2.0.0-alpha +## 2.0.0-alpha + * Depend on Dart 2. * Depend on Angel 2. * Remove `dart2_constant`. -# 1.2.0+2 +## 1.2.0+2 + * Code cleanup + housekeeping, update to `dart2_constant`, and ensured build works with `2.0.0-dev.64.1`. -# 1.2.0+1 +## 1.2.0+1 + * Removed a type annotation in `authenticateViaPopup` to prevent breaking with DDC. -# 1.2.0 +## 1.2.0 + * `ServiceList` now uses `Equality` from `package:collection` to compare items. * `Service`s will now add service errors to corresponding streams if there is a listener. -# 1.1.0+3 +## 1.1.0+3 + * `ServiceList` no longer ignores empty `index` events. -# 1.1.0+2 +## 1.1.0+2 + * Updated `ServiceList` to also fire on `index`. -# 1.1.0+1 -* Added `ServiceList`. \ No newline at end of file +## 1.1.0+1 + +* Added `ServiceList`. diff --git a/packages/client/README.md b/packages/client/README.md index 02087317..c05116f0 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -1,11 +1,14 @@ -# angel3_client -[![version](https://img.shields.io/badge/pub-v4.0.0-brightgreen)](https://pub.dartlang.org/packages/angel3_client) +# Angel3 Client + +[![version](https://img.shields.io/badge/pub-v4.0.1-brightgreen)](https://pub.dartlang.org/packages/angel3_client) [![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) [![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/client/LICENSE) -# Usage +A browser, mobile and command line based client that supports querying Angel3 servers + +## Usage ```dart // Choose one or the other, depending on platform @@ -18,8 +21,7 @@ main() async { } ``` -You can call `service` to receive an instance of `Service`, which acts as a client to a -service on the server at the given path (say that five times fast!). +You can call `service` to receive an instance of `Service`, which acts as a client to a service on the server at the given path (say that five times fast!). ```dart foo() async { @@ -30,8 +32,7 @@ foo() async { } ``` -The CLI client also supports reflection via `angel3_json_god`. There is no need to work with Maps; -you can use the same class on the client and the server. +The CLI client also supports reflection via `angel3_json_god`. There is no need to work with Maps; you can use the same class on the client and the server. ```dart class Todo extends Model { @@ -50,11 +51,12 @@ bar() async { } ``` -Just like on the server, services support `index`, `read`, `create`, `modify`, `update` and -`remove`. +Just like on the server, services support `index`, `read`, `create`, `modify`, `update` and `remove`. ## Authentication + Local auth: + ```dart var auth = await app.authenticate(type: 'local', credentials: {username: ..., password: ...}); print(auth.token); @@ -62,6 +64,7 @@ print(auth.data); // User object ``` Revive an existing jwt: + ```dart Future reviveJwt(String jwt) { return app.authenticate(credentials: {'token': jwt}); @@ -69,6 +72,7 @@ Future reviveJwt(String jwt) { ``` Via Popup: + ```dart app.authenticateViaPopup('/auth/google').listen((jwt) { // Do something with the JWT @@ -76,6 +80,7 @@ app.authenticateViaPopup('/auth/google').listen((jwt) { ``` Resume a session from localStorage (browser only): + ```dart // Automatically checks for JSON-encoded 'token' in localStorage, // and tries to revive it @@ -83,13 +88,14 @@ await app.authenticate(); ``` Logout: + ```dart await app.logout(); ``` -# Live Updates -Oftentimes, you will want to update a collection based on updates from a service. -Use `ServiceList` for this case: +## Live Updates + +Oftentimes, you will want to update a collection based on updates from a service. Use `ServiceList` for this case: ```dart build(BuildContext context) async { @@ -100,4 +106,4 @@ build(BuildContext context) async { builder: _yourBuildFunction, ); } -``` \ No newline at end of file +``` diff --git a/packages/client/lib/base_angel_client.dart b/packages/client/lib/base_angel_client.dart index 81b7148b..2d2cf108 100644 --- a/packages/client/lib/base_angel_client.dart +++ b/packages/client/lib/base_angel_client.dart @@ -7,7 +7,7 @@ import 'package:http/src/base_request.dart' as http; import 'package:http/src/request.dart' as http; import 'package:http/src/response.dart' as http; import 'package:http/src/streamed_response.dart' as http; -import 'package:path/path.dart' as p; +import 'package:path/path.dart'; import 'angel3_client.dart'; const Map _readHeaders = {'Accept': 'application/json'}; @@ -16,8 +16,8 @@ const Map _writeHeaders = { 'Content-Type': 'application/json' }; -Map? _buildQuery(Map? params) { - return params?.map((k, v) => MapEntry(k, v.toString())); +Map _buildQuery(Map? params) { + return params?.map((k, v) => MapEntry(k, v.toString())) ?? {}; } bool _invalid(http.Response response) => @@ -50,7 +50,9 @@ abstract class BaseAngelClient extends Angel { final StreamController _onAuthenticated = StreamController(); final List _services = []; - final http.BaseClient? client; + final http.BaseClient client; + + final _p = Context(style: Style.url); @override Stream get onAuthenticated => _onAuthenticated.stream; @@ -66,13 +68,12 @@ abstract class BaseAngelClient extends Angel { type ??= 'token'; var segments = baseUrl.pathSegments - .followedBy(p.split(authEndpoint)) + .followedBy(_p.split(authEndpoint)) .followedBy([type]); - // TODO: convert windows path to proper url - var p1 = p.joinAll(segments).replaceAll('\\', '/'); + //var p1 = p.joinAll(segments).replaceAll('\\', '/'); - var url = baseUrl.replace(path: p1); + var url = baseUrl.replace(path: _p.joinAll(segments)); http.Response response; if (credentials != null) { @@ -107,7 +108,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(); @@ -124,7 +125,7 @@ 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]. @@ -157,7 +158,7 @@ abstract class BaseAngelClient extends Angel { @override Service service(String path, {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 = BaseAngelService(client, this, url, deserializer: deserializer); _services.add(s); @@ -167,7 +168,7 @@ abstract class BaseAngelClient extends Angel { Uri _join(url) { var u = url is Uri ? url : Uri.parse(url.toString()); if (u.hasScheme || u.hasAuthority) return u; - return u.replace(path: p.join(baseUrl.path, u.path)); + return u.replace(path: _p.join(baseUrl.path, u.path)); } //@override @@ -208,9 +209,11 @@ class BaseAngelService extends Service { @override final BaseAngelClient app; final Uri baseUrl; - final http.BaseClient? client; + final http.BaseClient client; final AngelDeserializer? deserializer; + final _p = Context(style: Style.url); + final StreamController> _onIndexed = StreamController(); final StreamController _onRead = StreamController(), _onCreated = StreamController(), @@ -267,11 +270,11 @@ class BaseAngelService extends Service { 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); @@ -285,7 +288,14 @@ class BaseAngelService extends Service { } var v = json.decode(response.body) as List; - var r = v.map(deserialize).toList(); + //var r = v.map(deserialize).toList(); + var r = []; + v.forEach((element) { + var a = deserialize(element); + if (a != null) { + r.add(a); + } + }); _onIndexed.add(r); return r; } catch (e, st) { @@ -296,13 +306,15 @@ class BaseAngelService extends Service { } } - return null; + return []; } @override Future read(id, [Map? params]) async { + var pa = _p.join(baseUrl.path, id.toString()); + print(pa); var url = baseUrl.replace( - path: p.join(baseUrl.path, id.toString()), + path: _p.join(baseUrl.path, id.toString()), queryParameters: _buildQuery(params)); var response = await app.sendUnstreamed('GET', url, _readHeaders); @@ -362,7 +374,7 @@ class BaseAngelService extends Service { @override Future modify(id, data, [Map? params]) async { var url = baseUrl.replace( - path: p.join(baseUrl.path, id.toString()), + path: _p.join(baseUrl.path, id.toString()), queryParameters: _buildQuery(params)); var response = @@ -394,7 +406,7 @@ class BaseAngelService extends Service { @override Future update(id, data, [Map? params]) async { var url = baseUrl.replace( - path: p.join(baseUrl.path, id.toString()), + path: _p.join(baseUrl.path, id.toString()), queryParameters: _buildQuery(params)); var response = @@ -426,7 +438,7 @@ class BaseAngelService extends Service { @override Future remove(id, [Map? params]) async { var url = baseUrl.replace( - path: p.join(baseUrl.path, id.toString()), + path: _p.join(baseUrl.path, id.toString()), queryParameters: _buildQuery(params)); var response = await app.sendUnstreamed('DELETE', url, _readHeaders); diff --git a/packages/client/lib/io.dart b/packages/client/lib/io.dart index 9c76bdd8..30e93d58 100644 --- a/packages/client/lib/io.dart +++ b/packages/client/lib/io.dart @@ -44,7 +44,7 @@ class Rest extends BaseAngelClient { class RestService extends BaseAngelService { 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 diff --git a/packages/client/pubspec.yaml b/packages/client/pubspec.yaml index 77c76298..80d6f27b 100644 --- a/packages/client/pubspec.yaml +++ b/packages/client/pubspec.yaml @@ -1,7 +1,8 @@ name: angel3_client -version: 4.0.0 -description: Support for querying Angel servers in the browser, Flutter, and command-line. -homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/client +version: 4.0.1 +description: A browser, mobile and command line based client that supports querying Angel3 servers +homepage: https://angel3-framework.web.app/ +respository: https://github.com/dukefirehawk/angel/tree/angel3/packages/client environment: sdk: '>=2.12.0 <3.0.0' dependencies: @@ -9,7 +10,6 @@ dependencies: angel3_json_god: ^4.0.0 collection: ^1.15.0 http: ^0.13.1 - #dart_json_mapper: ^1.7.0 meta: ^1.3.0 path: ^1.8.0 dev_dependencies: diff --git a/packages/client/test/common.dart b/packages/client/test/common.dart index 429beeb9..f48ed3c1 100644 --- a/packages/client/test/common.dart +++ b/packages/client/test/common.dart @@ -12,7 +12,7 @@ class MockAngel extends BaseAngelClient { @override final SpecClient client = SpecClient(); - MockAngel() : super(null, 'http://localhost:3000'); + MockAngel() : super(SpecClient(), 'http://localhost:3000'); @override Stream authenticateViaPopup(String url, diff --git a/packages/client/test/list_test.dart b/packages/client/test/list_test.dart index 8557fb3c..db57ebe4 100644 --- a/packages/client/test/list_test.dart +++ b/packages/client/test/list_test.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:angel3_client/io.dart' as c; import 'package:angel3_framework/angel3_framework.dart' as s; import 'package:angel3_framework/http.dart' as s; +import 'package:angel3_container/mirrors.dart'; import 'package:pedantic/pedantic.dart'; import 'package:test/test.dart'; @@ -13,11 +14,11 @@ void main() { late StreamQueue queue; setUp(() async { - var serverApp = s.Angel(); + var serverApp = s.Angel(reflector: MirrorsReflector()); 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 = c.Rest(uri); list = c.ServiceList(app.service('api/todos')); diff --git a/packages/framework/test/services_test.dart b/packages/framework/test/services_test.dart index d787c05d..46a34745 100644 --- a/packages/framework/test/services_test.dart +++ b/packages/framework/test/services_test.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:angel3_container/mirrors.dart'; import 'package:angel3_framework/angel3_framework.dart'; import 'package:angel3_framework/http.dart';