From 319e195faa047f6ec75568b9eace2f871b38c7bf Mon Sep 17 00:00:00 2001 From: thosakwe Date: Sun, 27 Nov 2016 22:28:41 -0500 Subject: [PATCH] Finish after YoDesk --- lib/angel_client.dart | 11 ++- lib/auth_types.dart | 2 + lib/browser.dart | 143 ++++++++++++++++++++++++-------- lib/{cli.dart => io.dart} | 0 pubspec.yaml | 2 +- test/for_browser_tests.dart | 2 +- test/{cli.dart => io_test.dart} | 2 +- test/packages | 1 - 8 files changed, 125 insertions(+), 38 deletions(-) create mode 100644 lib/auth_types.dart rename lib/{cli.dart => io.dart} (100%) rename test/{cli.dart => io_test.dart} (99%) delete mode 120000 test/packages diff --git a/lib/angel_client.dart b/lib/angel_client.dart index 9ecc1526..9fdce34b 100644 --- a/lib/angel_client.dart +++ b/lib/angel_client.dart @@ -2,6 +2,7 @@ library angel_client; import 'dart:async'; +import 'auth_types.dart' as auth_types; /// A function that configures an [Angel] client in some way. typedef Future AngelConfigurer(Angel app); @@ -12,6 +13,8 @@ abstract class Angel { Angel(String this.basePath); + Future authenticate({String type: auth_types.LOCAL, credentials, String authEndpoint: '/auth'}); + /// Applies an [AngelConfigurer] to this instance. Future configure(AngelConfigurer configurer) async { await configurer(this); @@ -20,10 +23,16 @@ abstract class Angel { Service service(Pattern path, {Type type}); } +/// Represents the result of authentication with an Angel server. +abstract class AngelAuthResult { + Map get data; + String get token; +} + /// Queries a service on an Angel server, with the same API. abstract class Service { /// The Angel instance powering this service. - Angel app; + Angel get app; /// Retrieves all resources. Future index([Map params]); diff --git a/lib/auth_types.dart b/lib/auth_types.dart new file mode 100644 index 00000000..4328fa16 --- /dev/null +++ b/lib/auth_types.dart @@ -0,0 +1,2 @@ +const String GOOGLE = 'google'; +const String LOCAL = 'local'; \ No newline at end of file diff --git a/lib/browser.dart b/lib/browser.dart index 79307dd9..85b649df 100644 --- a/lib/browser.dart +++ b/lib/browser.dart @@ -1,92 +1,169 @@ /// Browser library for the Angel framework. library angel_client.browser; -import 'dart:async'; +import 'dart:async' show Completer, Future; import 'dart:convert' show JSON; -import 'dart:html'; +import 'dart:html' show HttpRequest; import 'angel_client.dart'; +import 'auth_types.dart' as auth_types; export 'angel_client.dart'; _buildQuery(Map params) { - if (params == null || params == {}) - return ""; + if (params == null || params == {}) return ""; String result = ""; return result; } +_send(HttpRequest request, [data]) { + final completer = new Completer(); + + request + ..onLoadEnd.listen((_) { + completer.complete(request.response); + }) + ..onError.listen((_) { + try { + throw new Exception( + 'Request failed with status code ${request.status}.'); + } catch (e, st) { + completer.completeError(e, st); + } + }); + + request.send(data); + return completer.future; +} + /// Queries an Angel server via REST. class Rest extends Angel { - Rest(String basePath) :super(basePath); + String _authToken; + + Rest(String basePath) : super(basePath); + + @override + Future authenticate( + {String type: auth_types.LOCAL, + credentials, + String authEndpoint: '/auth'}) async { + final url = '$authEndpoint/$type'; + + if (type == auth_types.LOCAL) { + final completer = new Completer(); + final request = new HttpRequest(); + request.open('POST', url); + request.responseType = 'json'; + request.setRequestHeader("Accept", "application/json"); + request.setRequestHeader("Content-Type", "application/json"); + + request + ..onLoadEnd.listen((_) { + completer + .complete(new _AngelAuthResultImpl.fromMap(request.response)); + }) + ..onError.listen((_) { + try { + throw new Exception( + 'Request failed with status code ${request.status}.'); + } catch (e, st) { + completer.completeError(e, st); + } + }); + + request.send(credentials); + + return completer.future; + } else { + throw new Exception('angel_client cannot authenticate as "$type" yet.'); + } + } @override RestService service(String path, {Type type}) { String uri = path.replaceAll(new RegExp(r"(^\/)|(\/+$)"), ""); - return new RestService("$basePath/$uri") - ..app = this; + return new _RestServiceImpl(this, "$basePath/$uri"); } } -/// Queries an Angel service via REST. -class RestService extends Service { - String basePath; +abstract class RestService extends Service { + RestService._(String basePath); +} - RestService(Pattern path) { - this.basePath = (path is RegExp) ? path.pattern : path; +class _AngelAuthResultImpl implements AngelAuthResult { + final Map data = {}; + final String token; + + _AngelAuthResultImpl({this.token, Map data: const {}}) { + this.data.addAll(data ?? {}); + } + + factory _AngelAuthResultImpl.fromMap(Map data) => + new _AngelAuthResultImpl(token: data['token'], data: data['data']); +} + +/// Queries an Angel service via REST. +class _RestServiceImpl extends RestService { + final Rest app; + String _basePath; + String get basePath => _basePath; + + _RestServiceImpl(this.app, String basePath) : super._(basePath) { + _basePath = basePath; } _makeBody(data) { return JSON.encode(data); } - HttpRequest buildRequest(String url, - {String method: "POST", bool write: true}) { + Future buildRequest(String url, + {String method: "POST", bool write: true}) async { HttpRequest request = new HttpRequest(); - request.open(method, url, async: false); + request.open(method, url); request.responseType = "json"; request.setRequestHeader("Accept", "application/json"); - if (write) - request.setRequestHeader("Content-Type", "application/json"); + if (write) request.setRequestHeader("Content-Type", "application/json"); + if (app._authToken != null) + request.setRequestHeader("Authorization", "Bearer ${app._authToken}"); return request; } @override Future index([Map params]) async { - return JSON.decode( - await HttpRequest.getString("$basePath/${_buildQuery(params)}")); + final request = await buildRequest('$basePath/${_buildQuery(params)}', + method: 'GET', write: false); + return await _send(request); } @override Future read(id, [Map params]) async { - return JSON.decode( - await HttpRequest.getString("$basePath/$id${_buildQuery(params)}")); + final request = await buildRequest('$basePath/$id${_buildQuery(params)}', + method: 'GET', write: false); + return await _send(request); } @override Future create(data, [Map params]) async { - var request = buildRequest("$basePath/${_buildQuery(params)}"); - request.send(_makeBody(data)); - return request.response; + final request = await buildRequest("$basePath/${_buildQuery(params)}"); + return await _send(request, _makeBody(data)); } @override Future modify(id, data, [Map params]) async { - var request = buildRequest("$basePath/$id${_buildQuery(params)}", method: "PATCH"); - request.send(_makeBody(data)); - return request.response; + final request = await buildRequest("$basePath/$id${_buildQuery(params)}", + method: "PATCH"); + return await _send(request, _makeBody(data)); } @override Future update(id, data, [Map params]) async { - var request = buildRequest("$basePath/$id${_buildQuery(params)}"); - request.send(_makeBody(data)); - return request.response; + final request = await buildRequest("$basePath/$id${_buildQuery(params)}"); + return await _send(request, _makeBody(data)); } @override Future remove(id, [Map params]) async { - var request = buildRequest("$basePath/$id${_buildQuery(params)}", method: "DELETE"); - request.send(); - return request.response; + final request = await buildRequest("$basePath/$id${_buildQuery(params)}", + method: "DELETE"); + return await _send(request); } } diff --git a/lib/cli.dart b/lib/io.dart similarity index 100% rename from lib/cli.dart rename to lib/io.dart diff --git a/pubspec.yaml b/pubspec.yaml index b516623c..0dbdcc30 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_client -version: 1.0.0-dev+7 +version: 1.0.0-dev+8 description: Client library for the Angel framework. author: Tobe O homepage: https://github.com/angel-dart/angel_client diff --git a/test/for_browser_tests.dart b/test/for_browser_tests.dart index 072f1d52..d020dc92 100644 --- a/test/for_browser_tests.dart +++ b/test/for_browser_tests.dart @@ -1,6 +1,6 @@ import 'dart:io'; import "package:angel_framework/angel_framework.dart"; -import "package:angel_framework/defs.dart"; +import "package:angel_framework/src/defs.dart"; main() async { Angel app = new Angel(); diff --git a/test/cli.dart b/test/io_test.dart similarity index 99% rename from test/cli.dart rename to test/io_test.dart index f632ca6d..894a3399 100644 --- a/test/cli.dart +++ b/test/io_test.dart @@ -1,5 +1,5 @@ import 'dart:io'; -import 'package:angel_client/cli.dart' as client; +import 'package:angel_client/io.dart' as client; import 'package:angel_framework/angel_framework.dart' as server; import 'package:http/http.dart' as http; import 'package:json_god/json_god.dart' as god; diff --git a/test/packages b/test/packages deleted file mode 120000 index a16c4050..00000000 --- a/test/packages +++ /dev/null @@ -1 +0,0 @@ -../packages \ No newline at end of file