From a3f9d6ce28eccd780a10d5f70c07ba052f8fa5b8 Mon Sep 17 00:00:00 2001 From: regiostech Date: Mon, 9 May 2016 18:51:07 -0400 Subject: [PATCH] Will be done soon --- .gitignore | 60 +++++++++++++++ .idea/runConfigurations/All_Tests.xml | 6 ++ .idea/vcs.xml | 6 ++ lib/angel_mongo.dart | 10 +++ lib/mongo_service.dart | 92 ++++++++++++++++++++++ pubspec.yaml | 12 +++ test/all_tests.dart | 106 ++++++++++++++++++++++++++ 7 files changed, 292 insertions(+) create mode 100644 .idea/runConfigurations/All_Tests.xml create mode 100644 .idea/vcs.xml create mode 100644 lib/angel_mongo.dart create mode 100644 lib/mongo_service.dart create mode 100644 pubspec.yaml create mode 100644 test/all_tests.dart diff --git a/.gitignore b/.gitignore index 7c280441..c9d518bb 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,63 @@ doc/api/ # Don't commit pubspec lock file # (Library packages only! Remove pattern if developing an application package) pubspec.lock +### Dart template +# See https://www.dartlang.org/tools/private-files.html + +# Files and directories created by pub + +# Files created by dart2js +# (Most Dart developers will use pub build to compile Dart, use/modify these +# rules if you intend to use dart2js directly +# Convention is to use extension '.dart.js' for Dart compiled to Javascript to +# differentiate from explicit Javascript files) + +# Directory created by dartdoc + +# Don't commit pubspec lock file +# (Library packages only! Remove pattern if developing an application package) +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties diff --git a/.idea/runConfigurations/All_Tests.xml b/.idea/runConfigurations/All_Tests.xml new file mode 100644 index 00000000..a824b209 --- /dev/null +++ b/.idea/runConfigurations/All_Tests.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/lib/angel_mongo.dart b/lib/angel_mongo.dart new file mode 100644 index 00000000..314a2e16 --- /dev/null +++ b/lib/angel_mongo.dart @@ -0,0 +1,10 @@ +library angel_mongo; +import 'dart:async'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:json_god/json_god.dart'; +import 'package:merge_map/merge_map.dart'; +import 'package:mongo_dart/mongo_dart.dart'; + +part 'mongo_service.dart'; + +final _god = new God(); diff --git a/lib/mongo_service.dart b/lib/mongo_service.dart new file mode 100644 index 00000000..c0a3bb7b --- /dev/null +++ b/lib/mongo_service.dart @@ -0,0 +1,92 @@ +part of angel_mongo; + +class MongoService extends Service { + DbCollection collection; + + MongoService(DbCollection this.collection); + + Map _jsonify(Map doc) { + Map result = {}; + for (var key in doc.keys) { + if (doc[key] is ObjectId) { + result[key] = doc[key].toHexString(); + } else result[key] = doc[key]; + } + return result; + } + + _lastItem() async { + return (await (await collection.find( + where.sortBy('\$natural', descending: true))).toList()) + .map(_jsonify) + .first; + } + + SelectorBuilder _makeQuery([Map params_]) { + Map params = params_ ?? {}; + SelectorBuilder result = where.exists('_id'); + + for (var key in params.keys) { + if (key == r'$sort') { + if (params[key] is Map) { + // If they send a map, then we'll sort by every key in the map + for (String fieldName in params[key].keys.where((x) => x is String)) { + var sorter = params[key][fieldName]; + if (sorter is num) { + result = result.sortBy(fieldName, descending: sorter == -1); + } else if (sorter is String) { + result = result.sortBy(fieldName, descending: sorter == "-1"); + } + } + } else if (params[key] is String) { + // If they send just a string, then we'll sort + // by that, ascending + result = result.sortBy(params[key]); + } + } + + else if (key is String) { + result = result.and(where.eq(key, params[key])); + } + } + + return result; + } + + @override + Future index([Map params]) async { + return await (await collection.find(_makeQuery(params))) + .map(_jsonify) + .toList(); + } + + @override + Future create(data, [Map params]) async { + Map item = (data is Map) ? data : _god.serializeToMap(data); + item = mergeMap([item, params]); + item['createdAt'] = new DateTime.now(); + await collection.insert(item); + return await _lastItem(); + } + + @override + Future read(id, [Map params]) async { + ObjectId id_; + try { + id_ = (id is ObjectId) ? id : new ObjectId.fromHexString( + id.toString()); + } catch (e) { + throw new AngelHttpException.BadRequest(); + } + + 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); + } +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 00000000..44a65084 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,12 @@ +name: angel_mongo +version: 1.0.0-dev +description: Core libraries for the Angel framework. +author: Tobe O +homepage: https://github.com/angel-dart/angel_framework +dependencies: + angel_framework: ">=0.0.0-dev.17 < 0.1.0" + json_god: ">=1.0.0 <2.0.0" + mongo_dart: ">= 0.2.5+1 < 1.0.0" +dev_dependencies: + http: ">= 0.11.3 < 0.12.0" + test: ">= 0.12.13 < 0.13.0" \ No newline at end of file diff --git a/test/all_tests.dart b/test/all_tests.dart new file mode 100644 index 00000000..832c17c8 --- /dev/null +++ b/test/all_tests.dart @@ -0,0 +1,106 @@ +import 'dart:io'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_mongo/angel_mongo.dart'; +import 'package:http/http.dart' as http; +import 'package:json_god/json_god.dart'; +import 'package:mongo_dart/mongo_dart.dart'; +import 'package:test/test.dart'; + +final headers = { + HttpHeaders.ACCEPT: ContentType.JSON.mimeType, + HttpHeaders.CONTENT_TYPE: ContentType.JSON.mimeType +}; + +wireHooked(HookedService hooked) { + hooked.onCreated.listen((item) { + print("Just created: $item"); + }); +} + +main() { + group('angel_mongo', () { + Angel app = new Angel(); + http.Client client; + God god = new God(); + Db db = new Db('mongodb://localhost:27017/angel_mongo'); + DbCollection testData; + String url; + + setUp(() async { + client = new http.Client(); + await db.open(); + testData = db.collection('test_data'); + // Delete anything before we start + await testData.remove(); + var service = new MongoService(testData); + var hooked = new HookedService(service); + wireHooked(hooked); + + app.use('/api', hooked); + HttpServer server = await app.startServer( + InternetAddress.LOOPBACK_IP_V4, 0); + url = "http://${server.address.host}:${server.port}"; + }); + + tearDown(() async { + // Delete anything left over + await testData.remove(); + await db.close(); + await app.httpServer.close(force: true); + client = null; + url = null; + }); + + test('insert items', () async { + Map testUser = {'hello': 'world'}; + + var response = await client.post( + "$url/api", body: god.serialize(testUser), headers: headers); + expect(response.statusCode, equals(HttpStatus.OK)); + + response = await client.get("$url/api"); + expect(response.statusCode, 200); + List users = god.deserialize(response.body); + expect(users.length, equals(1)); + }); + + test('read item', () async { + Map testUser = {'hello': 'world'}; + var response = await client.post( + "$url/api", body: god.serialize(testUser), headers: headers); + expect(response.statusCode, equals(HttpStatus.OK)); + Map created = god.deserialize(response.body); + + response = await client.get("$url/api/${created['_id']}"); + expect(response.statusCode, equals(HttpStatus.OK)); + Map read = god.deserialize(response.body); + expect(read['_id'], equals(created['_id'])); + expect(read['hello'], equals('world')); + expect(read['createdAt'], isNot(null)); + }); + + test('modify item', () async { + + }); + + test('update item', () async { + + }); + + test('remove item', () async { + + }); + + test('sort by string', () async { + + }); + + test('sort by map', () async { + + }); + + test('query parameters', () async { + + }); + }); +} \ No newline at end of file