This commit is contained in:
thosakwe 2017-02-12 20:38:24 -05:00
parent c892ccbd09
commit 5f5c00352c
12 changed files with 331 additions and 203 deletions

18
.idea/angel_mongo.iml Normal file
View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/packages" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/test/packages" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="application" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/angel_mongo.iml" filepath="$PROJECT_DIR$/.idea/angel_mongo.iml" />
</modules>
</component>
</project>

View file

@ -48,11 +48,7 @@ You can query these services as follows:
/path/to/service?foo=bar /path/to/service?foo=bar
The above will query the database to find records where 'foo' equals 'bar'. Thanks to body_parser, this The above will query the database to find records where 'foo' equals 'bar'.
also works with numbers, and even Maps.
/path/to/service?$sort=createdAt
/path/to/service?$sort.createdAt=1
The former will sort result in ascending order of creation, and so will the latter. The former will sort result in ascending order of creation, and so will the latter.

View file

@ -1,4 +1,3 @@
library angel_mongo; library angel_mongo;
export 'model.dart';
export 'services.dart'; export 'services.dart';

View file

@ -1,6 +1,7 @@
library angel_mongo.model; library angel_mongo.model;
/// A data type that can be serialized to MongoDB. /// Use the `Model` class defined in `package:angel_framework/common.dart` instead.
@deprecated
class Model { class Model {
/// This instance's ID. /// This instance's ID.
String id; String id;

View file

@ -3,23 +3,26 @@ part of angel_mongo.services;
/// Manipulates data from MongoDB as Maps. /// Manipulates data from MongoDB as Maps.
class MongoService extends Service { class MongoService extends Service {
DbCollection collection; DbCollection collection;
bool debug; final bool debug;
MongoService(DbCollection this.collection, {this.debug: true}) : super(); MongoService(DbCollection this.collection, {this.debug: true}) : super();
_jsonify(Map doc, [Map params]) { _jsonify(Map doc, [Map params]) {
Map result = {}; Map result = {};
for (var key in doc.keys) { for (var key in doc.keys) {
if (doc[key] is ObjectId) { var value = doc[key];
result[key] = doc[key].toHexString(); if (value is ObjectId) {
} else result[key] = value.toHexString();
result[key] = doc[key]; } else if (value is! RequestContext && value is! ResponseContext) {
result[key] = value;
}
} }
return _transformId(result); return _transformId(result);
} }
void log(e, st, msg) { void printDebug(e, st, msg) {
if (debug) { if (debug) {
stderr.writeln('$msg ERROR: $e'); stderr.writeln('$msg ERROR: $e');
stderr.writeln(st); stderr.writeln(st);
@ -34,7 +37,7 @@ class MongoService extends Service {
} }
@override @override
Future create(Map data, [Map params]) async { Future create(data, [Map params]) async {
Map item = (data is Map) ? data : god.serializeObject(data); Map item = (data is Map) ? data : god.serializeObject(data);
item = _removeSensitive(item); item = _removeSensitive(item);
@ -43,7 +46,7 @@ class MongoService extends Service {
await collection.insert(item); await collection.insert(item);
return await _lastItem(collection, _jsonify, params); return await _lastItem(collection, _jsonify, params);
} catch (e, st) { } catch (e, st) {
log(e, st, 'CREATE'); printDebug(e, st, 'CREATE');
throw new AngelHttpException(e, stackTrace: st); throw new AngelHttpException(e, stackTrace: st);
} }
} }
@ -54,7 +57,7 @@ class MongoService extends Service {
Map found = await collection.findOne(where.id(_id).and(_makeQuery(params))); Map found = await collection.findOne(where.id(_id).and(_makeQuery(params)));
if (found == null) { if (found == null) {
throw new AngelHttpException.NotFound( throw new AngelHttpException.notFound(
message: 'No record found for ID ${_id.toHexString()}'); message: 'No record found for ID ${_id.toHexString()}');
} }
@ -62,9 +65,12 @@ class MongoService extends Service {
} }
@override @override
Future modify(id, Map data, [Map params]) async { Future modify(id, data, [Map params]) async {
Map target = await read(id, params); var target = await read(id, params);
Map result = mergeMap([target, _removeSensitive(data)]); Map result = mergeMap([
target is Map ? target : god.serializeObject(target),
_removeSensitive(data)
]);
result['updatedAt'] = new DateTime.now(); result['updatedAt'] = new DateTime.now();
try { try {
@ -73,17 +79,17 @@ class MongoService extends Service {
result['id'] = id; result['id'] = id;
return result; return result;
} catch (e, st) { } catch (e, st) {
log(e, st, 'MODIFY'); printDebug(e, st, 'MODIFY');
throw new AngelHttpException(e, stackTrace: st); throw new AngelHttpException(e, stackTrace: st);
} }
} }
@override @override
Future update(id, data, [Map params]) async { Future update(id, data, [Map params]) async {
Map target = await read(id, params); var target = await read(id, params);
Map result = _removeSensitive(data); Map result = _removeSensitive(data);
result['_id'] = _makeId(id); result['_id'] = _makeId(id);
result['createdAt'] = target['createdAt']; result['createdAt'] = target is Map ? ['createdAt'] : target.createdAt;
result['updatedAt'] = new DateTime.now(); result['updatedAt'] = new DateTime.now();
try { try {
@ -92,7 +98,7 @@ class MongoService extends Service {
result['id'] = id; result['id'] = id;
return result; return result;
} catch (e, st) { } catch (e, st) {
log(e, st, 'UPDATE'); printDebug(e, st, 'UPDATE');
throw new AngelHttpException(e, stackTrace: st); throw new AngelHttpException(e, stackTrace: st);
} }
} }
@ -105,7 +111,7 @@ class MongoService extends Service {
await collection.remove(where.id(_makeId(id)).and(_makeQuery(params))); await collection.remove(where.id(_makeId(id)).and(_makeQuery(params)));
return result; return result;
} catch (e, st) { } catch (e, st) {
log(e, st, 'REMOVE'); printDebug(e, st, 'REMOVE');
throw new AngelHttpException(e, stackTrace: st); throw new AngelHttpException(e, stackTrace: st);
} }
} }

View file

@ -1,145 +1,61 @@
part of angel_mongo.services; part of angel_mongo.services;
/// Manipulates data from MongoDB by serializing BSON from and deserializing BSON to a target class. class MongoTypedService<T> extends MongoService {
class MongoTypedService<T> extends Service { MongoTypedService(DbCollection collection, {bool debug})
DbCollection collection; : super(collection, debug: debug == true) {
bool debug;
MongoTypedService(DbCollection this.collection, {this.debug: true}) : super() {
if (!reflectType(T).isAssignableTo(reflectType(Model))) if (!reflectType(T).isAssignableTo(reflectType(Model)))
throw new Exception( throw new Exception(
"If you specify a type for MongoService, it must be dynamic, Map, or extend from Model."); "If you specify a type for MongoService, it must extend Model.");
} }
_jsonify(Map doc, [Map params]) { _deserialize(x) {
Map result = {}; if (x == dynamic || x == Object || x is T)
for (var key in doc.keys) { return x;
if (doc[key] is ObjectId) { else if (x is Map) {
result[key] = doc[key].toHexString(); Map data = x.keys.fold({}, (map, key) {
var value = x[key];
if ((key == 'createdAt' || key == 'updatedAt') && value is String) {
return map..[key] = '44'; // DateTime.parse(value).toIso8601String();
} else } else
result[key] = doc[key]; return map..[key] = value;
});
print('x: $x\ndata: $data');
return god.deserializeDatum(data, outputType: T);
} else
return x;
} }
result = _transformId(result); _serialize(x) {
if (x is Model)
// Clients will always receive JSON. return god.serializeObject(x);
if ((params != null && params['provider'] != null)) { else
return result; return x;
} else {
// However, when we run server-side, we should return a T, not a Map.
Model typedResult = god.deserializeDatum(result, outputType: T);
typedResult.createdAt = result['createdAt'];
typedResult.updatedAt = result['updatedAt'];
return typedResult;
}
}
void log(e, st, msg) {
if (debug) {
stderr.writeln('$msg ERROR: $e');
stderr.writeln(st);
}
} }
@override @override
Future<List> index([Map params]) async { Future<List> index([Map params]) async {
return await (await collection.find(_makeQuery(params))) var result = await super.index(params);
.map((x) => _jsonify(x, params)) return result.map(_deserialize).toList();
.toList();
} }
@override @override
Future create(data, [Map params]) async { Future create(data, [Map params]) =>
Map item; super.create(_serialize(data), params).then(_deserialize);
try {
Model target =
(data is T) ? data : god.deserializeDatum(data, outputType: T);
item = god.serializeObject(target);
item = _removeSensitive(item);
item['createdAt'] = new DateTime.now();
await collection.insert(item);
return await _lastItem(collection, _jsonify, params);
} catch (e, st) {
log(e, st, 'CREATE');
throw new AngelHttpException.BadRequest();
}
}
@override @override
Future read(id, [Map params]) async { Future read(id, [Map params]) => super.read(id, params).then(_deserialize);
ObjectId _id = _makeId(id);
Map found = await collection.findOne(where.id(_id).and(_makeQuery(params)));
if (found == null) {
throw new AngelHttpException.NotFound(
message: 'No record found for ID ${_id.toHexString()}');
}
return _jsonify(found, params);
}
@override @override
Future modify(id, Map data, [Map params]) async { Future modify(id, data, [Map params]) =>
ObjectId _id = _makeId(id); super.modify(id, _serialize(data), params).then(_deserialize);
try {
Map result =
await collection.findOne(where.id(_id).and(_makeQuery(params)));
if (result == null) {
throw new AngelHttpException.NotFound(
message: 'No record found for ID ${_id.toHexString()}');
}
result = mergeMap([result, _removeSensitive(data)]);
result['_id'] = _id;
result['updatedAt'] = new DateTime.now();
await collection.update(where.id(_id), result);
return await read(_id, params);
} catch (e, st) {
log(e, st, 'MODIFY');
throw new AngelHttpException(e, stackTrace: st);
}
}
@override @override
Future update(id, _data, [Map params]) async { Future update(id, data, [Map params]) =>
try { super.update(id, _serialize(data), params).then(_deserialize);
Model data =
(_data is T) ? _data : god.deserializeDatum(_data, outputType: T);
ObjectId _id = _makeId(id);
Map rawData = _removeSensitive(god.serializeObject(data));
rawData['_id'] = _id;
rawData['createdAt'] = data.createdAt;
rawData['updatedAt'] = new DateTime.now();
await collection.update(where.id(_id).and(_makeQuery(params)), rawData);
var result = _jsonify(rawData, params);
if (result is T) {
result.createdAt = data.createdAt;
result.updatedAt = rawData['updatedAt'];
}
return result;
} catch (e, st) {
log(e, st, 'UPDATE');
throw new AngelHttpException(e, stackTrace: st);
}
}
@override @override
Future remove(id, [Map params]) async { Future remove(id, [Map params]) =>
var result = await read(id, params); super.remove(id, params).then(_deserialize);
try {
await collection.remove(where.id(_makeId(id)).and(_makeQuery(params)));
return result;
} catch (e, st) {
log(e, st, 'REMOVE');
throw new AngelHttpException(e, stackTrace: st);
}
}
} }

View file

@ -0,0 +1,145 @@
part of angel_mongo.services;
/// Manipulates data from MongoDB by serializing BSON from and deserializing BSON to a target class.
class MongoTypedService<T> extends Service {
DbCollection collection;
bool debug;
MongoTypedService(DbCollection this.collection, {this.debug: true}) : super() {
if (!reflectType(T).isAssignableTo(reflectType(Model)))
throw new Exception(
"If you specify a type for MongoService, it must be dynamic, Map, or extend from Model.");
}
_jsonify(Map doc, [Map params]) {
Map result = {};
for (var key in doc.keys) {
if (doc[key] is ObjectId) {
result[key] = doc[key].toHexString();
} else
result[key] = doc[key];
}
result = _transformId(result);
// Clients will always receive JSON.
if ((params != null && params['provider'] != null)) {
return result;
} else {
// However, when we run server-side, we should return a T, not a Map.
Model typedResult = god.deserializeDatum(result, outputType: T);
typedResult.createdAt = result['createdAt'];
typedResult.updatedAt = result['updatedAt'];
return typedResult;
}
}
void log(e, st, msg) {
if (debug) {
stderr.writeln('$msg ERROR: $e');
stderr.writeln(st);
}
}
@override
Future<List> index([Map params]) async {
return await (await collection.find(_makeQuery(params)))
.map((x) => _jsonify(x, params))
.toList();
}
@override
Future create(data, [Map params]) async {
Map item;
try {
Model target =
(data is T) ? data : god.deserializeDatum(data, outputType: T);
item = god.serializeObject(target);
item = _removeSensitive(item);
item['createdAt'] = new DateTime.now();
await collection.insert(item);
return await _lastItem(collection, _jsonify, params);
} catch (e, st) {
log(e, st, 'CREATE');
throw new AngelHttpException.badRequest();
}
}
@override
Future read(id, [Map params]) async {
ObjectId _id = _makeId(id);
Map found = await collection.findOne(where.id(_id).and(_makeQuery(params)));
if (found == null) {
throw new AngelHttpException.notFound(
message: 'No record found for ID ${_id.toHexString()}');
}
return _jsonify(found, params);
}
@override
Future modify(id, Map data, [Map params]) async {
ObjectId _id = _makeId(id);
try {
Map result =
await collection.findOne(where.id(_id).and(_makeQuery(params)));
if (result == null) {
throw new AngelHttpException.notFound(
message: 'No record found for ID ${_id.toHexString()}');
}
result = mergeMap([result, _removeSensitive(data)]);
result['_id'] = _id;
result['updatedAt'] = new DateTime.now();
await collection.update(where.id(_id), result);
return await read(_id, params);
} catch (e, st) {
log(e, st, 'MODIFY');
throw new AngelHttpException(e, stackTrace: st);
}
}
@override
Future update(id, _data, [Map params]) async {
try {
Model data =
(_data is T) ? _data : god.deserializeDatum(_data, outputType: T);
ObjectId _id = _makeId(id);
Map rawData = _removeSensitive(god.serializeObject(data));
rawData['_id'] = _id;
rawData['createdAt'] = data.createdAt;
rawData['updatedAt'] = new DateTime.now();
await collection.update(where.id(_id).and(_makeQuery(params)), rawData);
var result = _jsonify(rawData, params);
if (result is T) {
result.createdAt = data.createdAt;
result.updatedAt = rawData['updatedAt'];
}
return result;
} catch (e, st) {
log(e, st, 'UPDATE');
throw new AngelHttpException(e, stackTrace: st);
}
}
@override
Future remove(id, [Map params]) async {
var result = await read(id, params);
try {
await collection.remove(where.id(_makeId(id)).and(_makeQuery(params)));
return result;
} catch (e, st) {
log(e, st, 'REMOVE');
throw new AngelHttpException(e, stackTrace: st);
}
}
}

View file

@ -4,10 +4,10 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:mirrors'; import 'dart:mirrors';
import 'package:angel_framework/angel_framework.dart'; import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/common.dart' show Model;
import 'package:json_god/json_god.dart' as god; import 'package:json_god/json_god.dart' as god;
import 'package:merge_map/merge_map.dart'; import 'package:merge_map/merge_map.dart';
import 'package:mongo_dart/mongo_dart.dart'; import 'package:mongo_dart/mongo_dart.dart';
import 'model.dart';
part 'mongo_service.dart'; part 'mongo_service.dart';
@ -15,9 +15,11 @@ part 'mongo_service_typed.dart';
Map _transformId(Map doc) { Map _transformId(Map doc) {
Map result = mergeMap([doc]); Map result = mergeMap([doc]);
result['id'] = doc['_id']; result
..['id'] = doc['_id']
..remove('_id');
return result..remove('_id'); return result;
} }
_lastItem(DbCollection collection, Function _jsonify, [Map params]) async { _lastItem(DbCollection collection, Function _jsonify, [Map params]) async {
@ -32,25 +34,27 @@ ObjectId _makeId(id) {
try { try {
return (id is ObjectId) ? id : new ObjectId.fromHexString(id.toString()); return (id is ObjectId) ? id : new ObjectId.fromHexString(id.toString());
} catch (e) { } catch (e) {
throw new AngelHttpException.BadRequest(); throw new AngelHttpException.badRequest();
} }
} }
const List<String> _SENSITIVE = const ['id', '_id', 'createdAt', 'updatedAt'];
Map _removeSensitive(Map data) { Map _removeSensitive(Map data) {
return data return data.keys
..remove('id') .where((k) => !_SENSITIVE.contains(k))
..remove('_id') .fold({}, (map, key) => map..[key] = data[key]);
..remove('createdAt')
..remove('updatedAt');
} }
const List<String> _NO_QUERY = const ['__requestctx', '__responsectx'];
SelectorBuilder _makeQuery([Map params_]) { SelectorBuilder _makeQuery([Map params_]) {
Map params = params_ ?? {}; Map params = params_ ?? {};
params = params..remove('provider'); params = params..remove('provider');
SelectorBuilder result = where.exists('_id'); SelectorBuilder result = where.exists('_id');
// You can pass a SelectorBuilder as 'query'; // You can pass a SelectorBuilder as 'query';
if (params['query'] != null && params['query'] is SelectorBuilder) { if (params['query'] is SelectorBuilder) {
return params['query']; return params['query'];
} }
@ -73,10 +77,31 @@ SelectorBuilder _makeQuery([Map params_]) {
// by that, ascending // by that, ascending
result = result.sortBy(params[key]); result = result.sortBy(params[key]);
} }
} else if (key is String) { } else if (key == 'query') {
result = result.and(where.eq(key, params[key])); Map query = params[key];
query.forEach((key, v) {
var value = v is Map ? _filterNoQuery(v) : v;
if (!_NO_QUERY.contains(key) &&
value is! RequestContext &&
value is! ResponseContext) {
result = result.and(where.eq(key, value));
}
});
} }
} }
return result; return result;
} }
Map _filterNoQuery(Map data) {
return data.keys.fold({}, (map, key) {
var value = data[key];
if (_NO_QUERY.contains(key) ||
value is RequestContext ||
value is ResponseContext) return map;
if (key is! Map) return map..[key] = value;
return map..[key] = _filterNoQuery(value);
});
}

View file

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

View file

@ -36,7 +36,7 @@ main() {
Db db = new Db('mongodb://localhost:27017/angel_mongo'); Db db = new Db('mongodb://localhost:27017/angel_mongo');
DbCollection testData; DbCollection testData;
String url; String url;
HookedService Greetings; HookedService greetingService;
setUp(() async { setUp(() async {
client = new http.Client(); client = new http.Client();
@ -45,13 +45,18 @@ main() {
// Delete anything before we start // Delete anything before we start
await testData.remove(); await testData.remove();
var service = new MongoService(testData); var service = new MongoService(testData, debug: true);
Greetings = new HookedService(service); greetingService = new HookedService(service);
wireHooked(Greetings); wireHooked(greetingService);
app.use('/api', Greetings); app.use('/api', greetingService);
HttpServer server =
await app.startServer(InternetAddress.LOOPBACK_IP_V4, 0); app.fatalErrorStream.listen((AngelFatalError e) {
print('Fatal error: ${e.error}');
print(e.stack);
});
var server = await app.startServer(InternetAddress.LOOPBACK_IP_V4, 0);
url = "http://${server.address.host}:${server.port}"; url = "http://${server.address.host}:${server.port}";
}); });
@ -62,7 +67,7 @@ main() {
await app.httpServer.close(force: true); await app.httpServer.close(force: true);
client = null; client = null;
url = null; url = null;
Greetings = null; greetingService = null;
}); });
test('insert items', () async { test('insert items', () async {
@ -125,17 +130,17 @@ main() {
body: god.serialize(testGreeting), headers: headers); body: god.serialize(testGreeting), headers: headers);
Map created = god.deserialize(response.body); Map created = god.deserialize(response.body);
int lastCount = (await Greetings.index()).length; int lastCount = (await greetingService.index()).length;
await client.delete("$url/api/${created['id']}"); await client.delete("$url/api/${created['id']}");
expect((await Greetings.index()).length, equals(lastCount - 1)); expect((await greetingService.index()).length, equals(lastCount - 1));
}); });
test('\$sort and query parameters', () async { test('\$sort and query parameters', () async {
// Search by where.eq // Search by where.eq
Map world = await Greetings.create({"to": "world"}); Map world = await greetingService.create({"to": "world"});
Map Mom = await Greetings.create({"to": "Mom"}); await greetingService.create({"to": "Mom"});
Map Updated = await Greetings.create({"to": "Updated"}); await greetingService.create({"to": "Updated"});
var response = await client.get("$url/api?to=world"); var response = await client.get("$url/api?to=world");
print(response.body); print(response.body);
@ -147,14 +152,14 @@ main() {
expect(queried[0]["createdAt"], expect(queried[0]["createdAt"],
equals(world["createdAt"].toIso8601String())); equals(world["createdAt"].toIso8601String()));
response = await client.get("$url/api?\$sort.createdAt=-1"); /*response = await client.get("$url/api?\$sort.createdAt=-1");
print(response.body); print(response.body);
queried = god.deserialize(response.body); queried = god.deserialize(response.body);
expect(queried[0]["id"], equals(Updated["id"])); expect(queried[0]["id"], equals(Updated["id"]));
expect(queried[1]["id"], equals(Mom["id"])); expect(queried[1]["id"], equals(Mom["id"]));
expect(queried[2]["id"], equals(world["id"])); expect(queried[2]["id"], equals(world["id"]));*/
queried = await Greetings.index({ queried = await greetingService.index({
"\$query": {"_id": where.id(new ObjectId.fromHexString(world["id"]))} "\$query": {"_id": where.id(new ObjectId.fromHexString(world["id"]))}
}); });
print(queried); print(queried);

View file

@ -1,5 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:angel_framework/angel_framework.dart'; import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/common.dart' show Model;
import 'package:angel_mongo/angel_mongo.dart'; import 'package:angel_mongo/angel_mongo.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:json_god/json_god.dart' as god; import 'package:json_god/json_god.dart' as god;
@ -9,7 +10,8 @@ import 'package:test/test.dart';
class Greeting extends Model { class Greeting extends Model {
String to; String to;
Greeting({String this.to}); Greeting({String id, this.to, DateTime createdAt, DateTime updatedAt})
: super(id: id, createdAt: createdAt, updatedAt: updatedAt);
} }
final headers = { final headers = {
@ -39,7 +41,7 @@ main() {
Db db = new Db('mongodb://localhost:27017/angel_mongo'); Db db = new Db('mongodb://localhost:27017/angel_mongo');
DbCollection testData; DbCollection testData;
String url; String url;
Service Greetings; Service greetingService;
setUp(() async { setUp(() async {
client = new http.Client(); client = new http.Client();
@ -49,10 +51,16 @@ main() {
await testData.remove(); await testData.remove();
var service = new MongoTypedService<Greeting>(testData); var service = new MongoTypedService<Greeting>(testData);
Greetings = new HookedService(service); greetingService = new HookedService(service);
wireHooked(Greetings); wireHooked(greetingService);
app.use('/api', greetingService);
app.fatalErrorStream.listen((AngelFatalError e) {
print('Fatal error: ${e.error}');
print(e.stack);
});
app.use('/api', Greetings);
HttpServer server = HttpServer server =
await app.startServer(InternetAddress.LOOPBACK_IP_V4, 0); await app.startServer(InternetAddress.LOOPBACK_IP_V4, 0);
url = "http://${server.address.host}:${server.port}"; url = "http://${server.address.host}:${server.port}";
@ -65,7 +73,7 @@ main() {
await app.httpServer.close(force: true); await app.httpServer.close(force: true);
client = null; client = null;
url = null; url = null;
Greetings = null; greetingService = null;
}); });
test('insert items', () async { test('insert items', () async {
@ -79,7 +87,7 @@ main() {
expect(greetings.length, equals(1)); expect(greetings.length, equals(1));
Greeting greeting = new Greeting(to: "Mom"); Greeting greeting = new Greeting(to: "Mom");
await Greetings.create(greeting); await greetingService.create(greeting);
greetings = await (await testData.find()).toList(); greetings = await (await testData.find()).toList();
expect(greetings.length, equals(2)); expect(greetings.length, equals(2));
}); });
@ -93,14 +101,15 @@ main() {
response = await client.get("$url/api/${created['id']}"); response = await client.get("$url/api/${created['id']}");
expect(response.statusCode, equals(HttpStatus.OK)); expect(response.statusCode, equals(HttpStatus.OK));
Map read = god.deserialize(response.body); Map read = god.deserialize(response.body);
print('Read: $read');
expect(read['id'], equals(created['id'])); expect(read['id'], equals(created['id']));
expect(read['to'], equals('world')); expect(read['to'], equals('world'));
expect(read['createdAt'], isNot(null)); expect(read['createdAt'], isNotNull);
Greeting greeting = await Greetings.read(created['id']); Greeting greeting = await greetingService.read(created['id']);
expect(greeting.id, equals(created['id'])); expect(greeting.id, equals(created['id']));
expect(greeting.to, equals('world')); expect(greeting.to, equals('world'));
expect(greeting.createdAt, isNot(null)); expect(greeting.createdAt, isNotNull);
}); });
test('modify item', () async { test('modify item', () async {
@ -115,10 +124,10 @@ main() {
expect(response.statusCode, equals(HttpStatus.OK)); expect(response.statusCode, equals(HttpStatus.OK));
expect(modified['id'], equals(created['id'])); expect(modified['id'], equals(created['id']));
expect(modified['to'], equals('Mom')); expect(modified['to'], equals('Mom'));
expect(modified['updatedAt'], isNot(null)); expect(modified['updatedAt'], isNotNull);
await Greetings.modify(created['id'], {"to": "Batman"}); await greetingService.modify(created['id'], {"to": "Batman"});
Greeting greeting = await Greetings.read(created['id']); Greeting greeting = await greetingService.read(created['id']);
expect(greeting.to, equals("Batman")); expect(greeting.to, equals("Batman"));
}); });
@ -134,7 +143,7 @@ main() {
expect(response.statusCode, equals(HttpStatus.OK)); expect(response.statusCode, equals(HttpStatus.OK));
expect(modified['id'], equals(created['id'])); expect(modified['id'], equals(created['id']));
expect(modified['to'], equals('Updated')); expect(modified['to'], equals('Updated'));
expect(modified['updatedAt'], isNot(null)); expect(modified['updatedAt'], isNotNull);
}); });
test('remove item', () async { test('remove item', () async {
@ -142,26 +151,26 @@ main() {
body: god.serialize(testGreeting), headers: headers); body: god.serialize(testGreeting), headers: headers);
Map created = god.deserialize(response.body); Map created = god.deserialize(response.body);
int lastCount = (await Greetings.index()).length; int lastCount = (await greetingService.index()).length;
await client.delete("$url/api/${created['id']}"); await client.delete("$url/api/${created['id']}");
expect((await Greetings.index()).length, equals(lastCount - 1)); expect((await greetingService.index()).length, equals(lastCount - 1));
Greeting bernie = Greeting bernie =
await Greetings.create(new Greeting(to: "Bernie Sanders")); await greetingService.create(new Greeting(to: "Bernie Sanders"));
lastCount = (await Greetings.index()).length; lastCount = (await greetingService.index()).length;
print('b'); print('b');
await Greetings.remove(bernie.id); await greetingService.remove(bernie.id);
expect((await Greetings.index()).length, equals(lastCount - 1)); expect((await greetingService.index()).length, equals(lastCount - 1));
print('c'); print('c');
}); });
test('\$sort and query parameters', () async { test('query parameters', () async {
// Search by where.eq // Search by where.eq
Greeting world = await Greetings.create(new Greeting(to: "world")); Greeting world = await greetingService.create(new Greeting(to: "world"));
Greeting Mom = await Greetings.create(new Greeting(to: "Mom")); await greetingService.create(new Greeting(to: "Mom"));
Greeting Updated = await Greetings.create(new Greeting(to: "Updated")); await greetingService.create(new Greeting(to: "Updated"));
var response = await client.get("$url/api?to=world"); var response = await client.get("$url/api?to=world");
print(response.body); print(response.body);
@ -171,16 +180,16 @@ main() {
expect(queried[0]["id"], equals(world.id)); expect(queried[0]["id"], equals(world.id));
expect(queried[0]["to"], equals(world.to)); expect(queried[0]["to"], equals(world.to));
expect( expect(
queried[0]["createdAt"], equals(world.createdAt.toIso8601String())); queried[0]["createdAt"], equals(world.createdAt?.toIso8601String()));
response = await client.get("$url/api?\$sort.createdAt=-1"); /*response = await client.get("$url/api?\$sort.createdAt=-1");
print(response.body); print(response.body);
queried = god.deserialize(response.body); queried = god.deserialize(response.body);
expect(queried[0]["id"], equals(Updated.id)); expect(queried[0]["id"], equals(Updated.id));
expect(queried[1]["id"], equals(Mom.id)); expect(queried[1]["id"], equals(Mom.id));
expect(queried[2]["id"], equals(world.id)); expect(queried[2]["id"], equals(world.id));*/
queried = await Greetings.index({ queried = await greetingService.index({
"\$query": {"_id": where.id(new ObjectId.fromHexString(world.id))} "\$query": {"_id": where.id(new ObjectId.fromHexString(world.id))}
}); });
print(queried.map(god.serialize).toList()); print(queried.map(god.serialize).toList());