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.
Files changed (52) hide show
  1. package/CONTRIBUTING.md +50 -0
  2. package/README.md +223 -7
  3. package/docs/README.md +107 -7
  4. package/docs/_sidebar.md +38 -0
  5. package/docs/_topbar.md +7 -0
  6. package/docs/cli-reference.md +242 -0
  7. package/docs/comprehensions.md +98 -0
  8. package/docs/cover.md +11 -0
  9. package/docs/css/docuserve.css +73 -0
  10. package/docs/examples-walkthrough.md +138 -0
  11. package/docs/index.html +37 -20
  12. package/docs/integration-adapter.md +109 -0
  13. package/docs/mapping-files.md +140 -0
  14. package/docs/programmatic-api.md +173 -0
  15. package/docs/rest-api-reference.md +731 -0
  16. package/docs/retold-catalog.json +153 -0
  17. package/docs/retold-keyword-index.json +4828 -0
  18. package/examples/Example-001-CSV-Check.sh +29 -0
  19. package/examples/Example-002-CSV-Transform-Implicit.sh +31 -0
  20. package/examples/Example-003-CSV-Transform-CLI-Options.sh +39 -0
  21. package/examples/Example-004-CSV-Transform-Mapping-File.sh +41 -0
  22. package/examples/Example-005-Multi-Entity-Bookstore.sh +60 -0
  23. package/examples/Example-006-Multi-CSV-Intersect.sh +74 -0
  24. package/examples/Example-007-Comprehension-To-Array.sh +41 -0
  25. package/examples/Example-008-Comprehension-To-CSV.sh +51 -0
  26. package/examples/Example-009-JSON-Array-Transform.sh +46 -0
  27. package/examples/Example-010-Programmatic-API.js +138 -0
  28. package/examples/README.md +44 -0
  29. package/examples/output/.gitignore +2 -0
  30. package/package.json +7 -4
  31. package/source/Meadow-Integration.js +3 -1
  32. package/source/cli/Meadow-Integration-CLI-Program.js +4 -1
  33. package/source/cli/commands/Meadow-Integration-Command-ObjectArrayToCSV.js +49 -32
  34. package/source/cli/commands/Meadow-Integration-Command-Serve.js +51 -0
  35. package/source/restserver/Meadow-Integration-Server-Endpoints.js +83 -0
  36. package/source/restserver/Meadow-Integration-Server.js +86 -0
  37. package/source/restserver/endpoints/Endpoint-CSVCheck.js +91 -0
  38. package/source/restserver/endpoints/Endpoint-CSVTransform.js +189 -0
  39. package/source/restserver/endpoints/Endpoint-ComprehensionArray.js +121 -0
  40. package/source/restserver/endpoints/Endpoint-ComprehensionIntersect.js +166 -0
  41. package/source/restserver/endpoints/Endpoint-ComprehensionPush.js +209 -0
  42. package/source/restserver/endpoints/Endpoint-EntityFromTabularFolder.js +252 -0
  43. package/source/restserver/endpoints/Endpoint-JSONArrayTransform.js +238 -0
  44. package/source/restserver/endpoints/Endpoint-ObjectArrayToCSV.js +231 -0
  45. package/source/restserver/endpoints/Endpoint-TSVCheck.js +93 -0
  46. package/source/restserver/endpoints/Endpoint-TSVTransform.js +191 -0
  47. package/test/Meadow-Integration-Server_test.js +1170 -0
  48. package/test/data/test-comprehension-secondary.json +8 -0
  49. package/test/data/test-comprehension.json +8 -0
  50. package/test/data/test-small.csv +6 -0
  51. package/test/data/test-small.json +7 -0
  52. 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
+ }