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