retold-data-service 2.0.42 → 2.1.0
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 +15 -13
- package/source/services/data-cloner/DataCloner-Command-Connection.js +41 -1
- package/source/services/data-cloner/pict-app/Pict-Application-DataCloner.js +50 -16
- package/source/services/data-cloner/pict-app/providers/Pict-Provider-DataCloner.js +185 -96
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Connection.js +181 -383
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Export.js +34 -57
- package/source/services/data-cloner/web/data-cloner.js +1116 -530
- 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.1.0",
|
|
4
4
|
"description": "Serve up a whole model!",
|
|
5
5
|
"main": "source/Retold-Data-Service.js",
|
|
6
6
|
"bin": {
|
|
@@ -59,27 +59,29 @@
|
|
|
59
59
|
},
|
|
60
60
|
"homepage": "https://github.com/stevenvelozo/retold-data-service",
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"meadow-connection-sqlite": "^1.0.
|
|
62
|
+
"meadow-connection-sqlite": "^1.0.19",
|
|
63
63
|
"pict-docuserve": "^0.1.5",
|
|
64
64
|
"puppeteer": "^24.40.0",
|
|
65
|
-
"quackage": "^1.1.
|
|
65
|
+
"quackage": "^1.1.2",
|
|
66
66
|
"stricture": "^4.0.2",
|
|
67
67
|
"supertest": "^7.2.2"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"bibliograph": "^0.1.
|
|
71
|
-
"fable": "^3.1.
|
|
70
|
+
"bibliograph": "^0.1.6",
|
|
71
|
+
"fable": "^3.1.71",
|
|
72
72
|
"fable-serviceproviderbase": "^3.0.19",
|
|
73
|
-
"meadow": "^2.0.
|
|
74
|
-
"meadow-connection-
|
|
75
|
-
"
|
|
76
|
-
"meadow-
|
|
77
|
-
"meadow-
|
|
78
|
-
"
|
|
73
|
+
"meadow": "^2.0.37",
|
|
74
|
+
"meadow-connection-manager": "^1.1.0",
|
|
75
|
+
"pict-section-connection-form": "^0.0.1",
|
|
76
|
+
"meadow-connection-mysql": "^1.0.18",
|
|
77
|
+
"meadow-endpoints": "^4.0.17",
|
|
78
|
+
"meadow-integration": "^1.0.38",
|
|
79
|
+
"meadow-migrationmanager": "^0.0.14",
|
|
80
|
+
"orator": "^6.1.0",
|
|
79
81
|
"orator-http-proxy": "^1.0.5",
|
|
80
82
|
"orator-serviceserver-restify": "^2.0.10",
|
|
81
|
-
"orator-static-server": "^2.
|
|
82
|
-
"pict": "^1.0.
|
|
83
|
+
"orator-static-server": "^2.1.3",
|
|
84
|
+
"pict": "^1.0.365",
|
|
83
85
|
"pict-section-histogram": "^1.0.0",
|
|
84
86
|
"pict-sessionmanager": "^1.0.2",
|
|
85
87
|
"stricture": "^4.0.2"
|
|
@@ -2,17 +2,57 @@
|
|
|
2
2
|
* DataCloner Connection Management Routes
|
|
3
3
|
*
|
|
4
4
|
* Registers /clone/connection/* endpoints for managing the local database
|
|
5
|
-
* connection (status, configure, test).
|
|
5
|
+
* connection (status, configure, test, schemas).
|
|
6
6
|
*
|
|
7
7
|
* @param {Object} pDataClonerService - The RetoldDataServiceDataCloner instance
|
|
8
8
|
* @param {Object} pOratorServiceServer - The Orator ServiceServer instance
|
|
9
9
|
*/
|
|
10
|
+
const libMeadowConnectionManager = require('meadow-connection-manager');
|
|
11
|
+
|
|
10
12
|
module.exports = (pDataClonerService, pOratorServiceServer) =>
|
|
11
13
|
{
|
|
12
14
|
let tmpFable = pDataClonerService.fable;
|
|
13
15
|
let tmpCloneState = pDataClonerService.cloneState;
|
|
14
16
|
let tmpPrefix = pDataClonerService.routePrefix;
|
|
15
17
|
|
|
18
|
+
// MCM is the canonical aggregator for connection-form schemas; the
|
|
19
|
+
// DataCloner browser UI fetches /clone/connection/schemas and renders
|
|
20
|
+
// its provider form straight off the result, instead of carrying
|
|
21
|
+
// per-provider HTML in the bundle. Cached on the service so we don't
|
|
22
|
+
// re-walk the filesystem on every poll.
|
|
23
|
+
let tmpSchemaMCM = null;
|
|
24
|
+
let tmpCachedSchemas = null;
|
|
25
|
+
let tmpGetSchemas = () =>
|
|
26
|
+
{
|
|
27
|
+
if (tmpCachedSchemas) { return tmpCachedSchemas; }
|
|
28
|
+
if (!tmpSchemaMCM)
|
|
29
|
+
{
|
|
30
|
+
tmpSchemaMCM = new libMeadowConnectionManager(tmpFable, {}, 'datacloner-mcm-formschemas');
|
|
31
|
+
}
|
|
32
|
+
// Defensive: an older MCM (< 1.1.0) won't have the aggregator
|
|
33
|
+
// method. Surface an empty array rather than throwing so the
|
|
34
|
+
// UI can fall back to a "schemas unavailable" message.
|
|
35
|
+
if (typeof(tmpSchemaMCM.getAllProviderFormSchemas) !== 'function')
|
|
36
|
+
{
|
|
37
|
+
tmpFable.log.warn('DataCloner: meadow-connection-manager is older than 1.1.0; connection form schemas are not available. Run npm update meadow-connection-manager.');
|
|
38
|
+
tmpCachedSchemas = [];
|
|
39
|
+
return tmpCachedSchemas;
|
|
40
|
+
}
|
|
41
|
+
tmpCachedSchemas = tmpSchemaMCM.getAllProviderFormSchemas();
|
|
42
|
+
return tmpCachedSchemas;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// GET /clone/connection/schemas
|
|
46
|
+
// Returns the form schemas for every provider whose module is
|
|
47
|
+
// installed in the host environment. Drives the connection
|
|
48
|
+
// section's UI (provider picker + per-provider field block).
|
|
49
|
+
pOratorServiceServer.get(`${tmpPrefix}/connection/schemas`,
|
|
50
|
+
(pRequest, pResponse, fNext) =>
|
|
51
|
+
{
|
|
52
|
+
pResponse.send(200, { Schemas: tmpGetSchemas() });
|
|
53
|
+
return fNext();
|
|
54
|
+
});
|
|
55
|
+
|
|
16
56
|
// GET /clone/connection/status
|
|
17
57
|
pOratorServiceServer.get(`${tmpPrefix}/connection/status`,
|
|
18
58
|
(pRequest, pResponse, fNext) =>
|
|
@@ -11,6 +11,7 @@ const libViewSync = require('./views/PictView-DataCloner-Sync.js');
|
|
|
11
11
|
const libViewExport = require('./views/PictView-DataCloner-Export.js');
|
|
12
12
|
const libViewViewData = require('./views/PictView-DataCloner-ViewData.js');
|
|
13
13
|
const libViewHistogram = require('pict-section-histogram');
|
|
14
|
+
const libViewConnectionForm = require('pict-section-connection-form');
|
|
14
15
|
|
|
15
16
|
class DataClonerApplication extends libPictApplication
|
|
16
17
|
{
|
|
@@ -45,11 +46,35 @@ class DataClonerApplication extends libPictApplication
|
|
|
45
46
|
BarColor: '#4a90d9',
|
|
46
47
|
Bins: []
|
|
47
48
|
}, libViewHistogram);
|
|
49
|
+
|
|
50
|
+
// Shared schema-driven connection form. Renders into the slot
|
|
51
|
+
// the DataCloner-Connection accordion shell exposes; the
|
|
52
|
+
// provider's bootstrapConnectionSchemas() pumps the schemas in
|
|
53
|
+
// once the host's /clone/connection/schemas endpoint responds.
|
|
54
|
+
this.pict.addView('PictSection-ConnectionForm',
|
|
55
|
+
Object.assign({}, libViewConnectionForm.default_configuration,
|
|
56
|
+
{
|
|
57
|
+
ContainerSelector: '#DataCloner-Connection-FormSlot',
|
|
58
|
+
DefaultDestinationAddress: '#DataCloner-Connection-FormSlot',
|
|
59
|
+
SchemasAddress: 'AppData.DataCloner.Connection.Schemas',
|
|
60
|
+
ActiveAddress: 'AppData.DataCloner.Connection.ActiveProvider',
|
|
61
|
+
FieldIDPrefix: 'datacloner-conn'
|
|
62
|
+
}), libViewConnectionForm);
|
|
48
63
|
}
|
|
49
64
|
|
|
50
65
|
onAfterInitializeAsync(fCallback)
|
|
51
66
|
{
|
|
52
|
-
// Centralized state (replaces global variables)
|
|
67
|
+
// Centralized state (replaces global variables).
|
|
68
|
+
//
|
|
69
|
+
// PersistFields covers the static, non-connection inputs only.
|
|
70
|
+
// Connection-section fields (provider picker + per-provider
|
|
71
|
+
// inputs) are schema-driven now: their DOM ids and
|
|
72
|
+
// localStorage keys are derived at runtime from the host's
|
|
73
|
+
// /clone/connection/schemas response and persistence is hooked
|
|
74
|
+
// up by Pict-Provider-DataCloner#bootstrapConnectionSchemas
|
|
75
|
+
// after the schema-driven Connection view re-renders. See
|
|
76
|
+
// PictView-DataCloner-Connection.js for the field-id
|
|
77
|
+
// convention.
|
|
53
78
|
this.pict.AppData.DataCloner =
|
|
54
79
|
{
|
|
55
80
|
FetchedTables: [],
|
|
@@ -67,30 +92,32 @@ class DataClonerApplication extends libPictApplication
|
|
|
67
92
|
'serverURL', 'authMethod', 'authURI', 'checkURI',
|
|
68
93
|
'cookieName', 'cookieValueAddr', 'cookieValueTemplate', 'loginMarker',
|
|
69
94
|
'userName', 'password', 'schemaURL', 'pageSize', 'dateTimePrecisionMS',
|
|
70
|
-
'connProvider', 'sqliteFilePath',
|
|
71
|
-
'mysqlServer', 'mysqlPort', 'mysqlUser', 'mysqlPassword', 'mysqlDatabase', 'mysqlConnectionLimit',
|
|
72
|
-
'mssqlServer', 'mssqlPort', 'mssqlUser', 'mssqlPassword', 'mssqlDatabase', 'mssqlConnectionLimit',
|
|
73
|
-
'mssqlRequestTimeoutSec', 'mssqlConnectionTimeoutSec',
|
|
74
|
-
'mssqlConnectMaxAttempts', 'mssqlDDLMaxAttempts',
|
|
75
|
-
'mssqlRetryInitialDelaySec', 'mssqlRetryMaxDelaySec',
|
|
76
|
-
'postgresqlHost', 'postgresqlPort', 'postgresqlUser', 'postgresqlPassword', 'postgresqlDatabase', 'postgresqlConnectionLimit',
|
|
77
|
-
'solrHost', 'solrPort', 'solrCore', 'solrPath',
|
|
78
|
-
'mongodbHost', 'mongodbPort', 'mongodbUser', 'mongodbPassword', 'mongodbDatabase', 'mongodbConnectionLimit',
|
|
79
|
-
'rocksdbFolder',
|
|
80
|
-
'bibliographFolder',
|
|
81
95
|
'syncMaxRecords'
|
|
82
|
-
]
|
|
96
|
+
],
|
|
97
|
+
// Connection state — populated by bootstrapConnectionSchemas().
|
|
98
|
+
// Initialized empty here so the Connection view's first
|
|
99
|
+
// onBeforeRender finds a valid (if empty) shape.
|
|
100
|
+
Connection:
|
|
101
|
+
{
|
|
102
|
+
Schemas: [],
|
|
103
|
+
ActiveProvider: '',
|
|
104
|
+
ProviderOptions: [],
|
|
105
|
+
ProviderForms: [],
|
|
106
|
+
NoSchemasSlot: [{}],
|
|
107
|
+
PreviewText: 'Loading providers…'
|
|
108
|
+
}
|
|
83
109
|
};
|
|
84
110
|
|
|
85
111
|
// Make pict available for inline onclick handlers
|
|
86
112
|
window.pict = this.pict;
|
|
87
113
|
|
|
88
|
-
// Render layout (which chains child view renders via onAfterRender)
|
|
114
|
+
// Render layout (which chains child view renders via onAfterRender).
|
|
115
|
+
// The Connection view renders an empty shell here — the schemas
|
|
116
|
+
// arrive asynchronously and trigger a re-render once they land.
|
|
89
117
|
this.pict.views['DataCloner-Layout'].render();
|
|
90
118
|
|
|
91
|
-
// Post-render initialization
|
|
119
|
+
// Post-render initialization for the static (non-connection) UI.
|
|
92
120
|
this.pict.providers.DataCloner.initPersistence();
|
|
93
|
-
this.pict.views['DataCloner-Connection'].onProviderChange();
|
|
94
121
|
this.pict.providers.DataCloner.restoreDeployedTables();
|
|
95
122
|
this.pict.providers.DataCloner.startLiveStatusPolling();
|
|
96
123
|
this.pict.providers.DataCloner.initAccordionPreviews();
|
|
@@ -98,6 +125,13 @@ class DataClonerApplication extends libPictApplication
|
|
|
98
125
|
this.pict.views['DataCloner-Layout'].collapseAllSections();
|
|
99
126
|
this.pict.providers.DataCloner.initAutoProcess();
|
|
100
127
|
|
|
128
|
+
// Async: fetch the host's connection-form schemas and re-render
|
|
129
|
+
// the Connection section. bootstrapConnectionSchemas restores
|
|
130
|
+
// localStorage values + hooks save listeners once the new DOM
|
|
131
|
+
// is in place, then invokes onProviderChange() to surface the
|
|
132
|
+
// active provider's form.
|
|
133
|
+
this.pict.providers.DataCloner.bootstrapConnectionSchemas(function () { /* fire-and-forget */ });
|
|
134
|
+
|
|
101
135
|
return fCallback();
|
|
102
136
|
}
|
|
103
137
|
}
|
|
@@ -76,60 +76,25 @@ class DataClonerProvider extends libPictProvider
|
|
|
76
76
|
|
|
77
77
|
updateAllPreviews()
|
|
78
78
|
{
|
|
79
|
-
// Section 1 — Database Connection
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
let tmpHost = document.getElementById('mysqlServer').value || '127.0.0.1';
|
|
92
|
-
let tmpPort = document.getElementById('mysqlPort').value || '3306';
|
|
93
|
-
let tmpUser = document.getElementById('mysqlUser').value || 'root';
|
|
94
|
-
tmpPreview1 = 'MySQL on ' + tmpHost + ':' + tmpPort + ' as ' + tmpUser;
|
|
95
|
-
}
|
|
96
|
-
else if (tmpProvider === 'MSSQL')
|
|
97
|
-
{
|
|
98
|
-
let tmpHost = document.getElementById('mssqlServer').value || '127.0.0.1';
|
|
99
|
-
let tmpPort = document.getElementById('mssqlPort').value || '1433';
|
|
100
|
-
let tmpUser = document.getElementById('mssqlUser').value || 'sa';
|
|
101
|
-
tmpPreview1 = 'MSSQL on ' + tmpHost + ':' + tmpPort + ' as ' + tmpUser;
|
|
102
|
-
}
|
|
103
|
-
else if (tmpProvider === 'PostgreSQL')
|
|
104
|
-
{
|
|
105
|
-
let tmpHost = document.getElementById('postgresqlHost').value || '127.0.0.1';
|
|
106
|
-
let tmpPort = document.getElementById('postgresqlPort').value || '5432';
|
|
107
|
-
let tmpUser = document.getElementById('postgresqlUser').value || 'postgres';
|
|
108
|
-
tmpPreview1 = 'PostgreSQL on ' + tmpHost + ':' + tmpPort + ' as ' + tmpUser;
|
|
109
|
-
}
|
|
110
|
-
else if (tmpProvider === 'MongoDB')
|
|
111
|
-
{
|
|
112
|
-
let tmpHost = document.getElementById('mongodbHost').value || '127.0.0.1';
|
|
113
|
-
let tmpPort = document.getElementById('mongodbPort').value || '27017';
|
|
114
|
-
tmpPreview1 = 'MongoDB on ' + tmpHost + ':' + tmpPort;
|
|
115
|
-
}
|
|
116
|
-
else if (tmpProvider === 'Solr')
|
|
117
|
-
{
|
|
118
|
-
let tmpHost = document.getElementById('solrHost').value || '127.0.0.1';
|
|
119
|
-
let tmpPort = document.getElementById('solrPort').value || '8983';
|
|
120
|
-
tmpPreview1 = 'Solr on ' + tmpHost + ':' + tmpPort;
|
|
79
|
+
// Section 1 — Database Connection (schema-driven; the
|
|
80
|
+
// Connection view owns the heuristic that turns the active
|
|
81
|
+
// schema's field values into preview text). The view's
|
|
82
|
+
// _buildPreviewText reads live DOM values for the active
|
|
83
|
+
// provider and falls back to schema Defaults if a field's
|
|
84
|
+
// element doesn't exist yet.
|
|
85
|
+
let tmpConnView = this.pict.views['DataCloner-Connection'];
|
|
86
|
+
let tmpConnState = (this.pict.AppData.DataCloner && this.pict.AppData.DataCloner.Connection) || null;
|
|
87
|
+
let tmpPreview1Text;
|
|
88
|
+
if (tmpConnView && tmpConnState && (tmpConnState.Schemas || []).length > 0)
|
|
89
|
+
{
|
|
90
|
+
tmpPreview1Text = tmpConnView._buildPreviewText(tmpConnState);
|
|
121
91
|
}
|
|
122
|
-
else
|
|
123
|
-
{
|
|
124
|
-
let tmpFolder = document.getElementById('rocksdbFolder').value || '~/headlight-liveconnect-local/rocksdb';
|
|
125
|
-
tmpPreview1 = 'RocksDB at ' + tmpFolder;
|
|
126
|
-
}
|
|
127
|
-
else if (tmpProvider === 'Bibliograph')
|
|
92
|
+
else
|
|
128
93
|
{
|
|
129
|
-
|
|
130
|
-
tmpPreview1 = 'Bibliograph at ' + tmpFolder;
|
|
94
|
+
tmpPreview1Text = (tmpConnState && tmpConnState.PreviewText) || 'Loading providers…';
|
|
131
95
|
}
|
|
132
|
-
document.getElementById('preview1')
|
|
96
|
+
let tmpPreview1El = document.getElementById('preview1');
|
|
97
|
+
if (tmpPreview1El) { tmpPreview1El.textContent = tmpPreview1Text; }
|
|
133
98
|
|
|
134
99
|
// Section 2 — Remote Session
|
|
135
100
|
let tmpServerURL = document.getElementById('serverURL').value;
|
|
@@ -210,14 +175,12 @@ class DataClonerProvider extends libPictProvider
|
|
|
210
175
|
{
|
|
211
176
|
let tmpSelf = this;
|
|
212
177
|
|
|
178
|
+
// Static (non-connection) fields that drive accordion previews.
|
|
179
|
+
// Connection-section fields hook updateAllPreviews via
|
|
180
|
+
// _persistConnectionFields() once schemas load — see
|
|
181
|
+
// bootstrapConnectionSchemas().
|
|
213
182
|
let tmpPreviewFields = [
|
|
214
|
-
'connProvider',
|
|
215
|
-
'mysqlServer', 'mysqlPort', 'mysqlUser',
|
|
216
|
-
'mssqlServer', 'mssqlPort', 'mssqlUser',
|
|
217
|
-
'postgresqlHost', 'postgresqlPort', 'postgresqlUser',
|
|
218
|
-
'mongodbHost', 'mongodbPort',
|
|
219
|
-
'solrHost', 'solrPort',
|
|
220
|
-
'rocksdbFolder', 'bibliographFolder',
|
|
183
|
+
'connProvider',
|
|
221
184
|
'serverURL', 'userName',
|
|
222
185
|
'schemaURL',
|
|
223
186
|
'pageSize', 'dateTimePrecisionMS',
|
|
@@ -258,7 +221,17 @@ class DataClonerProvider extends libPictProvider
|
|
|
258
221
|
saveField(pFieldId)
|
|
259
222
|
{
|
|
260
223
|
let tmpEl = document.getElementById(pFieldId);
|
|
261
|
-
if (tmpEl)
|
|
224
|
+
if (!tmpEl) { return; }
|
|
225
|
+
// Checkboxes persist .checked, everything else persists .value.
|
|
226
|
+
// Older code special-cased a small set of checkbox ids (solrSecure,
|
|
227
|
+
// mssqlLegacyPagination, etc.); the schema-driven path no longer
|
|
228
|
+
// needs those — the checkbox handler in bootstrapConnectionSchemas
|
|
229
|
+
// stores 'true' / 'false' which restore picks up below.
|
|
230
|
+
if (tmpEl.type === 'checkbox')
|
|
231
|
+
{
|
|
232
|
+
localStorage.setItem('dataCloner_' + pFieldId, tmpEl.checked ? 'true' : 'false');
|
|
233
|
+
}
|
|
234
|
+
else
|
|
262
235
|
{
|
|
263
236
|
localStorage.setItem('dataCloner_' + pFieldId, tmpEl.value);
|
|
264
237
|
}
|
|
@@ -274,38 +247,40 @@ class DataClonerProvider extends libPictProvider
|
|
|
274
247
|
if (tmpSaved !== null)
|
|
275
248
|
{
|
|
276
249
|
let tmpEl = document.getElementById(tmpId);
|
|
277
|
-
if (tmpEl)
|
|
250
|
+
if (tmpEl)
|
|
251
|
+
{
|
|
252
|
+
if (tmpEl.type === 'checkbox')
|
|
253
|
+
{
|
|
254
|
+
tmpEl.checked = (tmpSaved === 'true');
|
|
255
|
+
}
|
|
256
|
+
else
|
|
257
|
+
{
|
|
258
|
+
tmpEl.value = tmpSaved;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
278
261
|
}
|
|
279
262
|
}
|
|
280
263
|
|
|
281
|
-
// Restore checkbox state
|
|
264
|
+
// Restore checkbox state for non-connection checkboxes that
|
|
265
|
+
// aren't in PersistFields (these all live outside the schema-
|
|
266
|
+
// driven Connection section, so they stay hardcoded).
|
|
282
267
|
let tmpSyncDeleted = localStorage.getItem('dataCloner_syncDeletedRecords');
|
|
283
268
|
if (tmpSyncDeleted !== null)
|
|
284
269
|
{
|
|
285
|
-
document.getElementById('syncDeletedRecords')
|
|
270
|
+
let tmpEl = document.getElementById('syncDeletedRecords');
|
|
271
|
+
if (tmpEl) tmpEl.checked = tmpSyncDeleted === 'true';
|
|
286
272
|
}
|
|
287
|
-
// Restore sync mode
|
|
288
273
|
let tmpSyncMode = localStorage.getItem('dataCloner_syncMode');
|
|
289
274
|
if (tmpSyncMode === 'Ongoing')
|
|
290
275
|
{
|
|
291
|
-
document.getElementById('syncModeOngoing')
|
|
292
|
-
|
|
293
|
-
let tmpSolrSecure = localStorage.getItem('dataCloner_solrSecure');
|
|
294
|
-
if (tmpSolrSecure !== null)
|
|
295
|
-
{
|
|
296
|
-
document.getElementById('solrSecure').checked = tmpSolrSecure === 'true';
|
|
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';
|
|
276
|
+
let tmpEl = document.getElementById('syncModeOngoing');
|
|
277
|
+
if (tmpEl) tmpEl.checked = true;
|
|
303
278
|
}
|
|
304
|
-
// Restore advanced ID pagination checkbox
|
|
305
279
|
let tmpAdvancedIDPagination = localStorage.getItem('dataCloner_syncAdvancedIDPagination');
|
|
306
280
|
if (tmpAdvancedIDPagination !== null)
|
|
307
281
|
{
|
|
308
|
-
document.getElementById('syncAdvancedIDPagination')
|
|
282
|
+
let tmpEl = document.getElementById('syncAdvancedIDPagination');
|
|
283
|
+
if (tmpEl) tmpEl.checked = tmpAdvancedIDPagination === 'true';
|
|
309
284
|
}
|
|
310
285
|
}
|
|
311
286
|
|
|
@@ -347,25 +322,9 @@ class DataClonerProvider extends libPictProvider
|
|
|
347
322
|
});
|
|
348
323
|
});
|
|
349
324
|
|
|
350
|
-
//
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
{
|
|
354
|
-
tmpSolrSecureEl.addEventListener('change', function()
|
|
355
|
-
{
|
|
356
|
-
localStorage.setItem('dataCloner_solrSecure', this.checked);
|
|
357
|
-
});
|
|
358
|
-
}
|
|
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
|
-
}
|
|
325
|
+
// (Connection-section checkboxes — solrSecure, mssqlLegacyPagination —
|
|
326
|
+
// are handled by bootstrapConnectionSchemas() which hooks change
|
|
327
|
+
// listeners after the schema-driven form renders.)
|
|
369
328
|
|
|
370
329
|
// Persist advanced ID pagination checkbox
|
|
371
330
|
let tmpAdvancedIDPaginationEl = document.getElementById('syncAdvancedIDPagination');
|
|
@@ -397,6 +356,136 @@ class DataClonerProvider extends libPictProvider
|
|
|
397
356
|
}
|
|
398
357
|
}
|
|
399
358
|
|
|
359
|
+
// ================================================================
|
|
360
|
+
// Connection Schemas Bootstrap
|
|
361
|
+
//
|
|
362
|
+
// Fetches the host's aggregated connection-form schemas and re-
|
|
363
|
+
// renders the Connection view so it shows the real provider list +
|
|
364
|
+
// per-provider field blocks. Then restores localStorage values
|
|
365
|
+
// for the new (schema-driven) DOM ids and hooks save listeners.
|
|
366
|
+
// ================================================================
|
|
367
|
+
|
|
368
|
+
bootstrapConnectionSchemas(fCallback)
|
|
369
|
+
{
|
|
370
|
+
let tmpSelf = this;
|
|
371
|
+
let tmpDone = (typeof(fCallback) === 'function') ? fCallback : function () {};
|
|
372
|
+
|
|
373
|
+
this.api('GET', '/clone/connection/schemas')
|
|
374
|
+
.then(function (pData)
|
|
375
|
+
{
|
|
376
|
+
let tmpSchemas = (pData && Array.isArray(pData.Schemas)) ? pData.Schemas : [];
|
|
377
|
+
tmpSelf._applyConnectionSchemas(tmpSchemas);
|
|
378
|
+
return tmpDone(null, tmpSchemas);
|
|
379
|
+
})
|
|
380
|
+
.catch(function (pError)
|
|
381
|
+
{
|
|
382
|
+
if (tmpSelf.fable && tmpSelf.fable.log && tmpSelf.fable.log.error)
|
|
383
|
+
{
|
|
384
|
+
tmpSelf.fable.log.error(`DataCloner: failed to fetch connection schemas: ${pError && pError.message}`);
|
|
385
|
+
}
|
|
386
|
+
// On failure, leave the empty-schema state in place — the
|
|
387
|
+
// shared view's "no schemas detected" notice will surface.
|
|
388
|
+
tmpSelf._applyConnectionSchemas([]);
|
|
389
|
+
return tmpDone(pError);
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
_applyConnectionSchemas(pSchemas)
|
|
394
|
+
{
|
|
395
|
+
let tmpAppData = this.pict.AppData.DataCloner;
|
|
396
|
+
if (!tmpAppData.Connection) { tmpAppData.Connection = { Schemas: [], ActiveProvider: '', PreviewText: '' }; }
|
|
397
|
+
tmpAppData.Connection.Schemas = pSchemas;
|
|
398
|
+
|
|
399
|
+
// Pick an initial ActiveProvider — restore from localStorage
|
|
400
|
+
// if the saved value matches one of the available providers,
|
|
401
|
+
// otherwise default to the first schema (or stay empty).
|
|
402
|
+
let tmpAvailable = pSchemas.map(function (pS) { return pS.Provider; });
|
|
403
|
+
let tmpSavedProvider = localStorage.getItem('dataCloner_activeProvider');
|
|
404
|
+
if (tmpSavedProvider && tmpAvailable.indexOf(tmpSavedProvider) >= 0)
|
|
405
|
+
{
|
|
406
|
+
tmpAppData.Connection.ActiveProvider = tmpSavedProvider;
|
|
407
|
+
}
|
|
408
|
+
else if (pSchemas.length > 0)
|
|
409
|
+
{
|
|
410
|
+
tmpAppData.Connection.ActiveProvider = pSchemas[0].Provider;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Hand the schemas to the shared view, which renders the form
|
|
414
|
+
// into #DataCloner-Connection-FormSlot.
|
|
415
|
+
let tmpForm = this.pict.views['PictSection-ConnectionForm'];
|
|
416
|
+
if (tmpForm)
|
|
417
|
+
{
|
|
418
|
+
let tmpSelf = this;
|
|
419
|
+
tmpForm.options.OnProviderChange = function (pProvider)
|
|
420
|
+
{
|
|
421
|
+
tmpAppData.Connection.ActiveProvider = pProvider;
|
|
422
|
+
localStorage.setItem('dataCloner_activeProvider', pProvider);
|
|
423
|
+
// Re-hook persistence on the new active form's inputs
|
|
424
|
+
// (the shared view re-renders on provider change, so the
|
|
425
|
+
// previous input listeners are gone).
|
|
426
|
+
tmpSelf._persistConnectionFields(pSchemas);
|
|
427
|
+
tmpSelf.updateAllPreviews();
|
|
428
|
+
};
|
|
429
|
+
if (tmpAppData.Connection.ActiveProvider)
|
|
430
|
+
{
|
|
431
|
+
tmpForm._ActiveProvider = tmpAppData.Connection.ActiveProvider;
|
|
432
|
+
}
|
|
433
|
+
tmpForm.setSchemas(pSchemas);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Re-render the DataCloner accordion shell so the preview text
|
|
437
|
+
// reflects the active provider.
|
|
438
|
+
let tmpAccordion = this.pict.views['DataCloner-Connection'];
|
|
439
|
+
if (tmpAccordion) { tmpAccordion.render(); }
|
|
440
|
+
|
|
441
|
+
// Restore values + hook input listeners on the freshly-rendered
|
|
442
|
+
// shared-view inputs.
|
|
443
|
+
this._persistConnectionFields(pSchemas);
|
|
444
|
+
this.updateAllPreviews();
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
_persistConnectionFields(pSchemas)
|
|
448
|
+
{
|
|
449
|
+
let tmpForm = this.pict.views['PictSection-ConnectionForm'];
|
|
450
|
+
if (!tmpForm) { return; }
|
|
451
|
+
|
|
452
|
+
let tmpSelf = this;
|
|
453
|
+
|
|
454
|
+
// Restore + hook every per-provider field. saveField()
|
|
455
|
+
// dispatches on element type internally so checkboxes and
|
|
456
|
+
// text inputs share the same path.
|
|
457
|
+
(pSchemas || []).forEach(function (pSchema)
|
|
458
|
+
{
|
|
459
|
+
(pSchema.Fields || []).forEach(function (pField)
|
|
460
|
+
{
|
|
461
|
+
let tmpId = tmpForm.fieldDOMId(pSchema.Provider, pField.Name);
|
|
462
|
+
let tmpEl = document.getElementById(tmpId);
|
|
463
|
+
if (!tmpEl) { return; }
|
|
464
|
+
|
|
465
|
+
let tmpSaved = localStorage.getItem('dataCloner_' + tmpId);
|
|
466
|
+
if (tmpSaved !== null)
|
|
467
|
+
{
|
|
468
|
+
if (tmpEl.type === 'checkbox')
|
|
469
|
+
{
|
|
470
|
+
tmpEl.checked = (tmpSaved === 'true');
|
|
471
|
+
}
|
|
472
|
+
else
|
|
473
|
+
{
|
|
474
|
+
tmpEl.value = tmpSaved;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Avoid double-binding when the shared view re-renders
|
|
479
|
+
// on provider change. We tag the element so subsequent
|
|
480
|
+
// runs of this method are no-ops.
|
|
481
|
+
if (tmpEl.dataset && tmpEl.dataset.dataclonerHooked === '1') { return; }
|
|
482
|
+
if (tmpEl.dataset) { tmpEl.dataset.dataclonerHooked = '1'; }
|
|
483
|
+
tmpEl.addEventListener('input', function () { tmpSelf.saveField(tmpId); tmpSelf.updateAllPreviews(); });
|
|
484
|
+
tmpEl.addEventListener('change', function () { tmpSelf.saveField(tmpId); tmpSelf.updateAllPreviews(); });
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
|
|
400
489
|
// ================================================================
|
|
401
490
|
// Live Status Indicator
|
|
402
491
|
// ================================================================
|