meadow-integration 1.0.6 → 1.0.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meadow-integration",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Meadow Data Integration",
5
5
  "bin": {
6
6
  "mdwint": "source/cli/Meadow-Integration-CLI-Run.js"
@@ -39,6 +39,7 @@ class MeadowSyncEntityInitial extends libFableServiceProviderBase
39
39
 
40
40
  this.DefaultIdentifier = this.EntitySchema.MeadowSchema.DefaultIdentifier;
41
41
  this.PageSize = this.options.PageSize || 100;
42
+ this.SyncDeletedRecords = this.options.SyncDeletedRecords || false;
42
43
 
43
44
  this.Meadow = false;
44
45
 
@@ -139,6 +140,136 @@ class MeadowSyncEntityInitial extends libFableServiceProviderBase
139
140
  return tmpRecordToCommit;
140
141
  }
141
142
 
143
+ syncDeletedRecords(fCallback)
144
+ {
145
+ const tmpDeletedColumn = this.EntitySchema.Columns.find((c) => c.Column == 'Deleted');
146
+ if (!tmpDeletedColumn)
147
+ {
148
+ this.fable.log.info(`No Deleted column for ${this.EntitySchema.TableName}; skipping delete sync.`);
149
+ return fCallback();
150
+ }
151
+
152
+ this.fable.log.info(`Checking for deleted records on server for ${this.EntitySchema.TableName}...`);
153
+
154
+ // Get the count of deleted records from the server.
155
+ // The explicit FBV~Deleted~EQ~1 filter overrides foxhound's automatic Deleted=0 filter.
156
+ this.fable.MeadowCloneRestClient.getJSON(`${this.EntitySchema.TableName}s/Count/FilteredTo/FBV~Deleted~EQ~1`,
157
+ (pError, pResponse, pBody) =>
158
+ {
159
+ if (pError || !pBody || !pBody.hasOwnProperty('Count'))
160
+ {
161
+ this.fable.log.warn(`Could not get deleted record count for ${this.EntitySchema.TableName}; skipping delete sync.`);
162
+ return fCallback();
163
+ }
164
+
165
+ const tmpDeletedCount = pBody.Count;
166
+ if (tmpDeletedCount < 1)
167
+ {
168
+ this.fable.log.info(`No deleted records on server for ${this.EntitySchema.TableName}.`);
169
+ return fCallback();
170
+ }
171
+
172
+ this.fable.log.info(`Found ${tmpDeletedCount} deleted records on server for ${this.EntitySchema.TableName}; syncing deletions...`);
173
+
174
+ // Generate paginated URLs for deleted records
175
+ const tmpDeleteURLPartials = [];
176
+ for (let i = 0; i < tmpDeletedCount; i += this.PageSize)
177
+ {
178
+ tmpDeleteURLPartials.push(`${this.EntitySchema.TableName}s/FilteredTo/FBV~Deleted~EQ~1~FSF~${this.DefaultIdentifier}~ASC~ASC/${i}/${this.PageSize}`);
179
+ }
180
+
181
+ this.fable.Utility.eachLimit(tmpDeleteURLPartials, 1,
182
+ (pURLPartial, fPageComplete) =>
183
+ {
184
+ this.fable.MeadowCloneRestClient.getJSON(pURLPartial,
185
+ (pDownloadError, pResponse, pBody) =>
186
+ {
187
+ if (pDownloadError || !pBody || !Array.isArray(pBody) || pBody.length < 1)
188
+ {
189
+ return fPageComplete();
190
+ }
191
+
192
+ this.fable.Utility.eachLimit(pBody, 5,
193
+ (pEntityRecord, fRecordComplete) =>
194
+ {
195
+ const tmpRecordID = pEntityRecord[this.DefaultIdentifier];
196
+ if (!tmpRecordID || tmpRecordID < 1)
197
+ {
198
+ return fRecordComplete();
199
+ }
200
+
201
+ // Read local record with delete tracking disabled so we can see all records
202
+ const tmpQuery = this.Meadow.query;
203
+ tmpQuery.addFilter(this.DefaultIdentifier, tmpRecordID);
204
+ tmpQuery.setDisableDeleteTracking(true);
205
+
206
+ this.Meadow.doRead(tmpQuery,
207
+ (pReadError, pQuery, pRecord) =>
208
+ {
209
+ if (pReadError || !pRecord)
210
+ {
211
+ // Record doesn't exist locally -- create it as deleted
212
+ const tmpRecordToCommit = this.marshalRecord(pEntityRecord);
213
+
214
+ const tmpCreateQuery = this.Meadow.query.addRecord(tmpRecordToCommit);
215
+ tmpCreateQuery.setDisableAutoIdentity(true);
216
+ tmpCreateQuery.setDisableAutoDateStamp(true);
217
+ tmpCreateQuery.setDisableAutoUserStamp(true);
218
+ tmpCreateQuery.setDisableDeleteTracking(true);
219
+ tmpCreateQuery.AllowIdentityInsert = true;
220
+
221
+ this.Meadow.doCreate(tmpCreateQuery,
222
+ (pCreateError) =>
223
+ {
224
+ if (pCreateError)
225
+ {
226
+ this.log.error(`Error creating deleted record ${this.EntitySchema.TableName} ID ${tmpRecordID}: ${pCreateError}`);
227
+ }
228
+ return fRecordComplete();
229
+ });
230
+ return;
231
+ }
232
+
233
+ if (pRecord.Deleted == 1)
234
+ {
235
+ // Already marked deleted locally
236
+ return fRecordComplete();
237
+ }
238
+
239
+ // Record exists locally but is not deleted -- update it
240
+ const tmpRecordToCommit = this.marshalRecord(pEntityRecord);
241
+
242
+ const tmpUpdateQuery = this.Meadow.query.addRecord(tmpRecordToCommit);
243
+ tmpUpdateQuery.setDisableAutoIdentity(true);
244
+ tmpUpdateQuery.setDisableAutoDateStamp(true);
245
+ tmpUpdateQuery.setDisableAutoUserStamp(true);
246
+ tmpUpdateQuery.setDisableDeleteTracking(true);
247
+
248
+ this.Meadow.doUpdate(tmpUpdateQuery,
249
+ (pUpdateError) =>
250
+ {
251
+ if (pUpdateError)
252
+ {
253
+ this.log.error(`Error marking record as deleted ${this.EntitySchema.TableName} ID ${tmpRecordID}: ${pUpdateError}`);
254
+ }
255
+ return fRecordComplete();
256
+ });
257
+ });
258
+ },
259
+ (pRecordSyncError) =>
260
+ {
261
+ return fPageComplete();
262
+ });
263
+ });
264
+ },
265
+ (pDeleteSyncError) =>
266
+ {
267
+ this.fable.log.info(`Delete sync complete for ${this.EntitySchema.TableName} (${tmpDeletedCount} deleted records processed).`);
268
+ return fCallback();
269
+ });
270
+ });
271
+ }
272
+
142
273
  sync(fCallback)
143
274
  {
144
275
  this.operation.createTimeStamp('EntityInitialSync');
@@ -356,7 +487,11 @@ class MeadowSyncEntityInitial extends libFableServiceProviderBase
356
487
  if (pError)
357
488
  {
358
489
  this.fable.log.error(`Error performing sync ${this.EntitySchema.TableName}: ${pError}`, { Error: pError });
359
- return fCallback();
490
+ }
491
+
492
+ if (this.SyncDeletedRecords)
493
+ {
494
+ return this.syncDeletedRecords(() => { return fCallback(); });
360
495
  }
361
496
 
362
497
  return fCallback();
@@ -39,6 +39,7 @@ class MeadowSyncEntityOngoing extends libFableServiceProviderBase
39
39
 
40
40
  this.DefaultIdentifier = this.EntitySchema.MeadowSchema.DefaultIdentifier;
41
41
  this.PageSize = this.options.PageSize || 100;
42
+ this.SyncDeletedRecords = this.options.SyncDeletedRecords || false;
42
43
 
43
44
  this.Meadow = false;
44
45
 
@@ -132,6 +133,136 @@ class MeadowSyncEntityOngoing extends libFableServiceProviderBase
132
133
  return tmpRecordToCommit;
133
134
  }
134
135
 
136
+ syncDeletedRecords(fCallback)
137
+ {
138
+ const tmpDeletedColumn = this.EntitySchema.Columns.find((c) => c.Column == 'Deleted');
139
+ if (!tmpDeletedColumn)
140
+ {
141
+ this.fable.log.info(`No Deleted column for ${this.EntitySchema.TableName}; skipping delete sync.`);
142
+ return fCallback();
143
+ }
144
+
145
+ this.fable.log.info(`Checking for deleted records on server for ${this.EntitySchema.TableName}...`);
146
+
147
+ // Get the count of deleted records from the server.
148
+ // The explicit FBV~Deleted~EQ~1 filter overrides foxhound's automatic Deleted=0 filter.
149
+ this.fable.MeadowCloneRestClient.getJSON(`${this.EntitySchema.TableName}s/Count/FilteredTo/FBV~Deleted~EQ~1`,
150
+ (pError, pResponse, pBody) =>
151
+ {
152
+ if (pError || !pBody || !pBody.hasOwnProperty('Count'))
153
+ {
154
+ this.fable.log.warn(`Could not get deleted record count for ${this.EntitySchema.TableName}; skipping delete sync.`);
155
+ return fCallback();
156
+ }
157
+
158
+ const tmpDeletedCount = pBody.Count;
159
+ if (tmpDeletedCount < 1)
160
+ {
161
+ this.fable.log.info(`No deleted records on server for ${this.EntitySchema.TableName}.`);
162
+ return fCallback();
163
+ }
164
+
165
+ this.fable.log.info(`Found ${tmpDeletedCount} deleted records on server for ${this.EntitySchema.TableName}; syncing deletions...`);
166
+
167
+ // Generate paginated URLs for deleted records
168
+ const tmpDeleteURLPartials = [];
169
+ for (let i = 0; i < tmpDeletedCount; i += this.PageSize)
170
+ {
171
+ tmpDeleteURLPartials.push(`${this.EntitySchema.TableName}s/FilteredTo/FBV~Deleted~EQ~1~FSF~${this.DefaultIdentifier}~ASC~ASC/${i}/${this.PageSize}`);
172
+ }
173
+
174
+ this.fable.Utility.eachLimit(tmpDeleteURLPartials, 1,
175
+ (pURLPartial, fPageComplete) =>
176
+ {
177
+ this.fable.MeadowCloneRestClient.getJSON(pURLPartial,
178
+ (pDownloadError, pResponse, pBody) =>
179
+ {
180
+ if (pDownloadError || !pBody || !Array.isArray(pBody) || pBody.length < 1)
181
+ {
182
+ return fPageComplete();
183
+ }
184
+
185
+ this.fable.Utility.eachLimit(pBody, 5,
186
+ (pEntityRecord, fRecordComplete) =>
187
+ {
188
+ const tmpRecordID = pEntityRecord[this.DefaultIdentifier];
189
+ if (!tmpRecordID || tmpRecordID < 1)
190
+ {
191
+ return fRecordComplete();
192
+ }
193
+
194
+ // Read local record with delete tracking disabled so we can see all records
195
+ const tmpQuery = this.Meadow.query;
196
+ tmpQuery.addFilter(this.DefaultIdentifier, tmpRecordID);
197
+ tmpQuery.setDisableDeleteTracking(true);
198
+
199
+ this.Meadow.doRead(tmpQuery,
200
+ (pReadError, pQuery, pRecord) =>
201
+ {
202
+ if (pReadError || !pRecord)
203
+ {
204
+ // Record doesn't exist locally -- create it as deleted
205
+ const tmpRecordToCommit = this.marshalRecord(pEntityRecord);
206
+
207
+ const tmpCreateQuery = this.Meadow.query.addRecord(tmpRecordToCommit);
208
+ tmpCreateQuery.setDisableAutoIdentity(true);
209
+ tmpCreateQuery.setDisableAutoDateStamp(true);
210
+ tmpCreateQuery.setDisableAutoUserStamp(true);
211
+ tmpCreateQuery.setDisableDeleteTracking(true);
212
+ tmpCreateQuery.AllowIdentityInsert = true;
213
+
214
+ this.Meadow.doCreate(tmpCreateQuery,
215
+ (pCreateError) =>
216
+ {
217
+ if (pCreateError)
218
+ {
219
+ this.log.error(`Error creating deleted record ${this.EntitySchema.TableName} ID ${tmpRecordID}: ${pCreateError}`);
220
+ }
221
+ return fRecordComplete();
222
+ });
223
+ return;
224
+ }
225
+
226
+ if (pRecord.Deleted == 1)
227
+ {
228
+ // Already marked deleted locally
229
+ return fRecordComplete();
230
+ }
231
+
232
+ // Record exists locally but is not deleted -- update it
233
+ const tmpRecordToCommit = this.marshalRecord(pEntityRecord);
234
+
235
+ const tmpUpdateQuery = this.Meadow.query.addRecord(tmpRecordToCommit);
236
+ tmpUpdateQuery.setDisableAutoIdentity(true);
237
+ tmpUpdateQuery.setDisableAutoDateStamp(true);
238
+ tmpUpdateQuery.setDisableAutoUserStamp(true);
239
+ tmpUpdateQuery.setDisableDeleteTracking(true);
240
+
241
+ this.Meadow.doUpdate(tmpUpdateQuery,
242
+ (pUpdateError) =>
243
+ {
244
+ if (pUpdateError)
245
+ {
246
+ this.log.error(`Error marking record as deleted ${this.EntitySchema.TableName} ID ${tmpRecordID}: ${pUpdateError}`);
247
+ }
248
+ return fRecordComplete();
249
+ });
250
+ });
251
+ },
252
+ (pRecordSyncError) =>
253
+ {
254
+ return fPageComplete();
255
+ });
256
+ });
257
+ },
258
+ (pDeleteSyncError) =>
259
+ {
260
+ this.fable.log.info(`Delete sync complete for ${this.EntitySchema.TableName} (${tmpDeletedCount} deleted records processed).`);
261
+ return fCallback();
262
+ });
263
+ });
264
+ }
265
+
135
266
  addSyncAnticipateEntry(tmpSyncState, tmpAnticipate)
136
267
  {
137
268
  tmpAnticipate.anticipate(
@@ -446,7 +577,11 @@ class MeadowSyncEntityOngoing extends libFableServiceProviderBase
446
577
  if (pError)
447
578
  {
448
579
  this.fable.log.error(`Error performing Update sync ${this.EntitySchema.TableName}: ${pError}`, { Error: pError });
449
- return fCallback();
580
+ }
581
+
582
+ if (this.SyncDeletedRecords)
583
+ {
584
+ return this.syncDeletedRecords(() => { return fCallback(); });
450
585
  }
451
586
 
452
587
  return fCallback();
@@ -43,6 +43,17 @@ class MeadowSync extends libFableServiceProviderBase
43
43
  this.SyncEntityOptions = JSON.parse(JSON.stringify(this.options.SyncEntityOptions));
44
44
  }
45
45
 
46
+ // When true, after syncing active records, also sync records marked Deleted=1 on the source.
47
+ this.SyncDeletedRecords = false;
48
+ if (this.fable.ProgramConfiguration.hasOwnProperty('SyncDeletedRecords'))
49
+ {
50
+ this.SyncDeletedRecords = !!this.fable.ProgramConfiguration.SyncDeletedRecords;
51
+ }
52
+ else if (this.options.hasOwnProperty('SyncDeletedRecords'))
53
+ {
54
+ this.SyncDeletedRecords = !!this.options.SyncDeletedRecords;
55
+ }
56
+
46
57
  this.MeadowSchema = false;
47
58
  this.MeadowSchemaTableList = false;
48
59
 
@@ -70,6 +81,7 @@ class MeadowSync extends libFableServiceProviderBase
70
81
  MeadowEntitySchema: tmpEntitySchema,
71
82
  ConnectionPool: this.options.ConnectionPool,
72
83
  PageSize: this.options.PageSize || 100,
84
+ SyncDeletedRecords: this.SyncDeletedRecords,
73
85
  };
74
86
 
75
87
  let tmpSyncEntity;