From 71fe379db94d06def761c349fe982435b96d0d60 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Thu, 23 Jun 2016 00:58:21 -0400 Subject: [PATCH] Functionality done --- README.md | 33 +++++++++++++++++++++++++++++++++ lib/angel_mongo.dart | 12 +++++++----- lib/mongo_service.dart | 19 +++++++++++-------- lib/mongo_service_typed.dart | 34 +++++++++++++++++++--------------- test/generic_tests.dart | 27 ++++++++++++++++++--------- test/packages | 1 + test/typed_tests.dart | 30 ++++++++++++++++++++++-------- 7 files changed, 111 insertions(+), 45 deletions(-) create mode 120000 test/packages diff --git a/README.md b/README.md index d4df454c..a29d9a53 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,35 @@ # angel_mongo + +![version 1.0.0-dev](https://img.shields.io/badge/version-1.0.0--dev-red.svg) + MongoDB-enabled services for the Angel framework. + +# Installation +Add the following to your `pubspec.yaml`: + +```yaml +dependencies: + angel_mongo: ^1.0.0-dev +``` + +# Usage +This library exposes three main classes: `Model`, `MongoService` and `MongoTypedService`. + +## Model +`Model` is class with no real functionality; however, it represents a basic MongoDB document, and your services should host inherited classes. + +```dart +@Hooked() +class User extends Model { + String username; + String password; +} +``` + +## MongoService +This class interacts with a `DbCollection` (from mongo_dart) and serializing data to and from Maps. + +## MongoTypedService +Does the same as above, but serializes to and from a target class using json_god and its support for reflection. + +See the tests for more usage examples. diff --git a/lib/angel_mongo.dart b/lib/angel_mongo.dart index 95808b28..2e6f1b88 100644 --- a/lib/angel_mongo.dart +++ b/lib/angel_mongo.dart @@ -23,7 +23,6 @@ class Model { DateTime updatedAt; } - Map _transformId(Map doc) { Map result = mergeMap([doc]); result['id'] = doc['_id']; @@ -33,8 +32,8 @@ Map _transformId(Map doc) { _lastItem(DbCollection collection, Function _jsonify, [Map params]) async { return (await (await collection - .find(where.sortBy('\$natural', descending: true))) - .toList()) + .find(where.sortBy('\$natural', descending: true))) + .toList()) .map((x) => _jsonify(x, params)) .first; } @@ -48,8 +47,11 @@ ObjectId _makeId(id) { } Map _removeSensitive(Map data) { - return data..remove('id')..remove('_id')..remove('createdAt')..remove( - 'updatedAt'); + return data + ..remove('id') + ..remove('_id') + ..remove('createdAt') + ..remove('updatedAt'); } SelectorBuilder _makeQuery([Map params_]) { diff --git a/lib/mongo_service.dart b/lib/mongo_service.dart index cf0f071e..4d0fd33e 100644 --- a/lib/mongo_service.dart +++ b/lib/mongo_service.dart @@ -4,14 +4,7 @@ part of angel_mongo; class MongoService extends Service { DbCollection collection; - MongoService(DbCollection this.collection):super(); - - Map _transformId(Map doc) { - Map result = mergeMap([doc]); - result['id'] = doc['_id']; - - return result..remove('_id'); - } + MongoService(DbCollection this.collection) : super(); _jsonify(Map doc, [Map params]) { Map result = {}; @@ -123,5 +116,15 @@ class MongoService extends Service { } } + @override + Future remove(id, [Map params]) async { + Map result = await read(id, params); + try { + await collection.remove(where.id(_makeId(id)).and(_makeQuery(params))); + return result; + } catch (e, st) { + throw new AngelHttpException(e, stackTrace: st); + } + } } diff --git a/lib/mongo_service_typed.dart b/lib/mongo_service_typed.dart index c4a11ec9..4a995ad1 100644 --- a/lib/mongo_service_typed.dart +++ b/lib/mongo_service_typed.dart @@ -4,19 +4,12 @@ part of angel_mongo; class MongoTypedService extends Service { DbCollection collection; - MongoTypedService(DbCollection this.collection):super() { + MongoTypedService(DbCollection this.collection) : 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."); } - Map _transformId(Map doc) { - Map result = mergeMap([doc]); - result['id'] = doc['_id']; - - return result..remove('_id'); - } - _jsonify(Map doc, [Map params]) { Map result = {}; for (var key in doc.keys) { @@ -31,8 +24,7 @@ class MongoTypedService extends Service { // Clients will always receive JSON. if ((params != null && params['provider'] != null)) { return result; - } - else { + } 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']; @@ -54,7 +46,7 @@ class MongoTypedService extends Service { try { Model target = - (data is T) ? data : god.deserializeDatum(data, outputType: T); + (data is T) ? data : god.deserializeDatum(data, outputType: T); item = god.serializeObject(target); item = _removeSensitive(item); @@ -86,8 +78,8 @@ class MongoTypedService extends Service { Future modify(id, Map data, [Map params]) async { ObjectId _id = _makeId(id); try { - Map result = await collection.findOne( - where.id(_id).and(_makeQuery(params))); + Map result = + await collection.findOne(where.id(_id).and(_makeQuery(params))); if (result == null) { throw new AngelHttpException.NotFound( @@ -108,8 +100,8 @@ class MongoTypedService extends Service { @override Future update(id, _data, [Map params]) async { try { - Model data = (_data is T) ? _data : god.deserializeDatum( - _data, outputType: T); + Model data = + (_data is T) ? _data : god.deserializeDatum(_data, outputType: T); ObjectId _id = _makeId(id); Map rawData = _removeSensitive(god.serializeObject(data)); rawData['_id'] = _id; @@ -128,4 +120,16 @@ class MongoTypedService extends Service { throw new AngelHttpException(e, stackTrace: st); } } + + @override + Future remove(id, [Map params]) async { + Map result = await read(id, params); + + try { + await collection.remove(where.id(_makeId(id)).and(_makeQuery(params))); + return result; + } catch (e, st) { + throw new AngelHttpException(e, stackTrace: st); + } + } } diff --git a/test/generic_tests.dart b/test/generic_tests.dart index 4e346135..c9e4c97e 100644 --- a/test/generic_tests.dart +++ b/test/generic_tests.dart @@ -23,6 +23,9 @@ wireHooked(HookedService hooked) { }) ..afterUpdated.listen((HookedServiceEvent event) { print("Just updated: ${event.result}"); + }) + ..afterRemoved.listen((HookedServiceEvent event) { + print("Just removed: ${event.result}"); }); } @@ -48,7 +51,7 @@ main() { app.use('/api', Greetings); 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}"; }); @@ -93,9 +96,8 @@ main() { expect(response.statusCode, equals(HttpStatus.OK)); Map created = god.deserialize(response.body); - response = await client.patch( - "$url/api/${created['id']}", body: god.serialize({"to": "Mom"}), - headers: headers); + response = await client.patch("$url/api/${created['id']}", + body: god.serialize({"to": "Mom"}), headers: headers); Map modified = god.deserialize(response.body); expect(response.statusCode, equals(HttpStatus.OK)); expect(modified['id'], equals(created['id'])); @@ -109,9 +111,8 @@ main() { expect(response.statusCode, equals(HttpStatus.OK)); Map created = god.deserialize(response.body); - response = await client.post( - "$url/api/${created['id']}", body: god.serialize({"to": "Updated"}), - headers: headers); + response = await client.post("$url/api/${created['id']}", + body: god.serialize({"to": "Updated"}), headers: headers); Map modified = god.deserialize(response.body); expect(response.statusCode, equals(HttpStatus.OK)); expect(modified['id'], equals(created['id'])); @@ -119,11 +120,19 @@ main() { expect(modified['updatedAt'], isNot(null)); }); - test('remove item', () async {}); + test('remove item', () async { + var response = await client.post("$url/api", + body: god.serialize(testGreeting), headers: headers); + Map created = god.deserialize(response.body); - test(r'$sort', () async { + int lastCount = (await Greetings.index()).length; + + await client.delete("$url/api/${created['id']}"); + expect((await Greetings.index()).length, equals(lastCount - 1)); }); + test(r'$sort', () async {}); + test('query parameters', () async {}); }); } diff --git a/test/packages b/test/packages new file mode 120000 index 00000000..a16c4050 --- /dev/null +++ b/test/packages @@ -0,0 +1 @@ +../packages \ No newline at end of file diff --git a/test/typed_tests.dart b/test/typed_tests.dart index d29eaeae..0f06f653 100644 --- a/test/typed_tests.dart +++ b/test/typed_tests.dart @@ -54,7 +54,7 @@ main() { app.use('/api', Greetings); 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}"; }); @@ -109,9 +109,8 @@ main() { expect(response.statusCode, equals(HttpStatus.OK)); Map created = god.deserialize(response.body); - response = await client.patch( - "$url/api/${created['id']}", body: god.serialize({"to": "Mom"}), - headers: headers); + response = await client.patch("$url/api/${created['id']}", + body: god.serialize({"to": "Mom"}), headers: headers); Map modified = god.deserialize(response.body); expect(response.statusCode, equals(HttpStatus.OK)); expect(modified['id'], equals(created['id'])); @@ -129,9 +128,8 @@ main() { expect(response.statusCode, equals(HttpStatus.OK)); Map created = god.deserialize(response.body); - response = await client.post( - "$url/api/${created['id']}", body: god.serialize({"to": "Updated"}), - headers: headers); + response = await client.post("$url/api/${created['id']}", + body: god.serialize({"to": "Updated"}), headers: headers); Map modified = god.deserialize(response.body); expect(response.statusCode, equals(HttpStatus.OK)); expect(modified['id'], equals(created['id'])); @@ -139,7 +137,23 @@ main() { expect(modified['updatedAt'], isNot(null)); }); - test('remove item', () async {}); + test('remove item', () async { + var response = await client.post("$url/api", + body: god.serialize(testGreeting), headers: headers); + Map created = god.deserialize(response.body); + + int lastCount = (await Greetings.index()).length; + + await client.delete("$url/api/${created['id']}"); + expect((await Greetings.index()).length, equals(lastCount - 1)); + + Greeting bernie = + await Greetings.create(new Greeting(to: "Bernie Sanders")); + lastCount = (await Greetings.index()).length; + + await Greetings.remove(bernie.id); + expect((await Greetings.index()).length, equals(lastCount - 1)); + }); test(r'$sort', () async {});