-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathrealmSync.js
185 lines (172 loc) · 6.35 KB
/
realmSync.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
import scripts from './helpers/scripts';
const Realm = require('realm');
import remoteSync from './helpers/remoteSync'
import sync from './helpers/sync';
import usnHandler from './helpers/usnHandler';
//this is for dynamoDb sync.
var React = require('react-native');
var {AsyncStorage} = React;
/**
* Realm sync decorates Realm's CRUD functions to provide syncing functionality for the database.
* @construction {Array} schema - schemas used in the database
* {String} remoteDBPath - the path of the remote service
* {String} path - the path of the realm database. If not declared, the default database is used.
*/
class RealmSync {
constructor(schema, remoteDBPath, path) {
this.remoteDBPath = remoteDBPath;
schema = schema || [];
schema.push(SyncQueue);
if (path) {
this.realm = new Realm({path: path, schema: schema});
} else {
this.realm = new Realm({schema: schema})
}
usnHandler.getHighestLocalUsn(this.realm);
}
/**
* Creates an object in the database. Appends a unique realm sync id to the object.
* https://realm.io/docs/react-native/latest/api/Realm.html#create
*
* @param {Object.type} type - schema type of the object
* @param {Object} properties - properties to be stored in the database
* @param {Object} update - true if properties are updating an exitsting object
* @returns {type} an object if successful, otherwise an error is returned.
*/
create(type, properties, update) {
update = update || false;
var objToUpdate;
if (update && properties.realmSyncId) {
var filterText = 'realmSyncId = "' + properties.realmSyncId + '"';
let filteredObjects = this.realm.objects(type).filtered(filterText);
if (filteredObjects.length > 0) {
objToUpdate = filteredObjects[0];
for (key in properties) {
objToUpdate[key] = properties[key];
}
}
} else {
properties.realmSyncId = scripts.generateGuid();
if (update === true) {
console.log("Can't find object in " + type + " with id " + properties.realmSyncId + ". Creating a new object" );
}
}
try {
let savedObject = objToUpdate || this.realm.create(type, properties, update);
scripts.addObjectToSyncQueue(this.realm, type, savedObject);
return savedObject;
} catch(error) {
console.log(error);
return error;
}
}
/**
* Gets the instance of realm instantiated.
* @returns {realmConstructor} an instance of Realm.
*/
getRealmInstance() {
return this.realm;
}
/**
* Deletes an object from the database and marks for deletion from the sync queue.
* https://realm.io/docs/react-native/latest/api/Realm.html#delete
* @param {Object} realmObject - object to be deleted
*/
delete(realmObject) {
let allRealmSyncIds = [];
if(realmObject.constructor.name === "Results") {
realmObject.forEach(object => {
scripts.markSyncQueueObjectAsDeleted(this.realm, object);
});
} else {
scripts.markSyncQueueObjectAsDeleted(this.realm, realmObject);
}
try {
this.realm.delete(realmObject);
} catch(error) {
console.log(error);
}
}
/**
* Synchronizes local data to a remote service.
* @param callback {function(error, success)} - callback to be called upon successful sync or error.
* @param policy - @see incremental sync.
*/
sync(callback, policy) {
var that = this;
AsyncStorage.getItem('authData')
.then((authData) => {
if(authData) {
var userId = JSON.parse(authData).userId;
// Get local sync count ond compare to remote service sync count to determine syncing action required
sync.getLastSyncCount(function (localSyncCount) {
remoteSync.getHighestUSN(that.remoteDBPath, userId, function (error, remoteServiceCount) {
// Only send data to remote service if sync count is the same as remote service
if (localSyncCount == remoteServiceCount) {
sendSyncQueueToRemoteService(userId);
} else if (localSyncCount == 0) { // else if local count is 0, perform a full sync
remoteSync.getUpdatesFromRemoteDB(that.remoteDBPath, localSyncCount, userId, function (error, data) {
if (error) {
callback(error, null);
} else {
var syncChunk = sync.convertRemoteDataToSyncChunk(data);
sync.localSyncFromServer(that.realm, syncChunk);
}
});
sendSyncQueueToRemoteService(userId);
} else { // otherwise perform an incremental sync
remoteSync.getUpdatesFromRemoteDB(that.remoteDBPath, localSyncCount, userId, function (error, data) {
if (error) {
callback(error, null);
} else {
var syncChunk = sync.convertRemoteDataToSyncChunk(data);
if (policy === undefined) {
policy = sync.timeRemoteServiceWinsPolicy;
}
sync.incrementalSync(that.realm, syncChunk, policy);
sendSyncQueueToRemoteService(userId);
}
});
}
});
});
}
})
.catch((error) => {
callback(error, null);
});
/**
* Send data from sync queue to the remote service.
* @param {String} userId - user's stored id.
*/
function sendSyncQueueToRemoteService(userId) {
var updates = sync.localSyncQueuePush(that.realm);
remoteSync.pushLocalUpdatesToDB(that.remoteDBPath, updates, userId, function (error, data) {
if (error) {
callback(error, null);
} else {
remoteSync.getHighestUSN(that.remoteDBPath, userId, function(error, highestUsn) {
if (!isNaN(highestUsn)) {
sync.setLastSyncCount(highestUsn, function(newCount) {
scripts.deleteObjectFromSyncQueue(that.realm);
callback(null, true);
});
}
});
}
});
}
};
}
class SyncQueue {}
SyncQueue.schema = {
name: 'SyncQueue',
properties: {
usn: Realm.Types.INT,
realmSyncId: Realm.Types.STRING,
type: Realm.Types.STRING,
body: Realm.Types.STRING,
modified: Realm.Types.INT
}
};
module.exports = RealmSync;