cordova-plugin-unvired-universal-sdk 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +59 -0
  2. package/aar/README.md +3 -0
  3. package/aar/Unvired_Kernel_Android.aar +0 -0
  4. package/aar/Unvired_Kernel_HTML5_Android.aar +0 -0
  5. package/package.json +69 -0
  6. package/plugin.xml +23 -0
  7. package/src/android/build.gradle +35 -0
  8. package/src/android/xml/provider_paths.xml +21 -0
  9. package/src/browser/UnviredPluginProxy.js +2430 -0
  10. package/src/browser/bootstrap.min.js +17 -0
  11. package/src/browser/codemirror.js +9755 -0
  12. package/src/browser/jquery-3.2.1.js +10253 -0
  13. package/src/browser/sql-wasm.wasm +0 -0
  14. package/src/browser/sql.js +203 -0
  15. package/src/browser/src_index_worker_js.unvired-db-worker.js +231 -0
  16. package/src/browser/unvired-db-worker.js +166 -0
  17. package/src/browser/vendors-node_modules_comlink_dist_esm_comlink_mjs.unvired-db-worker.js +22 -0
  18. package/src/ios/AttachmentPlugin.h +21 -0
  19. package/src/ios/AttachmentPlugin.m +180 -0
  20. package/src/ios/DataStructureHelper.h +28 -0
  21. package/src/ios/DataStructureHelper.m +188 -0
  22. package/src/ios/IOSAuthPlugin.h +14 -0
  23. package/src/ios/IOSAuthPlugin.m +13 -0
  24. package/src/ios/IOSDatabasePlugin.h +28 -0
  25. package/src/ios/IOSDatabasePlugin.m +253 -0
  26. package/src/ios/IOSFWSettingsPlugin.h +65 -0
  27. package/src/ios/IOSFWSettingsPlugin.m +363 -0
  28. package/src/ios/IOSLoggerPlugin.h +34 -0
  29. package/src/ios/IOSLoggerPlugin.m +198 -0
  30. package/src/ios/IOSLoginPlugin.h +29 -0
  31. package/src/ios/IOSLoginPlugin.m +480 -0
  32. package/src/ios/IOSProxyPlugin.h +21 -0
  33. package/src/ios/IOSProxyPlugin.m +172 -0
  34. package/src/ios/IOSSyncEnginePlugin.h +54 -0
  35. package/src/ios/IOSSyncEnginePlugin.m +847 -0
  36. package/src/ios/PluginConstants.h +195 -0
  37. package/src/ios/PluginHelper.h +29 -0
  38. package/src/ios/PluginHelper.m +74 -0
  39. package/src/ios/SyncHTML5Response.h +50 -0
  40. package/src/ios/SyncHTML5Response.m +68 -0
  41. package/www/applicationMeta/applicationMetadataParser.ts +285 -0
  42. package/www/applicationMeta/fieldConstants.ts +92 -0
  43. package/www/attachment/attachmentHelper.ts +326 -0
  44. package/www/attachment/attachmentQHelper.ts +158 -0
  45. package/www/attachment/attachmentService.ts +259 -0
  46. package/www/authenticationService.ts +746 -0
  47. package/www/bootstrap.min.js +17 -0
  48. package/www/codemirror.js +9755 -0
  49. package/www/database/appDatabaseManager.ts +54 -0
  50. package/www/database/databaseManager.ts +616 -0
  51. package/www/helper/dbCreateTablesManager.ts +354 -0
  52. package/www/helper/frameworkHelper.ts +127 -0
  53. package/www/helper/frameworkSettingsManager.ts +287 -0
  54. package/www/helper/getMessageTimerManager.ts +81 -0
  55. package/www/helper/httpConnection.ts +1051 -0
  56. package/www/helper/logger.ts +312 -0
  57. package/www/helper/notificationListnerHelper.ts +56 -0
  58. package/www/helper/passcodeGenerator.ts +61 -0
  59. package/www/helper/reconciler.ts +1062 -0
  60. package/www/helper/serverResponseHandler.ts +677 -0
  61. package/www/helper/serviceConstants.ts +254 -0
  62. package/www/helper/settingsHelper.ts +386 -0
  63. package/www/helper/status.ts +83 -0
  64. package/www/helper/syncInputDataManager.ts +205 -0
  65. package/www/helper/unviredAccount.ts +104 -0
  66. package/www/helper/unviredAccountManager.ts +120 -0
  67. package/www/helper/urlService.ts +43 -0
  68. package/www/helper/userSettingsManager.ts +172 -0
  69. package/www/helper/utils.ts +110 -0
  70. package/www/inbox/downloadMessageService.ts +270 -0
  71. package/www/inbox/inboxHelper.ts +132 -0
  72. package/www/inbox/inboxService.ts +223 -0
  73. package/www/jquery-3.2.1.js +10253 -0
  74. package/www/kernel.js +1380 -0
  75. package/www/outbox/outboxAttachmentManager.ts +152 -0
  76. package/www/outbox/outboxHelper.ts +67 -0
  77. package/www/outbox/outboxService.ts +519 -0
  78. package/www/sql-wasm.wasm +0 -0
  79. package/www/sql.js +209 -0
  80. package/www/subtract.ts +5 -0
  81. package/www/sum.ts +4 -0
  82. package/www/syncEngine.ts +687 -0
@@ -0,0 +1,1062 @@
1
+ import { FieldMeta, StructureMeta } from "../applicationMeta/applicationMetadataParser";
2
+ import * as FieldConstants from "../applicationMeta/fieldConstants";
3
+ import AttachmentQHelper from "../attachment/attachmentQHelper";
4
+ import { DatabaseManager, DatabaseType } from "../database/databaseManager";
5
+ import FrameworkHelper from "./frameworkHelper";
6
+ import { Logger } from "./logger";
7
+ import ServerResponseHandler from "./serverResponseHandler";
8
+ import * as ServiceConstants from './serviceConstants';
9
+ import SyncInputDataManager, { DataType } from "./syncInputDataManager";
10
+ import { ObjectStatus, SyncStatus } from "./utils";
11
+
12
+ export enum ReconcilerType {
13
+ RequestResponse,
14
+ PullPushQuery
15
+ }
16
+
17
+ class Reconciler {
18
+ private entityName: string = "";
19
+ private entityInDB: any = {};
20
+ private incomingEntity: any = {};
21
+ private incomingItems: any = {};
22
+ private conflictRule: string = "";
23
+ private isForeground: boolean = true;
24
+
25
+ private incomingItemLids: any = {};
26
+ private incomingAttachmentItemLids: any = {};
27
+
28
+ private conflictBe: any = {};
29
+
30
+ private databaseManager: DatabaseManager = DatabaseManager.getInstance();
31
+
32
+ private reconcilerType: ReconcilerType = ReconcilerType.RequestResponse;
33
+
34
+ constructor(
35
+ entityName: string,
36
+ entityInDB: any,
37
+ incomingEntity: any,
38
+ incomingItems: any,
39
+ conflictRule: string,
40
+ isForeground: boolean,
41
+ reconcilerType: ReconcilerType
42
+ ) {
43
+ this.entityName = entityName;
44
+ this.entityInDB = entityInDB;
45
+ this.incomingEntity = incomingEntity;
46
+ this.incomingItems = incomingItems;
47
+ this.conflictRule = conflictRule;
48
+ this.isForeground = isForeground;
49
+ this.reconcilerType = reconcilerType;
50
+ }
51
+
52
+ public getConflictBe(): any {
53
+ return this.conflictBe;
54
+ }
55
+
56
+ public async reconcile(structureMetas: StructureMeta[], fieldMetas: FieldMeta[], requestType: ServiceConstants.RequestType, lid: string): Promise<boolean> {
57
+ // Conflict Management.
58
+ // Check whether |currentIncomingHeader| is conflicted.
59
+ if (
60
+ this.incomingEntity[FieldConstants.FieldConflict]?.toString().toUpperCase() === "X"
61
+ ) {
62
+ const headerMeta = structureMetas.find(
63
+ (element) => element.structureName === this.entityName
64
+ );
65
+ Logger.logInfo(
66
+ "Reconciler",
67
+ "reconcile",
68
+ `Header with BE: ${headerMeta?.beName}, LID: ${this.entityInDB[FieldConstants.FieldLid]} is in Conflict. Sending Header to Conflict Manager.`
69
+ );
70
+ const conflictStatus = await this.manageConflict(
71
+ headerMeta,
72
+ this.entityInDB,
73
+ this.incomingEntity,
74
+ this.incomingItems
75
+ );
76
+ return conflictStatus;
77
+ }
78
+
79
+ let status = true;
80
+ const objectStatus = this.entityInDB[FieldConstants.FieldObjectStatus] as number;
81
+ switch (objectStatus) {
82
+ case ObjectStatus.global:
83
+ status = await this.actionHeaderForGlobal();
84
+ break;
85
+
86
+ case ObjectStatus.add:
87
+ status = await this.actionHeaderForAdd();
88
+ break;
89
+
90
+ case ObjectStatus.modify:
91
+ status = await this.actionHeaderForModify();
92
+ break;
93
+
94
+ case ObjectStatus.delete:
95
+ status = await this.actionHeaderForDelete();
96
+ break;
97
+
98
+ default:
99
+ break;
100
+ }
101
+
102
+ if (!status) {
103
+ return false;
104
+ }
105
+
106
+ // Handle Items
107
+ const keys = Object.keys(this.incomingItems);
108
+ for (const key of keys) {
109
+ const itemsArray = this.incomingItems[key] as any[];
110
+ for (const item of itemsArray) {
111
+ const itemInDB = await ServerResponseHandler.checkDuplicateBe(
112
+ key,
113
+ fieldMetas,
114
+ item,
115
+ requestType,
116
+ lid
117
+ );
118
+ if (key.endsWith(ServiceConstants.AttachmentBE)) {
119
+ // Handle Attachment Items
120
+ await this.handleAttachmentItems(key, itemInDB, item);
121
+ } else {
122
+ // Handle items without Attachment
123
+ await this.handleItems(key, itemInDB, item);
124
+ }
125
+ }
126
+ }
127
+
128
+ await this.handleDeletionOfItemsInDBWithoutIncomingItems(structureMetas);
129
+ await this.handleDeletionOfAttachmentItemsInDBWithoutIncomingItems(
130
+ structureMetas
131
+ );
132
+ return true;
133
+ }
134
+
135
+ private setHeaderFieldsAndStatuses() {
136
+ this.incomingEntity[FieldConstants.FieldLid] = this.entityInDB[FieldConstants.FieldLid];
137
+ this.incomingEntity[FieldConstants.FieldObjectStatus] = ObjectStatus.global;
138
+ this.incomingEntity[FieldConstants.FieldSyncStatus] = SyncStatus.none;
139
+ this.incomingEntity[FieldConstants.FieldInfoMsgCat] = '';
140
+ this.incomingEntity[FieldConstants.FieldTimestamp] = this.entityInDB[FieldConstants.FieldTimestamp];
141
+ }
142
+
143
+ private async insertDataIntoDB(tableName: string, dataToInsert: any, isHeader:boolean): Promise<boolean> {
144
+ this.setHeaderFieldsAndStatuses();
145
+ try {
146
+ await this.databaseManager.insert(DatabaseType.AppDb, tableName, dataToInsert, isHeader);
147
+ return true;
148
+ }
149
+ catch (e) {
150
+ Logger.logDebug("Reconciler", "insertDataIntoDB", `Error while inserting the data into database. Table Name: ${tableName}. Input Data: ${dataToInsert.toString()}. Error: ${JSON.stringify(e)}`);
151
+ Logger.logError("Reconciler", "insertDataIntoDB", "Error while inserting the data into database. " + JSON.stringify(e));
152
+ return false;
153
+ }
154
+ }
155
+
156
+ private async updateDataIntoDB(tableName: string, dataToUpdate: any, whereClaues:string): Promise<boolean> {
157
+ this.setHeaderFieldsAndStatuses();
158
+ try {
159
+ await this.databaseManager.update(DatabaseType.AppDb, tableName, dataToUpdate, whereClaues);
160
+ return true;
161
+ }
162
+ catch (e) {
163
+ Logger.logDebug("Reconciler", "updateDataIntoDB", `Error while updating the data into database. Table Name: ${tableName}. Input Data: ${JSON.stringify(dataToUpdate)}. Error: ${JSON.stringify(e)}`);
164
+ Logger.logError("Reconciler", "updateDataIntoDB", "Error while updating the data into database. " + JSON.stringify(e));
165
+ return false;
166
+ }
167
+ }
168
+
169
+ private async actionHeaderForGlobal(): Promise<boolean> {
170
+ if (this.isForeground && this.reconcilerType == ReconcilerType.RequestResponse) {
171
+ return await this.updateDataIntoDB(this.entityName, this.incomingEntity, `${FieldConstants.FieldLid}='${this.entityInDB[FieldConstants.FieldLid]}'`)
172
+ }
173
+ const syncStatus = this.entityInDB[FieldConstants.FieldSyncStatus] as number;
174
+ switch (syncStatus) {
175
+ case SyncStatus.none: {
176
+ return await this.updateDataIntoDB(this.entityName, this.incomingEntity, `${FieldConstants.FieldLid}='${this.entityInDB[FieldConstants.FieldLid]}'`);
177
+ }
178
+ case SyncStatus.queued:
179
+ case SyncStatus.sent:
180
+ case SyncStatus.error: {
181
+ Logger.logError(
182
+ "PullPushQueryReconciler",
183
+ "actionHeaderForGlobal",
184
+ `Reconciler Type: ${ReconcilerType[this.reconcilerType]}. Invalid case. Header in Object Status GLOBAL can only be in Sync Status NONE. Current Sync Status: ${syncStatus}, Header Information: ${this.entityName}. LID: ${this.entityInDB[FieldConstants.FieldLid]}`
185
+ );
186
+ return false;
187
+ }
188
+ default:
189
+ break;
190
+ }
191
+ return true;
192
+ }
193
+
194
+ private async actionHeaderForAdd(): Promise<boolean> {
195
+ if (this.isForeground && this.reconcilerType == ReconcilerType.RequestResponse) {
196
+ return await this.updateDataIntoDB(this.entityName, this.incomingEntity, `${FieldConstants.FieldLid}='${this.entityInDB[FieldConstants.FieldLid]}'`)
197
+ }
198
+ const syncStatus = this.entityInDB[FieldConstants.FieldSyncStatus] as number;
199
+ switch (syncStatus) {
200
+ case SyncStatus.none:
201
+ case SyncStatus.queued:
202
+ case SyncStatus.sent:
203
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
204
+ return await this.updateDataIntoDB(this.entityName, this.incomingEntity, `${FieldConstants.FieldLid}='${this.entityInDB[FieldConstants.FieldLid]}'`)
205
+ }
206
+ case SyncStatus.error: {
207
+ var logMessage = `Reconciler Type: ${ReconcilerType[this.reconcilerType]}. Ignoring the locally added DataStructure: ${this.entityName}.`
208
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
209
+ logMessage = `Reconciler Type: ${ReconcilerType[this.reconcilerType]}. Invalid case. Header in Object Status ADD can only be in Sync Status SENT. Current Sync Status: ${syncStatus}, Header Information: ${this.entityName}. LID: ${this.entityInDB[FieldConstants.FieldLid]}`;
210
+ }
211
+ Logger.logInfo("PullPushQueryReconciler", "actionHeaderForAdd", logMessage);
212
+ return true;
213
+ }
214
+ default:
215
+ break;
216
+ }
217
+ return false;
218
+ }
219
+
220
+ private async actionHeaderForModify(): Promise<boolean> {
221
+ if (this.isForeground && this.reconcilerType == ReconcilerType.RequestResponse) {
222
+ return await this.updateDataIntoDB(this.entityName, this.incomingEntity, `${FieldConstants.FieldLid}='${this.entityInDB[FieldConstants.FieldLid]}'`)
223
+ }
224
+ const syncStatus = this.entityInDB[FieldConstants.FieldSyncStatus] as number;
225
+ switch (syncStatus) {
226
+ case SyncStatus.none:
227
+ return await this.handleHeaderConflict();
228
+ case SyncStatus.sent: {
229
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
230
+ return await this.updateDataIntoDB(this.entityName, this.incomingEntity, `${FieldConstants.FieldLid}='${this.entityInDB[FieldConstants.FieldLid]}'`)
231
+ }
232
+ Logger.logInfo(
233
+ "PullPushQueryReconciler",
234
+ "actionHeaderForModify",
235
+ `Ignoring the locally added DataStructure: ${this.entityName}.`
236
+ );
237
+ return true;
238
+ }
239
+ case SyncStatus.queued:
240
+ case SyncStatus.error:
241
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
242
+ Logger.logError(
243
+ "RequestResponseReconciler",
244
+ "actionHeaderForModify",
245
+ `Invalid case. Header in Object Status MODIFY can only be in Sync Status SENT. Current Sync Status: ${syncStatus}, Header Information: ${this.entityName}. LID: ${this.entityInDB[FieldConstants.FieldLid]}`
246
+ );
247
+ return false;
248
+ }
249
+ else {
250
+ return await this.handleHeaderConflict();
251
+ }
252
+ default:
253
+ break;
254
+ }
255
+ return false;
256
+ }
257
+
258
+ private async actionHeaderForDelete(): Promise<boolean> {
259
+ // If the Execution type is Foreground then we should not check for the SyncStatus. Because SYNC Statuses typically do not change for a SYNC Call.
260
+ if (this.isForeground && this.reconcilerType == ReconcilerType.RequestResponse) {
261
+ Logger.logInfo("RequestResponseReconciler", "actionHeaderForDelete", `Deleting The Header in Database. Table: ${this.entityName}`);
262
+ // Delete the Current Header in Database.
263
+ const result = await this.databaseManager.delete(DatabaseType.AppDb, this.entityName, `${FieldConstants.FieldLid}='${this.entityInDB[FieldConstants.FieldLid]}'`);
264
+ return result !== 0;
265
+ }
266
+ const syncStatus = this.entityInDB[FieldConstants.FieldSyncStatus] as number;
267
+ switch (syncStatus) {
268
+ case SyncStatus.none:
269
+ case SyncStatus.queued:
270
+ case SyncStatus.error:
271
+ Logger.logError(
272
+ "PullPushQueryReconciler",
273
+ "actionHeaderForDelete",
274
+ `Invalid case. Header in Object Status DELETE can only be in Sync Status SENT. Current Sync Status: ${syncStatus}, Header Information: ${this.entityName}. LID: ${this.entityInDB[FieldConstants.FieldLid]}`
275
+ );
276
+ return false;
277
+ case SyncStatus.sent: {
278
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
279
+ Logger.logInfo("RequestResponseReconciler", "actionHeaderForDelete", `Deleting The Header in Database. Table: ${this.entityName}`);
280
+ // Delete the Current Header in Database.
281
+ const result = await this.databaseManager.delete(DatabaseType.AppDb, this.entityName, `${FieldConstants.FieldLid}='${this.entityInDB[FieldConstants.FieldLid]}'`);
282
+ return result !== 0;
283
+ }
284
+ return await this.updateDataIntoDB(this.entityName, this.incomingEntity, `${FieldConstants.FieldLid}='${this.entityInDB[FieldConstants.FieldLid]}'`);
285
+ }
286
+ default:
287
+ break;
288
+ }
289
+ return false;
290
+ }
291
+
292
+ private async handleHeaderConflict(): Promise<boolean> {
293
+ switch (this.conflictRule) {
294
+ case FieldConstants.ConflictModeServerWins:
295
+ // modify the header and set statuses
296
+ return await this.updateDataIntoDB(this.entityName, this.incomingEntity, `${FieldConstants.FieldLid}='${this.entityInDB[FieldConstants.FieldLid]}'`);
297
+
298
+ case FieldConstants.ConflictModeDeviceWins:
299
+ // Do not touch the header. Let the header data in the device remain. Do not do anything here.
300
+ break;
301
+
302
+ case FieldConstants.ConflictModeAppHandled:
303
+ // Provide the conflict object back so that the application can handle it
304
+ // TODO:
305
+ // if (this.conflictBe == null) {
306
+ // this.conflictBe = new ConflictBE(this.currentDBHeader, this.currentIncomingHeader);
307
+ // }
308
+ // this.conflictBe.isHeaderInConflict = true;
309
+ break;
310
+ }
311
+ return true;
312
+ }
313
+
314
+ private async handleItems(itemName: string, itemInDB: any, incomingItem: any): Promise<void> {
315
+ if (itemInDB == null) {
316
+ incomingItem[FieldConstants.FieldLid] = FrameworkHelper.getUUID();
317
+ incomingItem[FieldConstants.FieldFid] = this.entityInDB[FieldConstants.FieldLid];
318
+ incomingItem[FieldConstants.FieldObjectStatus] = ObjectStatus.global;
319
+ incomingItem[FieldConstants.FieldSyncStatus] = SyncStatus.none;
320
+
321
+ const isDatabaseOperationSuccessfull = await this.insertDataIntoDB(itemName, incomingItem, false);
322
+ if (!isDatabaseOperationSuccessfull) {
323
+ return;
324
+ }
325
+ } else {
326
+ const objectStatus = itemInDB[FieldConstants.FieldObjectStatus];
327
+ switch (objectStatus) {
328
+ case ObjectStatus.global:
329
+ await this.actionOnIncomingItemForGlobal(itemName, itemInDB, incomingItem);
330
+ break;
331
+
332
+ case ObjectStatus.add:
333
+ await this.actionOnIncomingItemForAdd(itemName, itemInDB, incomingItem);
334
+ break;
335
+
336
+ case ObjectStatus.modify:
337
+ await this.actionOnIncomingItemForModify(itemName, itemInDB, incomingItem);
338
+ break;
339
+
340
+ case ObjectStatus.delete:
341
+ await this.actionOnIncomingItemForDelete(itemName, itemInDB, incomingItem);
342
+ break;
343
+
344
+ default:
345
+ break;
346
+ }
347
+ }
348
+ this.addItemToCollection(itemName, incomingItem);
349
+ }
350
+
351
+ private async actionOnIncomingItemForGlobal(itemName: string, itemInDB: any, incomingItem: any): Promise<boolean> {
352
+ const syncStatus: number = itemInDB[FieldConstants.FieldSyncStatus];
353
+ switch (syncStatus) {
354
+ case SyncStatus.none:
355
+ // Instead of copying all fields from incomingItemDataStructure to itemDataStructureInDB, copying the LID from itemDataStructureInDB into incomingItemDataStructure
356
+ const item: any = this.setItemFieldsAndStatusesFor(itemInDB, incomingItem);
357
+ return await this.updateDataIntoDB(itemName, item, `${FieldConstants.FieldLid}='${incomingItem[FieldConstants.FieldLid]}'`);
358
+ case SyncStatus.error:
359
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
360
+ const item: any = this.setItemFieldsAndStatusesFor(itemInDB, incomingItem);
361
+ return await this.updateDataIntoDB(itemName, item, `${FieldConstants.FieldLid}='${incomingItem[FieldConstants.FieldLid]}'`);
362
+ }
363
+ case SyncStatus.queued:
364
+ case SyncStatus.sent:
365
+ Logger.logError("PullPushQueryReconciler", "actionOnIncomingItemForGlobal", `Invalid case. Items in Object Status GLOBAL can only be in Sync Status NONE. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${itemInDB[FieldConstants.FieldLid]}`);
366
+ break;
367
+ default:
368
+ break;
369
+ }
370
+ return false;
371
+ }
372
+
373
+ private async actionOnIncomingItemForAdd(itemName: string, itemInDB: any, incomingItem: any): Promise<boolean> {
374
+ if (this.isForeground && this.reconcilerType == ReconcilerType.RequestResponse) {
375
+ const item: any = this.setItemFieldsAndStatusesFor(itemInDB, incomingItem);
376
+ return await this.updateDataIntoDB(itemName, item, `${FieldConstants.FieldLid}='${incomingItem[FieldConstants.FieldLid]}'`);
377
+ }
378
+
379
+ const syncStatus: number = itemInDB[FieldConstants.FieldSyncStatus];
380
+ switch (syncStatus) {
381
+ case SyncStatus.none:
382
+ case SyncStatus.queued:
383
+ case SyncStatus.error:
384
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
385
+ // Delete Item. Not possible to relate an ADD status to incoming item
386
+ const result = await this.databaseManager.delete(DatabaseType.AppDb, itemName, `${FieldConstants.FieldLid}='${itemInDB[FieldConstants.FieldLid]}'`);
387
+ return result !== 0;
388
+ }
389
+ case SyncStatus.sent:
390
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
391
+ const item: any = this.setItemFieldsAndStatusesFor(itemInDB, incomingItem);
392
+ return await this.databaseManager.insertOrUpdate(DatabaseType.AppDb, itemName, item, false);
393
+ }
394
+ Logger.logInfo("PullPushQueryReconciler", "actionOnIncomingItemForModify", `Do not touch. Has to be handled by request-response. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${itemInDB[FieldConstants.FieldLid]}`);
395
+ break;
396
+ default:
397
+ break;
398
+ }
399
+ return true;
400
+ }
401
+
402
+ private async actionOnIncomingItemForModify(itemName: string, itemInDB: any, incomingItem: any): Promise<boolean> {
403
+ if (this.isForeground && this.reconcilerType == ReconcilerType.RequestResponse) {
404
+ const item: any = this.setItemFieldsAndStatusesFor(itemInDB, incomingItem);
405
+ return await this.updateDataIntoDB(itemName, item, `${FieldConstants.FieldLid}='${incomingItem[FieldConstants.FieldLid]}'`);
406
+ }
407
+ const syncStatus: number = itemInDB[FieldConstants.FieldSyncStatus];
408
+ switch (syncStatus) {
409
+ case SyncStatus.none:
410
+ return this.handleItemConflictWithItemDataStructure(itemName, itemInDB, incomingItem);
411
+ case SyncStatus.sent:
412
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
413
+ const item: any = this.setItemFieldsAndStatusesFor(itemInDB, incomingItem);
414
+ return await this.databaseManager.insertOrUpdate(DatabaseType.AppDb, itemName, item, false);
415
+ }
416
+ Logger.logInfo("PullPushQueryReconciler", "actionOnIncomingItemForModify", `Server did not return this item which is modified on the client and with the Sync Status as SENT. Reconciler retaining this item. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${itemInDB[FieldConstants.FieldLid]}`);
417
+ break;
418
+ case SyncStatus.queued:
419
+ case SyncStatus.error:
420
+ if (this.reconcilerType == ReconcilerType.PullPushQuery) {
421
+ return this.handleItemConflictWithItemDataStructure(itemName, itemInDB, incomingItem);
422
+ }
423
+ Logger.logError(
424
+ "RequestResponseReconciler",
425
+ "actionOnIncomingItemForModify",
426
+ `Invalid case. Items in Object Status MODIFY can only be in Sync Status SENT. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${itemInDB[FieldConstants.FieldLid]}`);
427
+
428
+ default:
429
+ break;
430
+ }
431
+ return false;
432
+ }
433
+
434
+ private async actionOnIncomingItemForDelete(itemName: string, itemInDB: any, incomingItem: any): Promise<boolean> {
435
+ const syncStatus: number = itemInDB[FieldConstants.FieldSyncStatus];
436
+ switch (syncStatus) {
437
+ case SyncStatus.none:
438
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
439
+ Logger.logError(
440
+ "RequestResponseReconciler",
441
+ "actionOnIncomingItemForDelete",
442
+ `Do not touch. Item has been modified after synchronization before getting a response. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${itemInDB[FieldConstants.FieldLid]}`);
443
+ return true;
444
+ }
445
+ case SyncStatus.queued:
446
+ case SyncStatus.error:
447
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
448
+ Logger.logError(
449
+ "RequestResponseReconciler",
450
+ "actionOnIncomingItemForDelete",
451
+ `Invalid case. Items in Object Status DELETE can only be in Sync Status SENT. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${itemInDB[FieldConstants.FieldLid]}`);
452
+ return false;
453
+ }
454
+ return this.handleItemConflictWithItemDataStructure(itemName, itemInDB, incomingItem);
455
+ case SyncStatus.sent:
456
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
457
+ const item: any = this.setItemFieldsAndStatusesFor(itemInDB, incomingItem);
458
+ return await this.updateDataIntoDB(itemName, item, `${FieldConstants.FieldLid}='${incomingItem[FieldConstants.FieldLid]}'`);
459
+ }
460
+ Logger.logInfo("PullPushQueryReconciler", "actionOnIncomingItemForDelete", `Server did not return this item which is marked for DELETE on the client and with the Sync Status as SENT. Reconciler retaining this item. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${itemInDB[FieldConstants.FieldLid]}`);
461
+ break;
462
+ default:
463
+ break;
464
+ }
465
+ return false;
466
+ }
467
+
468
+ private async handleItemConflictWithItemDataStructure(itemName: string, itemInDB: any, incomingItem: any): Promise<boolean> {
469
+ switch (this.conflictRule) {
470
+ case FieldConstants.ConflictModeServerWins: {
471
+ // modify the item and set statuses
472
+ const item = this.setItemFieldsAndStatusesFor(itemInDB, incomingItem);
473
+ return await this.updateDataIntoDB(itemName, item, `${FieldConstants.FieldLid}='${incomingItem[FieldConstants.FieldLid]}'`);
474
+ }
475
+
476
+ case FieldConstants.ConflictModeDeviceWins:
477
+ // Do not touch the item. Let the item data in the device remain. Do not do anything here.
478
+ break;
479
+
480
+ case FieldConstants.ConflictModeAppHandled: {
481
+ // Provide the conflict object back so that the application can handle it
482
+
483
+ // TODO:
484
+ // if (this.conflictBe == null) {
485
+ // this.conflictBe = new ConflictBE(this.currentDBHeader, this.currentIncomingHeader);
486
+ // }
487
+
488
+ // this.conflictBe.setItemInConflict(itemDataStructureInDB, incomingItemDataStructure);
489
+ }
490
+ break;
491
+ }
492
+ return true;
493
+ }
494
+
495
+ private async handleAttachmentItems(attachmentItemName: string, attachmentItemInDB: any, incomingAttachmentItem: any): Promise<void> {
496
+ // If attachmentItem is not in Db..then insert and add to collection else resolve the conflict..
497
+ if (attachmentItemInDB === null) {
498
+ incomingAttachmentItem[FieldConstants.FieldLid] = FrameworkHelper.getUUID();
499
+ incomingAttachmentItem[FieldConstants.FieldFid] = this.entityInDB[FieldConstants.FieldLid];
500
+ incomingAttachmentItem[FieldConstants.FieldObjectStatus] = ObjectStatus.global;
501
+ incomingAttachmentItem[FieldConstants.FieldSyncStatus] = SyncStatus.none;
502
+ try {
503
+ await this.insertDataIntoDB(attachmentItemName, incomingAttachmentItem, false);
504
+ AttachmentQHelper.checkAttachmentAndQueueForAutoDownload(attachmentItemName, incomingAttachmentItem);
505
+ } catch (e) {
506
+ Logger.logDebug("RequestResponseReconciler", "handleAttachmentItems", "Error while inserting attachment. Input data: " + incomingAttachmentItem.toString());
507
+ Logger.logError("RequestResponseReconciler", "handleAttachmentItems", "Error while inserting attachment. Error: " + JSON.stringify(e));
508
+ }
509
+ } else {
510
+ try {
511
+ await this.databaseManager.delete(DatabaseType.FrameworkDb, "InfoMessage", `belid='${attachmentItemInDB[FieldConstants.FieldLid]}'`);
512
+ } catch (e) {
513
+ Logger.logError("PullPushQueryReconciler", "handleAttachmentItems", "Error while deleting info message. Error: " + JSON.stringify(e));
514
+ }
515
+ const objectStatus = attachmentItemInDB[FieldConstants.FieldObjectStatus];
516
+ switch (objectStatus) {
517
+ case ObjectStatus.global:
518
+ await this.actionOnIncomingAttachmentItemForGlobal(attachmentItemName, attachmentItemInDB, incomingAttachmentItem);
519
+ break;
520
+
521
+ case ObjectStatus.add:
522
+ await this.actionOnIncomingAttachmentItemForAdd(attachmentItemName, attachmentItemInDB, incomingAttachmentItem);
523
+ break;
524
+
525
+ case ObjectStatus.modify:
526
+ await this.actionOnIncomingAttachmentItemForModify(attachmentItemName, attachmentItemInDB, incomingAttachmentItem);
527
+ break;
528
+
529
+ case ObjectStatus.delete:
530
+ await this.actionOnIncomingAttachmentItemForDelete(attachmentItemName, attachmentItemInDB, incomingAttachmentItem);
531
+ break;
532
+
533
+ default:
534
+ break;
535
+ }
536
+ }
537
+ this.addAttachmentItemToCollection(attachmentItemName, incomingAttachmentItem);
538
+ }
539
+
540
+ private async actionOnIncomingAttachmentItemForGlobal(itemName: string, attachmentItemInDB: any, incomingAttachmentItem: any): Promise<boolean> {
541
+ const syncStatus: number = attachmentItemInDB[FieldConstants.FieldSyncStatus];
542
+ switch (syncStatus) {
543
+ case SyncStatus.none:
544
+ const attachmentItemToUpdate: any = this.setAttachmentItemFieldsAndStatuses(attachmentItemInDB, incomingAttachmentItem);
545
+ return await this.updateDataIntoDB(itemName, attachmentItemToUpdate, `${FieldConstants.FieldLid}='${incomingAttachmentItem[FieldConstants.FieldLid]}'`)
546
+ case SyncStatus.queued:
547
+ case SyncStatus.sent:
548
+ case SyncStatus.error:
549
+ Logger.logError("PullPushQueryReconciler", "actionOnIncomingAttachmentItemForGlobal", `Invalid case. Items in this status should have OBJECT_STATUS as ADD, MODIFY or DELETE. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${attachmentItemInDB[FieldConstants.FieldLid]}`);
550
+ return false;
551
+ default:
552
+ break;
553
+ }
554
+ return false;
555
+ }
556
+
557
+ private async actionOnIncomingAttachmentItemForAdd(itemName: string, attachmentItemInDB: any, incomingAttachmentItem: any): Promise<boolean> {
558
+ if (this.isForeground && this.reconcilerType == ReconcilerType.RequestResponse) {
559
+ const attachmentItemToUpdate: any = this.setAttachmentItemFieldsAndStatuses(attachmentItemInDB, incomingAttachmentItem);
560
+ return await this.updateDataIntoDB(itemName, attachmentItemToUpdate, `${FieldConstants.FieldLid}='${incomingAttachmentItem[FieldConstants.FieldLid]}'`)
561
+ }
562
+ const syncStatus: number = attachmentItemInDB[FieldConstants.FieldSyncStatus];
563
+ switch (syncStatus) {
564
+ case SyncStatus.none:
565
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
566
+ Logger.logError("PullPushQueryReconciler", "actionOnIncomingAttachmentItemForAdd", `Do not touch. Item has been modified after synchronization before getting a response. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${attachmentItemInDB[FieldConstants.FieldLid]}`);
567
+ return false;
568
+ }
569
+ case SyncStatus.sent:
570
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
571
+ const attachmentItemToUpdate: any = this.setAttachmentItemFieldsAndStatuses(attachmentItemInDB, incomingAttachmentItem);
572
+ return await this.updateDataIntoDB(itemName, attachmentItemToUpdate, `${FieldConstants.FieldLid}='${incomingAttachmentItem[FieldConstants.FieldLid]}'`)
573
+ }
574
+ case SyncStatus.queued:
575
+ case SyncStatus.error:
576
+ Logger.logError("PullPushQueryReconciler", "actionOnIncomingAttachmentItemForAdd", `Do not touch. Has to be handled by the Attachment upload queue separately. For ERROR application has to resolve. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${attachmentItemInDB[FieldConstants.FieldLid]}`);
577
+ return false;
578
+ default:
579
+ break;
580
+ }
581
+ return false;
582
+ }
583
+
584
+ private async actionOnIncomingAttachmentItemForModify(itemName: string, attachmentItemInDB: any, incomingAttachmentItem: any): Promise<boolean> {
585
+ if (this.isForeground && this.reconcilerType == ReconcilerType.RequestResponse) {
586
+ const attachmentItemToUpdate: any = this.setAttachmentItemFieldsAndStatuses(attachmentItemInDB, incomingAttachmentItem);
587
+ return await this.updateDataIntoDB(itemName, attachmentItemToUpdate, `${FieldConstants.FieldLid}='${incomingAttachmentItem[FieldConstants.FieldLid]}'`)
588
+ }
589
+ const syncStatus: number = attachmentItemInDB[FieldConstants.FieldSyncStatus];
590
+ switch (syncStatus) {
591
+ case SyncStatus.none:
592
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
593
+ Logger.logError("PullPushQueryReconciler", "actionOnIncomingAttachmentItemForModify", `Do not touch. Has to be handled by the Attachment upload queue separately. For ERROR application has to resolve. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${attachmentItemInDB[FieldConstants.FieldLid]}`);
594
+ return true;
595
+ }
596
+ case SyncStatus.sent:
597
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
598
+ const attachmentItemToUpdate: any = this.setAttachmentItemFieldsAndStatuses(attachmentItemInDB, incomingAttachmentItem);
599
+ return await this.updateDataIntoDB(itemName, attachmentItemToUpdate, `${FieldConstants.FieldLid}='${incomingAttachmentItem[FieldConstants.FieldLid]}'`)
600
+ }
601
+ case SyncStatus.queued:
602
+ case SyncStatus.error:
603
+ // Invalid case. Items in Object Status MODIFY can only be in Sync Status SENT.
604
+ Logger.logError("PullPushQueryReconciler", "actionOnIncomingAttachmentItemForModify",`Do not touch. Has to be handled by the Attachment upload queue separately. For ERROR application has to resolve. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${attachmentItemInDB[FieldConstants.FieldLid]}`);
605
+ return false;
606
+ default:
607
+ break;
608
+ }
609
+ return false;
610
+ }
611
+
612
+ private async actionOnIncomingAttachmentItemForDelete(itemName: string, attachmentItemInDB: any, incomingAttachmentItem: any): Promise<boolean> {
613
+ const syncStatus: number = attachmentItemInDB[FieldConstants.FieldSyncStatus];
614
+ switch (syncStatus) {
615
+ case SyncStatus.none:
616
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
617
+ Logger.logError("PullPushQueryReconciler", "actionOnIncomingAttachmentItemForDelete", `Do not touch. Has to be handled by the Attachment upload queue separately. For ERROR application has to resolve. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${attachmentItemInDB[FieldConstants.FieldLid]}`);
618
+ return true;
619
+ }
620
+ case SyncStatus.sent:
621
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
622
+ const attachmentItemToUpdate: any = this.setAttachmentItemFieldsAndStatuses(attachmentItemInDB, incomingAttachmentItem);
623
+ return await this.updateDataIntoDB(itemName, attachmentItemToUpdate, `${FieldConstants.FieldLid}='${incomingAttachmentItem[FieldConstants.FieldLid]}'`)
624
+ }
625
+ case SyncStatus.queued:
626
+ case SyncStatus.error:
627
+ // Invalid case. Items in Object Status MODIFY can only be in Sync Status SENT.
628
+ Logger.logError("PullPushQueryReconciler", "actionOnIncomingAttachmentItemForDelete", `Do not touch. Has to be handled by the Attachment upload queue separately. For ERROR application has to resolve. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${attachmentItemInDB[FieldConstants.FieldLid]}`);
629
+ return false;
630
+ default:
631
+ break;
632
+ }
633
+ return false;
634
+ }
635
+
636
+ private addItemToCollection(itemName: string, item: any): void {
637
+ if (item == null) {
638
+ return;
639
+ }
640
+ let items: string[] | undefined = this.incomingItemLids[itemName];
641
+ if (items == null) {
642
+ items = [];
643
+ }
644
+ items.push(item[FieldConstants.FieldLid]);
645
+ this.incomingItemLids[itemName] = items;
646
+ }
647
+
648
+ private addAttachmentItemToCollection(attachmentItemName: string, attachmentItem: any): void {
649
+ if (attachmentItem == null) {
650
+ return;
651
+ }
652
+ let items: string[] | undefined = this.incomingAttachmentItemLids[attachmentItemName];
653
+ if (items == null) {
654
+ items = [];
655
+ }
656
+ items.push(attachmentItem[FieldConstants.FieldLid]);
657
+ this.incomingAttachmentItemLids[attachmentItemName] = items;
658
+ }
659
+
660
+ private setItemFieldsAndStatusesFor(itemInDB: any, incomingItem: any): any {
661
+ // Setting the LID of the incoming Item to the Item present in DB so that its easier for updation.
662
+ incomingItem[FieldConstants.FieldLid] = itemInDB[FieldConstants.FieldLid];
663
+ incomingItem[FieldConstants.FieldFid] = this.incomingEntity[FieldConstants.FieldLid];
664
+ incomingItem[FieldConstants.FieldObjectStatus] = ObjectStatus.global;
665
+ incomingItem[FieldConstants.FieldSyncStatus] = SyncStatus.none;
666
+ incomingItem[FieldConstants.FieldTimestamp] = itemInDB[FieldConstants.FieldTimestamp]; //TODO : CHeck if timestamp needs to be updated
667
+ return incomingItem;
668
+ }
669
+
670
+ private setAttachmentItemFieldsAndStatuses(attachmentItemInDB: any, incomingAttachmentItem: any): any {
671
+ incomingAttachmentItem[FieldConstants.FieldLid] = attachmentItemInDB[FieldConstants.FieldLid];
672
+ incomingAttachmentItem[FieldConstants.FieldFid] = this.incomingEntity[FieldConstants.FieldLid];
673
+ incomingAttachmentItem[FieldConstants.FieldObjectStatus] = ObjectStatus.global;
674
+ incomingAttachmentItem[FieldConstants.FieldSyncStatus] = SyncStatus.none;
675
+ incomingAttachmentItem[ServiceConstants.AttachmentItemFieldAttachmentStatus] = attachmentItemInDB[ServiceConstants.AttachmentItemFieldAttachmentStatus];
676
+ incomingAttachmentItem[ServiceConstants.AttachmentItemFieldFileName] = attachmentItemInDB[ServiceConstants.AttachmentItemFieldFileName];
677
+ incomingAttachmentItem[ServiceConstants.AttachmentItemFieldLocalPath] = attachmentItemInDB[ServiceConstants.AttachmentItemFieldLocalPath];
678
+ return incomingAttachmentItem;
679
+ }
680
+
681
+ private async handleDeletionOfItemsInDBWithoutIncomingItems(structureMetas: StructureMeta[]): Promise<void> {
682
+ // Handle Items in DB that do not have a matching incoming item from server
683
+
684
+ // Get All the Children Table Names which are not attachmenrts.
685
+ const childrenTableNames: string[] = structureMetas
686
+ .filter((element) => (!element.structureName.endsWith(ServiceConstants.AttachmentBE) && element.isHeader === "0"))
687
+ .map((e) => e.structureName);
688
+
689
+ for (const childTableName of childrenTableNames) {
690
+ // Get all items from the child item table that does not have incoming items
691
+ const incomingItemLids: string[] | undefined = this.incomingItemLids[childTableName];
692
+ let whereClauseForExcludingIncomingItems: string = `${FieldConstants.FieldFid} = '${this.entityInDB[FieldConstants.FieldLid]}'`;
693
+ if (incomingItemLids != null && incomingItemLids.length > 0) {
694
+ whereClauseForExcludingIncomingItems += ` AND ${FieldConstants.FieldLid} NOT IN (`;
695
+ for (let i = 0; i < incomingItemLids.length; i++) {
696
+ if (i !== 0) {
697
+ whereClauseForExcludingIncomingItems += ",";
698
+ }
699
+ whereClauseForExcludingIncomingItems += `'${incomingItemLids[i]}'`;
700
+ }
701
+ whereClauseForExcludingIncomingItems += ")";
702
+ }
703
+ const itemsInDBWithoutIncomingItems: any[] = await this.databaseManager.select(DatabaseType.AppDb, childTableName, whereClauseForExcludingIncomingItems);
704
+ if (itemsInDBWithoutIncomingItems.length > 0) {
705
+ await this.actionOnItemsInDBWithoutIncomingItems(childTableName, itemsInDBWithoutIncomingItems);
706
+ }
707
+ }
708
+ }
709
+
710
+ private async actionOnItemsInDBWithoutIncomingItems(itemName: string, itemsInDBWithoutIncomingItems: any[]): Promise<boolean> {
711
+ for (const itemInDB of itemsInDBWithoutIncomingItems) {
712
+ const objectStatus: number = itemInDB[FieldConstants.FieldObjectStatus];
713
+ switch (objectStatus) {
714
+ case ObjectStatus.global:
715
+ await this.actionOnDBItemWithoutIncomingItemForGlobal(itemName, itemInDB);
716
+ break;
717
+
718
+ case ObjectStatus.add:
719
+ await this.actionOnDBItemWithoutIncomingItemForAdd(itemName, itemInDB);
720
+ break;
721
+
722
+ case ObjectStatus.modify:
723
+ case ObjectStatus.delete:
724
+ await this.actionOnDBItemWithoutIncomingItemForModifyOrDelete(itemName, itemInDB);
725
+ break;
726
+
727
+ default:
728
+ break;
729
+ }
730
+ }
731
+ return true;
732
+ }
733
+
734
+ private async actionOnDBItemWithoutIncomingItemForGlobal(itemName: string, itemInDB: any): Promise<boolean> {
735
+ const syncStatus = itemInDB[FieldConstants.FieldSyncStatus] as number;
736
+ switch (syncStatus) {
737
+ case SyncStatus.none: {
738
+ const result = await this.databaseManager.delete(DatabaseType.AppDb, itemName, "" + FieldConstants.FieldLid + "='" + itemInDB[FieldConstants.FieldLid] + "'");
739
+ return result !== 0;
740
+ }
741
+ case SyncStatus.queued:
742
+ case SyncStatus.sent:
743
+ case SyncStatus.error: {
744
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
745
+ const result = await this.databaseManager.delete(DatabaseType.AppDb, itemName, "" + FieldConstants.FieldLid + "='" + itemInDB[FieldConstants.FieldLid] + "'");
746
+ return result !== 0;
747
+ }
748
+ Logger.logError(
749
+ "PullPushQueryReconciler",
750
+ "actionOnDBItemWithoutIncomingItemForGlobal",
751
+ `Invalid case. Items in Object Status GLOBAL can only be in Sync Status NONE. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${itemInDB[FieldConstants.FieldLid]}`
752
+ );
753
+ return false;
754
+ }
755
+ default:
756
+ break;
757
+ }
758
+ return true;
759
+ }
760
+
761
+ private async actionOnDBItemWithoutIncomingItemForAdd(itemName: string, itemInDB: any): Promise<boolean> {
762
+ const syncStatus = itemInDB[FieldConstants.FieldSyncStatus] as number;
763
+ switch (syncStatus) {
764
+ case SyncStatus.queued:
765
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
766
+ Logger.logError(
767
+ "PullPushQueryReconciler",
768
+ "actionOnDBItemWithoutIncomingItemForAdd",
769
+ `Server did not return this item, which was added in the device. Reconciler retaining this item. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${itemInDB[FieldConstants.FieldLid]}`
770
+ );
771
+ return false;
772
+ }
773
+ case SyncStatus.none:
774
+ case SyncStatus.sent:
775
+ case SyncStatus.error: {
776
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
777
+ const result = await this.databaseManager.delete(DatabaseType.AppDb, itemName, "" + FieldConstants.FieldLid + "='" + itemInDB[FieldConstants.FieldLid] + "'");
778
+ return result !== 0;
779
+ }
780
+ Logger.logError(
781
+ "PullPushQueryReconciler",
782
+ "actionOnDBItemWithoutIncomingItemForAdd",
783
+ `Server did not return this item, which was added in the device. Reconciler retaining this item. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${itemInDB[FieldConstants.FieldLid]}`
784
+ );
785
+ return false;
786
+ }
787
+ default:
788
+ break;
789
+ }
790
+ return true;
791
+ }
792
+
793
+ private async actionOnDBItemWithoutIncomingItemForModifyOrDelete(itemName: string, itemInDB: any): Promise<boolean> {
794
+ const syncStatus = itemInDB[FieldConstants.FieldSyncStatus] as number;
795
+ switch (syncStatus) {
796
+ case SyncStatus.none:
797
+ case SyncStatus.queued:
798
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
799
+ Logger.logError(
800
+ "RequestResponseReconciler",
801
+ "actionOnDBItemWithoutIncomingItemForModifyOrDelete",
802
+ `Invalid case. Cannot queue the same object again if the BE is waiting for a response. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${itemName}, Lid: ${itemInDB[FieldConstants.FieldLid]}`);
803
+ return false;
804
+ }
805
+ case SyncStatus.sent:
806
+ case SyncStatus.error: {
807
+ const result = await this.databaseManager.delete(DatabaseType.AppDb, itemName, "" + FieldConstants.FieldLid + "='" + itemInDB[FieldConstants.FieldLid] + "'");
808
+ return result !== 0;
809
+ }
810
+ default:
811
+ break;
812
+ }
813
+ return true;
814
+ }
815
+
816
+ private async handleDeletionOfAttachmentItemsInDBWithoutIncomingItems(
817
+ structureMetas: StructureMeta[]
818
+ ): Promise<void> {
819
+ // Handle Items in DB that do not have a matching incoming item from server
820
+
821
+ // Get all attachment table names
822
+ const childrenTableNames = structureMetas
823
+ .filter((element) => element.structureName.endsWith(ServiceConstants.AttachmentBE))
824
+ .map((e) => e.structureName);
825
+
826
+ Logger.logDebug(
827
+ "PullPushQueryReconciler",
828
+ "handleDeletionOfAttachmentItemsInDBWithoutIncomingItems",
829
+ `Children Table Names Which Support Attachments. ${childrenTableNames.toString()}`
830
+ );
831
+
832
+ if (childrenTableNames.length === 0) {
833
+ return;
834
+ }
835
+
836
+ for (const childTableName of childrenTableNames) {
837
+ // Get all items from the child item table that does not have incoming items
838
+ const incomingItemLids = this.incomingAttachmentItemLids[childTableName];
839
+ let whereClauseForExcludingIncomingItems = `${FieldConstants.FieldFid} = '${this.entityInDB[FieldConstants.FieldLid]}'`;
840
+ if (incomingItemLids && incomingItemLids.length > 0) {
841
+ whereClauseForExcludingIncomingItems += ` AND ${FieldConstants.FieldLid} NOT IN (`;
842
+ for (let i = 0; i < incomingItemLids.length; i++) {
843
+ if (i !== 0) {
844
+ whereClauseForExcludingIncomingItems += ",";
845
+ }
846
+ whereClauseForExcludingIncomingItems += `'${incomingItemLids[i]}'`;
847
+ }
848
+ whereClauseForExcludingIncomingItems += ")";
849
+ }
850
+ const itemsInDBWithoutIncomingItems = await this.databaseManager.select(DatabaseType.AppDb, childTableName, whereClauseForExcludingIncomingItems);
851
+ if (itemsInDBWithoutIncomingItems.length > 0) {
852
+ await this.actionOnAttachmentItemsInDBWithoutIncomingItems(childTableName, itemsInDBWithoutIncomingItems);
853
+ }
854
+ }
855
+ }
856
+
857
+ async actionOnAttachmentItemsInDBWithoutIncomingItems(attachmentItemName: string, attachmentItemsInDBWithoutIncomingItems: any[]): Promise<void> {
858
+ for (const attachmentItemInDB of attachmentItemsInDBWithoutIncomingItems) {
859
+ try {
860
+ await this.databaseManager.delete(DatabaseType.FrameworkDb, "InfoMessage", `belid='${attachmentItemInDB[FieldConstants.FieldLid]}'`);
861
+ } catch (e) {
862
+ Logger.logError(
863
+ "PullPushQueryReconciler",
864
+ "actionOnAttachmentItemsInDBWithoutIncomingItems",
865
+ "Error while deleting info message. Error: " + JSON.stringify(e)
866
+ );
867
+ }
868
+ const objectStatus = attachmentItemInDB[FieldConstants.FieldObjectStatus] as number;
869
+ switch (objectStatus) {
870
+ case ObjectStatus.global:
871
+ await this.actionOnDBAttachmentItemWithoutIncomingItemForGlobal(attachmentItemName, attachmentItemInDB);
872
+ break;
873
+
874
+ case ObjectStatus.add:
875
+ await this.actionOnDBAttachmentItemWithoutIncomingItemForAdd(attachmentItemName, attachmentItemInDB);
876
+ break;
877
+
878
+ case ObjectStatus.modify:
879
+ await this.actionOnDBAttachmentItemWithoutIncomingItemForModify(attachmentItemName, attachmentItemInDB);
880
+ break;
881
+
882
+ case ObjectStatus.delete:
883
+ await this.actionOnDBAttachmentItemWithoutIncomingItemForDelete(attachmentItemName, attachmentItemInDB);
884
+ break;
885
+
886
+ default:
887
+ break;
888
+ }
889
+ }
890
+ }
891
+
892
+ async actionOnDBAttachmentItemWithoutIncomingItemForGlobal(attachmentItemName: string, attachmentItemInDB: any): Promise<boolean> {
893
+ const syncStatus = attachmentItemInDB[FieldConstants.FieldSyncStatus] as number;
894
+ switch (syncStatus) {
895
+ case SyncStatus.none: // Backend does not have this item
896
+ {
897
+ return this.deleteAttachmentDataFromDB(attachmentItemName, attachmentItemInDB);
898
+ }
899
+ case SyncStatus.queued:
900
+ case SyncStatus.sent:
901
+ case SyncStatus.error:
902
+ {
903
+ // Do not touch. Has to be handled by the Attachment upload queue separately. For ERROR application has to resolve.
904
+ Logger.logError(
905
+ "PullPushQueryReconciler",
906
+ "actionOnDBAttachmentItemWithoutIncomingItemForGlobal",
907
+ `Invalid case. Items in Object Status GLOBAL can only be in Sync Status NONE. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${attachmentItemName}, Lid: ${attachmentItemInDB[FieldConstants.FieldLid]}`
908
+ );
909
+ }
910
+ break;
911
+
912
+ default:
913
+ break;
914
+ }
915
+ return true;
916
+ }
917
+
918
+ async actionOnDBAttachmentItemWithoutIncomingItemForAdd(attachmentItemName: string, attachmentItemInDB: any): Promise<boolean> {
919
+ const syncStatus = attachmentItemInDB[FieldConstants.FieldSyncStatus] as number;
920
+
921
+ switch (syncStatus) {
922
+ case SyncStatus.none:
923
+ // This comment is valid for Request Response Reconciler
924
+ // Backend does not have this time. Deleting the attachment.
925
+ // This can happen during a sync submission.
926
+ case SyncStatus.sent:
927
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
928
+ return this.deleteAttachmentDataFromDB(attachmentItemName, attachmentItemInDB);
929
+ }
930
+ case SyncStatus.queued:
931
+ case SyncStatus.error:
932
+ {
933
+ var errormessage = `Server did not return this item, which was added in the device. Reconciler retaining this item. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${attachmentItemName}, Lid: ${attachmentItemInDB[FieldConstants.FieldLid]}`;
934
+ if (this.reconcilerType == ReconcilerType.RequestResponse) {
935
+ errormessage = `Invalid case. Items in Object Status MODIFY can only be in Sync Status SENT. Current Sync Status: ${syncStatus}, Header: ${this.entityName}, LID: ${this.entityInDB[FieldConstants.FieldLid]}, Item: ${attachmentItemName}, Lid: ${attachmentItemInDB[FieldConstants.FieldLid]}`;
936
+ return false;
937
+ }
938
+ Logger.logInfo("PullPushQueryReconciler", "actionOnDBAttachmentItemWithoutIncomingItemForAdd", errormessage);
939
+ return false;
940
+ }
941
+ default:
942
+ break;
943
+ }
944
+ return true;
945
+ }
946
+
947
+ async actionOnDBAttachmentItemWithoutIncomingItemForModify(attachmentItemName: string, attachmentItemInDB: any): Promise<boolean> {
948
+ const syncStatus = attachmentItemInDB[FieldConstants.FieldSyncStatus] as number;
949
+
950
+ switch (syncStatus) {
951
+ case SyncStatus.none:
952
+ case SyncStatus.queued:
953
+ case SyncStatus.sent:
954
+ case SyncStatus.error:
955
+ {
956
+ return this.deleteAttachmentDataFromDB(attachmentItemName, attachmentItemInDB);
957
+ }
958
+ default:
959
+ break;
960
+ }
961
+ return true;
962
+ }
963
+
964
+ async actionOnDBAttachmentItemWithoutIncomingItemForDelete(attachmentItemName: string, attachmentItemInDB: any): Promise<boolean> {
965
+ const syncStatus = attachmentItemInDB[FieldConstants.FieldSyncStatus] as number;
966
+
967
+ switch (syncStatus) {
968
+ case SyncStatus.none:
969
+ case SyncStatus.queued:
970
+ case SyncStatus.sent:
971
+ case SyncStatus.error:
972
+ {
973
+ return this.deleteAttachmentDataFromDB(attachmentItemName, attachmentItemInDB);
974
+ }
975
+ default:
976
+ break;
977
+ }
978
+ return true;
979
+ }
980
+
981
+ private async deleteAttachmentDataFromDB(attachmentItemName: string, attachmentItemInDB: any): Promise<boolean> {
982
+ try {
983
+ const uid = attachmentItemInDB[ServiceConstants.AttachmentItemFieldUid] || "";
984
+ var result = await this.databaseManager.delete(DatabaseType.AppDb, attachmentItemName, `${FieldConstants.FieldLid}='${attachmentItemInDB[FieldConstants.FieldLid]}'`);
985
+ if (uid.length > 0) {
986
+ result = await this.databaseManager.delete(DatabaseType.FrameworkDb, "AttachmentQObject", `uid ='${uid}'`);
987
+ }
988
+ return result !== 0;
989
+ } catch (e) {
990
+ Logger.logDebug("PullPushQueryReconciler", "deleteAttachmentDataFromDB", "Error while deleting attachment. Input data: " + attachmentItemInDB.toString() + ". Error: " + JSON.stringify(e));
991
+ Logger.logError("PullPushQueryReconciler", "actionOnDBAttachmentItemWithoutIncomingItemForGlobal", "Error while deleting attachment.");
992
+ return false
993
+ }
994
+ }
995
+
996
+ async manageConflict(
997
+ headerMeta: StructureMeta,
998
+ currentDBHeader: { [key: string]: any },
999
+ currentIncomingHeader: { [key: string]: any },
1000
+ currentIncomingItems: { [key: string]: any }
1001
+ ): Promise<boolean> {
1002
+ // 1
1003
+ // Store the Local Data in the CONFLICT BE Data Field.
1004
+ // Generate Message For Sending to Server.
1005
+ const beData: { [key: string]: any } = {};
1006
+ beData[headerMeta.structureName] = currentDBHeader;
1007
+ const childItems = await SyncInputDataManager.getChildData(
1008
+ currentDBHeader[FieldConstants.FieldLid],
1009
+ headerMeta.structureName,
1010
+ DataType.all
1011
+ );
1012
+ Object.assign(beData, childItems);
1013
+
1014
+ const finalBeObject = {
1015
+ [headerMeta.beName]: [beData]
1016
+ };
1017
+ const jsonString = JSON.stringify(finalBeObject);
1018
+
1019
+ const inputJSON = {
1020
+ beName: headerMeta.beName,
1021
+ beHeaderLid: currentDBHeader[FieldConstants.FieldLid],
1022
+ data: jsonString
1023
+ };
1024
+ const result = await this.databaseManager.insert(DatabaseType.FrameworkDb, "ConflictBE", inputJSON, true);
1025
+ if (result === 0) {
1026
+ return false;
1027
+ }
1028
+ this.conflictBe = inputJSON;
1029
+
1030
+ // 2
1031
+ // Set the currentIncomingHeader's LID to currentDBHeader's LID
1032
+ currentIncomingHeader[FieldConstants.FieldLid] = currentDBHeader[FieldConstants.FieldLid];
1033
+
1034
+ // 3
1035
+ // Replace Headers and Items with the incoming header and items
1036
+ const status = await this.updateDataIntoDB(headerMeta.structureName, currentIncomingHeader, `${FieldConstants.FieldLid}='${currentDBHeader[FieldConstants.FieldLid]}'`);
1037
+ if (!status) {
1038
+ return false;
1039
+ }
1040
+ const keys = Object.keys(currentIncomingItems);
1041
+ for (const key of keys) {
1042
+ if (!key.endsWith(ServiceConstants.AttachmentBE)) {
1043
+ // delete all the existing items except attachment items
1044
+ const deleteStatus = await this.databaseManager.delete(DatabaseType.AppDb, key);
1045
+ if (deleteStatus === 0) {
1046
+ return false;
1047
+ }
1048
+ }
1049
+ const itemsArray = currentIncomingItems[key];
1050
+ for (const value of itemsArray) {
1051
+ // Insert All Incoming Items
1052
+ const insertStatus = await this.databaseManager.insert(DatabaseType.AppDb, key, value, false);
1053
+ if (!insertStatus) {
1054
+ return false;
1055
+ }
1056
+ }
1057
+ }
1058
+ return true;
1059
+ }
1060
+ }
1061
+
1062
+ export default Reconciler;