meadow-integration 1.0.28 → 1.0.30
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.
|
|
3
|
+
"version": "1.0.30",
|
|
4
4
|
"description": "Meadow Data Integration",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mdwint": "source/cli/Meadow-Integration-CLI-Run.js"
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
]
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"fable": "^3.1.
|
|
43
|
+
"fable": "^3.1.70",
|
|
44
44
|
"fable-serviceproviderbase": "^3.0.19",
|
|
45
45
|
"fast-xml-parser": "^4.4.1",
|
|
46
46
|
"meadow": "^2.0.33",
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
2
|
-
const Http = require('http');
|
|
3
|
-
const Https = require('https');
|
|
4
2
|
|
|
5
3
|
const defaultRestClientOptions = (
|
|
6
4
|
{
|
|
@@ -41,30 +39,22 @@ class MeadowCloneRestClient extends libFableServiceProviderBase
|
|
|
41
39
|
this._SessionToken = this.options.SessionToken;
|
|
42
40
|
}
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
this.
|
|
46
|
-
|
|
47
|
-
this.requestTimeout = this.options.RequestTimeout;
|
|
48
|
-
this.maxRequestTimeout = this.options.MaxRequestTimeout;
|
|
42
|
+
// Fable settings override instance options for timeouts
|
|
43
|
+
this.requestTimeout = this.fable.settings.RestClientRequestTimeout || this.options.RequestTimeout;
|
|
44
|
+
this.maxRequestTimeout = this.fable.settings.RestClientMaxRequestTimeout || this.options.MaxRequestTimeout;
|
|
49
45
|
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
this.restClient.prepareRequestOptions = (pOptions) =>
|
|
64
|
-
{
|
|
65
|
-
pOptions.agent = this.agent;
|
|
66
|
-
return pOptions;
|
|
67
|
-
};
|
|
46
|
+
// When keep-alive is enabled via fable settings (RestClientKeepAlive), use a
|
|
47
|
+
// socket timeout based on the longer of the two request timeouts so that
|
|
48
|
+
// slow MAX queries don't get killed at the socket level.
|
|
49
|
+
let tmpRestClientOptions = (
|
|
50
|
+
{
|
|
51
|
+
KeepAliveAgentOptions:
|
|
52
|
+
{
|
|
53
|
+
timeout: Math.max(this.requestTimeout, this.maxRequestTimeout)
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
this.restClient = this.fable.serviceManager.instantiateServiceProvider('RestClient', tmpRestClientOptions, 'MeadowCloneRestClient-RestClient');
|
|
57
|
+
this.cache = {};
|
|
68
58
|
}
|
|
69
59
|
|
|
70
60
|
prepareRequestOptions(pOptions)
|
|
@@ -174,119 +174,123 @@ class MeadowSyncEntityOngoingEventualConsistency extends libMeadowSyncEntityOngo
|
|
|
174
174
|
let tmpDeleteCap = (this.MaxRecordsPerEntity > 0)
|
|
175
175
|
? Math.min(tmpDeletedCount, this.MaxRecordsPerEntity)
|
|
176
176
|
: tmpDeletedCount;
|
|
177
|
-
const tmpDeleteURLPartials = [];
|
|
178
|
-
for (let i = 0; i < tmpDeleteCap; i += this.PageSize)
|
|
179
|
-
{
|
|
180
|
-
tmpDeleteURLPartials.push(this._appendDeletedQueryString(`${this.EntitySchema.TableName}s/FilteredTo/FBV~Deleted~EQ~1~FSF~${this.DefaultIdentifier}~ASC~ASC/${i}/${this.PageSize}`));
|
|
181
|
-
}
|
|
182
177
|
|
|
183
178
|
const tmpStartTime = Date.now();
|
|
184
179
|
let tmpProcessed = 0;
|
|
185
|
-
let
|
|
180
|
+
let tmpOffset = 0;
|
|
181
|
+
|
|
182
|
+
// Fetch deleted record pages one at a time using a recursive
|
|
183
|
+
// fetcher instead of pre-generating all URL partials. With
|
|
184
|
+
// millions of deleted records and a short time budget, the old
|
|
185
|
+
// eachLimit approach generated hundreds of thousands of partials
|
|
186
|
+
// and then had to skip them all when the budget expired.
|
|
187
|
+
const fFetchDeletedPage = () =>
|
|
188
|
+
{
|
|
189
|
+
// Time budget check — stop immediately
|
|
190
|
+
if (Date.now() - tmpStartTime >= this.BackSyncTimeLimit)
|
|
191
|
+
{
|
|
192
|
+
const tmpElapsed = Date.now() - tmpStartTime;
|
|
193
|
+
this.fable.log.info(`Delete sync time budget exhausted for ${this.EntitySchema.TableName} after ${tmpElapsed}ms (${tmpProcessed} of ${tmpDeletedCount} deleted records processed).`);
|
|
194
|
+
return fCallback();
|
|
195
|
+
}
|
|
186
196
|
|
|
187
|
-
|
|
188
|
-
(
|
|
197
|
+
// All pages processed
|
|
198
|
+
if (tmpOffset >= tmpDeleteCap)
|
|
189
199
|
{
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
200
|
+
const tmpElapsed = Date.now() - tmpStartTime;
|
|
201
|
+
this.fable.log.info(`Delete sync complete for ${this.EntitySchema.TableName} (${tmpProcessed} of ${tmpDeletedCount} deleted records processed in ${tmpElapsed}ms).`);
|
|
202
|
+
return fCallback();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const tmpURL = this._appendDeletedQueryString(`${this.EntitySchema.TableName}s/FilteredTo/FBV~Deleted~EQ~1~FSF~${this.DefaultIdentifier}~ASC~ASC/${tmpOffset}/${this.PageSize}`);
|
|
206
|
+
tmpOffset += this.PageSize;
|
|
196
207
|
|
|
197
|
-
|
|
198
|
-
|
|
208
|
+
this.fable.MeadowCloneRestClient.getJSON(tmpURL,
|
|
209
|
+
(pDownloadError, pResponse, pBody) =>
|
|
210
|
+
{
|
|
211
|
+
if (pDownloadError || !pBody || !Array.isArray(pBody) || pBody.length < 1)
|
|
199
212
|
{
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
213
|
+
// Empty page or error — we've reached the end
|
|
214
|
+
const tmpElapsed = Date.now() - tmpStartTime;
|
|
215
|
+
this.fable.log.info(`Delete sync complete for ${this.EntitySchema.TableName} (${tmpProcessed} of ${tmpDeletedCount} deleted records processed in ${tmpElapsed}ms).`);
|
|
216
|
+
return fCallback();
|
|
217
|
+
}
|
|
204
218
|
|
|
205
|
-
|
|
206
|
-
|
|
219
|
+
this.fable.Utility.eachLimit(pBody, 5,
|
|
220
|
+
(pEntityRecord, fRecordComplete) =>
|
|
221
|
+
{
|
|
222
|
+
const tmpRecordID = pEntityRecord[this.DefaultIdentifier];
|
|
223
|
+
if (!tmpRecordID || tmpRecordID < 1)
|
|
207
224
|
{
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
{
|
|
211
|
-
return setImmediate(fRecordComplete);
|
|
212
|
-
}
|
|
225
|
+
return setImmediate(fRecordComplete);
|
|
226
|
+
}
|
|
213
227
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
228
|
+
const tmpQuery = this.Meadow.query;
|
|
229
|
+
tmpQuery.addFilter(this.DefaultIdentifier, tmpRecordID);
|
|
230
|
+
tmpQuery.setDisableDeleteTracking(true);
|
|
217
231
|
|
|
218
|
-
|
|
219
|
-
|
|
232
|
+
this.Meadow.doRead(tmpQuery,
|
|
233
|
+
(pReadError, pQuery, pRecord) =>
|
|
234
|
+
{
|
|
235
|
+
if (pReadError || !pRecord)
|
|
220
236
|
{
|
|
221
|
-
if (pReadError || !pRecord)
|
|
222
|
-
{
|
|
223
|
-
const tmpRecordToCommit = this.marshalRecord(pEntityRecord);
|
|
224
|
-
|
|
225
|
-
const tmpCreateQuery = this.Meadow.query.addRecord(tmpRecordToCommit);
|
|
226
|
-
tmpCreateQuery.setDisableAutoIdentity(true);
|
|
227
|
-
tmpCreateQuery.setDisableAutoDateStamp(true);
|
|
228
|
-
tmpCreateQuery.setDisableAutoUserStamp(true);
|
|
229
|
-
tmpCreateQuery.setDisableDeleteTracking(true);
|
|
230
|
-
tmpCreateQuery.AllowIdentityInsert = true;
|
|
231
|
-
|
|
232
|
-
this.Meadow.doCreate(tmpCreateQuery,
|
|
233
|
-
(pCreateError) =>
|
|
234
|
-
{
|
|
235
|
-
if (pCreateError)
|
|
236
|
-
{
|
|
237
|
-
this.log.error(`Error creating deleted record ${this.EntitySchema.TableName} ID ${tmpRecordID}: ${pCreateError}`);
|
|
238
|
-
}
|
|
239
|
-
tmpProcessed++;
|
|
240
|
-
return setImmediate(fRecordComplete);
|
|
241
|
-
});
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (pRecord.Deleted == 1)
|
|
246
|
-
{
|
|
247
|
-
tmpProcessed++;
|
|
248
|
-
return setImmediate(fRecordComplete);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
237
|
const tmpRecordToCommit = this.marshalRecord(pEntityRecord);
|
|
252
238
|
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
239
|
+
const tmpCreateQuery = this.Meadow.query.addRecord(tmpRecordToCommit);
|
|
240
|
+
tmpCreateQuery.setDisableAutoIdentity(true);
|
|
241
|
+
tmpCreateQuery.setDisableAutoDateStamp(true);
|
|
242
|
+
tmpCreateQuery.setDisableAutoUserStamp(true);
|
|
243
|
+
tmpCreateQuery.setDisableDeleteTracking(true);
|
|
244
|
+
tmpCreateQuery.AllowIdentityInsert = true;
|
|
258
245
|
|
|
259
|
-
this.Meadow.
|
|
260
|
-
(
|
|
246
|
+
this.Meadow.doCreate(tmpCreateQuery,
|
|
247
|
+
(pCreateError) =>
|
|
261
248
|
{
|
|
262
|
-
if (
|
|
249
|
+
if (pCreateError)
|
|
263
250
|
{
|
|
264
|
-
this.log.error(`Error
|
|
251
|
+
this.log.error(`Error creating deleted record ${this.EntitySchema.TableName} ID ${tmpRecordID}: ${pCreateError}`);
|
|
265
252
|
}
|
|
266
253
|
tmpProcessed++;
|
|
267
254
|
return setImmediate(fRecordComplete);
|
|
268
255
|
});
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (pRecord.Deleted == 1)
|
|
260
|
+
{
|
|
261
|
+
tmpProcessed++;
|
|
262
|
+
return setImmediate(fRecordComplete);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const tmpRecordToCommit = this.marshalRecord(pEntityRecord);
|
|
266
|
+
|
|
267
|
+
const tmpUpdateQuery = this.Meadow.query.addRecord(tmpRecordToCommit);
|
|
268
|
+
tmpUpdateQuery.setDisableAutoIdentity(true);
|
|
269
|
+
tmpUpdateQuery.setDisableAutoDateStamp(true);
|
|
270
|
+
tmpUpdateQuery.setDisableAutoUserStamp(true);
|
|
271
|
+
tmpUpdateQuery.setDisableDeleteTracking(true);
|
|
272
|
+
|
|
273
|
+
this.Meadow.doUpdate(tmpUpdateQuery,
|
|
274
|
+
(pUpdateError) =>
|
|
275
|
+
{
|
|
276
|
+
if (pUpdateError)
|
|
277
|
+
{
|
|
278
|
+
this.log.error(`Error marking record as deleted ${this.EntitySchema.TableName} ID ${tmpRecordID}: ${pUpdateError}`);
|
|
279
|
+
}
|
|
280
|
+
tmpProcessed++;
|
|
281
|
+
return setImmediate(fRecordComplete);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
},
|
|
285
|
+
(pRecordSyncError) =>
|
|
286
|
+
{
|
|
287
|
+
// Page complete — fetch next page
|
|
288
|
+
return setImmediate(fFetchDeletedPage);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
fFetchDeletedPage();
|
|
290
294
|
});
|
|
291
295
|
}
|
|
292
296
|
|