meadow-integration 1.0.1 → 1.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/CONTRIBUTING.md +50 -0
- package/README.md +223 -7
- package/docs/README.md +107 -7
- package/docs/_sidebar.md +38 -0
- package/docs/_topbar.md +7 -0
- package/docs/cli-reference.md +242 -0
- package/docs/comprehensions.md +98 -0
- package/docs/cover.md +11 -0
- package/docs/css/docuserve.css +73 -0
- package/docs/examples-walkthrough.md +138 -0
- package/docs/index.html +37 -20
- package/docs/integration-adapter.md +109 -0
- package/docs/mapping-files.md +140 -0
- package/docs/programmatic-api.md +173 -0
- package/docs/rest-api-reference.md +731 -0
- package/docs/retold-catalog.json +153 -0
- package/docs/retold-keyword-index.json +4828 -0
- package/examples/Example-001-CSV-Check.sh +29 -0
- package/examples/Example-002-CSV-Transform-Implicit.sh +31 -0
- package/examples/Example-003-CSV-Transform-CLI-Options.sh +39 -0
- package/examples/Example-004-CSV-Transform-Mapping-File.sh +41 -0
- package/examples/Example-005-Multi-Entity-Bookstore.sh +60 -0
- package/examples/Example-006-Multi-CSV-Intersect.sh +74 -0
- package/examples/Example-007-Comprehension-To-Array.sh +41 -0
- package/examples/Example-008-Comprehension-To-CSV.sh +51 -0
- package/examples/Example-009-JSON-Array-Transform.sh +46 -0
- package/examples/Example-010-Programmatic-API.js +138 -0
- package/examples/README.md +44 -0
- package/examples/output/.gitignore +2 -0
- package/package.json +7 -4
- package/source/Meadow-Integration.js +3 -1
- package/source/cli/Meadow-Integration-CLI-Program.js +4 -1
- package/source/cli/commands/Meadow-Integration-Command-ObjectArrayToCSV.js +49 -32
- package/source/cli/commands/Meadow-Integration-Command-Serve.js +51 -0
- package/source/restserver/Meadow-Integration-Server-Endpoints.js +83 -0
- package/source/restserver/Meadow-Integration-Server.js +86 -0
- package/source/restserver/endpoints/Endpoint-CSVCheck.js +91 -0
- package/source/restserver/endpoints/Endpoint-CSVTransform.js +189 -0
- package/source/restserver/endpoints/Endpoint-ComprehensionArray.js +121 -0
- package/source/restserver/endpoints/Endpoint-ComprehensionIntersect.js +166 -0
- package/source/restserver/endpoints/Endpoint-ComprehensionPush.js +209 -0
- package/source/restserver/endpoints/Endpoint-EntityFromTabularFolder.js +252 -0
- package/source/restserver/endpoints/Endpoint-JSONArrayTransform.js +238 -0
- package/source/restserver/endpoints/Endpoint-ObjectArrayToCSV.js +231 -0
- package/source/restserver/endpoints/Endpoint-TSVCheck.js +93 -0
- package/source/restserver/endpoints/Endpoint-TSVTransform.js +191 -0
- package/test/Meadow-Integration-Server_test.js +1170 -0
- package/test/data/test-comprehension-secondary.json +8 -0
- package/test/data/test-comprehension.json +8 -0
- package/test/data/test-small.csv +6 -0
- package/test/data/test-small.json +7 -0
- package/test/data/test-small.tsv +6 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
const libFS = require('fs');
|
|
2
|
+
const libPath = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* POST /1.0/Entity/FromTabularFolder
|
|
6
|
+
*
|
|
7
|
+
* Generate entity comprehensions from tabular data files in a folder.
|
|
8
|
+
*
|
|
9
|
+
* Request body (JSON):
|
|
10
|
+
* {
|
|
11
|
+
* "Folder": "/path/to/folder/", // Folder containing tabular files
|
|
12
|
+
* "Entity": "ForcedEntity", // (optional) Force all files to a specific entity
|
|
13
|
+
* "MappingConfiguration": { ... } // (optional) Mapping hints configuration
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* Response: Comprehension JSON built from all tabular files in the folder.
|
|
17
|
+
*/
|
|
18
|
+
module.exports = function(pFable, pOrator)
|
|
19
|
+
{
|
|
20
|
+
pOrator.serviceServer.postWithBodyParser('/1.0/Entity/FromTabularFolder',
|
|
21
|
+
(pRequest, pResponse, fNext) =>
|
|
22
|
+
{
|
|
23
|
+
let tmpBody = pRequest.body || {};
|
|
24
|
+
|
|
25
|
+
if (!tmpBody.Folder || (typeof(tmpBody.Folder) !== 'string'))
|
|
26
|
+
{
|
|
27
|
+
pResponse.send(400, { Error: 'No valid Folder path provided in request body.' });
|
|
28
|
+
return fNext();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
pFable.instantiateServiceProvider('FilePersistence');
|
|
32
|
+
pFable.addAndInstantiateServiceTypeIfNotExists('MeadowIntegrationTabularTransform', require('../../services/tabular/Service-TabularTransform.js'));
|
|
33
|
+
|
|
34
|
+
let tmpFolderPath = pFable.FilePersistence.resolvePath(tmpBody.Folder);
|
|
35
|
+
|
|
36
|
+
if (!libFS.existsSync(tmpFolderPath))
|
|
37
|
+
{
|
|
38
|
+
pResponse.send(404, { Error: `Folder [${tmpFolderPath}] does not exist.` });
|
|
39
|
+
return fNext();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let tmpStat = libFS.statSync(tmpFolderPath);
|
|
43
|
+
if (!tmpStat.isDirectory())
|
|
44
|
+
{
|
|
45
|
+
pResponse.send(400, { Error: `Path [${tmpFolderPath}] is not a directory.` });
|
|
46
|
+
return fNext();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let tmpFiles;
|
|
50
|
+
try
|
|
51
|
+
{
|
|
52
|
+
tmpFiles = libFS.readdirSync(tmpFolderPath);
|
|
53
|
+
}
|
|
54
|
+
catch (pError)
|
|
55
|
+
{
|
|
56
|
+
pResponse.send(500, { Error: `Error reading folder [${tmpFolderPath}]: ${pError.message}` });
|
|
57
|
+
return fNext();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Filter to supported tabular file types
|
|
61
|
+
let tmpTabularFiles = tmpFiles.filter(
|
|
62
|
+
(pFile) =>
|
|
63
|
+
{
|
|
64
|
+
let tmpExt = libPath.extname(pFile).toLowerCase();
|
|
65
|
+
return (tmpExt === '.csv' || tmpExt === '.tsv' || tmpExt === '.json');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (tmpTabularFiles.length < 1)
|
|
69
|
+
{
|
|
70
|
+
pResponse.send(400, { Error: `No tabular files (CSV, TSV, JSON) found in folder [${tmpFolderPath}].` });
|
|
71
|
+
return fNext();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let tmpComprehension = {};
|
|
75
|
+
let tmpAnticipate = pFable.newAnticipate();
|
|
76
|
+
|
|
77
|
+
for (let f = 0; f < tmpTabularFiles.length; f++)
|
|
78
|
+
{
|
|
79
|
+
let tmpFileName = tmpTabularFiles[f];
|
|
80
|
+
let tmpFilePath = libPath.join(tmpFolderPath, tmpFileName);
|
|
81
|
+
let tmpExt = libPath.extname(tmpFileName).toLowerCase();
|
|
82
|
+
|
|
83
|
+
tmpAnticipate.anticipate(
|
|
84
|
+
(function(pFilePath, pFileName, pExt)
|
|
85
|
+
{
|
|
86
|
+
return function(fDone)
|
|
87
|
+
{
|
|
88
|
+
processTabularFile(pFable, pFilePath, pFileName, pExt, tmpBody, tmpComprehension, fDone);
|
|
89
|
+
};
|
|
90
|
+
})(tmpFilePath, tmpFileName, tmpExt));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
tmpAnticipate.wait(
|
|
94
|
+
(pError) =>
|
|
95
|
+
{
|
|
96
|
+
if (pError)
|
|
97
|
+
{
|
|
98
|
+
pFable.log.error(`Error processing tabular folder: ${pError}`, pError);
|
|
99
|
+
pResponse.send(500, { Error: `Error processing tabular folder: ${pError.message || pError}` });
|
|
100
|
+
return fNext();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let tmpEntityCount = Object.keys(tmpComprehension).length;
|
|
104
|
+
pFable.log.info(`Entity From Tabular Folder: Generated comprehension with ${tmpEntityCount} entity(ies) from ${tmpTabularFiles.length} file(s).`);
|
|
105
|
+
pResponse.send(200, tmpComprehension);
|
|
106
|
+
return fNext();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
function processTabularFile(pFable, pFilePath, pFileName, pExt, pOptions, pComprehension, fCallback)
|
|
112
|
+
{
|
|
113
|
+
let tmpMappingOutcome = pFable.MeadowIntegrationTabularTransform.newMappingOutcomeObject();
|
|
114
|
+
|
|
115
|
+
if (pOptions.Entity)
|
|
116
|
+
{
|
|
117
|
+
tmpMappingOutcome.UserConfiguration.Entity = pOptions.Entity;
|
|
118
|
+
}
|
|
119
|
+
if (pOptions.MappingConfiguration && (typeof(pOptions.MappingConfiguration) === 'object'))
|
|
120
|
+
{
|
|
121
|
+
tmpMappingOutcome.ExplicitConfiguration = pOptions.MappingConfiguration;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (pExt === '.json')
|
|
125
|
+
{
|
|
126
|
+
// JSON array file
|
|
127
|
+
let tmpRawContents;
|
|
128
|
+
try
|
|
129
|
+
{
|
|
130
|
+
tmpRawContents = pFable.FilePersistence.readFileSync(pFilePath, { encoding: 'utf8' });
|
|
131
|
+
let tmpRecords = JSON.parse(tmpRawContents);
|
|
132
|
+
if (!Array.isArray(tmpRecords))
|
|
133
|
+
{
|
|
134
|
+
pFable.log.warn(`File [${pFileName}] is not a JSON array. Skipping.`);
|
|
135
|
+
return fCallback();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
for (let i = 0; i < tmpRecords.length; i++)
|
|
139
|
+
{
|
|
140
|
+
let tmpIncomingRecord = tmpRecords[i];
|
|
141
|
+
tmpMappingOutcome.ParsedRowCount++;
|
|
142
|
+
|
|
143
|
+
if (tmpIncomingRecord)
|
|
144
|
+
{
|
|
145
|
+
if (!tmpMappingOutcome.ImplicitConfiguration)
|
|
146
|
+
{
|
|
147
|
+
tmpMappingOutcome.ImplicitConfiguration = pFable.MeadowIntegrationTabularTransform.generateMappingConfigurationPrototype(libPath.basename(pFileName, pExt), tmpIncomingRecord);
|
|
148
|
+
mergeConfiguration(tmpMappingOutcome);
|
|
149
|
+
}
|
|
150
|
+
pFable.MeadowIntegrationTabularTransform.addRecordToComprehension(tmpIncomingRecord, tmpMappingOutcome);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
mergeIntoMainComprehension(pComprehension, tmpMappingOutcome.Comprehension);
|
|
155
|
+
return fCallback();
|
|
156
|
+
}
|
|
157
|
+
catch (pError)
|
|
158
|
+
{
|
|
159
|
+
pFable.log.error(`Error parsing JSON file [${pFileName}]: ${pError.message}`);
|
|
160
|
+
return fCallback();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else
|
|
164
|
+
{
|
|
165
|
+
// Create a fresh CSVParser for each file to reset header state
|
|
166
|
+
let tmpCSVParser = pFable.instantiateServiceProviderWithoutRegistration('CSVParser');
|
|
167
|
+
|
|
168
|
+
// CSV or TSV file
|
|
169
|
+
if (pExt === '.tsv')
|
|
170
|
+
{
|
|
171
|
+
tmpCSVParser.Delimiter = '\t';
|
|
172
|
+
}
|
|
173
|
+
else
|
|
174
|
+
{
|
|
175
|
+
tmpCSVParser.Delimiter = ',';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const libReadline = require('readline');
|
|
179
|
+
const tmpReadline = libReadline.createInterface(
|
|
180
|
+
{
|
|
181
|
+
input: libFS.createReadStream(pFilePath),
|
|
182
|
+
crlfDelay: Infinity,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
tmpReadline.on('line',
|
|
186
|
+
(pLine) =>
|
|
187
|
+
{
|
|
188
|
+
const tmpIncomingRecord = tmpCSVParser.parseCSVLine(pLine);
|
|
189
|
+
tmpMappingOutcome.ParsedRowCount++;
|
|
190
|
+
|
|
191
|
+
if (tmpIncomingRecord)
|
|
192
|
+
{
|
|
193
|
+
if (!tmpMappingOutcome.ImplicitConfiguration)
|
|
194
|
+
{
|
|
195
|
+
tmpMappingOutcome.ImplicitConfiguration = pFable.MeadowIntegrationTabularTransform.generateMappingConfigurationPrototype(libPath.basename(pFileName, pExt), tmpIncomingRecord);
|
|
196
|
+
mergeConfiguration(tmpMappingOutcome);
|
|
197
|
+
}
|
|
198
|
+
pFable.MeadowIntegrationTabularTransform.addRecordToComprehension(tmpIncomingRecord, tmpMappingOutcome);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
tmpReadline.on('close',
|
|
203
|
+
() =>
|
|
204
|
+
{
|
|
205
|
+
mergeIntoMainComprehension(pComprehension, tmpMappingOutcome.Comprehension);
|
|
206
|
+
return fCallback();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
tmpReadline.on('error',
|
|
210
|
+
(pError) =>
|
|
211
|
+
{
|
|
212
|
+
pFable.log.error(`Error reading file [${pFileName}]: ${pError.message}`);
|
|
213
|
+
return fCallback();
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function mergeConfiguration(pMappingOutcome)
|
|
219
|
+
{
|
|
220
|
+
if ((!pMappingOutcome.ExplicitConfiguration) || (typeof(pMappingOutcome.ExplicitConfiguration) != 'object'))
|
|
221
|
+
{
|
|
222
|
+
pMappingOutcome.Configuration = Object.assign({}, pMappingOutcome.ImplicitConfiguration, pMappingOutcome.UserConfiguration);
|
|
223
|
+
}
|
|
224
|
+
else
|
|
225
|
+
{
|
|
226
|
+
pMappingOutcome.Configuration = Object.assign({}, pMappingOutcome.ImplicitConfiguration, pMappingOutcome.ExplicitConfiguration, pMappingOutcome.UserConfiguration);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!('GUIDName' in pMappingOutcome.Configuration))
|
|
230
|
+
{
|
|
231
|
+
pMappingOutcome.Configuration.GUIDName = `GUID${pMappingOutcome.Configuration.Entity}`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (!(pMappingOutcome.Configuration.Entity in pMappingOutcome.Comprehension))
|
|
235
|
+
{
|
|
236
|
+
pMappingOutcome.Comprehension[pMappingOutcome.Configuration.Entity] = {};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function mergeIntoMainComprehension(pMainComprehension, pFileComprehension)
|
|
241
|
+
{
|
|
242
|
+
let tmpEntities = Object.keys(pFileComprehension);
|
|
243
|
+
for (let i = 0; i < tmpEntities.length; i++)
|
|
244
|
+
{
|
|
245
|
+
let tmpEntity = tmpEntities[i];
|
|
246
|
+
if (!pMainComprehension[tmpEntity])
|
|
247
|
+
{
|
|
248
|
+
pMainComprehension[tmpEntity] = {};
|
|
249
|
+
}
|
|
250
|
+
Object.assign(pMainComprehension[tmpEntity], pFileComprehension[tmpEntity]);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
const libPath = require('path');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* POST /1.0/JSONArray/Transform
|
|
5
|
+
*
|
|
6
|
+
* Transform a JSON Array file into a comprehension.
|
|
7
|
+
*
|
|
8
|
+
* Request body (JSON):
|
|
9
|
+
* {
|
|
10
|
+
* "File": "/absolute/path/to/file.json", // The JSON array file to transform
|
|
11
|
+
* "Entity": "MyEntity", // (optional) Entity name
|
|
12
|
+
* "GUIDName": "GUIDMyEntity", // (optional) GUID column name
|
|
13
|
+
* "GUIDTemplate": "{~D:Record.id~}", // (optional) Pict template for GUID
|
|
14
|
+
* "Mappings": { "Col1": "{~D:Record.col1~}" }, // (optional) Column mappings object
|
|
15
|
+
* "MappingConfiguration": { ... }, // (optional) Full explicit mapping config
|
|
16
|
+
* "IncomingComprehension": { ... }, // (optional) Existing comprehension to merge into
|
|
17
|
+
* "Extended": false // (optional) Return full operation state
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* OR send a JSON array directly:
|
|
21
|
+
*
|
|
22
|
+
* POST /1.0/JSONArray/TransformRecords
|
|
23
|
+
* {
|
|
24
|
+
* "Records": [ {...}, {...}, ... ], // The JSON array of records
|
|
25
|
+
* "Entity": "MyEntity", // (optional) Entity name
|
|
26
|
+
* ...same options as above...
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* Response: Comprehension JSON (or extended state if Extended=true)
|
|
30
|
+
*/
|
|
31
|
+
module.exports = function(pFable, pOrator)
|
|
32
|
+
{
|
|
33
|
+
// File-based transform
|
|
34
|
+
pOrator.serviceServer.postWithBodyParser('/1.0/JSONArray/Transform',
|
|
35
|
+
(pRequest, pResponse, fNext) =>
|
|
36
|
+
{
|
|
37
|
+
let tmpBody = pRequest.body || {};
|
|
38
|
+
|
|
39
|
+
if (!tmpBody.File || (typeof(tmpBody.File) !== 'string'))
|
|
40
|
+
{
|
|
41
|
+
pResponse.send(400, { Error: 'No valid File path provided in request body.' });
|
|
42
|
+
return fNext();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
pFable.instantiateServiceProvider('FilePersistence');
|
|
46
|
+
pFable.addAndInstantiateServiceTypeIfNotExists('MeadowIntegrationTabularTransform', require('../../services/tabular/Service-TabularTransform.js'));
|
|
47
|
+
|
|
48
|
+
let tmpInputFilePath = pFable.FilePersistence.resolvePath(tmpBody.File);
|
|
49
|
+
|
|
50
|
+
if (!pFable.FilePersistence.existsSync(tmpInputFilePath))
|
|
51
|
+
{
|
|
52
|
+
pResponse.send(404, { Error: `File [${tmpInputFilePath}] does not exist.` });
|
|
53
|
+
return fNext();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let tmpJSONArrayRecords;
|
|
57
|
+
try
|
|
58
|
+
{
|
|
59
|
+
let tmpRawContents = pFable.FilePersistence.readFileSync(tmpInputFilePath, { encoding: 'utf8' });
|
|
60
|
+
tmpJSONArrayRecords = JSON.parse(tmpRawContents);
|
|
61
|
+
}
|
|
62
|
+
catch (pError)
|
|
63
|
+
{
|
|
64
|
+
pResponse.send(400, { Error: `Error parsing JSON file [${tmpInputFilePath}]: ${pError.message}` });
|
|
65
|
+
return fNext();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!Array.isArray(tmpJSONArrayRecords))
|
|
69
|
+
{
|
|
70
|
+
pResponse.send(400, { Error: `File [${tmpInputFilePath}] does not contain a valid JSON array.` });
|
|
71
|
+
return fNext();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let tmpResult = processJSONArrayTransform(pFable, tmpBody, tmpJSONArrayRecords, libPath.basename(tmpInputFilePath));
|
|
75
|
+
if (tmpResult.Error)
|
|
76
|
+
{
|
|
77
|
+
pResponse.send(400, tmpResult);
|
|
78
|
+
return fNext();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (tmpBody.Extended)
|
|
82
|
+
{
|
|
83
|
+
pResponse.send(200, tmpResult.MappingOutcome);
|
|
84
|
+
}
|
|
85
|
+
else
|
|
86
|
+
{
|
|
87
|
+
pResponse.send(200, tmpResult.MappingOutcome.Comprehension);
|
|
88
|
+
}
|
|
89
|
+
return fNext();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// In-memory records transform (no file needed)
|
|
93
|
+
pOrator.serviceServer.postWithBodyParser('/1.0/JSONArray/TransformRecords',
|
|
94
|
+
(pRequest, pResponse, fNext) =>
|
|
95
|
+
{
|
|
96
|
+
let tmpBody = pRequest.body || {};
|
|
97
|
+
|
|
98
|
+
if (!tmpBody.Records || !Array.isArray(tmpBody.Records))
|
|
99
|
+
{
|
|
100
|
+
pResponse.send(400, { Error: 'No valid Records array provided in request body.' });
|
|
101
|
+
return fNext();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (tmpBody.Records.length < 1)
|
|
105
|
+
{
|
|
106
|
+
pResponse.send(400, { Error: 'Records array is empty.' });
|
|
107
|
+
return fNext();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
pFable.addAndInstantiateServiceTypeIfNotExists('MeadowIntegrationTabularTransform', require('../../services/tabular/Service-TabularTransform.js'));
|
|
111
|
+
|
|
112
|
+
let tmpDatasetName = tmpBody.Entity || 'Records';
|
|
113
|
+
let tmpResult = processJSONArrayTransform(pFable, tmpBody, tmpBody.Records, tmpDatasetName);
|
|
114
|
+
if (tmpResult.Error)
|
|
115
|
+
{
|
|
116
|
+
pResponse.send(400, tmpResult);
|
|
117
|
+
return fNext();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (tmpBody.Extended)
|
|
121
|
+
{
|
|
122
|
+
pResponse.send(200, tmpResult.MappingOutcome);
|
|
123
|
+
}
|
|
124
|
+
else
|
|
125
|
+
{
|
|
126
|
+
pResponse.send(200, tmpResult.MappingOutcome.Comprehension);
|
|
127
|
+
}
|
|
128
|
+
return fNext();
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
function processJSONArrayTransform(pFable, pOptions, pRecords, pDatasetName)
|
|
133
|
+
{
|
|
134
|
+
let tmpMappingOutcome = pFable.MeadowIntegrationTabularTransform.newMappingOutcomeObject();
|
|
135
|
+
|
|
136
|
+
// Apply user configuration
|
|
137
|
+
if (pOptions.Entity)
|
|
138
|
+
{
|
|
139
|
+
tmpMappingOutcome.UserConfiguration.Entity = pOptions.Entity;
|
|
140
|
+
}
|
|
141
|
+
if (pOptions.GUIDName)
|
|
142
|
+
{
|
|
143
|
+
tmpMappingOutcome.UserConfiguration.GUIDName = pOptions.GUIDName;
|
|
144
|
+
}
|
|
145
|
+
if (pOptions.GUIDTemplate)
|
|
146
|
+
{
|
|
147
|
+
tmpMappingOutcome.UserConfiguration.GUIDTemplate = pOptions.GUIDTemplate;
|
|
148
|
+
}
|
|
149
|
+
if (pOptions.Mappings && (typeof(pOptions.Mappings) === 'object'))
|
|
150
|
+
{
|
|
151
|
+
tmpMappingOutcome.UserConfiguration.Mappings = pOptions.Mappings;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Apply explicit mapping configuration
|
|
155
|
+
if (pOptions.MappingConfiguration && (typeof(pOptions.MappingConfiguration) === 'object'))
|
|
156
|
+
{
|
|
157
|
+
tmpMappingOutcome.ExplicitConfiguration = pOptions.MappingConfiguration;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Apply incoming comprehension
|
|
161
|
+
if (pOptions.IncomingComprehension && (typeof(pOptions.IncomingComprehension) === 'object'))
|
|
162
|
+
{
|
|
163
|
+
tmpMappingOutcome.ExistingComprehension = pOptions.IncomingComprehension;
|
|
164
|
+
tmpMappingOutcome.Comprehension = JSON.parse(JSON.stringify(pOptions.IncomingComprehension));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for (let i = 0; i < pRecords.length; i++)
|
|
168
|
+
{
|
|
169
|
+
const tmpIncomingRecord = pRecords[i];
|
|
170
|
+
tmpMappingOutcome.ParsedRowCount++;
|
|
171
|
+
|
|
172
|
+
if (tmpIncomingRecord)
|
|
173
|
+
{
|
|
174
|
+
if (!tmpMappingOutcome.ImplicitConfiguration)
|
|
175
|
+
{
|
|
176
|
+
tmpMappingOutcome.ImplicitConfiguration = pFable.MeadowIntegrationTabularTransform.generateMappingConfigurationPrototype(pDatasetName, tmpIncomingRecord);
|
|
177
|
+
|
|
178
|
+
if ((!tmpMappingOutcome.ExplicitConfiguration) || (typeof(tmpMappingOutcome.ExplicitConfiguration) != 'object'))
|
|
179
|
+
{
|
|
180
|
+
tmpMappingOutcome.Configuration = Object.assign({}, tmpMappingOutcome.ImplicitConfiguration, tmpMappingOutcome.UserConfiguration);
|
|
181
|
+
}
|
|
182
|
+
else
|
|
183
|
+
{
|
|
184
|
+
tmpMappingOutcome.Configuration = Object.assign({}, tmpMappingOutcome.ImplicitConfiguration, tmpMappingOutcome.ExplicitConfiguration, tmpMappingOutcome.UserConfiguration);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!('GUIDName' in tmpMappingOutcome.Configuration))
|
|
188
|
+
{
|
|
189
|
+
tmpMappingOutcome.Configuration.GUIDName = `GUID${tmpMappingOutcome.Configuration.Entity}`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (!(tmpMappingOutcome.Configuration.Entity in tmpMappingOutcome.Comprehension))
|
|
193
|
+
{
|
|
194
|
+
tmpMappingOutcome.Comprehension[tmpMappingOutcome.Configuration.Entity] = {};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let tmpMappingRecordSolution = (
|
|
199
|
+
{
|
|
200
|
+
IncomingRecord: tmpIncomingRecord,
|
|
201
|
+
MappingConfiguration: tmpMappingOutcome.Configuration,
|
|
202
|
+
MappingOutcome: tmpMappingOutcome,
|
|
203
|
+
RowIndex: tmpMappingOutcome.ParsedRowCount,
|
|
204
|
+
NewRecordsGUIDUniqueness: [],
|
|
205
|
+
NewRecordPrototype: {},
|
|
206
|
+
Fable: pFable,
|
|
207
|
+
Pict: pFable,
|
|
208
|
+
AppData: pFable.AppData
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Run the solvers for this record
|
|
212
|
+
let tmpSolverResultsObject = {};
|
|
213
|
+
if (tmpMappingOutcome.Configuration.Solvers && Array.isArray(tmpMappingOutcome.Configuration.Solvers))
|
|
214
|
+
{
|
|
215
|
+
for (let j = 0; j < tmpMappingOutcome.Configuration.Solvers.length; j++)
|
|
216
|
+
{
|
|
217
|
+
let tmpSolver = tmpMappingOutcome.Configuration.Solvers[j];
|
|
218
|
+
pFable.ExpressionParser.solve(tmpSolver, tmpMappingRecordSolution, tmpSolverResultsObject, pFable.manifest, tmpMappingRecordSolution);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (tmpMappingOutcome.Configuration.MultipleGUIDUniqueness && tmpMappingRecordSolution.NewRecordsGUIDUniqueness.length > 0)
|
|
223
|
+
{
|
|
224
|
+
for (let j = 0; j < tmpMappingRecordSolution.NewRecordsGUIDUniqueness.length; j++)
|
|
225
|
+
{
|
|
226
|
+
pFable.MeadowIntegrationTabularTransform.addRecordToComprehension(tmpIncomingRecord, tmpMappingOutcome, tmpMappingRecordSolution.NewRecordPrototype, tmpMappingRecordSolution.NewRecordsGUIDUniqueness[j]);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
else if (!tmpMappingOutcome.Configuration.MultipleGUIDUniqueness)
|
|
230
|
+
{
|
|
231
|
+
pFable.MeadowIntegrationTabularTransform.addRecordToComprehension(tmpIncomingRecord, tmpMappingOutcome, tmpMappingRecordSolution.NewRecordPrototype);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
pFable.log.info(`JSON Array Transform: Parsed ${tmpMappingOutcome.ParsedRowCount} records from [${pDatasetName}].`);
|
|
237
|
+
return { MappingOutcome: tmpMappingOutcome };
|
|
238
|
+
}
|