meadow-integration 1.0.24 → 1.0.25
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.
- package/docs/_cover.md +1 -1
- package/docs/_version.json +7 -0
- package/docs/css/docuserve.css +277 -23
- package/docs/index.html +2 -2
- package/docs/retold-catalog.json +40 -1
- package/docs/retold-keyword-index.json +6150 -5279
- package/package.json +3 -2
- package/source/services/clone/Meadow-Service-Sync-Entity-ComparisonOnly.js +435 -0
- package/source/services/clone/Meadow-Service-Sync-Entity-OngoingEventualConsistency.js +353 -0
- package/source/services/clone/Meadow-Service-Sync-Entity-TrueUp.js +199 -0
- package/source/services/clone/Meadow-Service-Sync.js +61 -6
- package/test/Meadow-Integration-ComprehensionPush_test.js +102 -8
- package/test/Meadow-Integration-NewStrategies_test.js +1265 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "meadow-integration",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.25",
|
|
4
4
|
"description": "Meadow Data Integration",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mdwint": "source/cli/Meadow-Integration-CLI-Run.js"
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"meadow-connection-sqlite": "^1.0.18",
|
|
21
|
-
"
|
|
21
|
+
"pict-docuserve": "^0.1.5",
|
|
22
|
+
"quackage": "^1.1.0"
|
|
22
23
|
},
|
|
23
24
|
"mocha": {
|
|
24
25
|
"diff": true,
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
const libMeadowSyncEntityOngoing = require('./Meadow-Service-Sync-Entity-Ongoing.js');
|
|
2
|
+
|
|
3
|
+
class MeadowSyncEntityComparisonOnly extends libMeadowSyncEntityOngoing
|
|
4
|
+
{
|
|
5
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
6
|
+
{
|
|
7
|
+
super(pFable, pOptions, pServiceHash);
|
|
8
|
+
|
|
9
|
+
this.serviceType = 'MeadowSyncEntityComparisonOnly';
|
|
10
|
+
|
|
11
|
+
this.ComparisonReport = null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Compare a local ID range against the server and record the result in the
|
|
15
|
+
// report. Same logic as _bisectRange but never pulls or upserts records.
|
|
16
|
+
_compareRange(pMinID, pMaxID, pDepth, pReport, fCallback)
|
|
17
|
+
{
|
|
18
|
+
const tmpRangeSize = pMaxID - pMinID + 1;
|
|
19
|
+
const tmpIDCol = this.DefaultIdentifier;
|
|
20
|
+
const tmpRangeFilter = `FBV~${tmpIDCol}~GE~${pMinID}~FBV~${tmpIDCol}~LE~${pMaxID}`;
|
|
21
|
+
|
|
22
|
+
this._getLocalCount(pMinID, pMaxID,
|
|
23
|
+
(pLocalCountError, pLocalCount) =>
|
|
24
|
+
{
|
|
25
|
+
if (pLocalCountError)
|
|
26
|
+
{
|
|
27
|
+
this.fable.log.warn(`${this.EntitySchema.TableName}: compare local count error for range ${pMinID}-${pMaxID}: ${pLocalCountError}`);
|
|
28
|
+
pReport.Ranges.push(
|
|
29
|
+
{
|
|
30
|
+
MinID: pMinID,
|
|
31
|
+
MaxID: pMaxID,
|
|
32
|
+
Status: 'error',
|
|
33
|
+
Error: `Local count error: ${pLocalCountError}`
|
|
34
|
+
});
|
|
35
|
+
return fCallback();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this._getServerCount(tmpRangeFilter,
|
|
39
|
+
(pServerCountError, pServerCount) =>
|
|
40
|
+
{
|
|
41
|
+
if (pServerCountError)
|
|
42
|
+
{
|
|
43
|
+
this.fable.log.warn(`${this.EntitySchema.TableName}: compare server count error for range ${pMinID}-${pMaxID}: ${pServerCountError}`);
|
|
44
|
+
pReport.Ranges.push(
|
|
45
|
+
{
|
|
46
|
+
MinID: pMinID,
|
|
47
|
+
MaxID: pMaxID,
|
|
48
|
+
Status: 'error',
|
|
49
|
+
Error: `Server count error: ${pServerCountError}`
|
|
50
|
+
});
|
|
51
|
+
return fCallback();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (pLocalCount === pServerCount)
|
|
55
|
+
{
|
|
56
|
+
if (!this._hasUpdateDate)
|
|
57
|
+
{
|
|
58
|
+
// Counts match, no UpdateDate to check -- record as match
|
|
59
|
+
pReport.Ranges.push(
|
|
60
|
+
{
|
|
61
|
+
MinID: pMinID,
|
|
62
|
+
MaxID: pMaxID,
|
|
63
|
+
Status: 'match',
|
|
64
|
+
LocalCount: pLocalCount,
|
|
65
|
+
ServerCount: pServerCount
|
|
66
|
+
});
|
|
67
|
+
this._incrementProgress(pServerCount);
|
|
68
|
+
return fCallback();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Compare UpdateDate boundaries
|
|
72
|
+
this._getLocalMaxUpdateDate(pMinID, pMaxID,
|
|
73
|
+
(pLocalMaxErr, pLocalMaxDate) =>
|
|
74
|
+
{
|
|
75
|
+
if (pLocalMaxErr || !pLocalMaxDate)
|
|
76
|
+
{
|
|
77
|
+
// Can't determine UpdateDate -- treat as match by count
|
|
78
|
+
pReport.Ranges.push(
|
|
79
|
+
{
|
|
80
|
+
MinID: pMinID,
|
|
81
|
+
MaxID: pMaxID,
|
|
82
|
+
Status: 'match',
|
|
83
|
+
LocalCount: pLocalCount,
|
|
84
|
+
ServerCount: pServerCount
|
|
85
|
+
});
|
|
86
|
+
this._incrementProgress(pServerCount);
|
|
87
|
+
return fCallback();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const tmpMaxDateFilter = `${tmpRangeFilter}~FSF~UpdateDate~DESC~DESC`;
|
|
91
|
+
this._getServerRecords(tmpMaxDateFilter, 0, 1,
|
|
92
|
+
(pServerMaxErr, pServerMaxRecords) =>
|
|
93
|
+
{
|
|
94
|
+
if (pServerMaxErr || !pServerMaxRecords || pServerMaxRecords.length < 1)
|
|
95
|
+
{
|
|
96
|
+
pReport.Ranges.push(
|
|
97
|
+
{
|
|
98
|
+
MinID: pMinID,
|
|
99
|
+
MaxID: pMaxID,
|
|
100
|
+
Status: 'match',
|
|
101
|
+
LocalCount: pLocalCount,
|
|
102
|
+
ServerCount: pServerCount
|
|
103
|
+
});
|
|
104
|
+
this._incrementProgress(pServerCount);
|
|
105
|
+
return fCallback();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const tmpServerMaxDate = pServerMaxRecords[0].UpdateDate;
|
|
109
|
+
const tmpLocalNorm = this._normalizeDateUTC(pLocalMaxDate);
|
|
110
|
+
const tmpServerNorm = this._normalizeDateUTC(tmpServerMaxDate);
|
|
111
|
+
const tmpMaxDateDiff = Math.abs(tmpLocalNorm.diff(tmpServerNorm));
|
|
112
|
+
|
|
113
|
+
if (tmpMaxDateDiff <= this.DateTimePrecisionMS)
|
|
114
|
+
{
|
|
115
|
+
// Counts and dates match -- in sync
|
|
116
|
+
pReport.Ranges.push(
|
|
117
|
+
{
|
|
118
|
+
MinID: pMinID,
|
|
119
|
+
MaxID: pMaxID,
|
|
120
|
+
Status: 'match',
|
|
121
|
+
LocalCount: pLocalCount,
|
|
122
|
+
ServerCount: pServerCount
|
|
123
|
+
});
|
|
124
|
+
this._incrementProgress(pServerCount);
|
|
125
|
+
return fCallback();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Dates differ even though counts match
|
|
129
|
+
if (tmpRangeSize <= this.BisectMinRangeSize)
|
|
130
|
+
{
|
|
131
|
+
pReport.Ranges.push(
|
|
132
|
+
{
|
|
133
|
+
MinID: pMinID,
|
|
134
|
+
MaxID: pMaxID,
|
|
135
|
+
Status: 'mismatch',
|
|
136
|
+
LocalCount: pLocalCount,
|
|
137
|
+
ServerCount: pServerCount,
|
|
138
|
+
CountDifference: pServerCount - pLocalCount,
|
|
139
|
+
LocalMaxUpdateDate: tmpLocalNorm.toISOString(),
|
|
140
|
+
ServerMaxUpdateDate: tmpServerNorm.toISOString(),
|
|
141
|
+
UpdateDateDifferenceMS: tmpMaxDateDiff
|
|
142
|
+
});
|
|
143
|
+
this._incrementProgress(pServerCount);
|
|
144
|
+
return fCallback();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return this._compareSubdivideRange(pMinID, pMaxID, pDepth, pReport, fCallback);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Counts differ
|
|
154
|
+
if (tmpRangeSize <= this.BisectMinRangeSize)
|
|
155
|
+
{
|
|
156
|
+
const tmpMismatchEntry = {
|
|
157
|
+
MinID: pMinID,
|
|
158
|
+
MaxID: pMaxID,
|
|
159
|
+
Status: 'mismatch',
|
|
160
|
+
LocalCount: pLocalCount,
|
|
161
|
+
ServerCount: pServerCount,
|
|
162
|
+
CountDifference: pServerCount - pLocalCount
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Try to get UpdateDate info for the mismatch entry
|
|
166
|
+
if (this._hasUpdateDate)
|
|
167
|
+
{
|
|
168
|
+
this._getLocalMaxUpdateDate(pMinID, pMaxID,
|
|
169
|
+
(pLocalMaxErr, pLocalMaxDate) =>
|
|
170
|
+
{
|
|
171
|
+
if (!pLocalMaxErr && pLocalMaxDate)
|
|
172
|
+
{
|
|
173
|
+
const tmpMaxDateFilter = `${tmpRangeFilter}~FSF~UpdateDate~DESC~DESC`;
|
|
174
|
+
this._getServerRecords(tmpMaxDateFilter, 0, 1,
|
|
175
|
+
(pServerMaxErr, pServerMaxRecords) =>
|
|
176
|
+
{
|
|
177
|
+
if (!pServerMaxErr && pServerMaxRecords && pServerMaxRecords.length > 0)
|
|
178
|
+
{
|
|
179
|
+
const tmpLocalNorm = this._normalizeDateUTC(pLocalMaxDate);
|
|
180
|
+
const tmpServerNorm = this._normalizeDateUTC(pServerMaxRecords[0].UpdateDate);
|
|
181
|
+
tmpMismatchEntry.LocalMaxUpdateDate = tmpLocalNorm.toISOString();
|
|
182
|
+
tmpMismatchEntry.ServerMaxUpdateDate = tmpServerNorm.toISOString();
|
|
183
|
+
tmpMismatchEntry.UpdateDateDifferenceMS = Math.abs(tmpLocalNorm.diff(tmpServerNorm));
|
|
184
|
+
}
|
|
185
|
+
pReport.Ranges.push(tmpMismatchEntry);
|
|
186
|
+
this._incrementProgress(Math.max(pLocalCount, pServerCount));
|
|
187
|
+
return fCallback();
|
|
188
|
+
});
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
pReport.Ranges.push(tmpMismatchEntry);
|
|
192
|
+
this._incrementProgress(Math.max(pLocalCount, pServerCount));
|
|
193
|
+
return fCallback();
|
|
194
|
+
});
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
pReport.Ranges.push(tmpMismatchEntry);
|
|
199
|
+
this._incrementProgress(Math.max(pLocalCount, pServerCount));
|
|
200
|
+
return fCallback();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return this._compareSubdivideRange(pMinID, pMaxID, pDepth, pReport, fCallback);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
_compareSubdivideRange(pMinID, pMaxID, pDepth, pReport, fCallback)
|
|
209
|
+
{
|
|
210
|
+
const tmpMidID = Math.floor((pMinID + pMaxID) / 2);
|
|
211
|
+
|
|
212
|
+
this.fable.log.info(`${this.EntitySchema.TableName}: compare subdividing range ${pMinID}-${pMaxID} at ID ${tmpMidID} (depth ${pDepth})`);
|
|
213
|
+
|
|
214
|
+
this._compareRange(pMinID, tmpMidID, pDepth + 1, pReport,
|
|
215
|
+
() =>
|
|
216
|
+
{
|
|
217
|
+
this._compareRange(tmpMidID + 1, pMaxID, pDepth + 1, pReport, fCallback);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
_syncInternal(fCallback)
|
|
222
|
+
{
|
|
223
|
+
this.operation.createTimeStamp('EntityComparisonOnlySync');
|
|
224
|
+
|
|
225
|
+
this._totalSyncedThisSync = 0;
|
|
226
|
+
this._recordsCreated = 0;
|
|
227
|
+
this._recordsUpdated = 0;
|
|
228
|
+
|
|
229
|
+
const tmpSyncState = (
|
|
230
|
+
{
|
|
231
|
+
Local: { MaxIDEntity: -1, RecordCount: 0 },
|
|
232
|
+
Server: { MaxIDEntity: -1, RecordCount: 0 },
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
this._hasUpdateDate = false;
|
|
236
|
+
this._hasDeletedColumn = false;
|
|
237
|
+
|
|
238
|
+
if (this.EntitySchema && this.EntitySchema.MeadowSchema && Array.isArray(this.EntitySchema.MeadowSchema.Schema))
|
|
239
|
+
{
|
|
240
|
+
for (let i = 0; i < this.EntitySchema.MeadowSchema.Schema.length; i++)
|
|
241
|
+
{
|
|
242
|
+
const tmpColumn = this.EntitySchema.MeadowSchema.Schema[i];
|
|
243
|
+
if (tmpColumn.Column == 'UpdateDate')
|
|
244
|
+
{
|
|
245
|
+
this._hasUpdateDate = true;
|
|
246
|
+
}
|
|
247
|
+
if (tmpColumn.Type == 'Deleted' || tmpColumn.Column == 'Deleted')
|
|
248
|
+
{
|
|
249
|
+
this._hasDeletedColumn = true;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
this.fable.log.info(`Syncing with COMPARISON ONLY STRATEGY entity ${this.EntitySchema.TableName} (UpdateDate: ${this._hasUpdateDate})...`);
|
|
255
|
+
|
|
256
|
+
this.ComparisonReport = {
|
|
257
|
+
Entity: this.EntitySchema.TableName,
|
|
258
|
+
Timestamp: new Date().toISOString(),
|
|
259
|
+
Summary: {
|
|
260
|
+
LocalRecordCount: 0,
|
|
261
|
+
ServerRecordCount: 0,
|
|
262
|
+
LocalMaxID: 0,
|
|
263
|
+
ServerMaxID: 0,
|
|
264
|
+
TotalRangesChecked: 0,
|
|
265
|
+
MatchingRanges: 0,
|
|
266
|
+
MismatchedRanges: 0,
|
|
267
|
+
ErrorRanges: 0,
|
|
268
|
+
TotalCountDifference: 0
|
|
269
|
+
},
|
|
270
|
+
Ranges: []
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
this.fable.Utility.waterfall(
|
|
274
|
+
[
|
|
275
|
+
// ---- Stage 1: Gather local stats ----
|
|
276
|
+
(fStageComplete) =>
|
|
277
|
+
{
|
|
278
|
+
const tmpQuery = this.Meadow.query;
|
|
279
|
+
tmpQuery.setSort({ Column: this.DefaultIdentifier, Direction: 'Descending' });
|
|
280
|
+
tmpQuery.setCap(1);
|
|
281
|
+
if (!this._hasDeletedColumn)
|
|
282
|
+
{
|
|
283
|
+
tmpQuery.setDisableDeleteTracking(true);
|
|
284
|
+
}
|
|
285
|
+
this.Meadow.doRead(tmpQuery,
|
|
286
|
+
(pReadError, pQuery, pRecord) =>
|
|
287
|
+
{
|
|
288
|
+
if (pReadError)
|
|
289
|
+
{
|
|
290
|
+
this.fable.log.error(`Error reading local max entity ID ${this.EntitySchema.TableName}: ${pReadError}`);
|
|
291
|
+
return fStageComplete(`Error reading local max entity ID ${this.EntitySchema.TableName}: ${pReadError}`);
|
|
292
|
+
}
|
|
293
|
+
if (pRecord)
|
|
294
|
+
{
|
|
295
|
+
tmpSyncState.Local.MaxIDEntity = pRecord[this.DefaultIdentifier];
|
|
296
|
+
}
|
|
297
|
+
return fStageComplete();
|
|
298
|
+
});
|
|
299
|
+
},
|
|
300
|
+
(fStageComplete) =>
|
|
301
|
+
{
|
|
302
|
+
const tmpQuery = this.Meadow.query;
|
|
303
|
+
if (!this._hasDeletedColumn)
|
|
304
|
+
{
|
|
305
|
+
tmpQuery.setDisableDeleteTracking(true);
|
|
306
|
+
}
|
|
307
|
+
this.Meadow.doCount(tmpQuery,
|
|
308
|
+
(pCountError, pQuery, pCount) =>
|
|
309
|
+
{
|
|
310
|
+
if (pCountError)
|
|
311
|
+
{
|
|
312
|
+
this.fable.log.error(`Error getting local count of ${this.EntitySchema.TableName}: ${pCountError}`);
|
|
313
|
+
return fStageComplete(`Error getting local count of ${this.EntitySchema.TableName}: ${pCountError}`);
|
|
314
|
+
}
|
|
315
|
+
tmpSyncState.Local.RecordCount = pCount;
|
|
316
|
+
return fStageComplete();
|
|
317
|
+
});
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
// ---- Stage 2: Gather server stats ----
|
|
321
|
+
(fStageComplete) =>
|
|
322
|
+
{
|
|
323
|
+
this.fable.MeadowCloneRestClient.getJSON(`${this.EntitySchema.TableName}/Max/${this.DefaultIdentifier}`,
|
|
324
|
+
(pError, pResponse, pBody) =>
|
|
325
|
+
{
|
|
326
|
+
if (pError)
|
|
327
|
+
{
|
|
328
|
+
this.fable.log.warn(`Could not get server max entity ID for ${this.EntitySchema.TableName} (${pError}).`);
|
|
329
|
+
return fStageComplete();
|
|
330
|
+
}
|
|
331
|
+
if (pBody && pBody.hasOwnProperty(this.DefaultIdentifier))
|
|
332
|
+
{
|
|
333
|
+
tmpSyncState.Server.MaxIDEntity = pBody[this.DefaultIdentifier];
|
|
334
|
+
}
|
|
335
|
+
return fStageComplete();
|
|
336
|
+
});
|
|
337
|
+
},
|
|
338
|
+
(fStageComplete) =>
|
|
339
|
+
{
|
|
340
|
+
this._getServerCount(null,
|
|
341
|
+
(pError, pCount) =>
|
|
342
|
+
{
|
|
343
|
+
if (pError)
|
|
344
|
+
{
|
|
345
|
+
this.fable.log.warn(`Could not get server count for ${this.EntitySchema.TableName} (${pError}); estimating from max ID.`);
|
|
346
|
+
tmpSyncState.Server.RecordCount = tmpSyncState.Server.MaxIDEntity > 0 ? tmpSyncState.Server.MaxIDEntity : 0;
|
|
347
|
+
return fStageComplete();
|
|
348
|
+
}
|
|
349
|
+
tmpSyncState.Server.RecordCount = pCount;
|
|
350
|
+
return fStageComplete();
|
|
351
|
+
});
|
|
352
|
+
},
|
|
353
|
+
|
|
354
|
+
// Create progress tracker
|
|
355
|
+
(fStageComplete) =>
|
|
356
|
+
{
|
|
357
|
+
let tmpTrackerTotal = Math.max(tmpSyncState.Server.RecordCount, tmpSyncState.Local.RecordCount);
|
|
358
|
+
this.operation.createProgressTracker(tmpTrackerTotal, `FullSync-${this.EntitySchema.TableName}`);
|
|
359
|
+
return fStageComplete();
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
// ---- Stage 3: Comparison bisection ----
|
|
363
|
+
(fStageComplete) =>
|
|
364
|
+
{
|
|
365
|
+
const tmpMaxID = Math.max(tmpSyncState.Local.MaxIDEntity, tmpSyncState.Server.MaxIDEntity);
|
|
366
|
+
|
|
367
|
+
if (tmpMaxID < 1)
|
|
368
|
+
{
|
|
369
|
+
this.fable.log.info(`${this.EntitySchema.TableName}: no records on either side; nothing to compare.`);
|
|
370
|
+
return fStageComplete();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
this.fable.log.info(`${this.EntitySchema.TableName}: starting comparison bisection (ID range 1-${tmpMaxID})...`);
|
|
374
|
+
|
|
375
|
+
this._compareRange(1, tmpMaxID, 0, this.ComparisonReport, fStageComplete);
|
|
376
|
+
},
|
|
377
|
+
],
|
|
378
|
+
(pError) =>
|
|
379
|
+
{
|
|
380
|
+
if (pError)
|
|
381
|
+
{
|
|
382
|
+
this.fable.log.error(`Error performing comparison sync ${this.EntitySchema.TableName}: ${pError}`, { Error: pError });
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
let tmpTracker = this.operation.progressTrackers[`FullSync-${this.EntitySchema.TableName}`];
|
|
386
|
+
if (tmpTracker)
|
|
387
|
+
{
|
|
388
|
+
tmpTracker.CurrentCount = tmpTracker.TotalCount;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Finalize report summary
|
|
392
|
+
this.ComparisonReport.Summary.LocalRecordCount = tmpSyncState.Local.RecordCount;
|
|
393
|
+
this.ComparisonReport.Summary.ServerRecordCount = tmpSyncState.Server.RecordCount;
|
|
394
|
+
this.ComparisonReport.Summary.LocalMaxID = tmpSyncState.Local.MaxIDEntity;
|
|
395
|
+
this.ComparisonReport.Summary.ServerMaxID = tmpSyncState.Server.MaxIDEntity;
|
|
396
|
+
this.ComparisonReport.Summary.TotalRangesChecked = this.ComparisonReport.Ranges.length;
|
|
397
|
+
|
|
398
|
+
let tmpTotalCountDifference = 0;
|
|
399
|
+
for (let i = 0; i < this.ComparisonReport.Ranges.length; i++)
|
|
400
|
+
{
|
|
401
|
+
const tmpRange = this.ComparisonReport.Ranges[i];
|
|
402
|
+
if (tmpRange.Status === 'match')
|
|
403
|
+
{
|
|
404
|
+
this.ComparisonReport.Summary.MatchingRanges++;
|
|
405
|
+
}
|
|
406
|
+
else if (tmpRange.Status === 'mismatch')
|
|
407
|
+
{
|
|
408
|
+
this.ComparisonReport.Summary.MismatchedRanges++;
|
|
409
|
+
tmpTotalCountDifference += Math.abs(tmpRange.CountDifference || 0);
|
|
410
|
+
}
|
|
411
|
+
else if (tmpRange.Status === 'error')
|
|
412
|
+
{
|
|
413
|
+
this.ComparisonReport.Summary.ErrorRanges++;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
this.ComparisonReport.Summary.TotalCountDifference = tmpTotalCountDifference;
|
|
417
|
+
|
|
418
|
+
this.fable.log.info(`${this.EntitySchema.TableName}: comparison complete -- ${this.ComparisonReport.Summary.MatchingRanges} matching, ${this.ComparisonReport.Summary.MismatchedRanges} mismatched out of ${this.ComparisonReport.Summary.TotalRangesChecked} ranges.`);
|
|
419
|
+
|
|
420
|
+
this.syncResults = {
|
|
421
|
+
Created: 0,
|
|
422
|
+
Updated: 0,
|
|
423
|
+
Deleted: 0,
|
|
424
|
+
ServerRecordCount: tmpSyncState.Server.RecordCount,
|
|
425
|
+
LocalRecordCount: tmpSyncState.Local.RecordCount,
|
|
426
|
+
ComparisonReport: this.ComparisonReport
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
// No deleted record sync for comparison-only mode
|
|
430
|
+
return fCallback();
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
module.exports = MeadowSyncEntityComparisonOnly;
|