res8 = await client1.getSyncDiffs();
67 | expect(
68 | res8
69 | .map((e) => e
70 | .toMap(
71 | onlyFields: ['logicalClock', 'type', 'model', 'jsonData'])
72 | .values
73 | .toList())
74 | .toList(),
75 | [
76 | [0, 'create', 'model1', '{"id":"uuid1","field1":"abc"}'],
77 | [0, 'create', 'model1', '{"id":"uuid2","field1":"fgh"}'],
78 | [0, 'update', 'model1', '{"id":"uuid1","field1":"xxx"}'],
79 | [0, 'delete', 'model1', '{"id":"uuid2"}']
80 | ]);
81 | });
82 |
83 | test('Both clients sync', () async {
84 | await client1.synchronize();
85 | await client2.synchronize();
86 |
87 | MobyncResponse res1 = await client1.read('model1');
88 | expect(res1.success, true);
89 | expect(res1.data, [
90 | {'id': 'uuid1', 'field1': 'xxx'}
91 | ]);
92 |
93 | MobyncResponse res2 = await client2.read('model1');
94 | expect(res2.success, true);
95 | expect(res2.data, [
96 | {'id': 'uuid1', 'field1': 'xxx'}
97 | ]);
98 | });
99 |
100 | test('Client 2 delete object and both clients sync', () async {
101 | MobyncResponse res1 = await client2.delete('model1', 'uuid1');
102 | expect(res1.success, true);
103 |
104 | await client2.synchronize();
105 | await client1.synchronize();
106 |
107 | MobyncResponse res2 = await client1.read('model1');
108 | expect(res2.success, true);
109 | expect(res2.data, []);
110 |
111 | MobyncResponse res3 = await client2.read('model1');
112 | expect(res3.success, true);
113 | expect(res3.data, []);
114 | });
115 |
116 | test('Check logical clocks', () async {
117 | int logicalClock1 = await client1.getLogicalClock();
118 | expect(logicalClock1, 2);
119 |
120 | int logicalClock2 = await client2.getLogicalClock();
121 | expect(logicalClock2, 2);
122 | });
123 | }
124 |
--------------------------------------------------------------------------------
/test/client_integration_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:mobync/models/models.dart';
3 |
4 | import 'client_implementation.dart';
5 | import 'local_server_mockup.dart';
6 |
7 | void main() {
8 | MyMobyncClient client1, client2;
9 |
10 | setUpAll(() async {
11 | client1 = MyMobyncClient();
12 | client2 = MyMobyncClient();
13 | ServerMockup.instance.reset();
14 | });
15 |
16 | test('client 1 and 2 sanity check', () async {
17 | int logicalClock1 = await client1.getLogicalClock();
18 | expect(logicalClock1, 0);
19 |
20 | int logicalClock2 = await client2.getLogicalClock();
21 | expect(logicalClock2, 0);
22 |
23 | MobyncResponse res1 = await client1.read('model1');
24 | expect(res1.success, true);
25 | expect(res1.data, []);
26 |
27 | MobyncResponse res2 = await client2.read('model1');
28 | expect(res2.success, true);
29 | expect(res2.data, []);
30 | });
31 |
32 | test('client 1 creates local object and synchronizes', () async {
33 | final obj1 = {'id': 'uuid1', 'field1': 'a'};
34 | MobyncResponse res1 = await client1.create('model1', obj1);
35 | expect(res1.success, true);
36 |
37 | await client1.synchronize();
38 |
39 | int logicalClock1 = await client1.getLogicalClock();
40 | expect(logicalClock1, 1);
41 |
42 | MobyncResponse res = await client1.read('model1');
43 | expect(res.success, true);
44 | expect(res.data, [
45 | {'id': 'uuid1', 'field1': 'a'},
46 | ]);
47 | });
48 |
49 | test('client 1 and 2 create local objects but neither synchronize', () async {
50 | final obj1 = {'id': 'uuid2', 'field1': 'b'};
51 | MobyncResponse res1 = await client1.create('model1', obj1);
52 | expect(res1.success, true);
53 |
54 | final obj2 = {'id': 'uuid3', 'field1': 'c'};
55 | MobyncResponse res2 = await client2.create('model1', obj2);
56 | expect(res2.success, true);
57 | });
58 |
59 | test('client 2 synchonizes', () async {
60 | await client2.synchronize();
61 |
62 | MobyncResponse res = await client2.read('model1');
63 | expect(res.success, true);
64 | expect(res.data, [
65 | {'id': 'uuid1', 'field1': 'a'},
66 | {'id': 'uuid3', 'field1': 'c'},
67 | ]);
68 |
69 | int logicalClock2 = await client2.getLogicalClock();
70 | expect(logicalClock2, 2);
71 | });
72 |
73 | test('client 1 synchronizes', () async {
74 | await client1.synchronize();
75 |
76 | MobyncResponse res = await client1.read('model1');
77 | expect(res.success, true);
78 | expect(res.data, [
79 | {'id': 'uuid1', 'field1': 'a'},
80 | {'id': 'uuid2', 'field1': 'b'},
81 | {'id': 'uuid3', 'field1': 'c'},
82 | ]);
83 |
84 | int logicalClock1 = await client1.getLogicalClock();
85 | expect(logicalClock1, 3);
86 | });
87 |
88 | test('client 2 synchronizes', () async {
89 | await client2.synchronize();
90 |
91 | MobyncResponse res = await client2.read('model1');
92 | expect(res.success, true);
93 | expect(res.data, [
94 | {'id': 'uuid1', 'field1': 'a'},
95 | {'id': 'uuid2', 'field1': 'b'},
96 | {'id': 'uuid3', 'field1': 'c'},
97 | ]);
98 |
99 | int logicalClock2 = await client2.getLogicalClock();
100 | expect(logicalClock2, 3);
101 | });
102 |
103 | test('client 2 updates client 1 object and both synchronizes', () async {
104 | final obj = {'id': 'uuid1', 'field1': 'x'};
105 | MobyncResponse res = await client2.update('model1', obj);
106 | expect(res.success, true);
107 |
108 | await client2.synchronize();
109 | await client1.synchronize();
110 |
111 | MobyncResponse res1 = await client1.read('model1');
112 | expect(res1.success, true);
113 | expect(res1.data, [
114 | {'id': 'uuid1', 'field1': 'x'},
115 | {'id': 'uuid2', 'field1': 'b'},
116 | {'id': 'uuid3', 'field1': 'c'},
117 | ]);
118 |
119 | int logicalClock1 = await client1.getLogicalClock();
120 | expect(logicalClock1, 4);
121 |
122 | MobyncResponse res2 = await client2.read('model1');
123 | expect(res2.success, true);
124 | expect(res2.data, [
125 | {'id': 'uuid1', 'field1': 'x'},
126 | {'id': 'uuid2', 'field1': 'b'},
127 | {'id': 'uuid3', 'field1': 'c'},
128 | ]);
129 |
130 | int logicalClock2 = await client2.getLogicalClock();
131 | expect(logicalClock2, 4);
132 | });
133 |
134 | test('client 1 deletes client 2 object and both synchronizes', () async {
135 | MobyncResponse res = await client1.delete('model1', 'uuid3');
136 | expect(res.success, true);
137 |
138 | await client1.synchronize();
139 | await client2.synchronize();
140 |
141 | MobyncResponse res1 = await client1.read('model1');
142 | expect(res1.success, true);
143 | expect(res1.data, [
144 | {'id': 'uuid1', 'field1': 'x'},
145 | {'id': 'uuid2', 'field1': 'b'},
146 | ]);
147 |
148 | MobyncResponse res2 = await client2.read('model1');
149 | expect(res2.success, true);
150 | expect(res2.data, [
151 | {'id': 'uuid1', 'field1': 'x'},
152 | {'id': 'uuid2', 'field1': 'b'},
153 | ]);
154 | });
155 | }
156 |
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | archive:
5 | dependency: transitive
6 | description:
7 | name: archive
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.0.13"
11 | args:
12 | dependency: transitive
13 | description:
14 | name: args
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "1.6.0"
18 | async:
19 | dependency: transitive
20 | description:
21 | name: async
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "2.4.1"
25 | boolean_selector:
26 | dependency: transitive
27 | description:
28 | name: boolean_selector
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "2.0.0"
32 | charcode:
33 | dependency: transitive
34 | description:
35 | name: charcode
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.1.3"
39 | collection:
40 | dependency: transitive
41 | description:
42 | name: collection
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.14.12"
46 | convert:
47 | dependency: transitive
48 | description:
49 | name: convert
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "2.1.1"
53 | crypto:
54 | dependency: transitive
55 | description:
56 | name: crypto
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "2.1.4"
60 | equatable:
61 | dependency: "direct main"
62 | description:
63 | name: equatable
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "1.2.5"
67 | flutter:
68 | dependency: "direct main"
69 | description: flutter
70 | source: sdk
71 | version: "0.0.0"
72 | flutter_test:
73 | dependency: "direct dev"
74 | description: flutter
75 | source: sdk
76 | version: "0.0.0"
77 | http:
78 | dependency: "direct main"
79 | description:
80 | name: http
81 | url: "https://pub.dartlang.org"
82 | source: hosted
83 | version: "0.12.2"
84 | http_parser:
85 | dependency: transitive
86 | description:
87 | name: http_parser
88 | url: "https://pub.dartlang.org"
89 | source: hosted
90 | version: "3.1.4"
91 | image:
92 | dependency: transitive
93 | description:
94 | name: image
95 | url: "https://pub.dartlang.org"
96 | source: hosted
97 | version: "2.1.12"
98 | matcher:
99 | dependency: transitive
100 | description:
101 | name: matcher
102 | url: "https://pub.dartlang.org"
103 | source: hosted
104 | version: "0.12.6"
105 | meta:
106 | dependency: transitive
107 | description:
108 | name: meta
109 | url: "https://pub.dartlang.org"
110 | source: hosted
111 | version: "1.1.8"
112 | path:
113 | dependency: transitive
114 | description:
115 | name: path
116 | url: "https://pub.dartlang.org"
117 | source: hosted
118 | version: "1.6.4"
119 | pedantic:
120 | dependency: transitive
121 | description:
122 | name: pedantic
123 | url: "https://pub.dartlang.org"
124 | source: hosted
125 | version: "1.9.0"
126 | petitparser:
127 | dependency: transitive
128 | description:
129 | name: petitparser
130 | url: "https://pub.dartlang.org"
131 | source: hosted
132 | version: "2.4.0"
133 | quiver:
134 | dependency: transitive
135 | description:
136 | name: quiver
137 | url: "https://pub.dartlang.org"
138 | source: hosted
139 | version: "2.1.3"
140 | sky_engine:
141 | dependency: transitive
142 | description: flutter
143 | source: sdk
144 | version: "0.0.99"
145 | source_span:
146 | dependency: transitive
147 | description:
148 | name: source_span
149 | url: "https://pub.dartlang.org"
150 | source: hosted
151 | version: "1.7.0"
152 | stack_trace:
153 | dependency: transitive
154 | description:
155 | name: stack_trace
156 | url: "https://pub.dartlang.org"
157 | source: hosted
158 | version: "1.9.3"
159 | stream_channel:
160 | dependency: transitive
161 | description:
162 | name: stream_channel
163 | url: "https://pub.dartlang.org"
164 | source: hosted
165 | version: "2.0.0"
166 | string_scanner:
167 | dependency: transitive
168 | description:
169 | name: string_scanner
170 | url: "https://pub.dartlang.org"
171 | source: hosted
172 | version: "1.0.5"
173 | term_glyph:
174 | dependency: transitive
175 | description:
176 | name: term_glyph
177 | url: "https://pub.dartlang.org"
178 | source: hosted
179 | version: "1.1.0"
180 | test_api:
181 | dependency: transitive
182 | description:
183 | name: test_api
184 | url: "https://pub.dartlang.org"
185 | source: hosted
186 | version: "0.2.15"
187 | typed_data:
188 | dependency: transitive
189 | description:
190 | name: typed_data
191 | url: "https://pub.dartlang.org"
192 | source: hosted
193 | version: "1.1.6"
194 | uuid:
195 | dependency: "direct main"
196 | description:
197 | name: uuid
198 | url: "https://pub.dartlang.org"
199 | source: hosted
200 | version: "2.2.2"
201 | vector_math:
202 | dependency: transitive
203 | description:
204 | name: vector_math
205 | url: "https://pub.dartlang.org"
206 | source: hosted
207 | version: "2.0.8"
208 | xml:
209 | dependency: transitive
210 | description:
211 | name: xml
212 | url: "https://pub.dartlang.org"
213 | source: hosted
214 | version: "3.6.1"
215 | sdks:
216 | dart: ">=2.7.0 <3.0.0"
217 |
--------------------------------------------------------------------------------
/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.4.1"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.0.0"
18 | charcode:
19 | dependency: transitive
20 | description:
21 | name: charcode
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.1.3"
25 | clock:
26 | dependency: transitive
27 | description:
28 | name: clock
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.0.1"
32 | collection:
33 | dependency: transitive
34 | description:
35 | name: collection
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.14.12"
39 | convert:
40 | dependency: transitive
41 | description:
42 | name: convert
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "2.1.1"
46 | crypto:
47 | dependency: transitive
48 | description:
49 | name: crypto
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "2.1.5"
53 | cupertino_icons:
54 | dependency: "direct main"
55 | description:
56 | name: cupertino_icons
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "0.1.3"
60 | equatable:
61 | dependency: transitive
62 | description:
63 | name: equatable
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "1.2.5"
67 | fake_async:
68 | dependency: transitive
69 | description:
70 | name: fake_async
71 | url: "https://pub.dartlang.org"
72 | source: hosted
73 | version: "1.1.0"
74 | flutter:
75 | dependency: "direct main"
76 | description: flutter
77 | source: sdk
78 | version: "0.0.0"
79 | flutter_test:
80 | dependency: "direct dev"
81 | description: flutter
82 | source: sdk
83 | version: "0.0.0"
84 | http:
85 | dependency: "direct main"
86 | description:
87 | name: http
88 | url: "https://pub.dartlang.org"
89 | source: hosted
90 | version: "0.12.2"
91 | http_parser:
92 | dependency: transitive
93 | description:
94 | name: http_parser
95 | url: "https://pub.dartlang.org"
96 | source: hosted
97 | version: "3.1.4"
98 | matcher:
99 | dependency: transitive
100 | description:
101 | name: matcher
102 | url: "https://pub.dartlang.org"
103 | source: hosted
104 | version: "0.12.6"
105 | meta:
106 | dependency: transitive
107 | description:
108 | name: meta
109 | url: "https://pub.dartlang.org"
110 | source: hosted
111 | version: "1.1.8"
112 | mobync:
113 | dependency: "direct main"
114 | description:
115 | path: ".."
116 | relative: true
117 | source: path
118 | version: "0.0.6"
119 | path:
120 | dependency: transitive
121 | description:
122 | name: path
123 | url: "https://pub.dartlang.org"
124 | source: hosted
125 | version: "1.7.0"
126 | pedantic:
127 | dependency: transitive
128 | description:
129 | name: pedantic
130 | url: "https://pub.dartlang.org"
131 | source: hosted
132 | version: "1.9.0"
133 | sky_engine:
134 | dependency: transitive
135 | description: flutter
136 | source: sdk
137 | version: "0.0.99"
138 | source_span:
139 | dependency: transitive
140 | description:
141 | name: source_span
142 | url: "https://pub.dartlang.org"
143 | source: hosted
144 | version: "1.7.0"
145 | sqflite:
146 | dependency: "direct main"
147 | description:
148 | name: sqflite
149 | url: "https://pub.dartlang.org"
150 | source: hosted
151 | version: "1.3.1+2"
152 | sqflite_common:
153 | dependency: transitive
154 | description:
155 | name: sqflite_common
156 | url: "https://pub.dartlang.org"
157 | source: hosted
158 | version: "1.0.2+1"
159 | stack_trace:
160 | dependency: transitive
161 | description:
162 | name: stack_trace
163 | url: "https://pub.dartlang.org"
164 | source: hosted
165 | version: "1.9.3"
166 | stream_channel:
167 | dependency: transitive
168 | description:
169 | name: stream_channel
170 | url: "https://pub.dartlang.org"
171 | source: hosted
172 | version: "2.0.0"
173 | string_scanner:
174 | dependency: transitive
175 | description:
176 | name: string_scanner
177 | url: "https://pub.dartlang.org"
178 | source: hosted
179 | version: "1.0.5"
180 | synchronized:
181 | dependency: transitive
182 | description:
183 | name: synchronized
184 | url: "https://pub.dartlang.org"
185 | source: hosted
186 | version: "2.2.0+2"
187 | term_glyph:
188 | dependency: transitive
189 | description:
190 | name: term_glyph
191 | url: "https://pub.dartlang.org"
192 | source: hosted
193 | version: "1.1.0"
194 | test_api:
195 | dependency: transitive
196 | description:
197 | name: test_api
198 | url: "https://pub.dartlang.org"
199 | source: hosted
200 | version: "0.2.15"
201 | typed_data:
202 | dependency: transitive
203 | description:
204 | name: typed_data
205 | url: "https://pub.dartlang.org"
206 | source: hosted
207 | version: "1.1.6"
208 | uuid:
209 | dependency: "direct main"
210 | description:
211 | name: uuid
212 | url: "https://pub.dartlang.org"
213 | source: hosted
214 | version: "2.2.2"
215 | vector_math:
216 | dependency: transitive
217 | description:
218 | name: vector_math
219 | url: "https://pub.dartlang.org"
220 | source: hosted
221 | version: "2.0.8"
222 | sdks:
223 | dart: ">=2.8.0 <3.0.0"
224 | flutter: ">=1.10.1 <2.0.0"
225 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
The Mobync Flutter Lib
6 |
7 |
8 |
9 |
10 | [](https://travis-ci.com/mobync/flutter-client)
11 |
12 |
13 |
14 | ## Why use mobync
15 |
16 | Mobync is a synchronization library aimed to facilitate online-offline sync between multiple devices for any frontend, any backend, and any database.
17 |
18 | This repository implements the Mobync client library in Flutter, which means you can start using Mobync sync on your client regardless of which backend you might be using or even which database.
19 |
20 | As Mobync aims to provide online-offline sync between client and server, you will need to use the mobync library both on your frontend application and backend.
21 |
22 | Currently, Mobync has a Dart client implementation and a Python server implementation. That means you can plug Mobync on your Flutter app and provide online-offline synchronization.
23 |
24 | ### Online-offline synchronization
25 |
26 | Online-offline synchronization means that your app will work seamlessly both online and offline, the user can use without noticing any difference, and you can implement your app not worrying about this when using Mobync.
27 |
28 | Mobync will automatically make your app store your changes locally on your app's database when the user has no connection, and automatically sync the data to the backend when the user has internet.
29 |
30 | ### Multiple devices support
31 |
32 | Your user can use your service across multiple devices at the same time and all will have their data synchronized with Mobync.
33 |
34 | Mobync implements a protocol that merges the user data and resolves conflicts.
35 |
36 | Mobync's protocol allows mobile applications running on distributed clients to get synced to a single source of truth to manage users’ data using any storage type. Mobync users Dart and Flutter to implement this protocol and communicate to a web server written in Python.
37 |
38 | ## Example projects
39 |
40 | You can see some example projects using mobync on [Examples](https://github.com/mobync/flutter-client/blob/master/example).
41 |
42 |
43 | ## Mobync Flutter Client Package
44 |
45 | Using Mobync, you will wrap your database operations in such a way that any local data will get synced to a remote server, what will allow users from multiple clients to have an offline-online experience.
46 |
47 | ## Common usage
48 | ```dart
49 | MyMobyncClient client = MyMobyncClient.instance;
50 |
51 | /// Create an instance.
52 | final obj1 = {'id': 'uuid1', 'field1': 'a', 'field2': 'b'};
53 | MobyncResponse res1 = await client.create('model1', obj1);
54 |
55 | /// Update an instance.
56 | final obj = {'id': 'uuid1', 'field1': 'x'};
57 | MobyncResponse res = await client.update('model1', obj);
58 |
59 | /// Delete an instance.
60 | MobyncResponse res = await client.delete('model1', 'uuid1');
61 |
62 | /// Synchronize.
63 | await client.synchronize();
64 |
65 | /// Read from model.
66 | MobyncResponse res = await client.read('model1');
67 | /// Access data read.
68 | if(res.success)
69 | print(res.data);
70 | ```
71 |
72 | You might implement the ```MobyncClient``` abstract class. At this moment we do not support any migration system so it is up to the developer to use one from his preferences. Despite of that, the developer still have to implement the library-specific models.
73 |
74 | On the following snippet you can check out a Mobync implementation using SQLite on the client storage.
75 |
76 | ```dart
77 | import 'package:mobync/mobync.dart';
78 | import 'package:mobync/constants/constants.dart';
79 | import 'package:mobync/models/models.dart';
80 | import 'package:sqflite/sqflite.dart';
81 | import 'package:example/myModel.dart';
82 |
83 | class MyMobyncClient extends MobyncClient {
84 | MyMobyncClient._privateConstructor();
85 | static final MyMobyncClient instance = MyMobyncClient._privateConstructor();
86 |
87 | String get syncEndpoint => 'http://127.0.0.1:5000/sync';
88 |
89 | Database _database;
90 | Future get database async {
91 | if (_database == null) {
92 | var databasesPath = await getDatabasesPath();
93 | String path = '$databasesPath/demo.db';
94 | _database = await openDatabase(path, version: 1, onCreate: _onCreate);
95 | }
96 |
97 | return _database;
98 | }
99 |
100 | Future _onCreate(Database db, int version) async {
101 | await db.execute('''
102 | CREATE TABLE ${SyncDiff.tableName} (
103 | id TEXT PRIMARY KEY,
104 | logicalClock INTEGER,
105 | utcTimestamp INTEGER,
106 | type TEXT,
107 | model TEXT,
108 | jsonData TEXT
109 | )''');
110 | await db.execute('''
111 | CREATE TABLE ${SyncMetaData.tableName} (
112 | id TEXT PRIMARY KEY,
113 | logicalClock INTEGER
114 | )''');
115 | await _createMyTables(db, version);
116 | }
117 |
118 | Future _createMyTables(Database db, int version) async {
119 | /// ...
120 | }
121 |
122 | @override
123 | Future commitLocalCreate(String model, Map data) async {
124 | Database db = await database;
125 | return await db.insert(model, data);
126 | }
127 |
128 | @override
129 | Future commitLocalUpdate(String model, Map data) async {
130 | Database db = await database;
131 | return await db.update(model, data, where: 'id=?', whereArgs: [data['id']]);
132 | }
133 |
134 | @override
135 | Future commitLocalDelete(String model, String id) async {
136 | Database db = await database;
137 | return await db.delete(model, where: 'id=?', whereArgs: [id]);
138 | }
139 |
140 | @override
141 | Future> executeLocalRead(String model,
142 | {List filters}) async {
143 | Database db = await database;
144 | List