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,417 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retold Facto - Dataset Manager Service
|
|
3
|
+
*
|
|
4
|
+
* Manages dataset lifecycle including type validation (Raw, Compositional,
|
|
5
|
+
* Projection, Derived), source linking with reliability weights, and
|
|
6
|
+
* dataset statistics.
|
|
7
|
+
*
|
|
8
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
9
|
+
*/
|
|
10
|
+
const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
11
|
+
|
|
12
|
+
const defaultDatasetManagerOptions = (
|
|
13
|
+
{
|
|
14
|
+
RoutePrefix: '/facto'
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const VALID_DATASET_TYPES = ['Raw', 'Compositional', 'Projection', 'Derived'];
|
|
18
|
+
|
|
19
|
+
class RetoldFactoDatasetManager extends libFableServiceProviderBase
|
|
20
|
+
{
|
|
21
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
22
|
+
{
|
|
23
|
+
let tmpOptions = Object.assign({}, defaultDatasetManagerOptions, pOptions);
|
|
24
|
+
super(pFable, tmpOptions, pServiceHash);
|
|
25
|
+
|
|
26
|
+
this.serviceType = 'RetoldFactoDatasetManager';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Connect REST API routes for dataset management.
|
|
31
|
+
*
|
|
32
|
+
* @param {object} pOratorServiceServer - The Orator service server instance
|
|
33
|
+
*/
|
|
34
|
+
connectRoutes(pOratorServiceServer)
|
|
35
|
+
{
|
|
36
|
+
let tmpRoutePrefix = this.options.RoutePrefix;
|
|
37
|
+
|
|
38
|
+
// GET /facto/datasets/types -- list valid dataset types
|
|
39
|
+
pOratorServiceServer.doGet(`${tmpRoutePrefix}/datasets/types`,
|
|
40
|
+
(pRequest, pResponse, fNext) =>
|
|
41
|
+
{
|
|
42
|
+
pResponse.send({ Types: VALID_DATASET_TYPES });
|
|
43
|
+
return fNext();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// GET /facto/dataset/by-hash/:Hash -- look up a dataset by its human-readable Hash
|
|
47
|
+
pOratorServiceServer.doGet(`${tmpRoutePrefix}/dataset/by-hash/:Hash`,
|
|
48
|
+
(pRequest, pResponse, fNext) =>
|
|
49
|
+
{
|
|
50
|
+
let tmpHash = pRequest.params.Hash;
|
|
51
|
+
if (!tmpHash)
|
|
52
|
+
{
|
|
53
|
+
pResponse.send({ Error: 'Hash parameter is required' });
|
|
54
|
+
return fNext();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!this.fable.DAL || !this.fable.DAL.Dataset)
|
|
58
|
+
{
|
|
59
|
+
pResponse.send({ Error: 'Dataset DAL not initialized' });
|
|
60
|
+
return fNext();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let tmpQuery = this.fable.DAL.Dataset.query.clone()
|
|
64
|
+
.addFilter('Hash', tmpHash)
|
|
65
|
+
.addFilter('Deleted', 0);
|
|
66
|
+
|
|
67
|
+
this.fable.DAL.Dataset.doReads(tmpQuery,
|
|
68
|
+
(pError, pQuery, pRecords) =>
|
|
69
|
+
{
|
|
70
|
+
if (pError)
|
|
71
|
+
{
|
|
72
|
+
pResponse.send({ Error: pError.message || pError });
|
|
73
|
+
return fNext();
|
|
74
|
+
}
|
|
75
|
+
if (!pRecords || pRecords.length === 0)
|
|
76
|
+
{
|
|
77
|
+
pResponse.send({ Error: `No dataset found with Hash "${tmpHash}"` });
|
|
78
|
+
return fNext();
|
|
79
|
+
}
|
|
80
|
+
pResponse.send({ Dataset: pRecords[0] });
|
|
81
|
+
return fNext();
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// GET /facto/dataset/:IDDataset/stats -- get dataset statistics
|
|
86
|
+
pOratorServiceServer.doGet(`${tmpRoutePrefix}/dataset/:IDDataset/stats`,
|
|
87
|
+
(pRequest, pResponse, fNext) =>
|
|
88
|
+
{
|
|
89
|
+
let tmpIDDataset = parseInt(pRequest.params.IDDataset, 10);
|
|
90
|
+
if (isNaN(tmpIDDataset) || tmpIDDataset < 1)
|
|
91
|
+
{
|
|
92
|
+
pResponse.send({ Error: 'Invalid IDDataset parameter' });
|
|
93
|
+
return fNext();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!this.fable.DAL || !this.fable.DAL.Dataset)
|
|
97
|
+
{
|
|
98
|
+
pResponse.send({ Error: 'Dataset DAL not initialized' });
|
|
99
|
+
return fNext();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let tmpAnticipate = this.fable.newAnticipate();
|
|
103
|
+
let tmpResult = { IDDataset: tmpIDDataset, Dataset: false, RecordCount: 0, SourceCount: 0 };
|
|
104
|
+
|
|
105
|
+
// Load the dataset itself
|
|
106
|
+
tmpAnticipate.anticipate(
|
|
107
|
+
(fStep) =>
|
|
108
|
+
{
|
|
109
|
+
let tmpQuery = this.fable.DAL.Dataset.query.clone()
|
|
110
|
+
.addFilter('IDDataset', tmpIDDataset);
|
|
111
|
+
this.fable.DAL.Dataset.doRead(tmpQuery,
|
|
112
|
+
(pError, pQuery, pRecord) =>
|
|
113
|
+
{
|
|
114
|
+
if (!pError && pRecord)
|
|
115
|
+
{
|
|
116
|
+
tmpResult.Dataset = pRecord;
|
|
117
|
+
}
|
|
118
|
+
return fStep();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Count records in this dataset
|
|
123
|
+
tmpAnticipate.anticipate(
|
|
124
|
+
(fStep) =>
|
|
125
|
+
{
|
|
126
|
+
// For projection datasets, count from the projection entity's
|
|
127
|
+
// own table (registered in fable.DAL by the ProjectionEngine)
|
|
128
|
+
// rather than from the Record table.
|
|
129
|
+
if (tmpResult.Dataset && tmpResult.Dataset.Type === 'Projection' && tmpResult.Dataset.Name && this.fable.DAL[tmpResult.Dataset.Name])
|
|
130
|
+
{
|
|
131
|
+
let tmpProjectionDAL = this.fable.DAL[tmpResult.Dataset.Name];
|
|
132
|
+
let tmpQuery = tmpProjectionDAL.query.clone()
|
|
133
|
+
.addFilter('Deleted', 0);
|
|
134
|
+
tmpProjectionDAL.doCount(tmpQuery,
|
|
135
|
+
(pError, pQuery, pCount) =>
|
|
136
|
+
{
|
|
137
|
+
if (!pError)
|
|
138
|
+
{
|
|
139
|
+
tmpResult.RecordCount = pCount;
|
|
140
|
+
}
|
|
141
|
+
return fStep();
|
|
142
|
+
});
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (!this.fable.DAL.Record)
|
|
147
|
+
{
|
|
148
|
+
return fStep();
|
|
149
|
+
}
|
|
150
|
+
let tmpQuery = this.fable.DAL.Record.query.clone()
|
|
151
|
+
.addFilter('IDDataset', tmpIDDataset)
|
|
152
|
+
.addFilter('Deleted', 0);
|
|
153
|
+
this.fable.DAL.Record.doCount(tmpQuery,
|
|
154
|
+
(pError, pQuery, pCount) =>
|
|
155
|
+
{
|
|
156
|
+
if (!pError)
|
|
157
|
+
{
|
|
158
|
+
tmpResult.RecordCount = pCount;
|
|
159
|
+
}
|
|
160
|
+
return fStep();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Count linked sources
|
|
165
|
+
tmpAnticipate.anticipate(
|
|
166
|
+
(fStep) =>
|
|
167
|
+
{
|
|
168
|
+
if (!this.fable.DAL.DatasetSource)
|
|
169
|
+
{
|
|
170
|
+
return fStep();
|
|
171
|
+
}
|
|
172
|
+
let tmpQuery = this.fable.DAL.DatasetSource.query.clone()
|
|
173
|
+
.addFilter('IDDataset', tmpIDDataset)
|
|
174
|
+
.addFilter('Deleted', 0);
|
|
175
|
+
this.fable.DAL.DatasetSource.doCount(tmpQuery,
|
|
176
|
+
(pError, pQuery, pCount) =>
|
|
177
|
+
{
|
|
178
|
+
if (!pError)
|
|
179
|
+
{
|
|
180
|
+
tmpResult.SourceCount = pCount;
|
|
181
|
+
}
|
|
182
|
+
return fStep();
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
tmpAnticipate.wait(
|
|
187
|
+
(pError) =>
|
|
188
|
+
{
|
|
189
|
+
if (pError)
|
|
190
|
+
{
|
|
191
|
+
pResponse.send({ Error: pError.message || pError });
|
|
192
|
+
return fNext();
|
|
193
|
+
}
|
|
194
|
+
pResponse.send(tmpResult);
|
|
195
|
+
return fNext();
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// GET /facto/dataset/:IDDataset/sources -- list sources linked to a dataset
|
|
200
|
+
pOratorServiceServer.doGet(`${tmpRoutePrefix}/dataset/:IDDataset/sources`,
|
|
201
|
+
(pRequest, pResponse, fNext) =>
|
|
202
|
+
{
|
|
203
|
+
let tmpIDDataset = parseInt(pRequest.params.IDDataset, 10);
|
|
204
|
+
if (isNaN(tmpIDDataset) || tmpIDDataset < 1)
|
|
205
|
+
{
|
|
206
|
+
pResponse.send({ Error: 'Invalid IDDataset parameter', Sources: [] });
|
|
207
|
+
return fNext();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (!this.fable.DAL || !this.fable.DAL.DatasetSource)
|
|
211
|
+
{
|
|
212
|
+
pResponse.send({ Error: 'DatasetSource DAL not initialized', Sources: [] });
|
|
213
|
+
return fNext();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
let tmpQuery = this.fable.DAL.DatasetSource.query.clone()
|
|
217
|
+
.addFilter('IDDataset', tmpIDDataset)
|
|
218
|
+
.addFilter('Deleted', 0);
|
|
219
|
+
|
|
220
|
+
this.fable.DAL.DatasetSource.doReads(tmpQuery,
|
|
221
|
+
(pError, pQuery, pRecords) =>
|
|
222
|
+
{
|
|
223
|
+
if (pError)
|
|
224
|
+
{
|
|
225
|
+
this.fable.log.error(`DatasetManager error listing sources for dataset ${tmpIDDataset}: ${pError}`);
|
|
226
|
+
pResponse.send({ Error: pError.message || pError, Sources: [] });
|
|
227
|
+
return fNext();
|
|
228
|
+
}
|
|
229
|
+
pResponse.send({ IDDataset: tmpIDDataset, Count: pRecords.length, Sources: pRecords });
|
|
230
|
+
return fNext();
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// POST /facto/dataset/:IDDataset/source -- link a source to a dataset
|
|
235
|
+
pOratorServiceServer.doPost(`${tmpRoutePrefix}/dataset/:IDDataset/source`,
|
|
236
|
+
(pRequest, pResponse, fNext) =>
|
|
237
|
+
{
|
|
238
|
+
let tmpIDDataset = parseInt(pRequest.params.IDDataset, 10);
|
|
239
|
+
if (isNaN(tmpIDDataset) || tmpIDDataset < 1)
|
|
240
|
+
{
|
|
241
|
+
pResponse.send({ Error: 'Invalid IDDataset parameter' });
|
|
242
|
+
return fNext();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
let tmpBody = pRequest.body || {};
|
|
246
|
+
let tmpIDSource = parseInt(tmpBody.IDSource, 10);
|
|
247
|
+
if (isNaN(tmpIDSource) || tmpIDSource < 1)
|
|
248
|
+
{
|
|
249
|
+
pResponse.send({ Error: 'IDSource is required' });
|
|
250
|
+
return fNext();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (!this.fable.DAL || !this.fable.DAL.DatasetSource)
|
|
254
|
+
{
|
|
255
|
+
pResponse.send({ Error: 'DatasetSource DAL not initialized' });
|
|
256
|
+
return fNext();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
let tmpReliabilityWeight = parseFloat(tmpBody.ReliabilityWeight);
|
|
260
|
+
if (isNaN(tmpReliabilityWeight) || tmpReliabilityWeight < 0 || tmpReliabilityWeight > 1)
|
|
261
|
+
{
|
|
262
|
+
tmpReliabilityWeight = 1.0;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
let tmpQuery = this.fable.DAL.DatasetSource.query.clone()
|
|
266
|
+
.addRecord(
|
|
267
|
+
{
|
|
268
|
+
IDDataset: tmpIDDataset,
|
|
269
|
+
IDSource: tmpIDSource,
|
|
270
|
+
ReliabilityWeight: tmpReliabilityWeight
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
this.fable.DAL.DatasetSource.doCreate(tmpQuery,
|
|
274
|
+
(pError, pQuery, pQueryRead, pRecord) =>
|
|
275
|
+
{
|
|
276
|
+
if (pError)
|
|
277
|
+
{
|
|
278
|
+
this.fable.log.error(`DatasetManager error linking source ${tmpIDSource} to dataset ${tmpIDDataset}: ${pError}`);
|
|
279
|
+
pResponse.send({ Error: pError.message || pError });
|
|
280
|
+
return fNext();
|
|
281
|
+
}
|
|
282
|
+
pResponse.send({ Success: true, DatasetSource: pRecord });
|
|
283
|
+
return fNext();
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// GET /facto/dataset/:IDDataset/records -- list records in a dataset (paginated)
|
|
288
|
+
pOratorServiceServer.doGet(`${tmpRoutePrefix}/dataset/:IDDataset/records/:Begin/:Cap`,
|
|
289
|
+
(pRequest, pResponse, fNext) =>
|
|
290
|
+
{
|
|
291
|
+
let tmpIDDataset = parseInt(pRequest.params.IDDataset, 10);
|
|
292
|
+
let tmpBegin = parseInt(pRequest.params.Begin, 10) || 0;
|
|
293
|
+
let tmpCap = parseInt(pRequest.params.Cap, 10) || 50;
|
|
294
|
+
|
|
295
|
+
if (isNaN(tmpIDDataset) || tmpIDDataset < 1)
|
|
296
|
+
{
|
|
297
|
+
pResponse.send({ Error: 'Invalid IDDataset parameter', Records: [] });
|
|
298
|
+
return fNext();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (!this.fable.DAL || !this.fable.DAL.Record)
|
|
302
|
+
{
|
|
303
|
+
pResponse.send({ Error: 'Record DAL not initialized', Records: [] });
|
|
304
|
+
return fNext();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
let tmpQuery = this.fable.DAL.Record.query.clone()
|
|
308
|
+
.addFilter('IDDataset', tmpIDDataset)
|
|
309
|
+
.addFilter('Deleted', 0)
|
|
310
|
+
.setBegin(tmpBegin)
|
|
311
|
+
.setCap(tmpCap);
|
|
312
|
+
|
|
313
|
+
this.fable.DAL.Record.doReads(tmpQuery,
|
|
314
|
+
(pError, pQuery, pRecords) =>
|
|
315
|
+
{
|
|
316
|
+
if (pError)
|
|
317
|
+
{
|
|
318
|
+
pResponse.send({ Error: pError.message || pError, Records: [] });
|
|
319
|
+
return fNext();
|
|
320
|
+
}
|
|
321
|
+
pResponse.send({ IDDataset: tmpIDDataset, Begin: tmpBegin, Cap: tmpCap, Count: pRecords.length, Records: pRecords });
|
|
322
|
+
return fNext();
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// PUT /facto/dataset/:IDDataset/version-policy -- set VersionPolicy on a dataset
|
|
327
|
+
pOratorServiceServer.doPut(`${tmpRoutePrefix}/dataset/:IDDataset/version-policy`,
|
|
328
|
+
(pRequest, pResponse, fNext) =>
|
|
329
|
+
{
|
|
330
|
+
let tmpIDDataset = parseInt(pRequest.params.IDDataset, 10);
|
|
331
|
+
if (isNaN(tmpIDDataset) || tmpIDDataset < 1)
|
|
332
|
+
{
|
|
333
|
+
pResponse.send({ Error: 'Invalid IDDataset parameter' });
|
|
334
|
+
return fNext();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (!this.fable.DAL || !this.fable.DAL.Dataset)
|
|
338
|
+
{
|
|
339
|
+
pResponse.send({ Error: 'Dataset DAL not initialized' });
|
|
340
|
+
return fNext();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
let tmpBody = pRequest.body || {};
|
|
344
|
+
let tmpPolicy = tmpBody.VersionPolicy;
|
|
345
|
+
|
|
346
|
+
if (tmpPolicy !== 'Append' && tmpPolicy !== 'Replace')
|
|
347
|
+
{
|
|
348
|
+
pResponse.send({ Error: "VersionPolicy must be 'Append' or 'Replace'" });
|
|
349
|
+
return fNext();
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
let tmpQuery = this.fable.DAL.Dataset.query.clone()
|
|
353
|
+
.addRecord({ IDDataset: tmpIDDataset, VersionPolicy: tmpPolicy });
|
|
354
|
+
|
|
355
|
+
this.fable.DAL.Dataset.doUpdate(tmpQuery,
|
|
356
|
+
(pError, pQuery, pQueryRead, pRecord) =>
|
|
357
|
+
{
|
|
358
|
+
if (pError)
|
|
359
|
+
{
|
|
360
|
+
this.fable.log.error(`DatasetManager error setting VersionPolicy for dataset ${tmpIDDataset}: ${pError}`);
|
|
361
|
+
pResponse.send({ Error: pError.message || pError });
|
|
362
|
+
return fNext();
|
|
363
|
+
}
|
|
364
|
+
pResponse.send({ Success: true, Dataset: pRecord });
|
|
365
|
+
return fNext();
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// GET /facto/dataset/:IDDataset/versions -- list version history (IngestJobs) for a dataset
|
|
370
|
+
pOratorServiceServer.doGet(`${tmpRoutePrefix}/dataset/:IDDataset/versions`,
|
|
371
|
+
(pRequest, pResponse, fNext) =>
|
|
372
|
+
{
|
|
373
|
+
let tmpIDDataset = parseInt(pRequest.params.IDDataset, 10);
|
|
374
|
+
if (isNaN(tmpIDDataset) || tmpIDDataset < 1)
|
|
375
|
+
{
|
|
376
|
+
pResponse.send({ Error: 'Invalid IDDataset parameter', Versions: [] });
|
|
377
|
+
return fNext();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (!this.fable.DAL || !this.fable.DAL.IngestJob)
|
|
381
|
+
{
|
|
382
|
+
pResponse.send({ Error: 'IngestJob DAL not initialized', Versions: [] });
|
|
383
|
+
return fNext();
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
let tmpQuery = this.fable.DAL.IngestJob.query.clone()
|
|
387
|
+
.addFilter('IDDataset', tmpIDDataset)
|
|
388
|
+
.addFilter('Deleted', 0);
|
|
389
|
+
|
|
390
|
+
this.fable.DAL.IngestJob.doReads(tmpQuery,
|
|
391
|
+
(pError, pQuery, pRecords) =>
|
|
392
|
+
{
|
|
393
|
+
if (pError)
|
|
394
|
+
{
|
|
395
|
+
this.fable.log.error(`DatasetManager error listing versions for dataset ${tmpIDDataset}: ${pError}`);
|
|
396
|
+
pResponse.send({ Error: pError.message || pError, Versions: [] });
|
|
397
|
+
return fNext();
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Sort by DatasetVersion descending
|
|
401
|
+
pRecords.sort((a, b) =>
|
|
402
|
+
{
|
|
403
|
+
return (parseInt(b.DatasetVersion, 10) || 0) - (parseInt(a.DatasetVersion, 10) || 0);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
pResponse.send({ IDDataset: tmpIDDataset, Count: pRecords.length, Versions: pRecords });
|
|
407
|
+
return fNext();
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
this.fable.log.info(`DatasetManager routes connected at ${tmpRoutePrefix}/dataset(s)/*`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
module.exports = RetoldFactoDatasetManager;
|
|
416
|
+
module.exports.serviceType = 'RetoldFactoDatasetManager';
|
|
417
|
+
module.exports.default_configuration = defaultDatasetManagerOptions;
|