retold-data-service 2.0.13 → 2.0.16
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/.claude/launch.json +11 -0
- package/bin/retold-data-service-clone.js +286 -0
- package/package.json +18 -9
- package/source/Retold-Data-Service.js +275 -73
- package/source/services/Retold-Data-Service-ConnectionManager.js +277 -0
- package/source/services/Retold-Data-Service-MeadowEndpoints.js +217 -0
- package/source/services/Retold-Data-Service-ModelManager.js +335 -0
- package/source/services/data-cloner/DataCloner-Command-Connection.js +138 -0
- package/source/services/data-cloner/DataCloner-Command-Headless.js +357 -0
- package/source/services/data-cloner/DataCloner-Command-Schema.js +367 -0
- package/source/services/data-cloner/DataCloner-Command-Session.js +229 -0
- package/source/services/data-cloner/DataCloner-Command-Sync.js +491 -0
- package/source/services/data-cloner/DataCloner-Command-WebUI.js +40 -0
- package/source/services/data-cloner/DataCloner-ProviderRegistry.js +20 -0
- package/source/services/data-cloner/Retold-Data-Service-DataCloner.js +751 -0
- package/source/services/data-cloner/data-cloner-web.html +2706 -0
- package/source/services/integration-telemetry/IntegrationTelemetry-Command-Dashboard.js +60 -0
- package/source/services/integration-telemetry/IntegrationTelemetry-Command-Integrations.js +132 -0
- package/source/services/integration-telemetry/IntegrationTelemetry-Command-Runs.js +93 -0
- package/source/services/integration-telemetry/IntegrationTelemetry-StorageProvider-Base.js +116 -0
- package/source/services/integration-telemetry/IntegrationTelemetry-StorageProvider-Bibliograph.js +495 -0
- package/source/services/integration-telemetry/Retold-Data-Service-IntegrationTelemetry.js +224 -0
- package/source/services/meadow-integration/MeadowIntegration-Command-CSVCheck.js +85 -0
- package/source/services/meadow-integration/MeadowIntegration-Command-CSVTransform.js +180 -0
- package/source/services/meadow-integration/MeadowIntegration-Command-ComprehensionIntersect.js +153 -0
- package/source/services/meadow-integration/MeadowIntegration-Command-ComprehensionPush.js +190 -0
- package/source/services/meadow-integration/MeadowIntegration-Command-ComprehensionToArray.js +113 -0
- package/source/services/meadow-integration/MeadowIntegration-Command-ComprehensionToCSV.js +211 -0
- package/source/services/meadow-integration/MeadowIntegration-Command-EntityFromTabularFolder.js +244 -0
- package/source/services/meadow-integration/MeadowIntegration-Command-JSONArrayTransform.js +213 -0
- package/source/services/meadow-integration/MeadowIntegration-Command-TSVCheck.js +80 -0
- package/source/services/meadow-integration/MeadowIntegration-Command-TSVTransform.js +166 -0
- package/source/services/meadow-integration/Retold-Data-Service-MeadowIntegration.js +113 -0
- package/source/services/migration-manager/MigrationManager-Command-Connections.js +220 -0
- package/source/services/migration-manager/MigrationManager-Command-DiffMigrate.js +169 -0
- package/source/services/migration-manager/MigrationManager-Command-Schemas.js +532 -0
- package/source/services/migration-manager/MigrationManager-Command-WebUI.js +123 -0
- package/source/services/migration-manager/Retold-Data-Service-MigrationManager.js +357 -0
- package/source/services/stricture/Retold-Data-Service-Stricture.js +303 -0
- package/source/services/stricture/Stricture-Command-Compile.js +39 -0
- package/source/services/stricture/Stricture-Command-Generate-AuthorizationChart.js +14 -0
- package/source/services/stricture/Stricture-Command-Generate-DictionaryCSV.js +14 -0
- package/source/services/stricture/Stricture-Command-Generate-LaTeX.js +14 -0
- package/source/services/stricture/Stricture-Command-Generate-Markdown.js +14 -0
- package/source/services/stricture/Stricture-Command-Generate-Meadow.js +14 -0
- package/source/services/stricture/Stricture-Command-Generate-ModelGraph.js +14 -0
- package/source/services/stricture/Stricture-Command-Generate-MySQL.js +14 -0
- package/source/services/stricture/Stricture-Command-Generate-MySQLMigrate.js +14 -0
- package/source/services/stricture/Stricture-Command-Generate-Pict.js +14 -0
- package/source/services/stricture/Stricture-Command-Generate-TestObjectContainers.js +14 -0
- package/test/RetoldDataService_tests.js +161 -1
- package/debug/data/books.csv +0 -10001
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MigrationManager Command - Schema Management
|
|
3
|
+
*
|
|
4
|
+
* Routes for schema CRUD, compilation, visualization, and file management.
|
|
5
|
+
*
|
|
6
|
+
* GET /api/schemas
|
|
7
|
+
* GET /api/schemas/:name
|
|
8
|
+
* GET /api/schemas/:name/ddl
|
|
9
|
+
* PUT /api/schemas/:name/ddl
|
|
10
|
+
* GET /api/schemas/:name/files
|
|
11
|
+
* GET /api/schemas/:name/file/:filepath
|
|
12
|
+
* PUT /api/schemas/:name/file/:filepath
|
|
13
|
+
* POST /api/schemas/:name/compile
|
|
14
|
+
* GET /api/schemas/:name/visualize
|
|
15
|
+
* GET /api/schemas/:name/meadow-packages
|
|
16
|
+
*/
|
|
17
|
+
const libFs = require('fs');
|
|
18
|
+
const libPath = require('path');
|
|
19
|
+
|
|
20
|
+
module.exports = function(pMigrationService, pOratorServiceServer)
|
|
21
|
+
{
|
|
22
|
+
let tmpSchemaLibrary = pMigrationService._schemaLibrary;
|
|
23
|
+
let tmpStrictureAdapter = pMigrationService._strictureAdapter;
|
|
24
|
+
let tmpSchemaVisualizer = pMigrationService._schemaVisualizer;
|
|
25
|
+
let tmpFlowDataBuilder = pMigrationService._flowDataBuilder;
|
|
26
|
+
let tmpPrefix = pMigrationService.routePrefix;
|
|
27
|
+
|
|
28
|
+
// GET /api/schemas — list all schemas with compiled status
|
|
29
|
+
pOratorServiceServer.get(tmpPrefix + '/api/schemas',
|
|
30
|
+
(pRequest, pResponse, fNext) =>
|
|
31
|
+
{
|
|
32
|
+
try
|
|
33
|
+
{
|
|
34
|
+
let tmpNames = tmpSchemaLibrary.listSchemas();
|
|
35
|
+
let tmpSchemas = [];
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < tmpNames.length; i++)
|
|
38
|
+
{
|
|
39
|
+
let tmpEntry = tmpSchemaLibrary.getSchema(tmpNames[i]);
|
|
40
|
+
tmpSchemas.push(
|
|
41
|
+
{
|
|
42
|
+
Name: tmpEntry.Name,
|
|
43
|
+
IsCompiled: !!tmpEntry.CompiledSchema,
|
|
44
|
+
LastCompiled: tmpEntry.LastCompiled || null,
|
|
45
|
+
TableCount: (tmpEntry.CompiledSchema && tmpEntry.CompiledSchema.Tables)
|
|
46
|
+
? (Array.isArray(tmpEntry.CompiledSchema.Tables)
|
|
47
|
+
? tmpEntry.CompiledSchema.Tables.length
|
|
48
|
+
: Object.keys(tmpEntry.CompiledSchema.Tables).length)
|
|
49
|
+
: 0,
|
|
50
|
+
HasSourceFile: !!tmpEntry.SourceFilePath
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
pResponse.send({ Success: true, Schemas: tmpSchemas });
|
|
55
|
+
}
|
|
56
|
+
catch (pError)
|
|
57
|
+
{
|
|
58
|
+
pResponse.send(500, { Success: false, Error: pError.message });
|
|
59
|
+
}
|
|
60
|
+
return fNext();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// GET /api/schemas/:name — full schema detail
|
|
64
|
+
pOratorServiceServer.get(tmpPrefix + '/api/schemas/:name',
|
|
65
|
+
(pRequest, pResponse, fNext) =>
|
|
66
|
+
{
|
|
67
|
+
try
|
|
68
|
+
{
|
|
69
|
+
let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
|
|
70
|
+
|
|
71
|
+
if (!tmpEntry)
|
|
72
|
+
{
|
|
73
|
+
pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
|
|
74
|
+
return fNext();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
pResponse.send(
|
|
78
|
+
{
|
|
79
|
+
Success: true,
|
|
80
|
+
Schema:
|
|
81
|
+
{
|
|
82
|
+
Name: tmpEntry.Name,
|
|
83
|
+
DDL: tmpEntry.DDL,
|
|
84
|
+
CompiledSchema: tmpEntry.CompiledSchema,
|
|
85
|
+
MeadowPackages: tmpEntry.MeadowPackages,
|
|
86
|
+
IsCompiled: !!tmpEntry.CompiledSchema,
|
|
87
|
+
LastCompiled: tmpEntry.LastCompiled || null,
|
|
88
|
+
HasSourceFile: !!tmpEntry.SourceFilePath
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch (pError)
|
|
93
|
+
{
|
|
94
|
+
pResponse.send(500, { Success: false, Error: pError.message });
|
|
95
|
+
}
|
|
96
|
+
return fNext();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// GET /api/schemas/:name/ddl — raw DDL text
|
|
100
|
+
pOratorServiceServer.get(tmpPrefix + '/api/schemas/:name/ddl',
|
|
101
|
+
(pRequest, pResponse, fNext) =>
|
|
102
|
+
{
|
|
103
|
+
try
|
|
104
|
+
{
|
|
105
|
+
let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
|
|
106
|
+
|
|
107
|
+
if (!tmpEntry)
|
|
108
|
+
{
|
|
109
|
+
pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
|
|
110
|
+
return fNext();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
pResponse.send({ Success: true, Name: tmpEntry.Name, DDL: tmpEntry.DDL });
|
|
114
|
+
}
|
|
115
|
+
catch (pError)
|
|
116
|
+
{
|
|
117
|
+
pResponse.send(500, { Success: false, Error: pError.message });
|
|
118
|
+
}
|
|
119
|
+
return fNext();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// PUT /api/schemas/:name/ddl — update DDL text
|
|
123
|
+
pOratorServiceServer.put(tmpPrefix + '/api/schemas/:name/ddl',
|
|
124
|
+
(pRequest, pResponse, fNext) =>
|
|
125
|
+
{
|
|
126
|
+
try
|
|
127
|
+
{
|
|
128
|
+
let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
|
|
129
|
+
|
|
130
|
+
if (!tmpEntry)
|
|
131
|
+
{
|
|
132
|
+
// Create a new schema entry if it doesn't exist
|
|
133
|
+
tmpEntry = tmpSchemaLibrary.addSchema(pRequest.params.name, '');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let tmpDDL = '';
|
|
137
|
+
if (pRequest.body && typeof (pRequest.body) === 'object' && pRequest.body.DDL !== undefined)
|
|
138
|
+
{
|
|
139
|
+
tmpDDL = pRequest.body.DDL;
|
|
140
|
+
}
|
|
141
|
+
else if (typeof (pRequest.body) === 'string')
|
|
142
|
+
{
|
|
143
|
+
tmpDDL = pRequest.body;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
tmpEntry.DDL = tmpDDL;
|
|
147
|
+
// Clear compiled state when DDL changes
|
|
148
|
+
tmpEntry.CompiledSchema = null;
|
|
149
|
+
tmpEntry.MeadowPackages = null;
|
|
150
|
+
tmpEntry.LastCompiled = null;
|
|
151
|
+
|
|
152
|
+
// Also write back to the source file if it exists
|
|
153
|
+
if (tmpEntry.SourceFilePath)
|
|
154
|
+
{
|
|
155
|
+
try
|
|
156
|
+
{
|
|
157
|
+
libFs.writeFileSync(tmpEntry.SourceFilePath, tmpDDL, 'utf8');
|
|
158
|
+
}
|
|
159
|
+
catch (pWriteError)
|
|
160
|
+
{
|
|
161
|
+
pMigrationService._MM.log.warn(`Failed to write back to source file [${tmpEntry.SourceFilePath}]: ${pWriteError.message}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
pResponse.send({ Success: true, Name: tmpEntry.Name });
|
|
166
|
+
}
|
|
167
|
+
catch (pError)
|
|
168
|
+
{
|
|
169
|
+
pResponse.send(500, { Success: false, Error: pError.message });
|
|
170
|
+
}
|
|
171
|
+
return fNext();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// GET /api/schemas/:name/files — discover all DDL files (main + includes)
|
|
175
|
+
pOratorServiceServer.get(tmpPrefix + '/api/schemas/:name/files',
|
|
176
|
+
(pRequest, pResponse, fNext) =>
|
|
177
|
+
{
|
|
178
|
+
try
|
|
179
|
+
{
|
|
180
|
+
let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
|
|
181
|
+
|
|
182
|
+
if (!tmpEntry)
|
|
183
|
+
{
|
|
184
|
+
pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
|
|
185
|
+
return fNext();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let tmpModelPath = pMigrationService.modelPath;
|
|
189
|
+
|
|
190
|
+
if (tmpEntry.SourceFilePath && tmpModelPath)
|
|
191
|
+
{
|
|
192
|
+
let tmpFiles = pMigrationService.discoverIncludedFiles(tmpEntry.SourceFilePath, tmpModelPath);
|
|
193
|
+
|
|
194
|
+
pResponse.send(
|
|
195
|
+
{
|
|
196
|
+
Success: true,
|
|
197
|
+
Name: tmpEntry.Name,
|
|
198
|
+
HasSourceFile: true,
|
|
199
|
+
Files: tmpFiles.map(function(pFile)
|
|
200
|
+
{
|
|
201
|
+
let tmpLines = pFile.Content.split('\n');
|
|
202
|
+
let tmpTableCount = 0;
|
|
203
|
+
for (let k = 0; k < tmpLines.length; k++)
|
|
204
|
+
{
|
|
205
|
+
if (tmpLines[k].trimStart().charAt(0) === '!')
|
|
206
|
+
{
|
|
207
|
+
tmpTableCount++;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
RelativePath: pFile.RelativePath,
|
|
212
|
+
Content: pFile.Content,
|
|
213
|
+
IsMain: pFile.IsMain,
|
|
214
|
+
IncludedBy: pFile.IncludedBy || null,
|
|
215
|
+
Depth: pFile.Depth || 0,
|
|
216
|
+
Bytes: Buffer.byteLength(pFile.Content, 'utf8'),
|
|
217
|
+
LineCount: tmpLines.length,
|
|
218
|
+
TableCount: tmpTableCount
|
|
219
|
+
};
|
|
220
|
+
})
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
else
|
|
224
|
+
{
|
|
225
|
+
// Programmatic schema — return a single virtual file entry
|
|
226
|
+
let tmpDDL = tmpEntry.DDL || '';
|
|
227
|
+
let tmpDDLLines = tmpDDL.split('\n');
|
|
228
|
+
let tmpDDLTableCount = 0;
|
|
229
|
+
for (let k = 0; k < tmpDDLLines.length; k++)
|
|
230
|
+
{
|
|
231
|
+
if (tmpDDLLines[k].trimStart().charAt(0) === '!')
|
|
232
|
+
{
|
|
233
|
+
tmpDDLTableCount++;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
pResponse.send(
|
|
237
|
+
{
|
|
238
|
+
Success: true,
|
|
239
|
+
Name: tmpEntry.Name,
|
|
240
|
+
HasSourceFile: false,
|
|
241
|
+
Files:
|
|
242
|
+
[
|
|
243
|
+
{
|
|
244
|
+
RelativePath: tmpEntry.Name + '.ddl',
|
|
245
|
+
Content: tmpDDL,
|
|
246
|
+
IsMain: true,
|
|
247
|
+
IncludedBy: null,
|
|
248
|
+
Depth: 0,
|
|
249
|
+
Bytes: Buffer.byteLength(tmpDDL, 'utf8'),
|
|
250
|
+
LineCount: tmpDDLLines.length,
|
|
251
|
+
TableCount: tmpDDLTableCount
|
|
252
|
+
}
|
|
253
|
+
]
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch (pError)
|
|
258
|
+
{
|
|
259
|
+
pResponse.send(500, { Success: false, Error: pError.message });
|
|
260
|
+
}
|
|
261
|
+
return fNext();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// GET /api/schemas/:name/file/:filepath — read a specific child file
|
|
265
|
+
pOratorServiceServer.get(tmpPrefix + '/api/schemas/:name/file/:filepath',
|
|
266
|
+
(pRequest, pResponse, fNext) =>
|
|
267
|
+
{
|
|
268
|
+
try
|
|
269
|
+
{
|
|
270
|
+
let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
|
|
271
|
+
|
|
272
|
+
if (!tmpEntry)
|
|
273
|
+
{
|
|
274
|
+
pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
|
|
275
|
+
return fNext();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (!tmpEntry.SourceFilePath)
|
|
279
|
+
{
|
|
280
|
+
pResponse.send({ Success: true, Content: tmpEntry.DDL || '' });
|
|
281
|
+
return fNext();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
let tmpModelPath = pMigrationService.modelPath;
|
|
285
|
+
let tmpRelPath = decodeURIComponent(pRequest.params.filepath);
|
|
286
|
+
let tmpAbsPath = libPath.resolve(tmpModelPath, tmpRelPath);
|
|
287
|
+
let tmpAbsBase = libPath.resolve(tmpModelPath);
|
|
288
|
+
|
|
289
|
+
// Prevent directory traversal
|
|
290
|
+
if (!tmpAbsPath.startsWith(tmpAbsBase))
|
|
291
|
+
{
|
|
292
|
+
pResponse.send(403, { Success: false, Error: 'Path outside model directory.' });
|
|
293
|
+
return fNext();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
let tmpContent = libFs.readFileSync(tmpAbsPath, 'utf8');
|
|
297
|
+
pResponse.send({ Success: true, RelativePath: tmpRelPath, Content: tmpContent });
|
|
298
|
+
}
|
|
299
|
+
catch (pError)
|
|
300
|
+
{
|
|
301
|
+
pResponse.send(500, { Success: false, Error: pError.message });
|
|
302
|
+
}
|
|
303
|
+
return fNext();
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// PUT /api/schemas/:name/file/:filepath — write updated content to a specific child file
|
|
307
|
+
pOratorServiceServer.put(tmpPrefix + '/api/schemas/:name/file/:filepath',
|
|
308
|
+
(pRequest, pResponse, fNext) =>
|
|
309
|
+
{
|
|
310
|
+
try
|
|
311
|
+
{
|
|
312
|
+
let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
|
|
313
|
+
|
|
314
|
+
if (!tmpEntry)
|
|
315
|
+
{
|
|
316
|
+
pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
|
|
317
|
+
return fNext();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
let tmpContent = '';
|
|
321
|
+
if (pRequest.body && typeof (pRequest.body) === 'object' && pRequest.body.Content !== undefined)
|
|
322
|
+
{
|
|
323
|
+
tmpContent = pRequest.body.Content;
|
|
324
|
+
}
|
|
325
|
+
else if (typeof (pRequest.body) === 'string')
|
|
326
|
+
{
|
|
327
|
+
tmpContent = pRequest.body;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (!tmpEntry.SourceFilePath)
|
|
331
|
+
{
|
|
332
|
+
tmpEntry.DDL = tmpContent;
|
|
333
|
+
tmpEntry.CompiledSchema = null;
|
|
334
|
+
tmpEntry.MeadowPackages = null;
|
|
335
|
+
tmpEntry.LastCompiled = null;
|
|
336
|
+
pResponse.send({ Success: true });
|
|
337
|
+
return fNext();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
let tmpModelPath = pMigrationService.modelPath;
|
|
341
|
+
let tmpRelPath = decodeURIComponent(pRequest.params.filepath);
|
|
342
|
+
let tmpAbsPath = libPath.resolve(tmpModelPath, tmpRelPath);
|
|
343
|
+
let tmpAbsBase = libPath.resolve(tmpModelPath);
|
|
344
|
+
|
|
345
|
+
// Prevent directory traversal
|
|
346
|
+
if (!tmpAbsPath.startsWith(tmpAbsBase))
|
|
347
|
+
{
|
|
348
|
+
pResponse.send(403, { Success: false, Error: 'Path outside model directory.' });
|
|
349
|
+
return fNext();
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
libFs.writeFileSync(tmpAbsPath, tmpContent, 'utf8');
|
|
353
|
+
|
|
354
|
+
// If this is the main file, also update the schema entry DDL
|
|
355
|
+
if (tmpAbsPath === libPath.resolve(tmpEntry.SourceFilePath))
|
|
356
|
+
{
|
|
357
|
+
tmpEntry.DDL = tmpContent;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Clear compiled state since a file changed
|
|
361
|
+
tmpEntry.CompiledSchema = null;
|
|
362
|
+
tmpEntry.MeadowPackages = null;
|
|
363
|
+
tmpEntry.LastCompiled = null;
|
|
364
|
+
|
|
365
|
+
pResponse.send({ Success: true, RelativePath: tmpRelPath });
|
|
366
|
+
}
|
|
367
|
+
catch (pError)
|
|
368
|
+
{
|
|
369
|
+
pResponse.send(500, { Success: false, Error: pError.message });
|
|
370
|
+
}
|
|
371
|
+
return fNext();
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// POST /api/schemas/:name/compile — compile DDL
|
|
375
|
+
pOratorServiceServer.post(tmpPrefix + '/api/schemas/:name/compile',
|
|
376
|
+
(pRequest, pResponse, fNext) =>
|
|
377
|
+
{
|
|
378
|
+
try
|
|
379
|
+
{
|
|
380
|
+
let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
|
|
381
|
+
|
|
382
|
+
if (!tmpEntry)
|
|
383
|
+
{
|
|
384
|
+
pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
|
|
385
|
+
return fNext();
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (!tmpEntry.DDL)
|
|
389
|
+
{
|
|
390
|
+
pResponse.send(400, { Success: false, Error: 'Schema has no DDL text to compile.' });
|
|
391
|
+
return fNext();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
let tmpCompileHandler = (pError, pCompiledSchema, pMeadowPackages) =>
|
|
395
|
+
{
|
|
396
|
+
if (pError)
|
|
397
|
+
{
|
|
398
|
+
pResponse.send(500, { Success: false, Error: `Compilation failed: ${pError.message || pError}` });
|
|
399
|
+
return fNext();
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
tmpEntry.CompiledSchema = pCompiledSchema;
|
|
403
|
+
tmpEntry.MeadowPackages = pMeadowPackages;
|
|
404
|
+
tmpEntry.LastCompiled = new Date().toJSON();
|
|
405
|
+
|
|
406
|
+
pResponse.send(
|
|
407
|
+
{
|
|
408
|
+
Success: true,
|
|
409
|
+
Name: tmpEntry.Name,
|
|
410
|
+
CompiledSchema: tmpEntry.CompiledSchema,
|
|
411
|
+
MeadowPackages: tmpEntry.MeadowPackages,
|
|
412
|
+
LastCompiled: tmpEntry.LastCompiled
|
|
413
|
+
});
|
|
414
|
+
return fNext();
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
if (tmpEntry.SourceFilePath)
|
|
418
|
+
{
|
|
419
|
+
// Re-read the main file in case it was edited via the file API
|
|
420
|
+
try
|
|
421
|
+
{
|
|
422
|
+
tmpEntry.DDL = libFs.readFileSync(tmpEntry.SourceFilePath, 'utf8');
|
|
423
|
+
}
|
|
424
|
+
catch (pReadError)
|
|
425
|
+
{
|
|
426
|
+
// If we can't re-read, use the existing DDL
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
tmpStrictureAdapter.compileFileAndGenerate(tmpEntry.SourceFilePath, tmpCompileHandler);
|
|
430
|
+
}
|
|
431
|
+
else
|
|
432
|
+
{
|
|
433
|
+
tmpStrictureAdapter.compileAndGenerate(tmpEntry.DDL, tmpCompileHandler);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
catch (pError)
|
|
437
|
+
{
|
|
438
|
+
pResponse.send(500, { Success: false, Error: pError.message });
|
|
439
|
+
return fNext();
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
// GET /api/schemas/:name/visualize — visualization data
|
|
444
|
+
pOratorServiceServer.get(tmpPrefix + '/api/schemas/:name/visualize',
|
|
445
|
+
(pRequest, pResponse, fNext) =>
|
|
446
|
+
{
|
|
447
|
+
try
|
|
448
|
+
{
|
|
449
|
+
let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
|
|
450
|
+
|
|
451
|
+
if (!tmpEntry)
|
|
452
|
+
{
|
|
453
|
+
pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
|
|
454
|
+
return fNext();
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (!tmpEntry.CompiledSchema)
|
|
458
|
+
{
|
|
459
|
+
pResponse.send(400, { Success: false, Error: 'Schema has not been compiled yet.' });
|
|
460
|
+
return fNext();
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
let tmpTableList = tmpSchemaVisualizer.generateTableList(tmpEntry.CompiledSchema);
|
|
464
|
+
let tmpASCIIDiagram = tmpSchemaVisualizer.generateASCIIDiagram(tmpEntry.CompiledSchema);
|
|
465
|
+
let tmpRelationshipMap = tmpSchemaVisualizer.generateRelationshipMap(tmpEntry.CompiledSchema);
|
|
466
|
+
|
|
467
|
+
let tmpTableDetails = [];
|
|
468
|
+
let tmpTables = Array.isArray(tmpEntry.CompiledSchema.Tables)
|
|
469
|
+
? tmpEntry.CompiledSchema.Tables
|
|
470
|
+
: Object.values(tmpEntry.CompiledSchema.Tables || {});
|
|
471
|
+
|
|
472
|
+
for (let i = 0; i < tmpTables.length; i++)
|
|
473
|
+
{
|
|
474
|
+
tmpTableDetails.push(tmpSchemaVisualizer.generateTableDetail(tmpTables[i]));
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
let tmpFlowData = tmpFlowDataBuilder.buildFlowData(tmpEntry.CompiledSchema);
|
|
478
|
+
|
|
479
|
+
pResponse.send(
|
|
480
|
+
{
|
|
481
|
+
Success: true,
|
|
482
|
+
Name: tmpEntry.Name,
|
|
483
|
+
TableList: tmpTableList,
|
|
484
|
+
ASCIIDiagram: tmpASCIIDiagram,
|
|
485
|
+
RelationshipMap: tmpRelationshipMap,
|
|
486
|
+
TableDetails: tmpTableDetails,
|
|
487
|
+
FlowData: tmpFlowData
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
catch (pError)
|
|
491
|
+
{
|
|
492
|
+
pResponse.send(500, { Success: false, Error: pError.message });
|
|
493
|
+
}
|
|
494
|
+
return fNext();
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// GET /api/schemas/:name/meadow-packages — Meadow package JSON
|
|
498
|
+
pOratorServiceServer.get(tmpPrefix + '/api/schemas/:name/meadow-packages',
|
|
499
|
+
(pRequest, pResponse, fNext) =>
|
|
500
|
+
{
|
|
501
|
+
try
|
|
502
|
+
{
|
|
503
|
+
let tmpEntry = tmpSchemaLibrary.getSchema(pRequest.params.name);
|
|
504
|
+
|
|
505
|
+
if (!tmpEntry)
|
|
506
|
+
{
|
|
507
|
+
pResponse.send(404, { Success: false, Error: `Schema [${pRequest.params.name}] not found.` });
|
|
508
|
+
return fNext();
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (!tmpEntry.CompiledSchema)
|
|
512
|
+
{
|
|
513
|
+
pResponse.send(400, { Success: false, Error: 'Schema has not been compiled yet.' });
|
|
514
|
+
return fNext();
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
let tmpPackages = tmpEntry.MeadowPackages || tmpStrictureAdapter.generateMeadowPackages(tmpEntry.CompiledSchema);
|
|
518
|
+
|
|
519
|
+
pResponse.send(
|
|
520
|
+
{
|
|
521
|
+
Success: true,
|
|
522
|
+
Name: tmpEntry.Name,
|
|
523
|
+
MeadowPackages: tmpPackages
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
catch (pError)
|
|
527
|
+
{
|
|
528
|
+
pResponse.send(500, { Success: false, Error: pError.message });
|
|
529
|
+
}
|
|
530
|
+
return fNext();
|
|
531
|
+
});
|
|
532
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MigrationManager Command - Web UI
|
|
3
|
+
*
|
|
4
|
+
* Serves the migration manager web UI HTML and static JS library files.
|
|
5
|
+
* Library files are resolved from meadow-migrationmanager's node_modules
|
|
6
|
+
* at runtime, matching the standalone server's approach.
|
|
7
|
+
*
|
|
8
|
+
* When a RoutePrefix is configured (e.g. '/meadow-migrationmanager'),
|
|
9
|
+
* the HTML is patched at serve time to inject window.MIGRATION_MANAGER_BASE
|
|
10
|
+
* and rewrite script src paths so the web UI resolves all resources
|
|
11
|
+
* under the prefix.
|
|
12
|
+
*
|
|
13
|
+
* GET {prefix}/ (index.html)
|
|
14
|
+
* GET {prefix}/lib/codejar.js (CodeJar editor)
|
|
15
|
+
* GET {prefix}/lib/pict.min.js (Pict browser bundle)
|
|
16
|
+
* GET {prefix}/lib/pict-section-flow.min.js (Flow diagram visualization)
|
|
17
|
+
*/
|
|
18
|
+
const libFs = require('fs');
|
|
19
|
+
const libPath = require('path');
|
|
20
|
+
|
|
21
|
+
// Resolve paths relative to the meadow-migrationmanager package root
|
|
22
|
+
const _MigrationManagerBase = libPath.dirname(require.resolve('meadow-migrationmanager/package.json'));
|
|
23
|
+
|
|
24
|
+
module.exports = function(pMigrationService, pOratorServiceServer)
|
|
25
|
+
{
|
|
26
|
+
let tmpPrefix = pMigrationService.routePrefix;
|
|
27
|
+
|
|
28
|
+
// GET {prefix}/lib/codejar.js — serve CodeJar as a global (strip ES module export)
|
|
29
|
+
let tmpCodeJarPath = libPath.join(_MigrationManagerBase, 'node_modules', 'codejar', 'dist', 'codejar.js');
|
|
30
|
+
pOratorServiceServer.get(tmpPrefix + '/lib/codejar.js',
|
|
31
|
+
(pRequest, pResponse, fNext) =>
|
|
32
|
+
{
|
|
33
|
+
try
|
|
34
|
+
{
|
|
35
|
+
let tmpSource = libFs.readFileSync(tmpCodeJarPath, 'utf8');
|
|
36
|
+
// Strip the ES module `export ` keyword so CodeJar becomes a global function
|
|
37
|
+
tmpSource = tmpSource.replace('export function CodeJar', 'function CodeJar');
|
|
38
|
+
pResponse.writeHead(200, { 'Content-Type': 'application/javascript; charset=utf-8' });
|
|
39
|
+
pResponse.write(tmpSource);
|
|
40
|
+
pResponse.end();
|
|
41
|
+
}
|
|
42
|
+
catch (pError)
|
|
43
|
+
{
|
|
44
|
+
pResponse.send(500, { Success: false, Error: 'Failed to load CodeJar.' });
|
|
45
|
+
}
|
|
46
|
+
return fNext();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// GET {prefix}/lib/pict.min.js — serve Pict browser bundle
|
|
50
|
+
let tmpPictPath = libPath.join(_MigrationManagerBase, 'node_modules', 'pict', 'dist', 'pict.min.js');
|
|
51
|
+
pOratorServiceServer.get(tmpPrefix + '/lib/pict.min.js',
|
|
52
|
+
(pRequest, pResponse, fNext) =>
|
|
53
|
+
{
|
|
54
|
+
try
|
|
55
|
+
{
|
|
56
|
+
let tmpSource = libFs.readFileSync(tmpPictPath, 'utf8');
|
|
57
|
+
pResponse.writeHead(200, { 'Content-Type': 'application/javascript; charset=utf-8' });
|
|
58
|
+
pResponse.write(tmpSource);
|
|
59
|
+
pResponse.end();
|
|
60
|
+
}
|
|
61
|
+
catch (pError)
|
|
62
|
+
{
|
|
63
|
+
pResponse.send(500, { Success: false, Error: 'Failed to load Pict.' });
|
|
64
|
+
}
|
|
65
|
+
return fNext();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// GET {prefix}/lib/pict-section-flow.min.js — serve pict-section-flow browser bundle
|
|
69
|
+
let tmpFlowPath = libPath.join(_MigrationManagerBase, 'node_modules', 'pict-section-flow', 'dist', 'pict-section-flow.min.js');
|
|
70
|
+
pOratorServiceServer.get(tmpPrefix + '/lib/pict-section-flow.min.js',
|
|
71
|
+
(pRequest, pResponse, fNext) =>
|
|
72
|
+
{
|
|
73
|
+
try
|
|
74
|
+
{
|
|
75
|
+
let tmpSource = libFs.readFileSync(tmpFlowPath, 'utf8');
|
|
76
|
+
pResponse.writeHead(200, { 'Content-Type': 'application/javascript; charset=utf-8' });
|
|
77
|
+
pResponse.write(tmpSource);
|
|
78
|
+
pResponse.end();
|
|
79
|
+
}
|
|
80
|
+
catch (pError)
|
|
81
|
+
{
|
|
82
|
+
pResponse.send(500, { Success: false, Error: 'Failed to load pict-section-flow.' });
|
|
83
|
+
}
|
|
84
|
+
return fNext();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// GET {prefix}/ — serve the web UI HTML
|
|
88
|
+
let tmpHTMLPath = libPath.join(_MigrationManagerBase, 'source', 'web', 'index.html');
|
|
89
|
+
|
|
90
|
+
let fServeHTML = (pRequest, pResponse, fNext) =>
|
|
91
|
+
{
|
|
92
|
+
try
|
|
93
|
+
{
|
|
94
|
+
let tmpHTML = libFs.readFileSync(tmpHTMLPath, 'utf8');
|
|
95
|
+
|
|
96
|
+
// When a route prefix is set, inject the base path
|
|
97
|
+
// so the web UI's api() function and script src tags
|
|
98
|
+
// resolve to the correct prefixed URLs.
|
|
99
|
+
if (tmpPrefix)
|
|
100
|
+
{
|
|
101
|
+
tmpHTML = tmpHTML.replace(/src="\/lib\//g, 'src="' + tmpPrefix + '/lib/');
|
|
102
|
+
tmpHTML = tmpHTML.replace('<script src=', '<script>window.MIGRATION_MANAGER_BASE=\'' + tmpPrefix + '\';</script>\n<script src=');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
pResponse.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
106
|
+
pResponse.write(tmpHTML);
|
|
107
|
+
pResponse.end();
|
|
108
|
+
}
|
|
109
|
+
catch (pError)
|
|
110
|
+
{
|
|
111
|
+
pResponse.send(500, { Success: false, Error: 'Failed to load web UI.' });
|
|
112
|
+
}
|
|
113
|
+
return fNext();
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
pOratorServiceServer.get(tmpPrefix + '/', fServeHTML);
|
|
117
|
+
|
|
118
|
+
// Also serve without trailing slash when a prefix is active
|
|
119
|
+
if (tmpPrefix)
|
|
120
|
+
{
|
|
121
|
+
pOratorServiceServer.get(tmpPrefix, fServeHTML);
|
|
122
|
+
}
|
|
123
|
+
};
|