This commit is contained in:
thosakwe 2017-06-03 13:43:01 -04:00
parent 2479e8eaa7
commit f2fcc48642
6 changed files with 126 additions and 46 deletions

View file

@ -1,6 +1,6 @@
# angel_client # angel_client
[![pub 1.0.4](https://img.shields.io/badge/pub-1.0.4-brightgreen.svg)](https://pub.dartlang.org/packages/angel_client) [![pub 1.0.5](https://img.shields.io/badge/pub-1.0.5-brightgreen.svg)](https://pub.dartlang.org/packages/angel_client)
[![build status](https://travis-ci.org/angel-dart/client.svg)](https://travis-ci.org/angel-dart/client) [![build status](https://travis-ci.org/angel-dart/client.svg)](https://travis-ci.org/angel-dart/client)
Client library for the Angel framework. Client library for the Angel framework.

View file

@ -22,6 +22,9 @@ abstract class Angel {
Angel(String this.basePath); Angel(String this.basePath);
/// Fired whenever a WebSocket is successfully authenticated.
Stream<AngelAuthResult> get onAuthenticated;
Future<AngelAuthResult> authenticate( Future<AngelAuthResult> authenticate(
{String type, {String type,
credentials, credentials,
@ -41,7 +44,7 @@ abstract class Angel {
/// Logs the current user out of the application. /// Logs the current user out of the application.
Future logout(); Future logout();
Service service<T>(String path, {Type type, AngelDeserializer deserializer}); Service<T> service<T>(String path, {Type type, AngelDeserializer deserializer});
Future<http.Response> delete(String url, {Map<String, String> headers}); Future<http.Response> delete(String url, {Map<String, String> headers});
@ -87,10 +90,30 @@ class AngelAuthResult {
} }
/// Queries a service on an Angel server, with the same API. /// Queries a service on an Angel server, with the same API.
abstract class Service { abstract class Service<T> {
/// Fired on `indexed` events.
Stream<T> get onIndexed;
/// Fired on `read` events.
Stream<T> get onRead;
/// Fired on `created` events.
Stream<T> get onCreated;
/// Fired on `modified` events.
Stream<T> get onModified;
/// Fired on `updated` events.
Stream<T> get onUpdated;
/// Fired on `removed` events.
Stream<T> get onRemoved;
/// The Angel instance powering this service. /// The Angel instance powering this service.
Angel get app; Angel get app;
Future close();
/// Retrieves all resources. /// Retrieves all resources.
Future index([Map params]); Future index([Map params]);

View file

@ -54,8 +54,14 @@ AngelHttpException failure(http.Response response, {error, StackTrace stack}) {
} }
abstract class BaseAngelClient extends Angel { abstract class BaseAngelClient extends Angel {
final StreamController<AngelAuthResult> _onAuthenticated =
new StreamController<AngelAuthResult>();
final List<Service> _services = [];
final http.BaseClient client; final http.BaseClient client;
@override
Stream<AngelAuthResult> get onAuthenticated => _onAuthenticated.stream;
BaseAngelClient(this.client, String basePath) : super(basePath); BaseAngelClient(this.client, String basePath) : super(basePath);
@override @override
@ -87,7 +93,9 @@ abstract class BaseAngelClient extends Angel {
"Auth endpoint '$url' did not return a proper response."); "Auth endpoint '$url' did not return a proper response.");
} }
return new AngelAuthResult.fromMap(json); var r = new AngelAuthResult.fromMap(json);
_onAuthenticated.add(r);
return r;
} catch (e, st) { } catch (e, st) {
throw failure(response, error: e, stack: st); throw failure(response, error: e, stack: st);
} }
@ -117,7 +125,9 @@ abstract class BaseAngelClient extends Angel {
"Auth endpoint '$url' did not return a proper response."); "Auth endpoint '$url' did not return a proper response.");
} }
return new AngelAuthResult.fromMap(json); var r = new AngelAuthResult.fromMap(json);
_onAuthenticated.add(r);
return r;
} catch (e, st) { } catch (e, st) {
throw failure(response, error: e, stack: st); throw failure(response, error: e, stack: st);
} }
@ -126,6 +136,10 @@ abstract class BaseAngelClient extends Angel {
Future close() async { Future close() async {
client.close(); client.close();
_onAuthenticated.close();
Future.wait(_services.map((s) => s.close())).then((_) {
_services.clear();
});
} }
Future logout() async { Future logout() async {
@ -161,10 +175,13 @@ abstract class BaseAngelClient extends Angel {
} }
@override @override
Service service<T>(String path, {Type type, AngelDeserializer deserializer}) { Service<T> service<T>(String path,
{Type type, AngelDeserializer deserializer}) {
String uri = path.toString().replaceAll(straySlashes, ""); String uri = path.toString().replaceAll(straySlashes, "");
return new BaseAngelService(client, this, '$basePath/$uri', var s = new BaseAngelService<T>(client, this, '$basePath/$uri',
deserializer: deserializer); deserializer: deserializer);
_services.add(s);
return s;
} }
String _join(url) { String _join(url) {
@ -208,13 +225,48 @@ abstract class BaseAngelClient extends Angel {
} }
} }
class BaseAngelService extends Service { class BaseAngelService<T> extends Service<T> {
@override @override
final BaseAngelClient app; final BaseAngelClient app;
final String basePath; final String basePath;
final http.BaseClient client; final http.BaseClient client;
final AngelDeserializer deserializer; final AngelDeserializer deserializer;
final StreamController<T> _onIndexed = new StreamController<T>(),
_onRead = new StreamController<T>(),
_onCreated = new StreamController<T>(),
_onModified = new StreamController<T>(),
_onUpdated = new StreamController<T>(),
_onRemoved = new StreamController<T>();
@override
Stream<T> get onIndexed => _onIndexed.stream;
@override
Stream<T> get onRead => _onRead.stream;
@override
Stream<T> get onCreated => _onCreated.stream;
@override
Stream<T> get onModified => _onModified.stream;
@override
Stream<T> get onUpdated => _onUpdated.stream;
@override
Stream<T> get onRemoved => _onRemoved.stream;
@override
Future close() async {
_onIndexed.close();
_onRead.close();
_onCreated.close();
_onModified.close();
_onUpdated.close();
_onRemoved.close();
}
BaseAngelService(this.client, this.app, this.basePath, {this.deserializer}); BaseAngelService(this.client, this.app, this.basePath, {this.deserializer});
deserialize(x) { deserialize(x) {
@ -244,8 +296,15 @@ class BaseAngelService extends Service {
} }
final json = JSON.decode(response.body); final json = JSON.decode(response.body);
if (json is! List) return json;
return json.map(deserialize).toList(); if (json is! List) {
_onIndexed.add(json);
return json;
}
var r = json.map(deserialize).toList();
_onIndexed.add(r);
return r;
} catch (e, st) { } catch (e, st) {
throw failure(response, error: e, stack: st); throw failure(response, error: e, stack: st);
} }
@ -261,7 +320,9 @@ class BaseAngelService extends Service {
throw failure(response); throw failure(response);
} }
return deserialize(JSON.decode(response.body)); var r = deserialize(JSON.decode(response.body));
_onRead.add(r);
return r;
} catch (e, st) { } catch (e, st) {
throw failure(response, error: e, stack: st); throw failure(response, error: e, stack: st);
} }
@ -277,7 +338,9 @@ class BaseAngelService extends Service {
throw failure(response); throw failure(response);
} }
return deserialize(JSON.decode(response.body)); var r = deserialize(JSON.decode(response.body));
_onCreated.add(r);
return r;
} catch (e, st) { } catch (e, st) {
throw failure(response, error: e, stack: st); throw failure(response, error: e, stack: st);
} }
@ -293,7 +356,9 @@ class BaseAngelService extends Service {
throw failure(response); throw failure(response);
} }
return deserialize(JSON.decode(response.body)); var r = deserialize(JSON.decode(response.body));
_onModified.add(r);
return r;
} catch (e, st) { } catch (e, st) {
throw failure(response, error: e, stack: st); throw failure(response, error: e, stack: st);
} }
@ -309,7 +374,9 @@ class BaseAngelService extends Service {
throw failure(response); throw failure(response);
} }
return deserialize(JSON.decode(response.body)); var r = deserialize(JSON.decode(response.body));
_onUpdated.add(r);
return r;
} catch (e, st) { } catch (e, st) {
throw failure(response, error: e, stack: st); throw failure(response, error: e, stack: st);
} }
@ -325,7 +392,9 @@ class BaseAngelService extends Service {
throw failure(response); throw failure(response);
} }
return deserialize(JSON.decode(response.body)); var r = deserialize(JSON.decode(response.body));
_onRemoved.add(r);
return r;
} catch (e, st) { } catch (e, st) {
throw failure(response, error: e, stack: st); throw failure(response, error: e, stack: st);
} }

View file

@ -10,27 +10,40 @@ export 'angel_client.dart';
/// Queries an Angel server via REST. /// Queries an Angel server via REST.
class Rest extends BaseAngelClient { class Rest extends BaseAngelClient {
final List<Service> _services = [];
Rest(String path) : super(new http.Client(), path); Rest(String path) : super(new http.Client(), path);
@override @override
Service service<T>(String path, {Type type, AngelDeserializer deserializer}) { Service<T> service<T>(String path, {Type type, AngelDeserializer deserializer}) {
String uri = path.replaceAll(straySlashes, ""); String uri = path.replaceAll(straySlashes, "");
return new RestService( var s = new RestService<T>(
client, this, "$basePath/$uri", T != dynamic ? T : type); client, this, "$basePath/$uri", T != dynamic ? T : type);
_services.add(s);
return s;
} }
@override @override
Stream<String> authenticateViaPopup(String url, {String eventName: 'token'}) { Stream<String> authenticateViaPopup(String url, {String eventName: 'token'}) {
throw new UnimplementedError('Opening popup windows is not supported in the `dart:io` client.'); throw new UnimplementedError('Opening popup windows is not supported in the `dart:io` client.');
} }
Future close() async {
super.close();
Future.wait(_services.map((s) => s.close())).then((_) {
_services.clear();
});
}
} }
/// Queries an Angel service via REST. /// Queries an Angel service via REST.
class RestService extends BaseAngelService { class RestService<T> extends BaseAngelService<T> {
final Type type; final Type type;
RestService(http.BaseClient client, Angel app, String url, this.type) RestService(http.BaseClient client, Angel app, String url, this.type)
: super(client, app, url); : super(client, app, url);
@override
deserialize(x) { deserialize(x) {
if (type != null) { if (type != null) {
return x.runtimeType == type return x.runtimeType == type
@ -49,29 +62,4 @@ class RestService extends BaseAngelService {
return super.makeBody(x); return super.makeBody(x);
} }
@override
Future index([Map params]) async {
final items = await super.index(params);
if (items is! List) return items;
return items.map(deserialize).toList();
}
@override
Future read(id, [Map params]) => super.read(id, params).then(deserialize);
@override
Future create(data, [Map params]) =>
super.create(data, params).then(deserialize);
@override
Future modify(id, data, [Map params]) =>
super.modify(id, data, params).then(deserialize);
@override
Future update(id, data, [Map params]) =>
super.update(id, data, params).then(deserialize);
@override
Future remove(id, [Map params]) => super.remove(id, params).then(deserialize);
} }

View file

@ -1,5 +1,5 @@
name: angel_client name: angel_client
version: 1.0.4 version: 1.0.5
description: Client library for the Angel framework. description: Client library for the Angel framework.
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

View file

@ -19,7 +19,7 @@ main() {
httpServer = httpServer =
await serverApp.startServer(InternetAddress.LOOPBACK_IP_V4, 0); await serverApp.startServer(InternetAddress.LOOPBACK_IP_V4, 0);
url = "http://localhost:${httpServer.port}"; url = "http://localhost:${httpServer.port}";
serverApp.use("/postcards", new server.MemoryService<Postcard>()); serverApp.use("/postcards", new server.TypedService<Postcard>(new server.MapService()));
serverPostcards = serverApp.service("postcards"); serverPostcards = serverApp.service("postcards");
clientApp = new client.Rest(url); clientApp = new client.Rest(url);
@ -94,7 +94,7 @@ main() {
test("modify/update", () async { test("modify/update", () async {
var innerPostcards = var innerPostcards =
serverPostcards.inner as server.MemoryService<Postcard>; serverPostcards.inner as server.TypedService<Postcard>;
print(innerPostcards.items); print(innerPostcards.items);
Postcard mecca = await clientTypedPostcards Postcard mecca = await clientTypedPostcards
.create(new Postcard(location: "Mecca", message: "Pilgrimage")); .create(new Postcard(location: "Mecca", message: "Pilgrimage"));