Fixed client
This commit is contained in:
parent
d9d6ef1fcb
commit
c16b369de8
8 changed files with 98 additions and 59 deletions
|
@ -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<String, dynamic>` as body, not just `Map<String, String>`.
|
||||
|
||||
# 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<Data>`.
|
||||
* Add `Service.map`.
|
||||
|
||||
# 2.0.0-alpha.1
|
||||
## 2.0.0-alpha.1
|
||||
|
||||
* Refactor `params` to `Map<String, dynamic>`.
|
||||
|
||||
# 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`.
|
||||
## 1.1.0+1
|
||||
|
||||
* Added `ServiceList`.
|
||||
|
|
|
@ -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<AngelAuthResult> reviveJwt(String jwt) {
|
||||
return app.authenticate(credentials: {'token': jwt});
|
||||
|
@ -69,6 +72,7 @@ Future<AngelAuthResult> 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,
|
||||
);
|
||||
}
|
||||
```
|
||||
```
|
||||
|
|
|
@ -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<String, String> _readHeaders = {'Accept': 'application/json'};
|
||||
|
@ -16,8 +16,8 @@ const Map<String, String> _writeHeaders = {
|
|||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
Map<String, String>? _buildQuery(Map<String, dynamic>? params) {
|
||||
return params?.map((k, v) => MapEntry(k, v.toString()));
|
||||
Map<String, String> _buildQuery(Map<String, dynamic>? 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<AngelAuthResult> _onAuthenticated =
|
||||
StreamController<AngelAuthResult>();
|
||||
final List<Service> _services = [];
|
||||
final http.BaseClient? client;
|
||||
final http.BaseClient client;
|
||||
|
||||
final _p = Context(style: Style.url);
|
||||
|
||||
@override
|
||||
Stream<AngelAuthResult> 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<void> 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<Id, Data> service<Id, Data>(String path,
|
||||
{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,
|
||||
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<Id, Data> extends Service<Id, Data?> {
|
|||
@override
|
||||
final BaseAngelClient app;
|
||||
final Uri baseUrl;
|
||||
final http.BaseClient? client;
|
||||
final http.BaseClient client;
|
||||
final AngelDeserializer<Data>? deserializer;
|
||||
|
||||
final _p = Context(style: Style.url);
|
||||
|
||||
final StreamController<List<Data?>> _onIndexed = StreamController();
|
||||
final StreamController<Data?> _onRead = StreamController(),
|
||||
_onCreated = StreamController(),
|
||||
|
@ -267,11 +270,11 @@ class BaseAngelService<Id, Data> extends Service<Id, Data?> {
|
|||
request.headers['Authorization'] = 'Bearer ${app.authToken}';
|
||||
}
|
||||
|
||||
return client!.send(request);
|
||||
return client.send(request);
|
||||
}
|
||||
|
||||
@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 response = await app.sendUnstreamed('GET', url, _readHeaders);
|
||||
|
||||
|
@ -285,7 +288,14 @@ class BaseAngelService<Id, Data> extends Service<Id, Data?> {
|
|||
}
|
||||
|
||||
var v = json.decode(response.body) as List;
|
||||
var r = v.map(deserialize).toList();
|
||||
//var r = v.map(deserialize).toList();
|
||||
var r = <Data>[];
|
||||
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<Id, Data> extends Service<Id, Data?> {
|
|||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return [];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Data?> read(id, [Map<String, dynamic>? 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<Id, Data> extends Service<Id, Data?> {
|
|||
@override
|
||||
Future<Data?> modify(id, data, [Map<String, dynamic>? 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<Id, Data> extends Service<Id, Data?> {
|
|||
@override
|
||||
Future<Data?> update(id, data, [Map<String, dynamic>? 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<Id, Data> extends Service<Id, Data?> {
|
|||
@override
|
||||
Future<Data?> remove(id, [Map<String, dynamic>? 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);
|
||||
|
|
|
@ -44,7 +44,7 @@ class Rest extends BaseAngelClient {
|
|||
class RestService<Id, Data> extends BaseAngelService<Id, Data> {
|
||||
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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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<String> authenticateViaPopup(String url,
|
||||
|
|
|
@ -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'));
|
||||
|
|
|
@ -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';
|
||||
|
|
Loading…
Reference in a new issue