Restructured

This commit is contained in:
thosakwe 2016-09-03 08:02:32 -04:00
parent 6f7321a4af
commit 09613bbfa3
13 changed files with 34 additions and 315 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View file

@ -2,11 +2,6 @@
library angel_client;
import 'dart:async';
import 'dart:convert' show JSON;
import 'package:http/http.dart';
import 'package:json_god/json_god.dart' as god;
part 'rest.dart';
/// A function that configures an [Angel] client in some way.
typedef Future AngelConfigurer(Angel app);

View file

@ -4,7 +4,8 @@ library angel_client.browser;
import 'dart:async';
import 'dart:convert' show JSON;
import 'dart:html';
import 'shared.dart';
import 'angel_client.dart';
export 'angel_client.dart';
_buildQuery(Map params) {
if (params == null || params == {})
@ -21,7 +22,7 @@ class Rest extends Angel {
@override
RestService service(String path, {Type type}) {
String uri = path.replaceAll(new RegExp(r"(^\/)|(\/+$)"), "");
return new RestService._base("$basePath/$uri")
return new RestService("$basePath/$uri")
..app = this;
}
}
@ -30,7 +31,7 @@ class Rest extends Angel {
class RestService extends Service {
String basePath;
RestService._base(Pattern path) {
RestService(Pattern path) {
this.basePath = (path is RegExp) ? path.pattern : path;
}
@ -38,7 +39,7 @@ class RestService extends Service {
return JSON.encode(data);
}
HttpRequest _buildRequest(String url,
HttpRequest buildRequest(String url,
{String method: "POST", bool write: true}) {
HttpRequest request = new HttpRequest();
request.open(method, url, async: false);
@ -63,30 +64,29 @@ class RestService extends Service {
@override
Future create(data, [Map params]) async {
var request = _buildRequest("$basePath/${_buildQuery(params)}");
var request = buildRequest("$basePath/${_buildQuery(params)}");
request.send(_makeBody(data));
return request.response;
}
@override
Future modify(id, data, [Map params]) async {
var request = _buildRequest("$basePath/$id${_buildQuery(params)}", method: "PATCH");
var request = buildRequest("$basePath/$id${_buildQuery(params)}", method: "PATCH");
request.send(_makeBody(data));
return request.response;
}
@override
Future update(id, data, [Map params]) async {
var request = _buildRequest("$basePath/$id${_buildQuery(params)}");
var request = buildRequest("$basePath/$id${_buildQuery(params)}");
request.send(_makeBody(data));
return request.response;
}
@override
Future remove(id, [Map params]) async {
var request = _buildRequest("$basePath/$id${_buildQuery(params)}", method: "DELETE");
var request = buildRequest("$basePath/$id${_buildQuery(params)}", method: "DELETE");
request.send();
return request.response;
}
}

View file

@ -5,7 +5,8 @@ import 'dart:async';
import 'dart:convert' show JSON;
import 'package:http/http.dart';
import 'package:json_god/json_god.dart' as god;
import 'shared.dart';
import 'angel_client.dart';
export 'angel_client.dart';
_buildQuery(Map params) {
if (params == null || params == {})
@ -107,4 +108,3 @@ class RestService extends Service {
return god.deserialize(response.body, outputType: outputType);
}
}

View file

@ -1,101 +0,0 @@
part of angel_client;
_buildQuery(Map params) {
if (params == null || params == {})
return "";
String result = "";
return result;
}
const Map _readHeaders = const {
"Accept": "application/json"
};
const Map _writeHeaders = const {
"Accept": "application/json",
"Content-Type": "application/json"
};
class Rest extends Angel {
BaseClient client;
Rest(String path, BaseClient this.client) :super(path);
@override
RestService service(String path, {Type type}) {
String uri = path.replaceAll(new RegExp(r"(^\/)|(\/+$)"), "");
return new RestService._base("$basePath/$uri", client, type)
..app = this;
}
}
/// Queries an Angel service via REST.
class RestService extends Service {
String basePath;
BaseClient client;
Type outputType;
RestService._base(Pattern path, BaseClient this.client,
Type this.outputType) {
this.basePath = (path is RegExp) ? path.pattern : path;
}
_makeBody(data) {
if (outputType == null)
return JSON.encode(data);
else return god.serialize(data);
}
@override
Future<List> index([Map params]) async {
var response = await client.get(
"$basePath/${_buildQuery(params)}", headers: _readHeaders);
if (outputType == null)
return god.deserialize(response.body);
else {
return JSON.decode(response.body).map((x) =>
god.deserializeDatum(x, outputType: outputType)).toList();
}
}
@override
Future read(id, [Map params]) async {
var response = await client.get(
"$basePath/$id${_buildQuery(params)}", headers: _readHeaders);
return god.deserialize(response.body, outputType: outputType);
}
@override
Future create(data, [Map params]) async {
var response = await client.post(
"$basePath/${_buildQuery(params)}", body: _makeBody(data),
headers: _writeHeaders);
return god.deserialize(response.body, outputType: outputType);
}
@override
Future modify(id, data, [Map params]) async {
var response = await client.patch(
"$basePath/$id${_buildQuery(params)}", body: _makeBody(data),
headers: _writeHeaders);
return god.deserialize(response.body, outputType: outputType);
}
@override
Future update(id, data, [Map params]) async {
var response = await client.patch(
"$basePath/$id${_buildQuery(params)}", body: _makeBody(data),
headers: _writeHeaders);
return god.deserialize(response.body, outputType: outputType);
}
@override
Future remove(id, [Map params]) async {
var response = await client.delete(
"$basePath/$id${_buildQuery(params)}", headers: _readHeaders);
return god.deserialize(response.body, outputType: outputType);
}
}

View file

@ -1,44 +0,0 @@
import 'dart:async';
/// A function that configures an [Angel] client in some way.
typedef Future AngelConfigurer(Angel app);
/// Represents an Angel server that we are querying.
abstract class Angel {
/// The URL of the server.
String basePath;
Angel(String this.basePath);
/// Applies an [AngelConfigurer] to this instance.
Future configure(AngelConfigurer configurer) async {
await configurer(this);
}
/// Returns a representation of a service on the server.
Service service(Pattern path, {Type type});
}
/// Queries a service on an Angel server, with the same API.
abstract class Service {
/// The Angel instance powering this service.
Angel app;
/// Retrieves all resources.
Future<List> index([Map params]);
/// Retrieves the desired resource.
Future read(id, [Map params]);
/// Creates a resource.
Future create(data, [Map params]);
/// Modifies a resource.
Future modify(id, data, [Map params]);
/// Overwrites a resource.
Future update(id, data, [Map params]);
/// Removes the given resource.
Future remove(id, [Map params]);
}

View file

@ -1,5 +1,5 @@
name: angel_client
version: 1.0.0-dev+1
version: 1.0.0-dev+6
description: Client library for the Angel framework.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/angel_client

BIN
test/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -1,10 +1,11 @@
import 'package:angel_client/shared.dart';
import 'package:angel_client/browser.dart';
import 'package:test/test.dart';
main() async {
Angel app = new Rest("http://localhost:3000");
Service Todos = app.service("todos");
test("list todos", () async {
Angel app = new Rest("http://localhost:3001");
Service Todos = app.service("todos");
print(await Todos.index());
print(await Todos.index());
});
}

View file

@ -1,5 +1,4 @@
import 'dart:io';
import 'package:angel_client/shared.dart' as clientLib;
import 'package:angel_client/cli.dart' as client;
import 'package:angel_framework/angel_framework.dart' as server;
import 'package:http/http.dart' as http;
@ -11,18 +10,19 @@ main() {
group("rest", () {
server.Angel serverApp = new server.Angel();
server.HookedService serverPostcards;
clientLib.Angel clientApp;
clientLib.Service clientPostcards;
clientLib.Service clientTypedPostcards;
client.Angel clientApp;
client.Service clientPostcards;
client.Service clientTypedPostcards;
HttpServer httpServer;
String url;
setUp(() async {
httpServer =
await serverApp.startServer(InternetAddress.LOOPBACK_IP_V4, 3000);
httpServer = await serverApp.startServer(InternetAddress.LOOPBACK_IP_V4, 0);
url = "http://localhost:${httpServer.port}";
serverApp.use("/postcards", new server.MemoryService<Postcard>());
serverPostcards = serverApp.service("postcards");
clientApp = new client.Rest("http://localhost:3000", new http.Client());
clientApp = new client.Rest(url, new http.Client());
clientPostcards = clientApp.service("postcards");
clientTypedPostcards = clientApp.service("postcards", type: Postcard);
});

View file

@ -1,5 +1,6 @@
import 'dart:io';
import 'package:angel_framework/angel_framework.dart';
import "package:angel_framework/angel_framework.dart";
import "package:angel_framework/defs.dart";
main() async {
Angel app = new Angel();
@ -9,8 +10,8 @@ main() async {
app.use("/todos", new MemoryService<Todo>());
await app.startServer(InternetAddress.LOOPBACK_IP_V4, 3000);
print("Server up on localhost:3000");
await app.startServer(InternetAddress.LOOPBACK_IP_V4, 3001);
print("Server up on localhost:3001");
}
class Todo extends MemoryModel {

View file

@ -1,133 +0,0 @@
import 'dart:io';
import 'package:angel_client/angel_client.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;
import 'package:test/test.dart';
import 'shared.dart';
main() {
group("rest", () {
server.Angel serverApp = new server.Angel();
server.HookedService serverPostcards;
client.Angel clientApp;
client.Service clientPostcards;
client.Service clientTypedPostcards;
HttpServer httpServer;
setUp(() async {
httpServer =
await serverApp.startServer(InternetAddress.LOOPBACK_IP_V4, 3000);
serverApp.use("/postcards", new server.MemoryService<Postcard>());
serverPostcards = serverApp.service("postcards");
clientApp = new client.Rest("http://localhost:3000", new http.Client());
clientPostcards = clientApp.service("postcards");
clientTypedPostcards = clientApp.service("postcards", type: Postcard);
});
tearDown(() async {
await httpServer.close(force: true);
});
test("index", () async {
Postcard niagaraFalls = await serverPostcards.create(
new Postcard(location: "Niagara Falls", message: "Missing you!"));
List<Map> indexed = await clientPostcards.index();
print(indexed);
expect(indexed.length, equals(1));
expect(indexed[0].keys.length, equals(3));
expect(indexed[0]['id'], equals(niagaraFalls.id));
expect(indexed[0]['location'], equals(niagaraFalls.location));
expect(indexed[0]['message'], equals(niagaraFalls.message));
Postcard louvre = await serverPostcards.create(new Postcard(
location: "The Louvre", message: "The Mona Lisa was watching me!"));
print(god.serialize(louvre));
List<Postcard> typedIndexed = await clientTypedPostcards.index();
expect(typedIndexed.length, equals(2));
expect(typedIndexed[1], equals(louvre));
});
test("create/read", () async {
Map opry = {"location": "Grand Ole Opry", "message": "Yeehaw!"};
var created = await clientPostcards.create(opry);
print(created);
expect(created['id'] == null, equals(false));
expect(created["location"], equals(opry["location"]));
expect(created["message"], equals(opry["message"]));
var read = await clientPostcards.read(created['id']);
print(read);
expect(read['id'], equals(created['id']));
expect(read['location'], equals(created['location']));
expect(read['message'], equals(created['message']));
Postcard canyon = new Postcard(location: "Grand Canyon",
message: "But did you REALLY experience it???");
created = await clientTypedPostcards.create(canyon);
print(god.serialize(created));
expect(created.location, equals(canyon.location));
expect(created.message, equals(canyon.message));
read = await clientTypedPostcards.read(created.id);
print(god.serialize(read));
expect(read.id, equals(created.id));
expect(read.location, equals(created.location));
expect(read.message, equals(created.message));
});
test("modify/update", () async {
server.MemoryService<Postcard> innerPostcards = serverPostcards.inner;
print(innerPostcards.items);
Postcard mecca = await clientTypedPostcards.create(
new Postcard(location: "Mecca", message: "Pilgrimage"));
print(god.serialize(mecca));
// I'm too lazy to write the tests twice, because I know it works
// So I'll modify using the type-based client, and update using the
// map-based one
print("Postcards on server: " +
god.serialize(await serverPostcards.index()));
print("Postcards on client: " +
god.serialize(await clientPostcards.index()));
Postcard modified = await clientTypedPostcards.modify(
mecca.id, {"location": "Saudi Arabia"});
print(god.serialize(modified));
expect(modified.id, equals(mecca.id));
expect(modified.location, equals("Saudi Arabia"));
expect(modified.message, equals(mecca.message));
Map updated = await clientPostcards.update(
mecca.id, {"location": "Full", "message": "Overwrite"});
print(updated);
expect(updated.keys.length, equals(3));
expect(updated['id'], equals(mecca.id));
expect(updated['location'], equals("Full"));
expect(updated['message'], equals("Overwrite"));
});
test("remove", () async {
Postcard remove1 = await clientTypedPostcards.create(
{"location": "remove", "message": "#1"});
Postcard remove2 = await clientTypedPostcards.create(
{"location": "remove", "message": "#2"});
print(god.serialize([remove1, remove2]));
Map removed1 = await clientPostcards.remove(remove1.id);
expect(removed1.keys.length, equals(3));
expect(removed1['id'], equals(remove1.id));
expect(removed1['location'], equals(remove1.location));
expect(removed1['message'], equals(remove1.message));
Postcard removed2 = await clientTypedPostcards.remove(remove2.id);
expect(removed2, equals(remove2));
});
});
}

View file

@ -1,4 +1,4 @@
import 'package:angel_framework/angel_framework.dart';
import "package:angel_framework/defs.dart";
class Postcard extends MemoryModel {
int id;