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
|
# 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.
|
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;
|
DateTime updatedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Map _transformId(Map doc) {
|
Map _transformId(Map doc) {
|
||||||
Map result = mergeMap([doc]);
|
Map result = mergeMap([doc]);
|
||||||
result['id'] = doc['_id'];
|
result['id'] = doc['_id'];
|
||||||
|
@ -33,8 +32,8 @@ Map _transformId(Map doc) {
|
||||||
|
|
||||||
_lastItem(DbCollection collection, Function _jsonify, [Map params]) async {
|
_lastItem(DbCollection collection, Function _jsonify, [Map params]) async {
|
||||||
return (await (await collection
|
return (await (await collection
|
||||||
.find(where.sortBy('\$natural', descending: true)))
|
.find(where.sortBy('\$natural', descending: true)))
|
||||||
.toList())
|
.toList())
|
||||||
.map((x) => _jsonify(x, params))
|
.map((x) => _jsonify(x, params))
|
||||||
.first;
|
.first;
|
||||||
}
|
}
|
||||||
|
@ -48,8 +47,11 @@ ObjectId _makeId(id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Map _removeSensitive(Map data) {
|
Map _removeSensitive(Map data) {
|
||||||
return data..remove('id')..remove('_id')..remove('createdAt')..remove(
|
return data
|
||||||
'updatedAt');
|
..remove('id')
|
||||||
|
..remove('_id')
|
||||||
|
..remove('createdAt')
|
||||||
|
..remove('updatedAt');
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectorBuilder _makeQuery([Map params_]) {
|
SelectorBuilder _makeQuery([Map params_]) {
|
||||||
|
|
|
@ -4,14 +4,7 @@ part of angel_mongo;
|
||||||
class MongoService extends Service {
|
class MongoService extends Service {
|
||||||
DbCollection collection;
|
DbCollection collection;
|
||||||
|
|
||||||
MongoService(DbCollection this.collection):super();
|
MongoService(DbCollection this.collection) : super();
|
||||||
|
|
||||||
Map _transformId(Map doc) {
|
|
||||||
Map result = mergeMap([doc]);
|
|
||||||
result['id'] = doc['_id'];
|
|
||||||
|
|
||||||
return result..remove('_id');
|
|
||||||
}
|
|
||||||
|
|
||||||
_jsonify(Map doc, [Map params]) {
|
_jsonify(Map doc, [Map params]) {
|
||||||
Map result = {};
|
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 {
|
class MongoTypedService<T> extends Service {
|
||||||
DbCollection collection;
|
DbCollection collection;
|
||||||
|
|
||||||
MongoTypedService(DbCollection this.collection):super() {
|
MongoTypedService(DbCollection this.collection) : 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 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]) {
|
_jsonify(Map doc, [Map params]) {
|
||||||
Map result = {};
|
Map result = {};
|
||||||
for (var key in doc.keys) {
|
for (var key in doc.keys) {
|
||||||
|
@ -31,8 +24,7 @@ class MongoTypedService<T> extends Service {
|
||||||
// Clients will always receive JSON.
|
// Clients will always receive JSON.
|
||||||
if ((params != null && params['provider'] != null)) {
|
if ((params != null && params['provider'] != null)) {
|
||||||
return result;
|
return result;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// However, when we run server-side, we should return a T, not a Map.
|
// However, when we run server-side, we should return a T, not a Map.
|
||||||
Model typedResult = god.deserializeDatum(result, outputType: T);
|
Model typedResult = god.deserializeDatum(result, outputType: T);
|
||||||
typedResult.createdAt = result['createdAt'];
|
typedResult.createdAt = result['createdAt'];
|
||||||
|
@ -54,7 +46,7 @@ class MongoTypedService<T> extends Service {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Model target =
|
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 = god.serializeObject(target);
|
||||||
item = _removeSensitive(item);
|
item = _removeSensitive(item);
|
||||||
|
|
||||||
|
@ -86,8 +78,8 @@ class MongoTypedService<T> extends Service {
|
||||||
Future modify(id, Map data, [Map params]) async {
|
Future modify(id, Map data, [Map params]) async {
|
||||||
ObjectId _id = _makeId(id);
|
ObjectId _id = _makeId(id);
|
||||||
try {
|
try {
|
||||||
Map result = await collection.findOne(
|
Map result =
|
||||||
where.id(_id).and(_makeQuery(params)));
|
await collection.findOne(where.id(_id).and(_makeQuery(params)));
|
||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw new AngelHttpException.NotFound(
|
throw new AngelHttpException.NotFound(
|
||||||
|
@ -108,8 +100,8 @@ class MongoTypedService<T> extends Service {
|
||||||
@override
|
@override
|
||||||
Future update(id, _data, [Map params]) async {
|
Future update(id, _data, [Map params]) async {
|
||||||
try {
|
try {
|
||||||
Model data = (_data is T) ? _data : god.deserializeDatum(
|
Model data =
|
||||||
_data, outputType: T);
|
(_data is T) ? _data : god.deserializeDatum(_data, outputType: T);
|
||||||
ObjectId _id = _makeId(id);
|
ObjectId _id = _makeId(id);
|
||||||
Map rawData = _removeSensitive(god.serializeObject(data));
|
Map rawData = _removeSensitive(god.serializeObject(data));
|
||||||
rawData['_id'] = _id;
|
rawData['_id'] = _id;
|
||||||
|
@ -128,4 +120,16 @@ class MongoTypedService<T> extends Service {
|
||||||
throw new AngelHttpException(e, stackTrace: st);
|
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) {
|
..afterUpdated.listen((HookedServiceEvent event) {
|
||||||
print("Just updated: ${event.result}");
|
print("Just updated: ${event.result}");
|
||||||
|
})
|
||||||
|
..afterRemoved.listen((HookedServiceEvent event) {
|
||||||
|
print("Just removed: ${event.result}");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +51,7 @@ main() {
|
||||||
|
|
||||||
app.use('/api', Greetings);
|
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}";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -93,9 +96,8 @@ main() {
|
||||||
expect(response.statusCode, equals(HttpStatus.OK));
|
expect(response.statusCode, equals(HttpStatus.OK));
|
||||||
Map created = god.deserialize(response.body);
|
Map created = god.deserialize(response.body);
|
||||||
|
|
||||||
response = await client.patch(
|
response = await client.patch("$url/api/${created['id']}",
|
||||||
"$url/api/${created['id']}", body: god.serialize({"to": "Mom"}),
|
body: god.serialize({"to": "Mom"}), headers: headers);
|
||||||
headers: headers);
|
|
||||||
Map modified = god.deserialize(response.body);
|
Map modified = god.deserialize(response.body);
|
||||||
expect(response.statusCode, equals(HttpStatus.OK));
|
expect(response.statusCode, equals(HttpStatus.OK));
|
||||||
expect(modified['id'], equals(created['id']));
|
expect(modified['id'], equals(created['id']));
|
||||||
|
@ -109,9 +111,8 @@ main() {
|
||||||
expect(response.statusCode, equals(HttpStatus.OK));
|
expect(response.statusCode, equals(HttpStatus.OK));
|
||||||
Map created = god.deserialize(response.body);
|
Map created = god.deserialize(response.body);
|
||||||
|
|
||||||
response = await client.post(
|
response = await client.post("$url/api/${created['id']}",
|
||||||
"$url/api/${created['id']}", body: god.serialize({"to": "Updated"}),
|
body: god.serialize({"to": "Updated"}), headers: headers);
|
||||||
headers: headers);
|
|
||||||
Map modified = god.deserialize(response.body);
|
Map modified = god.deserialize(response.body);
|
||||||
expect(response.statusCode, equals(HttpStatus.OK));
|
expect(response.statusCode, equals(HttpStatus.OK));
|
||||||
expect(modified['id'], equals(created['id']));
|
expect(modified['id'], equals(created['id']));
|
||||||
|
@ -119,11 +120,19 @@ main() {
|
||||||
expect(modified['updatedAt'], isNot(null));
|
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 {});
|
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);
|
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}";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -109,9 +109,8 @@ main() {
|
||||||
expect(response.statusCode, equals(HttpStatus.OK));
|
expect(response.statusCode, equals(HttpStatus.OK));
|
||||||
Map created = god.deserialize(response.body);
|
Map created = god.deserialize(response.body);
|
||||||
|
|
||||||
response = await client.patch(
|
response = await client.patch("$url/api/${created['id']}",
|
||||||
"$url/api/${created['id']}", body: god.serialize({"to": "Mom"}),
|
body: god.serialize({"to": "Mom"}), headers: headers);
|
||||||
headers: headers);
|
|
||||||
Map modified = god.deserialize(response.body);
|
Map modified = god.deserialize(response.body);
|
||||||
expect(response.statusCode, equals(HttpStatus.OK));
|
expect(response.statusCode, equals(HttpStatus.OK));
|
||||||
expect(modified['id'], equals(created['id']));
|
expect(modified['id'], equals(created['id']));
|
||||||
|
@ -129,9 +128,8 @@ main() {
|
||||||
expect(response.statusCode, equals(HttpStatus.OK));
|
expect(response.statusCode, equals(HttpStatus.OK));
|
||||||
Map created = god.deserialize(response.body);
|
Map created = god.deserialize(response.body);
|
||||||
|
|
||||||
response = await client.post(
|
response = await client.post("$url/api/${created['id']}",
|
||||||
"$url/api/${created['id']}", body: god.serialize({"to": "Updated"}),
|
body: god.serialize({"to": "Updated"}), headers: headers);
|
||||||
headers: headers);
|
|
||||||
Map modified = god.deserialize(response.body);
|
Map modified = god.deserialize(response.body);
|
||||||
expect(response.statusCode, equals(HttpStatus.OK));
|
expect(response.statusCode, equals(HttpStatus.OK));
|
||||||
expect(modified['id'], equals(created['id']));
|
expect(modified['id'], equals(created['id']));
|
||||||
|
@ -139,7 +137,23 @@ main() {
|
||||||
expect(modified['updatedAt'], isNot(null));
|
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 {});
|
test(r'$sort', () async {});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue