Functionality done
This commit is contained in:
parent
7d54936623
commit
71fe379db9
7 changed files with 111 additions and 45 deletions
33
README.md
33
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<T>`.
|
||||
|
||||
## 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<T>
|
||||
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.
|
||||
|
|
|
@ -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_]) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,19 +4,12 @@ part of angel_mongo;
|
|||
class MongoTypedService<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {});
|
||||
});
|
||||
}
|
||||
|
|
1
test/packages
Symbolic link
1
test/packages
Symbolic link
|
@ -0,0 +1 @@
|
|||
../packages
|
|
@ -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 {});
|
||||
|
||||
|
|
Loading…
Reference in a new issue