Updated to support dart 3
This commit is contained in:
parent
5a97dc51fc
commit
a0ade9d3a3
13 changed files with 609 additions and 0 deletions
12
packages/rethinkdb/AUTHORS.md
Normal file
12
packages/rethinkdb/AUTHORS.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Primary Authors
|
||||||
|
===============
|
||||||
|
|
||||||
|
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
|
||||||
|
|
||||||
|
Thomas is the current maintainer of the code base. He has refactored and migrated the
|
||||||
|
code base to support NNBD.
|
||||||
|
|
||||||
|
* __[Tobe O](thosakwe@gmail.com)__
|
||||||
|
|
||||||
|
Tobe has written much of the original code prior to NNBD migration. He has moved on and
|
||||||
|
is no longer involved with the project.
|
35
packages/rethinkdb/CHANGELOG.md
Normal file
35
packages/rethinkdb/CHANGELOG.md
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# Change Log
|
||||||
|
|
||||||
|
## 8.0.0
|
||||||
|
|
||||||
|
* Require Dart >= 3.3
|
||||||
|
* Updated `lints` to 4.0.0
|
||||||
|
|
||||||
|
## 7.0.0
|
||||||
|
|
||||||
|
* Skipped release
|
||||||
|
|
||||||
|
## 6.0.0
|
||||||
|
|
||||||
|
* Skipped release
|
||||||
|
|
||||||
|
## 5.0.0
|
||||||
|
|
||||||
|
* Skipped release
|
||||||
|
|
||||||
|
## 4.0.0
|
||||||
|
|
||||||
|
* Skipped release
|
||||||
|
|
||||||
|
## 3.0.0
|
||||||
|
|
||||||
|
* Skipped release
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
|
* Migrated to support Dart >= 2.12 NNBD
|
||||||
|
|
||||||
|
## 1.1.0
|
||||||
|
|
||||||
|
* Moved to `package:rethinkdb_driver`
|
||||||
|
* Fixed references to old hooked event names
|
29
packages/rethinkdb/LICENSE
Normal file
29
packages/rethinkdb/LICENSE
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, dukefirehawk.com
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
94
packages/rethinkdb/README.md
Normal file
94
packages/rethinkdb/README.md
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
# Angel3 RethinkDB
|
||||||
|
|
||||||
|
[![version 1.0.7](https://img.shields.io/badge/pub-1.0.7-brightgreen.svg)](https://pub.dartlang.org/packages/angel_rethink)
|
||||||
|
[![build status](https://travis-ci.org/angel-dart/rethink.svg?branch=master)](https://travis-ci.org/angel-dart/rethink)
|
||||||
|
|
||||||
|
RethinkDB-enabled services for the Angel framework.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Add the following to your `pubspec.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
angel3_rethink: ^8.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
`package:rethinkdb_driver2` will be installed as well.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
This library exposes one class: `RethinkService`. By default, these services will even
|
||||||
|
listen to [changefeeds](https://www.rethinkdb.com/docs/changefeeds/ruby/) from the database,
|
||||||
|
which makes them very suitable for WebSocket use.
|
||||||
|
|
||||||
|
However, only `CREATED`, `UPDATED` and `REMOVED` events will be fired. This is technically not
|
||||||
|
a problem, as it lowers the numbers of events you have to handle on the client side. ;)
|
||||||
|
|
||||||
|
## Model
|
||||||
|
|
||||||
|
`Model` is class with no real functionality; however, it represents a basic document, and your services should host inherited classes.
|
||||||
|
Other Angel service providers host `Model` as well, so you will easily be able to modify your application if you ever switch databases.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
class User extends Model {
|
||||||
|
String username;
|
||||||
|
String password;
|
||||||
|
}
|
||||||
|
|
||||||
|
main() async {
|
||||||
|
var r = new RethinkDb();
|
||||||
|
var conn = await r.connect();
|
||||||
|
|
||||||
|
app.use('/api/users', new RethinkService(conn, r.table('users')));
|
||||||
|
|
||||||
|
// Add type de/serialization if you want
|
||||||
|
app.use('/api/users', new TypedService<User>(new RethinkService(conn, r.table('users'))));
|
||||||
|
|
||||||
|
// You don't have to even use a table...
|
||||||
|
app.use('/api/pro_users', new RethinkService(conn, r.table('users').filter({'membership': 'pro'})));
|
||||||
|
|
||||||
|
app.service('api/users').afterCreated.listen((event) {
|
||||||
|
print("New user: ${event.result}");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## RethinkService
|
||||||
|
|
||||||
|
This class interacts with a `Query` (usually a table) and serializes data to and from Maps.
|
||||||
|
|
||||||
|
## RethinkTypedService<T>
|
||||||
|
|
||||||
|
Does the same as above, but serializes to and from a target class using `package:json_god` and its support for reflection.
|
||||||
|
|
||||||
|
## Querying
|
||||||
|
|
||||||
|
You can query these services as follows:
|
||||||
|
|
||||||
|
/path/to/service?foo=bar
|
||||||
|
|
||||||
|
The above will query the database to find records where 'foo' equals 'bar'.
|
||||||
|
|
||||||
|
The former will sort result in ascending order of creation, and so will the latter.
|
||||||
|
|
||||||
|
You can use advanced queries:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// Pass an actual query...
|
||||||
|
service.index({'query': r.table('foo').filter(...)});
|
||||||
|
|
||||||
|
// Or, a function that creates a query from a table...
|
||||||
|
service.index({'query': (table) => table.getAll('foo')});
|
||||||
|
|
||||||
|
// Or, a Map, which will be transformed into a `filter` query:
|
||||||
|
service.index({'query': {'foo': 'bar', 'baz': 'quux'}});
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also apply sorting by adding a `reql` parameter on the server-side.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
service.index({'reql': (query) => query.sort(...)});
|
||||||
|
```
|
||||||
|
|
||||||
|
See the tests for more usage examples.
|
1
packages/rethinkdb/analysis_options.yaml
Normal file
1
packages/rethinkdb/analysis_options.yaml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
include: package:lints/recommended.yaml
|
18
packages/rethinkdb/example/example.dart
Normal file
18
packages/rethinkdb/example/example.dart
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import 'package:angel3_framework/angel3_framework.dart';
|
||||||
|
import 'package:angel3_rethinkdb/angel3_rethinkdb.dart';
|
||||||
|
import 'package:belatuk_rethinkdb/belatuk_rethinkdb.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
RethinkDb r = RethinkDb();
|
||||||
|
var conn = await r.connect();
|
||||||
|
|
||||||
|
Angel app = Angel();
|
||||||
|
app.use('/todos', RethinkService(conn, r.table('todos')));
|
||||||
|
|
||||||
|
app.errorHandler = (e, req, res) async {
|
||||||
|
print('Whoops: $e');
|
||||||
|
};
|
||||||
|
|
||||||
|
app.logger = Logger.detached('angel')..onRecord.listen(print);
|
||||||
|
}
|
1
packages/rethinkdb/lib/angel3_rethinkdb.dart
Normal file
1
packages/rethinkdb/lib/angel3_rethinkdb.dart
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export 'src/rethink_service.dart';
|
283
packages/rethinkdb/lib/src/rethink_service.dart
Normal file
283
packages/rethinkdb/lib/src/rethink_service.dart
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
import 'dart:async';
|
||||||
|
//import 'dart:io';
|
||||||
|
import 'package:angel3_framework/angel3_framework.dart';
|
||||||
|
import 'package:belatuk_json_serializer/belatuk_json_serializer.dart' as god;
|
||||||
|
import 'package:belatuk_rethinkdb/belatuk_rethinkdb.dart';
|
||||||
|
|
||||||
|
// Extends a RethinkDB query.
|
||||||
|
typedef QueryCallback = RqlQuery Function(RqlQuery query);
|
||||||
|
|
||||||
|
/// Queries a single RethinkDB table or query.
|
||||||
|
class RethinkService extends Service {
|
||||||
|
/// If set to `true`, clients can remove all items by passing a `null` `id` to `remove`.
|
||||||
|
///
|
||||||
|
/// `false` by default.
|
||||||
|
final bool allowRemoveAll;
|
||||||
|
|
||||||
|
/// If set to `true`, parameters in `req.query` are applied to the database query.
|
||||||
|
final bool allowQuery;
|
||||||
|
|
||||||
|
final bool debug;
|
||||||
|
|
||||||
|
/// If set to `true`, then a HookedService mounted over this instance
|
||||||
|
/// will fire events when RethinkDB pushes events.
|
||||||
|
///
|
||||||
|
/// Good for scaling. ;)
|
||||||
|
final bool listenForChanges;
|
||||||
|
|
||||||
|
final Connection connection;
|
||||||
|
|
||||||
|
/// Doesn't actually have to be a table, just a RethinkDB query.
|
||||||
|
///
|
||||||
|
/// However, a table is the most common usecase.
|
||||||
|
final RqlQuery table;
|
||||||
|
|
||||||
|
RethinkService(this.connection, this.table,
|
||||||
|
{this.allowRemoveAll = false,
|
||||||
|
this.allowQuery = true,
|
||||||
|
this.debug = false,
|
||||||
|
this.listenForChanges = true})
|
||||||
|
: super();
|
||||||
|
|
||||||
|
RqlQuery buildQuery(RqlQuery initialQuery, Map params) {
|
||||||
|
params['broadcast'] = params.containsKey('broadcast')
|
||||||
|
? params['broadcast']
|
||||||
|
: (listenForChanges != true);
|
||||||
|
|
||||||
|
var q = _getQueryInner(initialQuery, params);
|
||||||
|
|
||||||
|
if (params.containsKey('reql') == true && params['reql'] is QueryCallback) {
|
||||||
|
q = params['reql'](q) as RqlQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
RqlQuery _getQueryInner(RqlQuery query, Map params) {
|
||||||
|
if (!params.containsKey('query')) {
|
||||||
|
return query;
|
||||||
|
} else {
|
||||||
|
if (params['query'] is RqlQuery) {
|
||||||
|
return params['query'] as RqlQuery;
|
||||||
|
} else if (params['query'] is QueryCallback) {
|
||||||
|
return params['query'](table) as RqlQuery;
|
||||||
|
} else if (params['query'] is! Map || allowQuery != true) {
|
||||||
|
return query;
|
||||||
|
} else {
|
||||||
|
var q = params['query'] as Map;
|
||||||
|
return q.keys.fold<RqlQuery>(query, (out, key) {
|
||||||
|
var val = q[key];
|
||||||
|
|
||||||
|
if (val is RequestContext ||
|
||||||
|
val is ResponseContext ||
|
||||||
|
key == 'provider' ||
|
||||||
|
val is Providers) {
|
||||||
|
return out;
|
||||||
|
} else {
|
||||||
|
return out.filter({key.toString(): val});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _sendQuery(RqlQuery query) async {
|
||||||
|
var result = await query.run(connection);
|
||||||
|
|
||||||
|
if (result is Cursor) {
|
||||||
|
return await result.toList();
|
||||||
|
} else if (result is Map && result['generated_keys'] is List) {
|
||||||
|
if (result['generated_keys'].length == 1) {
|
||||||
|
return await read(result['generated_keys'].first);
|
||||||
|
}
|
||||||
|
//return await Future.wait(result['generated_keys'].map(read));
|
||||||
|
return await result['generated_keys'].map(read);
|
||||||
|
} else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic _serialize(data) {
|
||||||
|
if (data is Map) {
|
||||||
|
return data;
|
||||||
|
} else if (data is Iterable) {
|
||||||
|
return data.map(_serialize).toList();
|
||||||
|
} else {
|
||||||
|
return god.serializeObject(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic _squeeze(data) {
|
||||||
|
if (data is Map) {
|
||||||
|
return data.keys.fold<Map>({}, (map, k) => map..[k.toString()] = data[k]);
|
||||||
|
} else if (data is Iterable) {
|
||||||
|
return data.map(_squeeze).toList();
|
||||||
|
} else {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onHooked(HookedService hookedService) {
|
||||||
|
if (listenForChanges == true) {
|
||||||
|
listenToQuery(table, hookedService);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future listenToQuery(RqlQuery query, HookedService hookedService) async {
|
||||||
|
var feed =
|
||||||
|
await query.changes({'include_types': true}).run(connection) as Feed;
|
||||||
|
|
||||||
|
Future<dynamic> onData(dynamic event) {
|
||||||
|
if (event != null && event is Map) {
|
||||||
|
var type = event['type']?.toString();
|
||||||
|
var newVal = event['new_val'];
|
||||||
|
var oldVal = event['old_val'];
|
||||||
|
|
||||||
|
if (type == 'add') {
|
||||||
|
// Create
|
||||||
|
hookedService.fireEvent(
|
||||||
|
hookedService.afterCreated,
|
||||||
|
HookedServiceEvent(
|
||||||
|
true, null, null, this, HookedServiceEvent.created,
|
||||||
|
result: newVal));
|
||||||
|
} else if (type == 'change') {
|
||||||
|
// Update
|
||||||
|
hookedService.fireEvent(
|
||||||
|
hookedService.afterCreated,
|
||||||
|
HookedServiceEvent(
|
||||||
|
true, null, null, this, HookedServiceEvent.updated,
|
||||||
|
result: newVal, id: oldVal['id'], data: newVal));
|
||||||
|
} else if (type == 'remove') {
|
||||||
|
// Remove
|
||||||
|
hookedService.fireEvent(
|
||||||
|
hookedService.afterCreated,
|
||||||
|
HookedServiceEvent(
|
||||||
|
true, null, null, this, HookedServiceEvent.removed,
|
||||||
|
result: oldVal, id: oldVal['id']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Future.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
feed.listen(onData);
|
||||||
|
/*
|
||||||
|
feed.listen((Map event) {
|
||||||
|
var type = event['type']?.toString();
|
||||||
|
var newVal = event['new_val'], oldVal = event['old_val'];
|
||||||
|
|
||||||
|
if (type == 'add') {
|
||||||
|
// Create
|
||||||
|
hookedService.fireEvent(
|
||||||
|
hookedService.afterCreated,
|
||||||
|
HookedServiceEvent(
|
||||||
|
true, null, null, this, HookedServiceEvent.created,
|
||||||
|
result: newVal));
|
||||||
|
} else if (type == 'change') {
|
||||||
|
// Update
|
||||||
|
hookedService.fireEvent(
|
||||||
|
hookedService.afterCreated,
|
||||||
|
HookedServiceEvent(
|
||||||
|
true, null, null, this, HookedServiceEvent.updated,
|
||||||
|
result: newVal, id: oldVal['id'], data: newVal));
|
||||||
|
} else if (type == 'remove') {
|
||||||
|
// Remove
|
||||||
|
hookedService.fireEvent(
|
||||||
|
hookedService.afterCreated,
|
||||||
|
HookedServiceEvent(
|
||||||
|
true, null, null, this, HookedServiceEvent.removed,
|
||||||
|
result: oldVal, id: oldVal['id']));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Invalid override method
|
||||||
|
/*
|
||||||
|
@override
|
||||||
|
Future index([Map params]) async {
|
||||||
|
var query = buildQuery(table, params);
|
||||||
|
return await _sendQuery(query);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
@override
|
||||||
|
Future read(id, [Map? params]) async {
|
||||||
|
var query = buildQuery(table.get(id?.toString()), params ?? {});
|
||||||
|
var found = await _sendQuery(query);
|
||||||
|
//print('Found for $id: $found');
|
||||||
|
|
||||||
|
if (found == null) {
|
||||||
|
throw AngelHttpException.notFound(message: 'No record found for ID $id');
|
||||||
|
} else {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future create(data, [Map? params]) async {
|
||||||
|
if (table is! Table) throw AngelHttpException.methodNotAllowed();
|
||||||
|
|
||||||
|
var d = _serialize(data);
|
||||||
|
var q = table as Table;
|
||||||
|
var query = buildQuery(q.insert(_squeeze(d)), params ?? {});
|
||||||
|
return await _sendQuery(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future modify(id, data, [Map? params]) async {
|
||||||
|
var d = _serialize(data);
|
||||||
|
|
||||||
|
if (d is Map && d.containsKey('id')) {
|
||||||
|
try {
|
||||||
|
await read(d['id'], params);
|
||||||
|
} on AngelHttpException catch (e) {
|
||||||
|
if (e.statusCode == 404) {
|
||||||
|
return await create(data, params);
|
||||||
|
} else {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var query = buildQuery(table.get(id?.toString()), params ?? {}).update(d);
|
||||||
|
await _sendQuery(query);
|
||||||
|
return await read(id, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future update(id, data, [Map? params]) async {
|
||||||
|
var d = _serialize(data);
|
||||||
|
|
||||||
|
if (d is Map && d.containsKey('id')) {
|
||||||
|
try {
|
||||||
|
await read(d['id'], params);
|
||||||
|
} on AngelHttpException catch (e) {
|
||||||
|
if (e.statusCode == 404) {
|
||||||
|
return await create(data, params);
|
||||||
|
} else {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d is Map && !d.containsKey('id')) d['id'] = id.toString();
|
||||||
|
var query = buildQuery(table.get(id?.toString()), params ?? {}).replace(d);
|
||||||
|
await _sendQuery(query);
|
||||||
|
return await read(id, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future remove(id, [Map? params]) async {
|
||||||
|
if (id == null ||
|
||||||
|
id == 'null' &&
|
||||||
|
(allowRemoveAll == true ||
|
||||||
|
params?.containsKey('provider') != true)) {
|
||||||
|
return await _sendQuery(table.delete());
|
||||||
|
} else {
|
||||||
|
var prior = await read(id, params);
|
||||||
|
var query = buildQuery(table.get(id), params ?? {}).delete();
|
||||||
|
await _sendQuery(query);
|
||||||
|
return prior;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
packages/rethinkdb/pubspec.yaml
Normal file
24
packages/rethinkdb/pubspec.yaml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
name: angel3_rethinkdb
|
||||||
|
version: 8.0.0
|
||||||
|
description: RethinkDB-enabled services for the Angel3 framework.
|
||||||
|
publish_to: none
|
||||||
|
environment:
|
||||||
|
sdk: ">=3.3.0 <4.0.0"
|
||||||
|
homepage: https://angel3-framework.web.app/
|
||||||
|
repository: https://github.com/dart-backend/angel/tree/master/packages/rethinkdb
|
||||||
|
dependencies:
|
||||||
|
angel3_framework: ^8.4.0
|
||||||
|
belatuk_json_serializer: ^7.0.0
|
||||||
|
belatuk_rethinkdb: ^1.0.0
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
angel3_client: ^8.0.0
|
||||||
|
angel3_test: ^8.0.0
|
||||||
|
logging: ^1.2.0
|
||||||
|
test: ^1.25.0
|
||||||
|
lints: ^4.0.0
|
||||||
|
|
||||||
|
dependency_overrides:
|
||||||
|
belatuk_rethinkdb:
|
||||||
|
path: ../../../rethink_db
|
||||||
|
|
6
packages/rethinkdb/test/README.md
Normal file
6
packages/rethinkdb/test/README.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Tests
|
||||||
|
|
||||||
|
The tests expect you to have installed RethinkDB. You must have a `test` database
|
||||||
|
available, and a server ready at the default port.
|
||||||
|
|
||||||
|
Also, the tests expect a table named `todos`.
|
11
packages/rethinkdb/test/bootstrap.dart
Normal file
11
packages/rethinkdb/test/bootstrap.dart
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:belatuk_rethinkdb/belatuk_rethinkdb.dart';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
var r = RethinkDb();
|
||||||
|
await r.connect().then((conn) {
|
||||||
|
r.tableCreate('todos').run(conn);
|
||||||
|
print('Done');
|
||||||
|
exit(0);
|
||||||
|
});
|
||||||
|
}
|
10
packages/rethinkdb/test/common.dart
Normal file
10
packages/rethinkdb/test/common.dart
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
class Todo {
|
||||||
|
String? title;
|
||||||
|
bool completed;
|
||||||
|
|
||||||
|
Todo({this.title, this.completed = false});
|
||||||
|
|
||||||
|
Map toJson() {
|
||||||
|
return {'title': title, 'completed': completed == true};
|
||||||
|
}
|
||||||
|
}
|
85
packages/rethinkdb/test/generic_test.dart
Normal file
85
packages/rethinkdb/test/generic_test.dart
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import 'package:angel3_client/angel3_client.dart' as c;
|
||||||
|
import 'package:angel3_framework/angel3_framework.dart';
|
||||||
|
import 'package:angel3_rethinkdb/angel3_rethinkdb.dart';
|
||||||
|
import 'package:angel3_test/angel3_test.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:belatuk_rethinkdb/belatuk_rethinkdb.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'common.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
Angel app;
|
||||||
|
late TestClient client;
|
||||||
|
RethinkDb r;
|
||||||
|
late c.Service todoService;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
r = RethinkDb();
|
||||||
|
var conn = await r.connect();
|
||||||
|
|
||||||
|
app = Angel();
|
||||||
|
app.use('/todos', RethinkService(conn, r.table('todos')));
|
||||||
|
|
||||||
|
app.errorHandler = (e, req, res) async {
|
||||||
|
print('Whoops: $e');
|
||||||
|
};
|
||||||
|
|
||||||
|
app.logger = Logger.detached('angel')..onRecord.listen(print);
|
||||||
|
|
||||||
|
client = await connectTo(app);
|
||||||
|
todoService = client.service('todos');
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() => client.close());
|
||||||
|
|
||||||
|
test('index', () async {
|
||||||
|
var result = await todoService.index();
|
||||||
|
print('Response: $result');
|
||||||
|
expect(result, isList);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('create+read', () async {
|
||||||
|
var todo = Todo(title: 'Clean your room');
|
||||||
|
var creation = await todoService.create(todo.toJson());
|
||||||
|
print('Creation: $creation');
|
||||||
|
|
||||||
|
var id = creation['id'];
|
||||||
|
var result = await todoService.read(id);
|
||||||
|
|
||||||
|
print('Response: $result');
|
||||||
|
expect(result, isMap);
|
||||||
|
expect(result['id'], equals(id));
|
||||||
|
expect(result['title'], equals(todo.title));
|
||||||
|
expect(result['completed'], equals(todo.completed));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('modify', () async {
|
||||||
|
var todo = Todo(title: 'Clean your room');
|
||||||
|
var creation = await todoService.create(todo.toJson());
|
||||||
|
print('Creation: $creation');
|
||||||
|
|
||||||
|
var id = creation['id'];
|
||||||
|
var result = await todoService.modify(id, {'title': 'Eat healthy'});
|
||||||
|
|
||||||
|
print('Response: $result');
|
||||||
|
expect(result, isMap);
|
||||||
|
expect(result['id'], equals(id));
|
||||||
|
expect(result['title'], equals('Eat healthy'));
|
||||||
|
expect(result['completed'], equals(todo.completed));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('remove', () async {
|
||||||
|
var todo = Todo(title: 'Clean your room');
|
||||||
|
var creation = await todoService.create(todo.toJson());
|
||||||
|
print('Creation: $creation');
|
||||||
|
|
||||||
|
var id = creation['id'];
|
||||||
|
var result = await todoService.remove(id);
|
||||||
|
|
||||||
|
print('Response: $result');
|
||||||
|
expect(result, isMap);
|
||||||
|
expect(result['id'], equals(id));
|
||||||
|
expect(result['title'], equals(todo.title));
|
||||||
|
expect(result['completed'], equals(todo.completed));
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue