retold-facto 0.0.4
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/.dockerignore +8 -0
- package/.quackage.json +19 -0
- package/Dockerfile +26 -0
- package/bin/retold-facto.js +909 -0
- package/examples/facto-government-data.sqlite +0 -0
- package/examples/government-data-catalog.json +137 -0
- package/examples/government-data-loader.js +1432 -0
- package/package.json +91 -0
- package/scripts/facto-download.js +425 -0
- package/source/Retold-Facto.js +1042 -0
- package/source/services/Retold-Facto-BeaconProvider.js +511 -0
- package/source/services/Retold-Facto-CatalogManager.js +1252 -0
- package/source/services/Retold-Facto-DataLakeService.js +1642 -0
- package/source/services/Retold-Facto-DatasetManager.js +417 -0
- package/source/services/Retold-Facto-IngestEngine.js +1315 -0
- package/source/services/Retold-Facto-ProjectionEngine.js +3960 -0
- package/source/services/Retold-Facto-RecordManager.js +360 -0
- package/source/services/Retold-Facto-SchemaManager.js +1110 -0
- package/source/services/Retold-Facto-SourceFolderScanner.js +2243 -0
- package/source/services/Retold-Facto-SourceManager.js +730 -0
- package/source/services/Retold-Facto-StoreConnectionManager.js +441 -0
- package/source/services/Retold-Facto-ThroughputMonitor.js +478 -0
- package/source/services/web-app/codemirror-entry.js +7 -0
- package/source/services/web-app/pict-app/Pict-Application-Facto-Configuration.json +9 -0
- package/source/services/web-app/pict-app/Pict-Application-Facto.js +70 -0
- package/source/services/web-app/pict-app/Pict-Facto-Bundle.js +11 -0
- package/source/services/web-app/pict-app/providers/Pict-Provider-Facto-UI.js +66 -0
- package/source/services/web-app/pict-app/providers/Pict-Provider-Facto.js +69 -0
- package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Catalog.js +93 -0
- package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Connections.js +42 -0
- package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Datasets.js +605 -0
- package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Projections.js +188 -0
- package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Scanner.js +80 -0
- package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Schema.js +116 -0
- package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Sources.js +104 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Catalog.js +526 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Datasets.js +173 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Ingest.js +259 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Layout.js +191 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Projections.js +231 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Records.js +326 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Scanner.js +624 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Sources.js +201 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Throughput.js +456 -0
- package/source/services/web-app/pict-app-full/Pict-Application-Facto-Full-Configuration.json +14 -0
- package/source/services/web-app/pict-app-full/Pict-Application-Facto-Full.js +391 -0
- package/source/services/web-app/pict-app-full/providers/PictRouter-Facto-Configuration.json +56 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-BottomBar.js +68 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Connections.js +340 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Dashboard.js +149 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Dashboards.js +819 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Datasets.js +178 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-IngestJobs.js +99 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Layout.js +62 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-MappingEditor.js +158 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-ProjectionDetail.js +1120 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Projections.js +172 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-QueryPanel.js +119 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-RecordViewer.js +663 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Records.js +648 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Scanner.js +1017 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SchemaDetail.js +1404 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SchemaDocEditor.js +1036 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SchemaEditor.js +636 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SchemaResearch.js +357 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SourceDetail.js +822 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SourceEditor.js +1036 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SourceResearch.js +487 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Sources.js +165 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Throughput.js +439 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-TopBar.js +335 -0
- package/source/services/web-app/pict-app-full/views/projections/Facto-Projections-Constants.js +71 -0
- package/source/services/web-app/web/chart.min.js +20 -0
- package/source/services/web-app/web/codemirror-bundle.js +30099 -0
- package/source/services/web-app/web/css/facto-themes.css +467 -0
- package/source/services/web-app/web/css/facto.css +502 -0
- package/source/services/web-app/web/index.html +28 -0
- package/source/services/web-app/web/retold-facto.js +12138 -0
- package/source/services/web-app/web/retold-facto.js.map +1 -0
- package/source/services/web-app/web/retold-facto.min.js +2 -0
- package/source/services/web-app/web/retold-facto.min.js.map +1 -0
- package/source/services/web-app/web/simple/index.html +17 -0
- package/test/Facto_Browser_Integration_tests.js +798 -0
- package/test/RetoldFacto_tests.js +4117 -0
- package/test/fixtures/weather-readings.csv +17 -0
- package/test/fixtures/weather-stations.csv +9 -0
- package/test/model/MeadowModel-Extended.json +8497 -0
- package/test/model/MeadowModel-PICT.json +1 -0
- package/test/model/MeadowModel.json +1355 -0
- package/test/model/ddl/Facto.ddl +225 -0
- package/test/model/fable-configuration.json +14 -0
|
@@ -0,0 +1,909 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Retold Facto — CLI Entry Point
|
|
4
|
+
*
|
|
5
|
+
* A data warehouse and knowledge graph storage system.
|
|
6
|
+
*
|
|
7
|
+
* Subcommands:
|
|
8
|
+
* serve Start the API server with web UI (default)
|
|
9
|
+
* init Initialize the database schema
|
|
10
|
+
* ingest <file> Ingest a CSV or JSON file into a dataset
|
|
11
|
+
* source list List all registered sources
|
|
12
|
+
* source add <name> Register a new data source
|
|
13
|
+
* dataset list List all datasets
|
|
14
|
+
* dataset add <name> Create a new dataset
|
|
15
|
+
*
|
|
16
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
17
|
+
*/
|
|
18
|
+
const libFable = require('pict');
|
|
19
|
+
const libMeadowConnectionManager = require('meadow-connection-manager');
|
|
20
|
+
const libRetoldFacto = require('../source/Retold-Facto.js');
|
|
21
|
+
|
|
22
|
+
const libFs = require('fs');
|
|
23
|
+
const libPath = require('path');
|
|
24
|
+
|
|
25
|
+
// ================================================================
|
|
26
|
+
// CLI Argument Parsing
|
|
27
|
+
// ================================================================
|
|
28
|
+
|
|
29
|
+
let _CLIConfig = null;
|
|
30
|
+
let _CLILogPath = null;
|
|
31
|
+
let _CLIPort = null;
|
|
32
|
+
let _CLIDBPath = null;
|
|
33
|
+
let _CLIScanPaths = null;
|
|
34
|
+
let _CLICommand = 'serve';
|
|
35
|
+
let _CLIArgs = [];
|
|
36
|
+
|
|
37
|
+
// Parse arguments
|
|
38
|
+
let tmpArgs = process.argv.slice(2);
|
|
39
|
+
let tmpPositionalIndex = 0;
|
|
40
|
+
|
|
41
|
+
for (let i = 0; i < tmpArgs.length; i++)
|
|
42
|
+
{
|
|
43
|
+
let tmpArg = tmpArgs[i];
|
|
44
|
+
|
|
45
|
+
if (tmpArg === '--config' || tmpArg === '-c')
|
|
46
|
+
{
|
|
47
|
+
if (tmpArgs[i + 1])
|
|
48
|
+
{
|
|
49
|
+
let tmpConfigPath = libPath.resolve(tmpArgs[i + 1]);
|
|
50
|
+
try
|
|
51
|
+
{
|
|
52
|
+
let tmpRaw = libFs.readFileSync(tmpConfigPath, 'utf8');
|
|
53
|
+
_CLIConfig = JSON.parse(tmpRaw);
|
|
54
|
+
console.log(`Retold Facto: Loaded config from ${tmpConfigPath}`);
|
|
55
|
+
}
|
|
56
|
+
catch (pConfigError)
|
|
57
|
+
{
|
|
58
|
+
console.error(`Retold Facto: Failed to load config from ${tmpConfigPath}: ${pConfigError.message}`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
i++;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else if (tmpArg === '--port' || tmpArg === '-p')
|
|
65
|
+
{
|
|
66
|
+
if (tmpArgs[i + 1])
|
|
67
|
+
{
|
|
68
|
+
_CLIPort = parseInt(tmpArgs[i + 1], 10);
|
|
69
|
+
i++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else if (tmpArg === '--db' || tmpArg === '-d')
|
|
73
|
+
{
|
|
74
|
+
if (tmpArgs[i + 1])
|
|
75
|
+
{
|
|
76
|
+
_CLIDBPath = libPath.resolve(tmpArgs[i + 1]);
|
|
77
|
+
i++;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else if (tmpArg === '--log' || tmpArg === '-l')
|
|
81
|
+
{
|
|
82
|
+
if (tmpArgs[i + 1] && !tmpArgs[i + 1].startsWith('-'))
|
|
83
|
+
{
|
|
84
|
+
_CLILogPath = libPath.resolve(tmpArgs[i + 1]);
|
|
85
|
+
i++;
|
|
86
|
+
}
|
|
87
|
+
else
|
|
88
|
+
{
|
|
89
|
+
_CLILogPath = `${process.cwd()}/Facto-Run-${Date.now()}.log`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (tmpArg === '--scan-path' || tmpArg === '-s')
|
|
93
|
+
{
|
|
94
|
+
if (tmpArgs[i + 1])
|
|
95
|
+
{
|
|
96
|
+
if (!_CLIScanPaths)
|
|
97
|
+
{
|
|
98
|
+
_CLIScanPaths = [];
|
|
99
|
+
}
|
|
100
|
+
_CLIScanPaths.push(libPath.resolve(tmpArgs[i + 1]));
|
|
101
|
+
i++;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else if (tmpArg === '--help' || tmpArg === '-h')
|
|
105
|
+
{
|
|
106
|
+
printHelp();
|
|
107
|
+
process.exit(0);
|
|
108
|
+
}
|
|
109
|
+
else if (!tmpArg.startsWith('-'))
|
|
110
|
+
{
|
|
111
|
+
// Positional argument
|
|
112
|
+
if (tmpPositionalIndex === 0)
|
|
113
|
+
{
|
|
114
|
+
_CLICommand = tmpArg;
|
|
115
|
+
}
|
|
116
|
+
else
|
|
117
|
+
{
|
|
118
|
+
_CLIArgs.push(tmpArg);
|
|
119
|
+
}
|
|
120
|
+
tmpPositionalIndex++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function printHelp()
|
|
125
|
+
{
|
|
126
|
+
console.log(`
|
|
127
|
+
Retold Facto — Data Warehouse and Knowledge Graph Storage
|
|
128
|
+
|
|
129
|
+
Usage:
|
|
130
|
+
retold-facto [command] [options]
|
|
131
|
+
|
|
132
|
+
Commands:
|
|
133
|
+
serve Start the API server with web UI (default)
|
|
134
|
+
init Initialize/create the database schema
|
|
135
|
+
ingest <file> Ingest a CSV or JSON file into a dataset
|
|
136
|
+
source list List all registered sources
|
|
137
|
+
source add <name> [options] Register a new data source
|
|
138
|
+
dataset list List all datasets
|
|
139
|
+
dataset add <name> [options] Create a new dataset
|
|
140
|
+
scan <folder> Scan a folder tree and list discovered datasets
|
|
141
|
+
scan provision <folder> [name] Provision one or all discovered datasets
|
|
142
|
+
scan ingest <folder> [name] Ingest one or all discovered datasets
|
|
143
|
+
|
|
144
|
+
Options:
|
|
145
|
+
--config, -c <path> Path to a JSON config file
|
|
146
|
+
--port, -p <port> Override the API server port (default: 8386)
|
|
147
|
+
--db, -d <path> Path to SQLite database file (default: ./data/facto.sqlite)
|
|
148
|
+
--scan-path, -s <path> Add a scan path for serve mode (repeatable)
|
|
149
|
+
--log, -l [path] Write log output to a file
|
|
150
|
+
--help, -h Show this help
|
|
151
|
+
|
|
152
|
+
Ingest Options (positional after file path):
|
|
153
|
+
retold-facto ingest <file> <dataset-id> <source-id> [type]
|
|
154
|
+
|
|
155
|
+
Source Add Options:
|
|
156
|
+
retold-facto source add <name> <type> [url]
|
|
157
|
+
|
|
158
|
+
Dataset Add Options:
|
|
159
|
+
retold-facto dataset add <name> <type> [description]
|
|
160
|
+
|
|
161
|
+
Examples:
|
|
162
|
+
retold-facto Start server on default port
|
|
163
|
+
retold-facto serve --port 9000 Start server on port 9000
|
|
164
|
+
retold-facto init Create database tables
|
|
165
|
+
retold-facto ingest data.csv 1 1 Ingest CSV into dataset 1 from source 1
|
|
166
|
+
retold-facto source add "Census API" API Register an API source
|
|
167
|
+
retold-facto dataset add "Pop 2020" Raw Create a Raw dataset
|
|
168
|
+
`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ================================================================
|
|
172
|
+
// Configuration
|
|
173
|
+
// ================================================================
|
|
174
|
+
|
|
175
|
+
let _Settings = (
|
|
176
|
+
{
|
|
177
|
+
Product: 'RetoldFacto',
|
|
178
|
+
ProductVersion: '0.0.1',
|
|
179
|
+
APIServerPort: _CLIPort || parseInt(process.env.PORT, 10) || 8386,
|
|
180
|
+
LogStreams:
|
|
181
|
+
[
|
|
182
|
+
{
|
|
183
|
+
streamtype: 'console'
|
|
184
|
+
}
|
|
185
|
+
],
|
|
186
|
+
|
|
187
|
+
SQLite:
|
|
188
|
+
{
|
|
189
|
+
SQLiteFilePath: _CLIDBPath || libPath.join(process.cwd(), 'data', 'facto.sqlite')
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Merge CLI config if provided
|
|
194
|
+
if (_CLIConfig)
|
|
195
|
+
{
|
|
196
|
+
Object.assign(_Settings, _CLIConfig);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (_CLILogPath)
|
|
200
|
+
{
|
|
201
|
+
_Settings.LogStreams.push(
|
|
202
|
+
{
|
|
203
|
+
loggertype: 'simpleflatfile',
|
|
204
|
+
outputloglinestoconsole: false,
|
|
205
|
+
showtimestamps: true,
|
|
206
|
+
formattedtimestamps: true,
|
|
207
|
+
level: 'trace',
|
|
208
|
+
path: _CLILogPath
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// For non-serve commands, use quieter logging
|
|
213
|
+
if (_CLICommand !== 'serve')
|
|
214
|
+
{
|
|
215
|
+
_Settings.LogStreams = [{ streamtype: 'console', level: 'warn' }];
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Ensure the data directory exists
|
|
219
|
+
let _DataDir = libPath.dirname(_Settings.SQLite.SQLiteFilePath);
|
|
220
|
+
if (_DataDir !== ':memory:' && !libFs.existsSync(_DataDir))
|
|
221
|
+
{
|
|
222
|
+
libFs.mkdirSync(_DataDir, { recursive: true });
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ================================================================
|
|
226
|
+
// Bootstrap
|
|
227
|
+
// ================================================================
|
|
228
|
+
|
|
229
|
+
let _Fable = new libFable(_Settings);
|
|
230
|
+
|
|
231
|
+
_Fable.serviceManager.addServiceType('MeadowConnectionManager', libMeadowConnectionManager);
|
|
232
|
+
_Fable.serviceManager.instantiateServiceProvider('MeadowConnectionManager');
|
|
233
|
+
|
|
234
|
+
_Fable.MeadowConnectionManager.connect('facto',
|
|
235
|
+
{
|
|
236
|
+
Type: 'SQLite',
|
|
237
|
+
SQLiteFilePath: _Settings.SQLite.SQLiteFilePath
|
|
238
|
+
},
|
|
239
|
+
(pError, pConnection) =>
|
|
240
|
+
{
|
|
241
|
+
if (pError)
|
|
242
|
+
{
|
|
243
|
+
console.error(`SQLite connection error: ${pError}`);
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Bridge: Meadow DAL providers look up fable.MeadowSQLiteProvider
|
|
248
|
+
_Fable.MeadowSQLiteProvider = pConnection.instance;
|
|
249
|
+
_Fable.settings.MeadowProvider = 'SQLite';
|
|
250
|
+
|
|
251
|
+
switch (_CLICommand)
|
|
252
|
+
{
|
|
253
|
+
case 'serve':
|
|
254
|
+
commandServe();
|
|
255
|
+
break;
|
|
256
|
+
case 'init':
|
|
257
|
+
commandInit();
|
|
258
|
+
break;
|
|
259
|
+
case 'ingest':
|
|
260
|
+
commandIngest();
|
|
261
|
+
break;
|
|
262
|
+
case 'source':
|
|
263
|
+
commandSource();
|
|
264
|
+
break;
|
|
265
|
+
case 'dataset':
|
|
266
|
+
commandDataset();
|
|
267
|
+
break;
|
|
268
|
+
case 'scan':
|
|
269
|
+
commandScan();
|
|
270
|
+
break;
|
|
271
|
+
default:
|
|
272
|
+
console.error(`Unknown command: ${_CLICommand}`);
|
|
273
|
+
printHelp();
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// ================================================================
|
|
279
|
+
// Command: serve
|
|
280
|
+
// ================================================================
|
|
281
|
+
function commandServe()
|
|
282
|
+
{
|
|
283
|
+
let tmpFactoConfig = {
|
|
284
|
+
RoutePrefix: '/facto',
|
|
285
|
+
DefaultCertaintyValue: 0.5,
|
|
286
|
+
ScanPaths: _CLIScanPaths || [],
|
|
287
|
+
AutoProvision: false,
|
|
288
|
+
AutoIngest: false
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
_Fable.serviceManager.addServiceType('RetoldFacto', libRetoldFacto);
|
|
292
|
+
let tmpFactoService = _Fable.serviceManager.instantiateServiceProvider('RetoldFacto',
|
|
293
|
+
{
|
|
294
|
+
StorageProvider: 'SQLite',
|
|
295
|
+
AutoCreateSchema: true,
|
|
296
|
+
|
|
297
|
+
FullMeadowSchemaPath: libPath.join(__dirname, '..', 'test', 'model') + '/',
|
|
298
|
+
FullMeadowSchemaFilename: 'MeadowModel-Extended.json',
|
|
299
|
+
|
|
300
|
+
Endpoints:
|
|
301
|
+
{
|
|
302
|
+
MeadowEndpoints: true,
|
|
303
|
+
SourceManager: true,
|
|
304
|
+
RecordManager: true,
|
|
305
|
+
DatasetManager: true,
|
|
306
|
+
IngestEngine: true,
|
|
307
|
+
ProjectionEngine: true,
|
|
308
|
+
CatalogManager: true,
|
|
309
|
+
StoreConnectionManager: true,
|
|
310
|
+
SourceFolderScanner: true,
|
|
311
|
+
SchemaManager: true,
|
|
312
|
+
WebUI: true
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
Facto: tmpFactoConfig
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
tmpFactoService.initializeService(
|
|
319
|
+
(pInitError) =>
|
|
320
|
+
{
|
|
321
|
+
if (pInitError)
|
|
322
|
+
{
|
|
323
|
+
_Fable.log.error(`Initialization error: ${pInitError}`);
|
|
324
|
+
process.exit(1);
|
|
325
|
+
}
|
|
326
|
+
_Fable.log.info(`Retold Facto running on port ${_Settings.APIServerPort}`);
|
|
327
|
+
_Fable.log.info(`API: http://localhost:${_Settings.APIServerPort}/1.0/`);
|
|
328
|
+
_Fable.log.info(`Facto: http://localhost:${_Settings.APIServerPort}/facto/`);
|
|
329
|
+
_Fable.log.info(`Web UI: http://localhost:${_Settings.APIServerPort}/`);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// ================================================================
|
|
334
|
+
// Command: init
|
|
335
|
+
// ================================================================
|
|
336
|
+
function commandInit()
|
|
337
|
+
{
|
|
338
|
+
console.log('Initializing Facto database schema...');
|
|
339
|
+
try
|
|
340
|
+
{
|
|
341
|
+
_Fable.MeadowSQLiteProvider.db.exec(libRetoldFacto.FACTO_SCHEMA_SQL);
|
|
342
|
+
console.log('Schema created successfully.');
|
|
343
|
+
console.log(`Database: ${_Settings.SQLite.SQLiteFilePath}`);
|
|
344
|
+
}
|
|
345
|
+
catch (pError)
|
|
346
|
+
{
|
|
347
|
+
console.error(`Error creating schema: ${pError.message}`);
|
|
348
|
+
process.exit(1);
|
|
349
|
+
}
|
|
350
|
+
process.exit(0);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// ================================================================
|
|
354
|
+
// Command: ingest <file> [dataset-id] [source-id] [type]
|
|
355
|
+
// ================================================================
|
|
356
|
+
function commandIngest()
|
|
357
|
+
{
|
|
358
|
+
let tmpFilePath = _CLIArgs[0];
|
|
359
|
+
if (!tmpFilePath)
|
|
360
|
+
{
|
|
361
|
+
console.error('Error: File path is required for ingest command.');
|
|
362
|
+
console.error('Usage: retold-facto ingest <file> [dataset-id] [source-id] [type]');
|
|
363
|
+
process.exit(1);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
tmpFilePath = libPath.resolve(tmpFilePath);
|
|
367
|
+
if (!libFs.existsSync(tmpFilePath))
|
|
368
|
+
{
|
|
369
|
+
console.error(`Error: File not found: ${tmpFilePath}`);
|
|
370
|
+
process.exit(1);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
let tmpIDDataset = parseInt(_CLIArgs[1], 10) || 1;
|
|
374
|
+
let tmpIDSource = parseInt(_CLIArgs[2], 10) || 1;
|
|
375
|
+
let tmpRecordType = _CLIArgs[3] || 'file-ingest';
|
|
376
|
+
|
|
377
|
+
// Create schema, set up service, then ingest
|
|
378
|
+
_Fable.MeadowSQLiteProvider.db.exec(libRetoldFacto.FACTO_SCHEMA_SQL);
|
|
379
|
+
|
|
380
|
+
_Fable.serviceManager.addServiceType('RetoldFacto', libRetoldFacto);
|
|
381
|
+
let tmpFactoService = _Fable.serviceManager.instantiateServiceProvider('RetoldFacto',
|
|
382
|
+
{
|
|
383
|
+
StorageProvider: 'SQLite',
|
|
384
|
+
AutoStartOrator: false,
|
|
385
|
+
|
|
386
|
+
FullMeadowSchemaPath: libPath.join(__dirname, '..', 'test', 'model') + '/',
|
|
387
|
+
FullMeadowSchemaFilename: 'MeadowModel-Extended.json',
|
|
388
|
+
|
|
389
|
+
Endpoints:
|
|
390
|
+
{
|
|
391
|
+
MeadowEndpoints: true,
|
|
392
|
+
SourceManager: false,
|
|
393
|
+
RecordManager: false,
|
|
394
|
+
DatasetManager: false,
|
|
395
|
+
IngestEngine: true,
|
|
396
|
+
ProjectionEngine: false,
|
|
397
|
+
WebUI: false
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
tmpFactoService.initializeService(
|
|
402
|
+
(pInitError) =>
|
|
403
|
+
{
|
|
404
|
+
if (pInitError)
|
|
405
|
+
{
|
|
406
|
+
console.error(`Initialization error: ${pInitError}`);
|
|
407
|
+
process.exit(1);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
console.log(`Ingesting file: ${tmpFilePath}`);
|
|
411
|
+
console.log(` Dataset ID: ${tmpIDDataset}, Source ID: ${tmpIDSource}, Type: ${tmpRecordType}`);
|
|
412
|
+
|
|
413
|
+
_Fable.RetoldFactoIngestEngine.ingestFile(tmpFilePath, tmpIDDataset, tmpIDSource,
|
|
414
|
+
{ type: tmpRecordType },
|
|
415
|
+
(pIngestError, pResult) =>
|
|
416
|
+
{
|
|
417
|
+
if (pIngestError)
|
|
418
|
+
{
|
|
419
|
+
console.error(`Ingest error: ${pIngestError.message}`);
|
|
420
|
+
process.exit(1);
|
|
421
|
+
}
|
|
422
|
+
console.log(`Ingest complete:`);
|
|
423
|
+
console.log(` Format: ${pResult.Format}`);
|
|
424
|
+
console.log(` Total: ${pResult.Total}`);
|
|
425
|
+
console.log(` Ingested: ${pResult.Ingested}`);
|
|
426
|
+
console.log(` Errors: ${pResult.Errors}`);
|
|
427
|
+
process.exit(0);
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// ================================================================
|
|
433
|
+
// Command: source [list|add]
|
|
434
|
+
// ================================================================
|
|
435
|
+
function commandSource()
|
|
436
|
+
{
|
|
437
|
+
let tmpSubCommand = _CLIArgs[0] || 'list';
|
|
438
|
+
|
|
439
|
+
// Create schema and set up service
|
|
440
|
+
_Fable.MeadowSQLiteProvider.db.exec(libRetoldFacto.FACTO_SCHEMA_SQL);
|
|
441
|
+
|
|
442
|
+
_Fable.serviceManager.addServiceType('RetoldFacto', libRetoldFacto);
|
|
443
|
+
let tmpFactoService = _Fable.serviceManager.instantiateServiceProvider('RetoldFacto',
|
|
444
|
+
{
|
|
445
|
+
StorageProvider: 'SQLite',
|
|
446
|
+
AutoStartOrator: false,
|
|
447
|
+
|
|
448
|
+
FullMeadowSchemaPath: libPath.join(__dirname, '..', 'test', 'model') + '/',
|
|
449
|
+
FullMeadowSchemaFilename: 'MeadowModel-Extended.json',
|
|
450
|
+
|
|
451
|
+
Endpoints:
|
|
452
|
+
{
|
|
453
|
+
MeadowEndpoints: true,
|
|
454
|
+
SourceManager: false,
|
|
455
|
+
RecordManager: false,
|
|
456
|
+
DatasetManager: false,
|
|
457
|
+
IngestEngine: false,
|
|
458
|
+
ProjectionEngine: false,
|
|
459
|
+
WebUI: false
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
tmpFactoService.initializeService(
|
|
464
|
+
(pInitError) =>
|
|
465
|
+
{
|
|
466
|
+
if (pInitError)
|
|
467
|
+
{
|
|
468
|
+
console.error(`Initialization error: ${pInitError}`);
|
|
469
|
+
process.exit(1);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (tmpSubCommand === 'list')
|
|
473
|
+
{
|
|
474
|
+
let tmpQuery = _Fable.DAL.Source.query.clone()
|
|
475
|
+
.addFilter('Deleted', 0)
|
|
476
|
+
.setCap(1000);
|
|
477
|
+
|
|
478
|
+
_Fable.DAL.Source.doReads(tmpQuery,
|
|
479
|
+
(pError, pQuery, pRecords) =>
|
|
480
|
+
{
|
|
481
|
+
if (pError)
|
|
482
|
+
{
|
|
483
|
+
console.error(`Error listing sources: ${pError}`);
|
|
484
|
+
process.exit(1);
|
|
485
|
+
}
|
|
486
|
+
if (pRecords.length === 0)
|
|
487
|
+
{
|
|
488
|
+
console.log('No sources registered.');
|
|
489
|
+
}
|
|
490
|
+
else
|
|
491
|
+
{
|
|
492
|
+
console.log(`\n ID | Name | Type | Active`);
|
|
493
|
+
console.log(`------+--------------------------------+----------+-------`);
|
|
494
|
+
for (let i = 0; i < pRecords.length; i++)
|
|
495
|
+
{
|
|
496
|
+
let tmpS = pRecords[i];
|
|
497
|
+
let tmpName = (tmpS.Name || '').substring(0, 30).padEnd(30);
|
|
498
|
+
let tmpType = (tmpS.Type || '').substring(0, 8).padEnd(8);
|
|
499
|
+
let tmpActive = tmpS.Active ? 'Yes' : 'No';
|
|
500
|
+
console.log(` ${String(tmpS.IDSource).padStart(3)} | ${tmpName} | ${tmpType} | ${tmpActive}`);
|
|
501
|
+
}
|
|
502
|
+
console.log(`\n Total: ${pRecords.length} source(s)`);
|
|
503
|
+
}
|
|
504
|
+
process.exit(0);
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
else if (tmpSubCommand === 'add')
|
|
508
|
+
{
|
|
509
|
+
let tmpName = _CLIArgs[1];
|
|
510
|
+
if (!tmpName)
|
|
511
|
+
{
|
|
512
|
+
console.error('Error: Name is required for source add.');
|
|
513
|
+
console.error('Usage: retold-facto source add <name> [type] [url]');
|
|
514
|
+
process.exit(1);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
let tmpType = _CLIArgs[2] || 'Manual';
|
|
518
|
+
let tmpURL = _CLIArgs[3] || '';
|
|
519
|
+
|
|
520
|
+
let tmpQuery = _Fable.DAL.Source.query.clone()
|
|
521
|
+
.addRecord(
|
|
522
|
+
{
|
|
523
|
+
Name: tmpName,
|
|
524
|
+
Type: tmpType,
|
|
525
|
+
URL: tmpURL,
|
|
526
|
+
Active: 1
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
_Fable.DAL.Source.doCreate(tmpQuery,
|
|
530
|
+
(pError, pQuery, pQueryRead, pRecord) =>
|
|
531
|
+
{
|
|
532
|
+
if (pError)
|
|
533
|
+
{
|
|
534
|
+
console.error(`Error creating source: ${pError}`);
|
|
535
|
+
process.exit(1);
|
|
536
|
+
}
|
|
537
|
+
console.log(`Source created: #${pRecord.IDSource} "${pRecord.Name}" (${pRecord.Type})`);
|
|
538
|
+
process.exit(0);
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
else
|
|
542
|
+
{
|
|
543
|
+
console.error(`Unknown source subcommand: ${tmpSubCommand}`);
|
|
544
|
+
console.error('Usage: retold-facto source [list|add]');
|
|
545
|
+
process.exit(1);
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// ================================================================
|
|
551
|
+
// Command: dataset [list|add]
|
|
552
|
+
// ================================================================
|
|
553
|
+
function commandDataset()
|
|
554
|
+
{
|
|
555
|
+
let tmpSubCommand = _CLIArgs[0] || 'list';
|
|
556
|
+
|
|
557
|
+
// Create schema and set up service
|
|
558
|
+
_Fable.MeadowSQLiteProvider.db.exec(libRetoldFacto.FACTO_SCHEMA_SQL);
|
|
559
|
+
|
|
560
|
+
_Fable.serviceManager.addServiceType('RetoldFacto', libRetoldFacto);
|
|
561
|
+
let tmpFactoService = _Fable.serviceManager.instantiateServiceProvider('RetoldFacto',
|
|
562
|
+
{
|
|
563
|
+
StorageProvider: 'SQLite',
|
|
564
|
+
AutoStartOrator: false,
|
|
565
|
+
|
|
566
|
+
FullMeadowSchemaPath: libPath.join(__dirname, '..', 'test', 'model') + '/',
|
|
567
|
+
FullMeadowSchemaFilename: 'MeadowModel-Extended.json',
|
|
568
|
+
|
|
569
|
+
Endpoints:
|
|
570
|
+
{
|
|
571
|
+
MeadowEndpoints: true,
|
|
572
|
+
SourceManager: false,
|
|
573
|
+
RecordManager: false,
|
|
574
|
+
DatasetManager: false,
|
|
575
|
+
IngestEngine: false,
|
|
576
|
+
ProjectionEngine: false,
|
|
577
|
+
WebUI: false
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
tmpFactoService.initializeService(
|
|
582
|
+
(pInitError) =>
|
|
583
|
+
{
|
|
584
|
+
if (pInitError)
|
|
585
|
+
{
|
|
586
|
+
console.error(`Initialization error: ${pInitError}`);
|
|
587
|
+
process.exit(1);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (tmpSubCommand === 'list')
|
|
591
|
+
{
|
|
592
|
+
let tmpQuery = _Fable.DAL.Dataset.query.clone()
|
|
593
|
+
.addFilter('Deleted', 0)
|
|
594
|
+
.setCap(1000);
|
|
595
|
+
|
|
596
|
+
_Fable.DAL.Dataset.doReads(tmpQuery,
|
|
597
|
+
(pError, pQuery, pRecords) =>
|
|
598
|
+
{
|
|
599
|
+
if (pError)
|
|
600
|
+
{
|
|
601
|
+
console.error(`Error listing datasets: ${pError}`);
|
|
602
|
+
process.exit(1);
|
|
603
|
+
}
|
|
604
|
+
if (pRecords.length === 0)
|
|
605
|
+
{
|
|
606
|
+
console.log('No datasets created.');
|
|
607
|
+
}
|
|
608
|
+
else
|
|
609
|
+
{
|
|
610
|
+
console.log(`\n ID | Name | Type | Description`);
|
|
611
|
+
console.log(`------+--------------------------------+----------------+----------------------------`);
|
|
612
|
+
for (let i = 0; i < pRecords.length; i++)
|
|
613
|
+
{
|
|
614
|
+
let tmpD = pRecords[i];
|
|
615
|
+
let tmpName = (tmpD.Name || '').substring(0, 30).padEnd(30);
|
|
616
|
+
let tmpType = (tmpD.Type || '').substring(0, 14).padEnd(14);
|
|
617
|
+
let tmpDesc = (tmpD.Description || '').substring(0, 26);
|
|
618
|
+
console.log(` ${String(tmpD.IDDataset).padStart(3)} | ${tmpName} | ${tmpType} | ${tmpDesc}`);
|
|
619
|
+
}
|
|
620
|
+
console.log(`\n Total: ${pRecords.length} dataset(s)`);
|
|
621
|
+
}
|
|
622
|
+
process.exit(0);
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
else if (tmpSubCommand === 'add')
|
|
626
|
+
{
|
|
627
|
+
let tmpName = _CLIArgs[1];
|
|
628
|
+
if (!tmpName)
|
|
629
|
+
{
|
|
630
|
+
console.error('Error: Name is required for dataset add.');
|
|
631
|
+
console.error('Usage: retold-facto dataset add <name> [type] [description]');
|
|
632
|
+
process.exit(1);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
let tmpType = _CLIArgs[2] || 'Raw';
|
|
636
|
+
let tmpDescription = _CLIArgs[3] || '';
|
|
637
|
+
|
|
638
|
+
let tmpValidTypes = ['Raw', 'Compositional', 'Projection', 'Derived'];
|
|
639
|
+
if (tmpValidTypes.indexOf(tmpType) < 0)
|
|
640
|
+
{
|
|
641
|
+
console.error(`Error: Invalid dataset type "${tmpType}". Valid types: ${tmpValidTypes.join(', ')}`);
|
|
642
|
+
process.exit(1);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
let tmpQuery = _Fable.DAL.Dataset.query.clone()
|
|
646
|
+
.addRecord(
|
|
647
|
+
{
|
|
648
|
+
Name: tmpName,
|
|
649
|
+
Type: tmpType,
|
|
650
|
+
Description: tmpDescription
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
_Fable.DAL.Dataset.doCreate(tmpQuery,
|
|
654
|
+
(pError, pQuery, pQueryRead, pRecord) =>
|
|
655
|
+
{
|
|
656
|
+
if (pError)
|
|
657
|
+
{
|
|
658
|
+
console.error(`Error creating dataset: ${pError}`);
|
|
659
|
+
process.exit(1);
|
|
660
|
+
}
|
|
661
|
+
console.log(`Dataset created: #${pRecord.IDDataset} "${pRecord.Name}" (${pRecord.Type})`);
|
|
662
|
+
process.exit(0);
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
else
|
|
666
|
+
{
|
|
667
|
+
console.error(`Unknown dataset subcommand: ${tmpSubCommand}`);
|
|
668
|
+
console.error('Usage: retold-facto dataset [list|add]');
|
|
669
|
+
process.exit(1);
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// ================================================================
|
|
675
|
+
// Command: scan <folder> | scan provision <folder> [name] | scan ingest <folder> [name]
|
|
676
|
+
// ================================================================
|
|
677
|
+
function commandScan()
|
|
678
|
+
{
|
|
679
|
+
let tmpSubCommand = _CLIArgs[0];
|
|
680
|
+
if (!tmpSubCommand)
|
|
681
|
+
{
|
|
682
|
+
console.error('Error: Folder path is required for scan command.');
|
|
683
|
+
console.error('Usage: retold-facto scan <folder>');
|
|
684
|
+
console.error(' retold-facto scan provision <folder> [dataset-name]');
|
|
685
|
+
console.error(' retold-facto scan ingest <folder> [dataset-name]');
|
|
686
|
+
process.exit(1);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Determine if this is a subcommand or a folder path
|
|
690
|
+
let tmpIsSubCommand = (tmpSubCommand === 'provision' || tmpSubCommand === 'ingest');
|
|
691
|
+
let tmpScanFolder;
|
|
692
|
+
let tmpDatasetName;
|
|
693
|
+
|
|
694
|
+
if (tmpIsSubCommand)
|
|
695
|
+
{
|
|
696
|
+
tmpScanFolder = _CLIArgs[1];
|
|
697
|
+
tmpDatasetName = _CLIArgs[2];
|
|
698
|
+
if (!tmpScanFolder)
|
|
699
|
+
{
|
|
700
|
+
console.error(`Error: Folder path is required for scan ${tmpSubCommand}.`);
|
|
701
|
+
process.exit(1);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
else
|
|
705
|
+
{
|
|
706
|
+
// First arg is the folder path, treat as a plain scan
|
|
707
|
+
tmpScanFolder = tmpSubCommand;
|
|
708
|
+
tmpSubCommand = 'list';
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
tmpScanFolder = libPath.resolve(tmpScanFolder);
|
|
712
|
+
|
|
713
|
+
if (!libFs.existsSync(tmpScanFolder))
|
|
714
|
+
{
|
|
715
|
+
console.error(`Error: Folder not found: ${tmpScanFolder}`);
|
|
716
|
+
process.exit(1);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Determine if provision/ingest need the full service (with DB)
|
|
720
|
+
let tmpNeedsDB = (tmpSubCommand === 'provision' || tmpSubCommand === 'ingest');
|
|
721
|
+
|
|
722
|
+
if (tmpNeedsDB)
|
|
723
|
+
{
|
|
724
|
+
_Fable.MeadowSQLiteProvider.db.exec(libRetoldFacto.FACTO_SCHEMA_SQL);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
_Fable.serviceManager.addServiceType('RetoldFacto', libRetoldFacto);
|
|
728
|
+
let tmpFactoService = _Fable.serviceManager.instantiateServiceProvider('RetoldFacto',
|
|
729
|
+
{
|
|
730
|
+
StorageProvider: 'SQLite',
|
|
731
|
+
AutoStartOrator: false,
|
|
732
|
+
AutoCreateSchema: tmpNeedsDB,
|
|
733
|
+
|
|
734
|
+
FullMeadowSchemaPath: libPath.join(__dirname, '..', 'test', 'model') + '/',
|
|
735
|
+
FullMeadowSchemaFilename: 'MeadowModel-Extended.json',
|
|
736
|
+
|
|
737
|
+
Endpoints:
|
|
738
|
+
{
|
|
739
|
+
MeadowEndpoints: tmpNeedsDB,
|
|
740
|
+
SourceManager: false,
|
|
741
|
+
RecordManager: false,
|
|
742
|
+
DatasetManager: false,
|
|
743
|
+
IngestEngine: tmpNeedsDB,
|
|
744
|
+
ProjectionEngine: false,
|
|
745
|
+
CatalogManager: tmpNeedsDB,
|
|
746
|
+
SourceFolderScanner: false,
|
|
747
|
+
WebUI: false
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
tmpFactoService.initializeService(
|
|
752
|
+
(pInitError) =>
|
|
753
|
+
{
|
|
754
|
+
if (pInitError)
|
|
755
|
+
{
|
|
756
|
+
console.error(`Initialization error: ${pInitError}`);
|
|
757
|
+
process.exit(1);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
let tmpScanner = _Fable.RetoldFactoSourceFolderScanner;
|
|
761
|
+
|
|
762
|
+
tmpScanner.addScanPath(tmpScanFolder,
|
|
763
|
+
(pScanError) =>
|
|
764
|
+
{
|
|
765
|
+
if (pScanError)
|
|
766
|
+
{
|
|
767
|
+
console.error(`Scan error: ${pScanError}`);
|
|
768
|
+
process.exit(1);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
if (tmpSubCommand === 'list')
|
|
772
|
+
{
|
|
773
|
+
let tmpDatasets = tmpScanner.getDiscoveredDatasets();
|
|
774
|
+
if (tmpDatasets.length === 0)
|
|
775
|
+
{
|
|
776
|
+
console.log('No datasets discovered.');
|
|
777
|
+
process.exit(0);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
console.log(`\nDiscovered ${tmpDatasets.length} dataset(s) in ${tmpScanFolder}\n`);
|
|
781
|
+
console.log(` ${'Name'.padEnd(40)} | ${'Status'.padEnd(12)} | ${'Data Files'.padEnd(10)} | Title`);
|
|
782
|
+
console.log(` ${''.padEnd(40, '-')}+${''.padEnd(14, '-')}+${''.padEnd(12, '-')}+${''.padEnd(30, '-')}`);
|
|
783
|
+
for (let i = 0; i < tmpDatasets.length; i++)
|
|
784
|
+
{
|
|
785
|
+
let tmpDS = tmpDatasets[i];
|
|
786
|
+
let tmpName = (tmpDS.FolderName || '').substring(0, 38).padEnd(40);
|
|
787
|
+
let tmpStatus = (tmpDS.Status || '').padEnd(12);
|
|
788
|
+
let tmpFiles = String(tmpDS.DataFiles ? tmpDS.DataFiles.length : 0).padEnd(10);
|
|
789
|
+
let tmpTitle = (tmpDS.Title || '').substring(0, 40);
|
|
790
|
+
console.log(` ${tmpName}| ${tmpStatus}| ${tmpFiles}| ${tmpTitle}`);
|
|
791
|
+
}
|
|
792
|
+
console.log('');
|
|
793
|
+
process.exit(0);
|
|
794
|
+
}
|
|
795
|
+
else if (tmpSubCommand === 'provision')
|
|
796
|
+
{
|
|
797
|
+
if (tmpDatasetName)
|
|
798
|
+
{
|
|
799
|
+
// Provision a single dataset
|
|
800
|
+
let tmpDS = tmpScanner.getDiscoveredDatasetByName(tmpDatasetName);
|
|
801
|
+
if (!tmpDS)
|
|
802
|
+
{
|
|
803
|
+
console.error(`Dataset not found: ${tmpDatasetName}`);
|
|
804
|
+
process.exit(1);
|
|
805
|
+
}
|
|
806
|
+
tmpScanner.provisionDataset(tmpDS.FolderPath,
|
|
807
|
+
(pProvError) =>
|
|
808
|
+
{
|
|
809
|
+
if (pProvError)
|
|
810
|
+
{
|
|
811
|
+
console.error(`Provision error: ${pProvError}`);
|
|
812
|
+
process.exit(1);
|
|
813
|
+
}
|
|
814
|
+
console.log(`Provisioned: ${tmpDS.FolderName} (Source #${tmpDS.IDSource}, Dataset #${tmpDS.IDDataset})`);
|
|
815
|
+
process.exit(0);
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
else
|
|
819
|
+
{
|
|
820
|
+
// Provision all
|
|
821
|
+
let tmpAllDatasets = tmpScanner.getDiscoveredDatasets();
|
|
822
|
+
let tmpCount = 0;
|
|
823
|
+
let tmpErrors = 0;
|
|
824
|
+
|
|
825
|
+
let tmpProvisionNext = () =>
|
|
826
|
+
{
|
|
827
|
+
if (tmpCount >= tmpAllDatasets.length)
|
|
828
|
+
{
|
|
829
|
+
console.log(`\nProvisioned ${tmpCount - tmpErrors} of ${tmpAllDatasets.length} dataset(s) (${tmpErrors} error(s))`);
|
|
830
|
+
process.exit(tmpErrors > 0 ? 1 : 0);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
let tmpDS = tmpAllDatasets[tmpCount];
|
|
834
|
+
tmpScanner.provisionDataset(tmpDS.FolderPath,
|
|
835
|
+
(pProvError) =>
|
|
836
|
+
{
|
|
837
|
+
if (pProvError)
|
|
838
|
+
{
|
|
839
|
+
console.error(` Error provisioning ${tmpDS.FolderName}: ${pProvError}`);
|
|
840
|
+
tmpErrors++;
|
|
841
|
+
}
|
|
842
|
+
else
|
|
843
|
+
{
|
|
844
|
+
console.log(` Provisioned: ${tmpDS.FolderName}`);
|
|
845
|
+
}
|
|
846
|
+
tmpCount++;
|
|
847
|
+
tmpProvisionNext();
|
|
848
|
+
});
|
|
849
|
+
};
|
|
850
|
+
console.log(`Provisioning ${tmpAllDatasets.length} dataset(s)...`);
|
|
851
|
+
tmpProvisionNext();
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
else if (tmpSubCommand === 'ingest')
|
|
855
|
+
{
|
|
856
|
+
if (tmpDatasetName)
|
|
857
|
+
{
|
|
858
|
+
// Ingest a single dataset
|
|
859
|
+
let tmpDS = tmpScanner.getDiscoveredDatasetByName(tmpDatasetName);
|
|
860
|
+
if (!tmpDS)
|
|
861
|
+
{
|
|
862
|
+
console.error(`Dataset not found: ${tmpDatasetName}`);
|
|
863
|
+
process.exit(1);
|
|
864
|
+
}
|
|
865
|
+
// Provision first if needed
|
|
866
|
+
let tmpDoIngest = () =>
|
|
867
|
+
{
|
|
868
|
+
tmpScanner.ingestDataset(tmpDS.FolderPath, {},
|
|
869
|
+
(pIngestError) =>
|
|
870
|
+
{
|
|
871
|
+
if (pIngestError)
|
|
872
|
+
{
|
|
873
|
+
console.error(`Ingest error: ${pIngestError}`);
|
|
874
|
+
process.exit(1);
|
|
875
|
+
}
|
|
876
|
+
console.log(`Ingested: ${tmpDS.FolderName}`);
|
|
877
|
+
process.exit(0);
|
|
878
|
+
});
|
|
879
|
+
};
|
|
880
|
+
|
|
881
|
+
if (tmpDS.Status === 'Discovered')
|
|
882
|
+
{
|
|
883
|
+
tmpScanner.provisionDataset(tmpDS.FolderPath,
|
|
884
|
+
(pProvError) =>
|
|
885
|
+
{
|
|
886
|
+
if (pProvError)
|
|
887
|
+
{
|
|
888
|
+
console.error(`Provision error: ${pProvError}`);
|
|
889
|
+
process.exit(1);
|
|
890
|
+
}
|
|
891
|
+
console.log(` Provisioned: ${tmpDS.FolderName}`);
|
|
892
|
+
tmpDoIngest();
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
else
|
|
896
|
+
{
|
|
897
|
+
tmpDoIngest();
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
else
|
|
901
|
+
{
|
|
902
|
+
console.error('Error: Dataset name is required for scan ingest.');
|
|
903
|
+
console.error('Usage: retold-facto scan ingest <folder> <dataset-name>');
|
|
904
|
+
process.exit(1);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
});
|
|
909
|
+
}
|