retold-data-service 2.0.37 → 2.0.39
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 +3 -3
- package/source/services/data-cloner/Retold-Data-Service-DataCloner.js +35 -18
- package/source/services/data-cloner/pict-app/providers/Pict-Provider-DataCloner.js +16 -0
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Connection.js +9 -0
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Export.js +8 -0
- package/source/services/data-cloner/web/data-cloner.js +28 -0
- package/source/services/data-cloner/web/data-cloner.js.map +1 -1
- package/source/services/data-cloner/web/data-cloner.min.js +1 -1
- package/source/services/data-cloner/web/data-cloner.min.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "retold-data-service",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.39",
|
|
4
4
|
"description": "Serve up a whole model!",
|
|
5
5
|
"main": "source/Retold-Data-Service.js",
|
|
6
6
|
"bin": {
|
|
@@ -73,8 +73,8 @@
|
|
|
73
73
|
"meadow": "^2.0.33",
|
|
74
74
|
"meadow-connection-mysql": "^1.0.14",
|
|
75
75
|
"meadow-endpoints": "^4.0.15",
|
|
76
|
-
"meadow-integration": "^1.0.
|
|
77
|
-
"meadow-migrationmanager": "^0.0.
|
|
76
|
+
"meadow-integration": "^1.0.32",
|
|
77
|
+
"meadow-migrationmanager": "^0.0.10",
|
|
78
78
|
"orator": "^6.0.4",
|
|
79
79
|
"orator-http-proxy": "^1.0.5",
|
|
80
80
|
"orator-serviceserver-restify": "^2.0.10",
|
|
@@ -1018,12 +1018,19 @@ class RetoldDataServiceDataCloner extends libFableServiceProviderBase
|
|
|
1018
1018
|
let tmpTracker = tmpSyncEntity.operation.progressTrackers[`FullSync-${tmpTableName}`];
|
|
1019
1019
|
if (tmpTracker)
|
|
1020
1020
|
{
|
|
1021
|
-
|
|
1021
|
+
// Clamp both to >= 0: Total can be estimated from
|
|
1022
|
+
// Server.RecordCount - Local.RecordCount, which is
|
|
1023
|
+
// negative when local has extra records (e.g. after
|
|
1024
|
+
// server-side deletes). A negative total in the
|
|
1025
|
+
// progress report is nonsensical.
|
|
1026
|
+
tmpProgress.Total = Math.max(tmpTracker.TotalCount || 0, 0);
|
|
1022
1027
|
tmpProgress.Synced = Math.max(tmpTracker.CurrentCount || 0, 0);
|
|
1023
1028
|
}
|
|
1024
1029
|
}
|
|
1025
1030
|
|
|
1026
1031
|
// Read per-record breakdown from the sync entity
|
|
1032
|
+
let tmpEntityErrors = 0;
|
|
1033
|
+
let tmpEntitySkipped = 0;
|
|
1027
1034
|
if (tmpSyncEntity && tmpSyncEntity.syncResults)
|
|
1028
1035
|
{
|
|
1029
1036
|
let tmpResults = tmpSyncEntity.syncResults;
|
|
@@ -1033,12 +1040,12 @@ class RetoldDataServiceDataCloner extends libFableServiceProviderBase
|
|
|
1033
1040
|
tmpProgress.Deleted = tmpResults.Deleted || 0;
|
|
1034
1041
|
tmpProgress.ServerTotal = tmpResults.ServerRecordCount || 0;
|
|
1035
1042
|
tmpProgress.LocalCountBefore = tmpResults.LocalRecordCount || 0;
|
|
1043
|
+
tmpEntityErrors = tmpResults.Errors || 0;
|
|
1044
|
+
tmpEntitySkipped = tmpResults.Skipped || 0;
|
|
1036
1045
|
}
|
|
1037
1046
|
|
|
1038
1047
|
let tmpRESTErrors = this._cloneState.SyncRESTErrors[tmpTableName] || 0;
|
|
1039
|
-
tmpProgress.Errors = tmpRESTErrors;
|
|
1040
|
-
|
|
1041
|
-
let tmpMissing = tmpProgress.Total - tmpProgress.Synced;
|
|
1048
|
+
tmpProgress.Errors = tmpRESTErrors + tmpEntityErrors;
|
|
1042
1049
|
|
|
1043
1050
|
if (pError)
|
|
1044
1051
|
{
|
|
@@ -1048,25 +1055,35 @@ class RetoldDataServiceDataCloner extends libFableServiceProviderBase
|
|
|
1048
1055
|
this.logSyncEvent('TableError', `Sync [${tmpTableName}] — error: ${pError}`,
|
|
1049
1056
|
{ Table: tmpTableName, Total: tmpProgress.Total, Synced: tmpProgress.Synced, Error: `${pError}` });
|
|
1050
1057
|
}
|
|
1051
|
-
else if (tmpRESTErrors > 0)
|
|
1052
|
-
{
|
|
1053
|
-
tmpProgress.Status = 'Error';
|
|
1054
|
-
tmpProgress.ErrorMessage = `${tmpRESTErrors} REST error(s) during sync`;
|
|
1055
|
-
this.fable.log.warn(`Data Cloner: Sync [${tmpTableName}] — completed with ${tmpRESTErrors} REST error(s). ${tmpProgress.Synced}/${tmpProgress.Total} records synced.`);
|
|
1056
|
-
this.logSyncEvent('TableError', `Sync [${tmpTableName}] — ${tmpRESTErrors} REST error(s).`,
|
|
1057
|
-
{ Table: tmpTableName, Total: tmpProgress.Total, Synced: tmpProgress.Synced, RESTErrors: tmpRESTErrors });
|
|
1058
|
-
}
|
|
1059
|
-
else if (tmpProgress.Total > 0 && tmpMissing > 0)
|
|
1058
|
+
else if (tmpRESTErrors > 0 || tmpEntityErrors > 0)
|
|
1060
1059
|
{
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1060
|
+
// Only treat the sync as Partial/Error when the sync
|
|
1061
|
+
// entity actually reported errors (REST fetch errors or
|
|
1062
|
+
// per-record commit errors). A count-mismatch between
|
|
1063
|
+
// estimated Total and actual Synced is not itself an
|
|
1064
|
+
// error — pre-count estimates can be stale (records
|
|
1065
|
+
// were updated rather than added, local already has
|
|
1066
|
+
// records past the server's max ID, etc.), and the
|
|
1067
|
+
// sync code correctly downloads nothing in those
|
|
1068
|
+
// cases. Reporting "N skipped" for pure estimate
|
|
1069
|
+
// drift is noisy and misleading.
|
|
1070
|
+
let tmpPartialMessage = (tmpRESTErrors > 0 && tmpEntityErrors > 0)
|
|
1071
|
+
? `${tmpRESTErrors} REST error(s) and ${tmpEntityErrors} record error(s) during sync`
|
|
1072
|
+
: (tmpRESTErrors > 0)
|
|
1073
|
+
? `${tmpRESTErrors} REST error(s) during sync`
|
|
1074
|
+
: `${tmpEntityErrors} record error(s) during sync`;
|
|
1075
|
+
tmpProgress.Status = (tmpRESTErrors > 0) ? 'Error' : 'Partial';
|
|
1076
|
+
tmpProgress.ErrorMessage = tmpPartialMessage;
|
|
1077
|
+
tmpProgress.Skipped = tmpEntitySkipped;
|
|
1078
|
+
this.fable.log.warn(`Data Cloner: Sync [${tmpTableName}] — ${tmpPartialMessage}. ${tmpProgress.Synced}/${tmpProgress.Total} records synced.`);
|
|
1079
|
+
this.logSyncEvent(tmpProgress.Status === 'Error' ? 'TableError' : 'TablePartial',
|
|
1080
|
+
`Sync [${tmpTableName}] — ${tmpPartialMessage}.`,
|
|
1081
|
+
{ Table: tmpTableName, Total: tmpProgress.Total, Synced: tmpProgress.Synced, RESTErrors: tmpRESTErrors, RecordErrors: tmpEntityErrors });
|
|
1066
1082
|
}
|
|
1067
1083
|
else
|
|
1068
1084
|
{
|
|
1069
1085
|
tmpProgress.Status = 'Complete';
|
|
1086
|
+
tmpProgress.Skipped = tmpEntitySkipped;
|
|
1070
1087
|
this.fable.log.info(`Data Cloner: Sync [${tmpTableName}] — complete. ${tmpProgress.Synced}/${tmpProgress.Total} records synced.`);
|
|
1071
1088
|
this.logSyncEvent('TableComplete', `Sync [${tmpTableName}] — complete. ${tmpProgress.Synced}/${tmpProgress.Total} records.`,
|
|
1072
1089
|
{ Table: tmpTableName, Total: tmpProgress.Total, Synced: tmpProgress.Synced });
|
|
@@ -295,6 +295,12 @@ class DataClonerProvider extends libPictProvider
|
|
|
295
295
|
{
|
|
296
296
|
document.getElementById('solrSecure').checked = tmpSolrSecure === 'true';
|
|
297
297
|
}
|
|
298
|
+
let tmpMssqlLegacyPagination = localStorage.getItem('dataCloner_mssqlLegacyPagination');
|
|
299
|
+
if (tmpMssqlLegacyPagination !== null)
|
|
300
|
+
{
|
|
301
|
+
let tmpEl = document.getElementById('mssqlLegacyPagination');
|
|
302
|
+
if (tmpEl) tmpEl.checked = tmpMssqlLegacyPagination === 'true';
|
|
303
|
+
}
|
|
298
304
|
// Restore advanced ID pagination checkbox
|
|
299
305
|
let tmpAdvancedIDPagination = localStorage.getItem('dataCloner_syncAdvancedIDPagination');
|
|
300
306
|
if (tmpAdvancedIDPagination !== null)
|
|
@@ -351,6 +357,16 @@ class DataClonerProvider extends libPictProvider
|
|
|
351
357
|
});
|
|
352
358
|
}
|
|
353
359
|
|
|
360
|
+
// Persist MSSQL legacy pagination checkbox
|
|
361
|
+
let tmpMssqlLegacyPaginationEl = document.getElementById('mssqlLegacyPagination');
|
|
362
|
+
if (tmpMssqlLegacyPaginationEl)
|
|
363
|
+
{
|
|
364
|
+
tmpMssqlLegacyPaginationEl.addEventListener('change', function()
|
|
365
|
+
{
|
|
366
|
+
localStorage.setItem('dataCloner_mssqlLegacyPagination', this.checked);
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
354
370
|
// Persist advanced ID pagination checkbox
|
|
355
371
|
let tmpAdvancedIDPaginationEl = document.getElementById('syncAdvancedIDPagination');
|
|
356
372
|
if (tmpAdvancedIDPaginationEl)
|
|
@@ -48,6 +48,11 @@ class DataClonerConnectionView extends libPictView
|
|
|
48
48
|
tmpConfig.password = document.getElementById('mssqlPassword').value;
|
|
49
49
|
tmpConfig.database = document.getElementById('mssqlDatabase').value.trim();
|
|
50
50
|
tmpConfig.connectionLimit = parseInt(document.getElementById('mssqlConnectionLimit').value, 10) || 20;
|
|
51
|
+
// Use ROW_NUMBER() pagination instead of OFFSET/FETCH for
|
|
52
|
+
// SQL Server 2008 R2 / 2012 or databases whose compatibility
|
|
53
|
+
// level is < 110 (the parser rejects OFFSET/FETCH syntax
|
|
54
|
+
// otherwise).
|
|
55
|
+
tmpConfig.LegacyPagination = document.getElementById('mssqlLegacyPagination').checked;
|
|
51
56
|
}
|
|
52
57
|
else if (tmpProvider === 'PostgreSQL')
|
|
53
58
|
{
|
|
@@ -291,6 +296,10 @@ module.exports.default_configuration =
|
|
|
291
296
|
</div>
|
|
292
297
|
<div></div>
|
|
293
298
|
</div>
|
|
299
|
+
<div style="margin-top:8px">
|
|
300
|
+
<input type="checkbox" id="mssqlLegacyPagination">
|
|
301
|
+
<label for="mssqlLegacyPagination" title="Enable for SQL Server 2008 R2 / 2012 or databases at compatibility_level < 110. Uses ROW_NUMBER() pagination instead of OFFSET/FETCH.">Legacy pagination (SQL Server < 2012 / compat level < 110)</label>
|
|
302
|
+
</div>
|
|
294
303
|
</div>
|
|
295
304
|
|
|
296
305
|
<!-- PostgreSQL Config -->
|
|
@@ -37,6 +37,10 @@ class DataClonerExportView extends libPictView
|
|
|
37
37
|
tmpDbConfig.password = document.getElementById('mssqlPassword').value;
|
|
38
38
|
tmpDbConfig.database = document.getElementById('mssqlDatabase').value.trim();
|
|
39
39
|
tmpDbConfig.connectionLimit = parseInt(document.getElementById('mssqlConnectionLimit').value, 10) || 20;
|
|
40
|
+
if (document.getElementById('mssqlLegacyPagination').checked)
|
|
41
|
+
{
|
|
42
|
+
tmpDbConfig.LegacyPagination = true;
|
|
43
|
+
}
|
|
40
44
|
}
|
|
41
45
|
else if (tmpProvider === 'PostgreSQL')
|
|
42
46
|
{
|
|
@@ -156,6 +160,10 @@ class DataClonerExportView extends libPictView
|
|
|
156
160
|
tmpConfig.Destination.MSSQL.password = document.getElementById('mssqlPassword').value || '';
|
|
157
161
|
tmpConfig.Destination.MSSQL.database = document.getElementById('mssqlDatabase').value.trim() || 'meadow';
|
|
158
162
|
tmpConfig.Destination.MSSQL.ConnectionPoolLimit = parseInt(document.getElementById('mssqlConnectionLimit').value, 10) || 20;
|
|
163
|
+
if (document.getElementById('mssqlLegacyPagination').checked)
|
|
164
|
+
{
|
|
165
|
+
tmpConfig.Destination.MSSQL.LegacyPagination = true;
|
|
166
|
+
}
|
|
159
167
|
}
|
|
160
168
|
else
|
|
161
169
|
{
|
|
@@ -5072,6 +5072,11 @@
|
|
|
5072
5072
|
if (tmpSolrSecure !== null) {
|
|
5073
5073
|
document.getElementById('solrSecure').checked = tmpSolrSecure === 'true';
|
|
5074
5074
|
}
|
|
5075
|
+
let tmpMssqlLegacyPagination = localStorage.getItem('dataCloner_mssqlLegacyPagination');
|
|
5076
|
+
if (tmpMssqlLegacyPagination !== null) {
|
|
5077
|
+
let tmpEl = document.getElementById('mssqlLegacyPagination');
|
|
5078
|
+
if (tmpEl) tmpEl.checked = tmpMssqlLegacyPagination === 'true';
|
|
5079
|
+
}
|
|
5075
5080
|
// Restore advanced ID pagination checkbox
|
|
5076
5081
|
let tmpAdvancedIDPagination = localStorage.getItem('dataCloner_syncAdvancedIDPagination');
|
|
5077
5082
|
if (tmpAdvancedIDPagination !== null) {
|
|
@@ -5119,6 +5124,14 @@
|
|
|
5119
5124
|
});
|
|
5120
5125
|
}
|
|
5121
5126
|
|
|
5127
|
+
// Persist MSSQL legacy pagination checkbox
|
|
5128
|
+
let tmpMssqlLegacyPaginationEl = document.getElementById('mssqlLegacyPagination');
|
|
5129
|
+
if (tmpMssqlLegacyPaginationEl) {
|
|
5130
|
+
tmpMssqlLegacyPaginationEl.addEventListener('change', function () {
|
|
5131
|
+
localStorage.setItem('dataCloner_mssqlLegacyPagination', this.checked);
|
|
5132
|
+
});
|
|
5133
|
+
}
|
|
5134
|
+
|
|
5122
5135
|
// Persist advanced ID pagination checkbox
|
|
5123
5136
|
let tmpAdvancedIDPaginationEl = document.getElementById('syncAdvancedIDPagination');
|
|
5124
5137
|
if (tmpAdvancedIDPaginationEl) {
|
|
@@ -5851,6 +5864,11 @@
|
|
|
5851
5864
|
tmpConfig.password = document.getElementById('mssqlPassword').value;
|
|
5852
5865
|
tmpConfig.database = document.getElementById('mssqlDatabase').value.trim();
|
|
5853
5866
|
tmpConfig.connectionLimit = parseInt(document.getElementById('mssqlConnectionLimit').value, 10) || 20;
|
|
5867
|
+
// Use ROW_NUMBER() pagination instead of OFFSET/FETCH for
|
|
5868
|
+
// SQL Server 2008 R2 / 2012 or databases whose compatibility
|
|
5869
|
+
// level is < 110 (the parser rejects OFFSET/FETCH syntax
|
|
5870
|
+
// otherwise).
|
|
5871
|
+
tmpConfig.LegacyPagination = document.getElementById('mssqlLegacyPagination').checked;
|
|
5854
5872
|
} else if (tmpProvider === 'PostgreSQL') {
|
|
5855
5873
|
tmpConfig.host = document.getElementById('postgresqlHost').value.trim() || '127.0.0.1';
|
|
5856
5874
|
tmpConfig.port = parseInt(document.getElementById('postgresqlPort').value, 10) || 5432;
|
|
@@ -6044,6 +6062,10 @@
|
|
|
6044
6062
|
</div>
|
|
6045
6063
|
<div></div>
|
|
6046
6064
|
</div>
|
|
6065
|
+
<div style="margin-top:8px">
|
|
6066
|
+
<input type="checkbox" id="mssqlLegacyPagination">
|
|
6067
|
+
<label for="mssqlLegacyPagination" title="Enable for SQL Server 2008 R2 / 2012 or databases at compatibility_level < 110. Uses ROW_NUMBER() pagination instead of OFFSET/FETCH.">Legacy pagination (SQL Server < 2012 / compat level < 110)</label>
|
|
6068
|
+
</div>
|
|
6047
6069
|
</div>
|
|
6048
6070
|
|
|
6049
6071
|
<!-- PostgreSQL Config -->
|
|
@@ -6362,6 +6384,9 @@
|
|
|
6362
6384
|
tmpDbConfig.password = document.getElementById('mssqlPassword').value;
|
|
6363
6385
|
tmpDbConfig.database = document.getElementById('mssqlDatabase').value.trim();
|
|
6364
6386
|
tmpDbConfig.connectionLimit = parseInt(document.getElementById('mssqlConnectionLimit').value, 10) || 20;
|
|
6387
|
+
if (document.getElementById('mssqlLegacyPagination').checked) {
|
|
6388
|
+
tmpDbConfig.LegacyPagination = true;
|
|
6389
|
+
}
|
|
6365
6390
|
} else if (tmpProvider === 'PostgreSQL') {
|
|
6366
6391
|
tmpDbConfig.host = document.getElementById('postgresqlHost').value.trim() || '127.0.0.1';
|
|
6367
6392
|
tmpDbConfig.port = parseInt(document.getElementById('postgresqlPort').value, 10) || 5432;
|
|
@@ -6465,6 +6490,9 @@
|
|
|
6465
6490
|
tmpConfig.Destination.MSSQL.password = document.getElementById('mssqlPassword').value || '';
|
|
6466
6491
|
tmpConfig.Destination.MSSQL.database = document.getElementById('mssqlDatabase').value.trim() || 'meadow';
|
|
6467
6492
|
tmpConfig.Destination.MSSQL.ConnectionPoolLimit = parseInt(document.getElementById('mssqlConnectionLimit').value, 10) || 20;
|
|
6493
|
+
if (document.getElementById('mssqlLegacyPagination').checked) {
|
|
6494
|
+
tmpConfig.Destination.MSSQL.LegacyPagination = true;
|
|
6495
|
+
}
|
|
6468
6496
|
} else {
|
|
6469
6497
|
// Default to MySQL placeholder for unsupported providers
|
|
6470
6498
|
tmpConfig.Destination.Provider = 'MySQL';
|