This commit is contained in:
Tobe O 2018-10-18 18:58:03 -04:00
parent d52fd36e45
commit 31ff913784
10 changed files with 58 additions and 147 deletions

1
.gitignore vendored
View file

@ -7,6 +7,7 @@
.pub/ .pub/
build/ build/
**/packages/ **/packages/
.dart_tool
# Files created by dart2js # Files created by dart2js
# (Most Dart developers will use pub build to compile Dart, use/modify these # (Most Dart developers will use pub build to compile Dart, use/modify these

3
CHANGELOG.md Normal file
View file

@ -0,0 +1,3 @@
# 2.0.0-rc.0
* Delete `mongo_service_typed`.
* Update for Angel 2.

3
analysis_options.yaml Normal file
View file

@ -0,0 +1,3 @@
analyzer:
strong-mode:
implicit-casts: false

View file

@ -1,7 +1,7 @@
part of angel_mongo.services; 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<String, Map<String, dynamic>> {
DbCollection collection; DbCollection collection;
/// If set to `true`, clients can remove all items by passing a `null` `id` to `remove`. /// If set to `true`, clients can remove all items by passing a `null` `id` to `remove`.
@ -18,14 +18,14 @@ class MongoService extends Service {
{this.allowRemoveAll: false, this.allowQuery: true, this.debug: true}) {this.allowRemoveAll: false, this.allowQuery: true, this.debug: true})
: super(); : super();
SelectorBuilder _makeQuery([Map params_]) { SelectorBuilder _makeQuery([Map<String, dynamic> params_]) {
Map params = new Map.from(params_ ?? {}); Map params = new Map.from(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'] is SelectorBuilder) { if (params['query'] is SelectorBuilder) {
return params['query']; return params['query'] as SelectorBuilder;
} }
for (var key in params.keys) { for (var key in params.keys) {
@ -47,18 +47,18 @@ class MongoService extends Service {
} else if (params[key] is String && key == r'$sort') { } else if (params[key] is String && key == r'$sort') {
// If they send just a string, then we'll sort // If they send just a string, then we'll sort
// by that, ascending // by that, ascending
result = result.sortBy(params[key]); result = result.sortBy(params[key] as String);
} }
} else if (key == 'query' && } else if (key == 'query' &&
(allowQuery == true || !params.containsKey('provider'))) { (allowQuery == true || !params.containsKey('provider'))) {
Map query = params[key]; Map query = params[key];
query.forEach((key, v) { query.forEach((key, v) {
var value = v is Map ? _filterNoQuery(v) : v; var value = v is Map<String, dynamic> ? _filterNoQuery(v) : v;
if (!_NO_QUERY.contains(key) && if (!_NO_QUERY.contains(key) &&
value is! RequestContext && value is! RequestContext &&
value is! ResponseContext) { value is! ResponseContext) {
result = result.and(where.eq(key, value)); result = result.and(where.eq(key as String, value));
} }
}); });
} }
@ -67,8 +67,9 @@ class MongoService extends Service {
return result; return result;
} }
_jsonify(Map doc, [Map params]) { Map<String, dynamic> _jsonify(Map<String, dynamic> doc,
Map result = {}; [Map<String, dynamic> params]) {
var result = <String, dynamic>{};
for (var key in doc.keys) { for (var key in doc.keys) {
var value = doc[key]; var value = doc[key];
@ -83,7 +84,8 @@ class MongoService extends Service {
} }
@override @override
Future<List> index([Map params]) async { Future<List<Map<String, dynamic>>> index(
[Map<String, dynamic> params]) async {
return await (await collection.find(_makeQuery(params))) return await (await collection.find(_makeQuery(params)))
.map((x) => _jsonify(x, params)) .map((x) => _jsonify(x, params))
.toList(); .toList();
@ -92,9 +94,9 @@ class MongoService extends Service {
static const String _NONCE_KEY = '__angel__mongo__nonce__key__'; static const String _NONCE_KEY = '__angel__mongo__nonce__key__';
@override @override
Future create(data, [Map params]) async { Future<Map<String, dynamic>> create(Map<String, dynamic> data,
Map item = (data is Map) ? data : god.serializeObject(data); [Map<String, dynamic> params]) async {
item = _removeSensitive(item); var item = _removeSensitive(data);
try { try {
String nonce = (await collection.db.getNonce())['nonce']; String nonce = (await collection.db.getNonce())['nonce'];
@ -110,9 +112,10 @@ class MongoService extends Service {
} }
@override @override
Future read(id, [Map params]) async { Future<Map<String, dynamic>> read(String id,
[Map<String, dynamic> params]) async {
ObjectId _id = _makeId(id); ObjectId _id = _makeId(id);
Map found = await collection.findOne(where.id(_id).and(_makeQuery(params))); var found = await collection.findOne(where.id(_id).and(_makeQuery(params)));
if (found == null) { if (found == null) {
throw new AngelHttpException.notFound( throw new AngelHttpException.notFound(
@ -123,22 +126,20 @@ class MongoService extends Service {
} }
@override @override
Future modify(id, data, [Map params]) async { Future<Map<String, dynamic>> modify(String id, data,
var target; [Map<String, dynamic> params]) async {
Map<String, dynamic> target;
try { try {
target = await read(id, params); target = await read(id, params);
} on AngelHttpException catch (e) { } on AngelHttpException catch (e) {
if (e.statusCode == HttpStatus.NOT_FOUND) if (e.statusCode == 404)
return await create(data, params); return await create(data, params);
else else
rethrow; rethrow;
} }
Map result = mergeMap([ var result = mergeMap([target, _removeSensitive(data)]);
target is Map ? target : god.serializeObject(target),
_removeSensitive(data)
]);
//result['updatedAt'] = new DateTime.now().toIso8601String(); //result['updatedAt'] = new DateTime.now().toIso8601String();
try { try {
@ -154,8 +155,9 @@ class MongoService extends Service {
} }
@override @override
Future update(id, data, [Map params]) async { Future<Map<String, dynamic>> update(String id, Map<String, dynamic> data,
Map result = _removeSensitive(data); [Map<String, dynamic> params]) async {
var result = _removeSensitive(data);
result['_id'] = _makeId(id); result['_id'] = _makeId(id);
/*result['createdAt'] = /*result['createdAt'] =
target is Map ? target['createdAt'] : target.createdAt; target is Map ? target['createdAt'] : target.createdAt;
@ -181,7 +183,8 @@ class MongoService extends Service {
} }
@override @override
Future remove(id, [Map params]) async { Future<Map<String, dynamic>> remove(String id,
[Map<String, dynamic> params]) async {
if (id == null || if (id == null ||
id == 'null' && id == 'null' &&
(allowRemoveAll == true || (allowRemoveAll == true ||

View file

@ -1,84 +0,0 @@
part of angel_mongo.services;
/// Use a normal TypedService instead.
@deprecated
class MongoTypedService<T> extends MongoService {
MongoTypedService(DbCollection collection, {bool allowRemoveAll, bool debug})
: super(collection,
allowRemoveAll: allowRemoveAll == true, debug: debug == true) {
if (!reflectType(T).isAssignableTo(reflectType(Model)))
throw new Exception(
"If you specify a type for MongoService, it must extend Model.");
}
_deserialize(x) {
// print('DESERIALIZE: $x (${x.runtimeType})');
if (x == dynamic || x == Object || x is T)
return x;
else if (x is Map) {
Map data = x.keys.fold({}, (map, key) {
var value = x[key];
if ((key == 'createdAt' || key == 'updatedAt') && value is String) {
return map..[key] = DateTime.parse(value).toIso8601String();
} else if (value is DateTime) {
return map..[key] = value.toIso8601String();
} else {
return map..[key] = value;
}
});
Model result = god.deserializeDatum(data, outputType: T);
if (x['createdAt'] is String) {
result.createdAt = DateTime.parse(x['createdAt']);
} else if (x['createdAt'] is DateTime) {
result.createdAt = x['createdAt'];
}
if (x['updatedAt'] is String) {
result.updatedAt = DateTime.parse(x['updatedAt']);
} else if (x['updatedAt'] is DateTime) {
result.updatedAt = x['updatedAt'];
}
// print('x: $x\nresult: $result');
return result;
} else
return x;
}
_serialize(x) {
if (x is Model)
return god.serializeObject(x);
else if (x is Map)
return x;
else
throw new ArgumentError('Cannot serialize ${x.runtimeType}');
}
@override
Future<List> index([Map params]) async {
var result = await super.index(params);
return result.map(_deserialize).toList();
}
@override
Future create(data, [Map params]) =>
super.create(_serialize(data), params).then(_deserialize);
@override
Future read(id, [Map params]) => super.read(id, params).then(_deserialize);
@override
Future modify(id, data, [Map params]) =>
super.modify(id, _serialize(data), params).then(_deserialize);
@override
Future update(id, data, [Map params]) =>
super.update(id, _serialize(data), params).then(_deserialize);
@override
Future remove(id, [Map params]) =>
super.remove(id, params).then(_deserialize);
}

View file

@ -1,20 +1,14 @@
library angel_mongo.services; library angel_mongo.services;
import 'dart:async'; import 'dart:async';
import 'dart:io';
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: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';
part 'mongo_service.dart'; part 'mongo_service.dart';
part 'mongo_service_typed.dart'; Map<String, dynamic> _transformId(Map<String, dynamic> doc) {
var result = new Map<String, dynamic>.from(doc);
Map _transformId(Map doc) {
Map result = new Map.from(doc);
result result
..['id'] = doc['_id'] ..['id'] = doc['_id']
..remove('_id'); ..remove('_id');
@ -22,14 +16,6 @@ Map _transformId(Map doc) {
return result; return result;
} }
_lastItem(DbCollection collection, Function _jsonify, [Map params]) async {
return (await (await collection
.find(where.sortBy('\$natural', descending: true)))
.toList())
.map((x) => _jsonify(x, params))
.first;
}
ObjectId _makeId(id) { 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());
@ -40,7 +26,7 @@ ObjectId _makeId(id) {
const List<String> _SENSITIVE = const ['id', '_id', 'createdAt', 'updatedAt']; const List<String> _SENSITIVE = const ['id', '_id', 'createdAt', 'updatedAt'];
Map _removeSensitive(Map data) { Map<String, dynamic> _removeSensitive(Map<String, dynamic> data) {
return data.keys return data.keys
.where((k) => !_SENSITIVE.contains(k)) .where((k) => !_SENSITIVE.contains(k))
.fold({}, (map, key) => map..[key] = data[key]); .fold({}, (map, key) => map..[key] = data[key]);
@ -48,7 +34,7 @@ Map _removeSensitive(Map data) {
const List<String> _NO_QUERY = const ['__requestctx', '__responsectx']; const List<String> _NO_QUERY = const ['__requestctx', '__responsectx'];
Map _filterNoQuery(Map data) { Map<String, dynamic> _filterNoQuery(Map<String, dynamic> data) {
return data.keys.fold({}, (map, key) { return data.keys.fold({}, (map, key) {
var value = data[key]; var value = data[key];
@ -56,6 +42,6 @@ Map _filterNoQuery(Map data) {
value is RequestContext || value is RequestContext ||
value is ResponseContext) return map; value is ResponseContext) return map;
if (key is! Map) return map..[key] = value; if (key is! Map) return map..[key] = value;
return map..[key] = _filterNoQuery(value); return map..[key] = _filterNoQuery(value as Map<String, dynamic>);
}); });
} }

View file

@ -1,12 +1,14 @@
name: angel_mongo name: angel_mongo
version: 1.1.6 version: 2.0.0-rc.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
environment:
sdk: ">=2.0.0-dev <3.0.0"
dependencies: dependencies:
angel_framework: ">=1.0.0-dev < 2.0.0" angel_framework: ^2.0.0-alpha
json_god: ">=2.0.0-beta <3.0.0" json_god: ">=2.0.0-beta <3.0.0"
mongo_dart: ">= 0.2.7 < 1.0.0" mongo_dart: ">= 0.2.7 < 1.0.0"
dev_dependencies: dev_dependencies:
http: ">= 0.11.3 < 0.12.0" http: ">= 0.11.3 < 0.12.0"
test: ">= 0.12.13 < 0.13.0" test: ^1.0.0

View file

@ -1,4 +1,3 @@
import 'dart:io';
import 'package:angel_framework/angel_framework.dart'; import 'package:angel_framework/angel_framework.dart';
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;
@ -7,8 +6,8 @@ import 'package:mongo_dart/mongo_dart.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
final headers = { final headers = {
HttpHeaders.ACCEPT: ContentType.JSON.mimeType, 'accept': 'application/json',
HttpHeaders.CONTENT_TYPE: ContentType.JSON.mimeType 'content-type': 'application/json'
}; };
final Map testGreeting = {'to': 'world'}; final Map testGreeting = {'to': 'world'};
@ -23,18 +22,19 @@ wireHooked(HookedService hooked) {
main() { main() {
group('Generic Tests', () { group('Generic Tests', () {
Angel app = new Angel(); Angel app = new Angel();
AngelHttp transport = new AngelHttp(app);
http.Client client; http.Client client;
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 greetingService; HookedService<String, Map<String, dynamic>, MongoService> greetingService;
setUp(() async { setUp(() async {
client = new http.Client(); client = new http.Client();
await db.open(); await db.open();
testData = db.collection('test_data'); testData = db.collection('test_data');
// Delete anything before we start // Delete anything before we start
await testData.remove(); await testData.remove({});
var service = new MongoService(testData, debug: true); var service = new MongoService(testData, debug: true);
greetingService = new HookedService(service); greetingService = new HookedService(service);
@ -42,20 +42,15 @@ main() {
app.use('/api', greetingService); app.use('/api', greetingService);
app.fatalErrorStream.listen((AngelFatalError e) { var server = await transport.startServer('127.0.0.1', 0);
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}";
}); });
tearDown(() async { tearDown(() async {
// Delete anything left over // Delete anything left over
await testData.remove(); await testData.remove({});
await db.close(); await db.close();
await app.httpServer.close(force: true); await transport.close();
client = null; client = null;
url = null; url = null;
greetingService = null; greetingService = null;
@ -150,8 +145,10 @@ main() {
expect(queried[2]["id"], equals(world["id"]));*/ expect(queried[2]["id"], equals(world["id"]));*/
queried = await greetingService.index({ queried = await greetingService.index({
"\$query": {"_id": where.id(new ObjectId.fromHexString(world["id"]))} "\$query": {
}); "_id": where.id(new ObjectId.fromHexString(world["id"] as String))
}
}) as List<Map<String, dynamic>>;
print(queried); print(queried);
expect(queried.length, equals(1)); expect(queried.length, equals(1));
expect(queried[0], equals(world)); expect(queried[0], equals(world));