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,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /1.0/Comprehension/ToArray
|
|
3
|
+
*
|
|
4
|
+
* Convert an object-based comprehension into an array.
|
|
5
|
+
*
|
|
6
|
+
* Request body (JSON):
|
|
7
|
+
* {
|
|
8
|
+
* "Comprehension": { ... }, // The comprehension object to convert
|
|
9
|
+
* "Entity": "MyEntity" // (optional) Entity name; auto-inferred if omitted
|
|
10
|
+
* }
|
|
11
|
+
*
|
|
12
|
+
* OR file-based:
|
|
13
|
+
*
|
|
14
|
+
* POST /1.0/Comprehension/ToArrayFromFile
|
|
15
|
+
* {
|
|
16
|
+
* "File": "/path/to/comprehension.json", // Comprehension file path
|
|
17
|
+
* "Entity": "MyEntity" // (optional) Entity name
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* Response: JSON array of records
|
|
21
|
+
*/
|
|
22
|
+
module.exports = function(pFable, pOrator)
|
|
23
|
+
{
|
|
24
|
+
// In-memory conversion
|
|
25
|
+
pOrator.serviceServer.postWithBodyParser('/1.0/Comprehension/ToArray',
|
|
26
|
+
(pRequest, pResponse, fNext) =>
|
|
27
|
+
{
|
|
28
|
+
let tmpBody = pRequest.body || {};
|
|
29
|
+
|
|
30
|
+
if (!tmpBody.Comprehension || (typeof(tmpBody.Comprehension) !== 'object'))
|
|
31
|
+
{
|
|
32
|
+
pResponse.send(400, { Error: 'No valid Comprehension object provided in request body.' });
|
|
33
|
+
return fNext();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let tmpResult = processComprehensionToArray(pFable, tmpBody.Comprehension, tmpBody.Entity);
|
|
37
|
+
if (tmpResult.Error)
|
|
38
|
+
{
|
|
39
|
+
pResponse.send(400, tmpResult);
|
|
40
|
+
return fNext();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
pResponse.send(200, tmpResult.RecordArray);
|
|
44
|
+
return fNext();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// File-based conversion
|
|
48
|
+
pOrator.serviceServer.postWithBodyParser('/1.0/Comprehension/ToArrayFromFile',
|
|
49
|
+
(pRequest, pResponse, fNext) =>
|
|
50
|
+
{
|
|
51
|
+
let tmpBody = pRequest.body || {};
|
|
52
|
+
|
|
53
|
+
if (!tmpBody.File || (typeof(tmpBody.File) !== 'string'))
|
|
54
|
+
{
|
|
55
|
+
pResponse.send(400, { Error: 'No valid File path provided in request body.' });
|
|
56
|
+
return fNext();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
pFable.instantiateServiceProvider('FilePersistence');
|
|
60
|
+
|
|
61
|
+
let tmpFilePath = pFable.FilePersistence.resolvePath(tmpBody.File);
|
|
62
|
+
|
|
63
|
+
if (!pFable.FilePersistence.existsSync(tmpFilePath))
|
|
64
|
+
{
|
|
65
|
+
pResponse.send(404, { Error: `File [${tmpFilePath}] does not exist.` });
|
|
66
|
+
return fNext();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let tmpComprehension;
|
|
70
|
+
try
|
|
71
|
+
{
|
|
72
|
+
tmpComprehension = JSON.parse(pFable.FilePersistence.readFileSync(tmpFilePath));
|
|
73
|
+
}
|
|
74
|
+
catch (pError)
|
|
75
|
+
{
|
|
76
|
+
pResponse.send(400, { Error: `Error parsing comprehension file: ${pError.message}` });
|
|
77
|
+
return fNext();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let tmpResult = processComprehensionToArray(pFable, tmpComprehension, tmpBody.Entity);
|
|
81
|
+
if (tmpResult.Error)
|
|
82
|
+
{
|
|
83
|
+
pResponse.send(400, tmpResult);
|
|
84
|
+
return fNext();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
pResponse.send(200, tmpResult.RecordArray);
|
|
88
|
+
return fNext();
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
function processComprehensionToArray(pFable, pComprehension, pEntity)
|
|
93
|
+
{
|
|
94
|
+
let tmpEntity = pEntity;
|
|
95
|
+
|
|
96
|
+
if (!tmpEntity)
|
|
97
|
+
{
|
|
98
|
+
let tmpEntityInference = Object.keys(pComprehension);
|
|
99
|
+
if (tmpEntityInference.length > 0)
|
|
100
|
+
{
|
|
101
|
+
tmpEntity = tmpEntityInference[0];
|
|
102
|
+
pFable.log.info(`No entity specified. Using [${tmpEntity}] as the inferred entity.`);
|
|
103
|
+
}
|
|
104
|
+
else
|
|
105
|
+
{
|
|
106
|
+
return { Error: 'No entity specified and no entities found in the comprehension.' };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let tmpEntityRecords = pComprehension[tmpEntity] || {};
|
|
111
|
+
let tmpRecordArray = [];
|
|
112
|
+
|
|
113
|
+
let tmpRecordKeys = Object.keys(tmpEntityRecords);
|
|
114
|
+
for (let i = 0; i < tmpRecordKeys.length; i++)
|
|
115
|
+
{
|
|
116
|
+
tmpRecordArray.push(tmpEntityRecords[tmpRecordKeys[i]]);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
pFable.log.info(`Comprehension ToArray: Converted ${tmpRecordArray.length} records for entity [${tmpEntity}].`);
|
|
120
|
+
return { RecordArray: tmpRecordArray };
|
|
121
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
const libPath = require('path');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* POST /1.0/Comprehension/Intersect
|
|
5
|
+
*
|
|
6
|
+
* Merge two comprehension objects together.
|
|
7
|
+
*
|
|
8
|
+
* Request body (JSON):
|
|
9
|
+
* {
|
|
10
|
+
* "PrimaryComprehension": { ... }, // The primary comprehension object
|
|
11
|
+
* "SecondaryComprehension": { ... }, // The secondary comprehension to merge in
|
|
12
|
+
* "Entity": "MyEntity" // (optional) Entity name; auto-inferred if omitted
|
|
13
|
+
* }
|
|
14
|
+
*
|
|
15
|
+
* OR file-based:
|
|
16
|
+
*
|
|
17
|
+
* POST /1.0/Comprehension/IntersectFiles
|
|
18
|
+
* {
|
|
19
|
+
* "File": "/path/to/primary.json", // Primary comprehension file
|
|
20
|
+
* "IntersectFile": "/path/to/secondary.json",// Secondary comprehension file
|
|
21
|
+
* "Entity": "MyEntity" // (optional) Entity name
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* Response: Merged comprehension JSON
|
|
25
|
+
*/
|
|
26
|
+
module.exports = function(pFable, pOrator)
|
|
27
|
+
{
|
|
28
|
+
// In-memory intersection
|
|
29
|
+
pOrator.serviceServer.postWithBodyParser('/1.0/Comprehension/Intersect',
|
|
30
|
+
(pRequest, pResponse, fNext) =>
|
|
31
|
+
{
|
|
32
|
+
let tmpBody = pRequest.body || {};
|
|
33
|
+
|
|
34
|
+
if (!tmpBody.PrimaryComprehension || (typeof(tmpBody.PrimaryComprehension) !== 'object'))
|
|
35
|
+
{
|
|
36
|
+
pResponse.send(400, { Error: 'No valid PrimaryComprehension object provided in request body.' });
|
|
37
|
+
return fNext();
|
|
38
|
+
}
|
|
39
|
+
if (!tmpBody.SecondaryComprehension || (typeof(tmpBody.SecondaryComprehension) !== 'object'))
|
|
40
|
+
{
|
|
41
|
+
pResponse.send(400, { Error: 'No valid SecondaryComprehension object provided in request body.' });
|
|
42
|
+
return fNext();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let tmpResult = processComprehensionIntersect(pFable, tmpBody.PrimaryComprehension, tmpBody.SecondaryComprehension, tmpBody.Entity);
|
|
46
|
+
if (tmpResult.Error)
|
|
47
|
+
{
|
|
48
|
+
pResponse.send(400, tmpResult);
|
|
49
|
+
return fNext();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
pResponse.send(200, tmpResult.Comprehension);
|
|
53
|
+
return fNext();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// File-based intersection
|
|
57
|
+
pOrator.serviceServer.postWithBodyParser('/1.0/Comprehension/IntersectFiles',
|
|
58
|
+
(pRequest, pResponse, fNext) =>
|
|
59
|
+
{
|
|
60
|
+
let tmpBody = pRequest.body || {};
|
|
61
|
+
|
|
62
|
+
if (!tmpBody.File || (typeof(tmpBody.File) !== 'string'))
|
|
63
|
+
{
|
|
64
|
+
pResponse.send(400, { Error: 'No valid File path provided in request body.' });
|
|
65
|
+
return fNext();
|
|
66
|
+
}
|
|
67
|
+
if (!tmpBody.IntersectFile || (typeof(tmpBody.IntersectFile) !== 'string'))
|
|
68
|
+
{
|
|
69
|
+
pResponse.send(400, { Error: 'No valid IntersectFile path provided in request body.' });
|
|
70
|
+
return fNext();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
pFable.instantiateServiceProvider('FilePersistence');
|
|
74
|
+
|
|
75
|
+
let tmpPrimaryFilePath = pFable.FilePersistence.resolvePath(tmpBody.File);
|
|
76
|
+
let tmpSecondaryFilePath = pFable.FilePersistence.resolvePath(tmpBody.IntersectFile);
|
|
77
|
+
|
|
78
|
+
if (!pFable.FilePersistence.existsSync(tmpPrimaryFilePath))
|
|
79
|
+
{
|
|
80
|
+
pResponse.send(404, { Error: `Primary file [${tmpPrimaryFilePath}] does not exist.` });
|
|
81
|
+
return fNext();
|
|
82
|
+
}
|
|
83
|
+
if (!pFable.FilePersistence.existsSync(tmpSecondaryFilePath))
|
|
84
|
+
{
|
|
85
|
+
pResponse.send(404, { Error: `Secondary file [${tmpSecondaryFilePath}] does not exist.` });
|
|
86
|
+
return fNext();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let tmpPrimaryComprehension;
|
|
90
|
+
let tmpSecondaryComprehension;
|
|
91
|
+
|
|
92
|
+
try
|
|
93
|
+
{
|
|
94
|
+
tmpPrimaryComprehension = JSON.parse(pFable.FilePersistence.readFileSync(tmpPrimaryFilePath));
|
|
95
|
+
}
|
|
96
|
+
catch (pError)
|
|
97
|
+
{
|
|
98
|
+
pResponse.send(400, { Error: `Error parsing primary comprehension file: ${pError.message}` });
|
|
99
|
+
return fNext();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try
|
|
103
|
+
{
|
|
104
|
+
tmpSecondaryComprehension = JSON.parse(pFable.FilePersistence.readFileSync(tmpSecondaryFilePath));
|
|
105
|
+
}
|
|
106
|
+
catch (pError)
|
|
107
|
+
{
|
|
108
|
+
pResponse.send(400, { Error: `Error parsing secondary comprehension file: ${pError.message}` });
|
|
109
|
+
return fNext();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let tmpResult = processComprehensionIntersect(pFable, tmpPrimaryComprehension, tmpSecondaryComprehension, tmpBody.Entity);
|
|
113
|
+
if (tmpResult.Error)
|
|
114
|
+
{
|
|
115
|
+
pResponse.send(400, tmpResult);
|
|
116
|
+
return fNext();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
pResponse.send(200, tmpResult.Comprehension);
|
|
120
|
+
return fNext();
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
function processComprehensionIntersect(pFable, pPrimaryComprehension, pSecondaryComprehension, pEntity)
|
|
125
|
+
{
|
|
126
|
+
let tmpEntity = pEntity;
|
|
127
|
+
|
|
128
|
+
if (!tmpEntity)
|
|
129
|
+
{
|
|
130
|
+
let tmpEntityInference = Object.keys(pPrimaryComprehension);
|
|
131
|
+
if (tmpEntityInference.length > 0)
|
|
132
|
+
{
|
|
133
|
+
tmpEntity = tmpEntityInference[0];
|
|
134
|
+
pFable.log.info(`No entity specified. Using [${tmpEntity}] as the inferred entity.`);
|
|
135
|
+
}
|
|
136
|
+
else
|
|
137
|
+
{
|
|
138
|
+
return { Error: 'No entity specified and no entities found in the primary comprehension.' };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Deep clone the primary to avoid mutation
|
|
143
|
+
let tmpResultComprehension = JSON.parse(JSON.stringify(pPrimaryComprehension));
|
|
144
|
+
|
|
145
|
+
if (!tmpResultComprehension[tmpEntity])
|
|
146
|
+
{
|
|
147
|
+
tmpResultComprehension[tmpEntity] = {};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let tmpIntersectingKeys = Object.keys(pSecondaryComprehension[tmpEntity] || {});
|
|
151
|
+
for (let i = 0; i < tmpIntersectingKeys.length; i++)
|
|
152
|
+
{
|
|
153
|
+
const tmpRecordGUID = tmpIntersectingKeys[i];
|
|
154
|
+
if (tmpResultComprehension[tmpEntity][tmpRecordGUID])
|
|
155
|
+
{
|
|
156
|
+
tmpResultComprehension[tmpEntity][tmpRecordGUID] = Object.assign(tmpResultComprehension[tmpEntity][tmpRecordGUID], pSecondaryComprehension[tmpEntity][tmpRecordGUID]);
|
|
157
|
+
}
|
|
158
|
+
else
|
|
159
|
+
{
|
|
160
|
+
tmpResultComprehension[tmpEntity][tmpRecordGUID] = pSecondaryComprehension[tmpEntity][tmpRecordGUID];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
pFable.log.info(`Comprehension Intersect: Merged ${tmpIntersectingKeys.length} records for entity [${tmpEntity}].`);
|
|
165
|
+
return { Comprehension: tmpResultComprehension };
|
|
166
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
const libPath = require('path');
|
|
2
|
+
|
|
3
|
+
const libIntegrationAdapter = require('../../Meadow-Service-Integration-Adapter.js');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* POST /1.0/Comprehension/Push
|
|
7
|
+
*
|
|
8
|
+
* Push a comprehension to Meadow REST APIs via the Integration Adapter.
|
|
9
|
+
*
|
|
10
|
+
* Request body (JSON):
|
|
11
|
+
* {
|
|
12
|
+
* "Comprehension": { ... }, // The comprehension object to push
|
|
13
|
+
* "GUIDPrefix": "INTG-", // (optional) GUID prefix for the comprehension push
|
|
14
|
+
* "EntityGUIDPrefix": "E-", // (optional) GUID prefix per entity
|
|
15
|
+
* "ServerURL": "http://localhost:8086/1.0/" // (optional) Target Meadow API server URL
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* OR file-based:
|
|
19
|
+
*
|
|
20
|
+
* POST /1.0/Comprehension/PushFile
|
|
21
|
+
* {
|
|
22
|
+
* "File": "/path/to/comprehension.json", // Comprehension file path
|
|
23
|
+
* "GUIDPrefix": "INTG-", // (optional) GUID prefix
|
|
24
|
+
* "EntityGUIDPrefix": "E-", // (optional) Entity GUID prefix
|
|
25
|
+
* "ServerURL": "http://localhost:8086/1.0/" // (optional) Target Meadow API server URL
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
* Response: { "Success": true, "EntitiesPushed": [...], "Message": "..." }
|
|
29
|
+
*/
|
|
30
|
+
module.exports = function(pFable, pOrator)
|
|
31
|
+
{
|
|
32
|
+
// In-memory push
|
|
33
|
+
pOrator.serviceServer.postWithBodyParser('/1.0/Comprehension/Push',
|
|
34
|
+
(pRequest, pResponse, fNext) =>
|
|
35
|
+
{
|
|
36
|
+
let tmpBody = pRequest.body || {};
|
|
37
|
+
|
|
38
|
+
if (!tmpBody.Comprehension || (typeof(tmpBody.Comprehension) !== 'object'))
|
|
39
|
+
{
|
|
40
|
+
pResponse.send(400, { Error: 'No valid Comprehension object provided in request body.' });
|
|
41
|
+
return fNext();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
pushComprehension(pFable, tmpBody.Comprehension, tmpBody,
|
|
45
|
+
(pError, pResult) =>
|
|
46
|
+
{
|
|
47
|
+
if (pError)
|
|
48
|
+
{
|
|
49
|
+
pResponse.send(500, { Error: `Error pushing comprehension: ${pError.message || pError}` });
|
|
50
|
+
return fNext();
|
|
51
|
+
}
|
|
52
|
+
pResponse.send(200, pResult);
|
|
53
|
+
return fNext();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// File-based push
|
|
58
|
+
pOrator.serviceServer.postWithBodyParser('/1.0/Comprehension/PushFile',
|
|
59
|
+
(pRequest, pResponse, fNext) =>
|
|
60
|
+
{
|
|
61
|
+
let tmpBody = pRequest.body || {};
|
|
62
|
+
|
|
63
|
+
if (!tmpBody.File || (typeof(tmpBody.File) !== 'string'))
|
|
64
|
+
{
|
|
65
|
+
pResponse.send(400, { Error: 'No valid File path provided in request body.' });
|
|
66
|
+
return fNext();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
pFable.instantiateServiceProvider('FilePersistence');
|
|
70
|
+
|
|
71
|
+
let tmpFilePath = pFable.FilePersistence.resolvePath(tmpBody.File);
|
|
72
|
+
|
|
73
|
+
if (!pFable.FilePersistence.existsSync(tmpFilePath))
|
|
74
|
+
{
|
|
75
|
+
pResponse.send(404, { Error: `File [${tmpFilePath}] does not exist.` });
|
|
76
|
+
return fNext();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let tmpComprehension;
|
|
80
|
+
try
|
|
81
|
+
{
|
|
82
|
+
tmpComprehension = JSON.parse(pFable.FilePersistence.readFileSync(tmpFilePath));
|
|
83
|
+
}
|
|
84
|
+
catch (pError)
|
|
85
|
+
{
|
|
86
|
+
pResponse.send(400, { Error: `Error parsing comprehension file: ${pError.message}` });
|
|
87
|
+
return fNext();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
pushComprehension(pFable, tmpComprehension, tmpBody,
|
|
91
|
+
(pError, pResult) =>
|
|
92
|
+
{
|
|
93
|
+
if (pError)
|
|
94
|
+
{
|
|
95
|
+
pResponse.send(500, { Error: `Error pushing comprehension: ${pError.message || pError}` });
|
|
96
|
+
return fNext();
|
|
97
|
+
}
|
|
98
|
+
pResponse.send(200, pResult);
|
|
99
|
+
return fNext();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
function getCapitalLettersAsString(pInputString)
|
|
105
|
+
{
|
|
106
|
+
let tmpRegex = /[A-Z]/g;
|
|
107
|
+
let tmpMatch = pInputString.match(tmpRegex);
|
|
108
|
+
return tmpMatch ? tmpMatch.join('') : 'UNK';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function pushComprehension(pFable, pComprehension, pOptions, fCallback)
|
|
112
|
+
{
|
|
113
|
+
pFable.serviceManager.addServiceType('IntegrationAdapter', libIntegrationAdapter);
|
|
114
|
+
|
|
115
|
+
let tmpAnticipate = pFable.newAnticipate();
|
|
116
|
+
let tmpEntitiesPushed = [];
|
|
117
|
+
|
|
118
|
+
let tmpIntegrationAdapterSet = Object.keys(pComprehension);
|
|
119
|
+
|
|
120
|
+
pFable.log.info(`Pushing comprehension with ${tmpIntegrationAdapterSet.length} entity(ies) to Meadow APIs...`);
|
|
121
|
+
|
|
122
|
+
tmpAnticipate.anticipate(
|
|
123
|
+
(fDone) =>
|
|
124
|
+
{
|
|
125
|
+
try
|
|
126
|
+
{
|
|
127
|
+
for (let i = 0; i < tmpIntegrationAdapterSet.length; i++)
|
|
128
|
+
{
|
|
129
|
+
let tmpAdapterKey = tmpIntegrationAdapterSet[i];
|
|
130
|
+
let tmpAdapterOptions = { Entity: tmpAdapterKey, EntityGUIDMarshalPrefix: getCapitalLettersAsString(tmpAdapterKey) };
|
|
131
|
+
|
|
132
|
+
if (pOptions.ServerURL)
|
|
133
|
+
{
|
|
134
|
+
tmpAdapterOptions.ServerURL = pOptions.ServerURL;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
libIntegrationAdapter.getAdapter(pFable, tmpAdapterKey, getCapitalLettersAsString(tmpAdapterKey), { SimpleMarshal: true, ForceMarshal: true });
|
|
138
|
+
|
|
139
|
+
let tmpAdapter = pFable.servicesMap.IntegrationAdapter[tmpAdapterKey];
|
|
140
|
+
|
|
141
|
+
if (pOptions.GUIDPrefix)
|
|
142
|
+
{
|
|
143
|
+
tmpAdapter.AdapterSetGUIDMarshalPrefix = pOptions.GUIDPrefix;
|
|
144
|
+
}
|
|
145
|
+
if (pOptions.EntityGUIDPrefix)
|
|
146
|
+
{
|
|
147
|
+
tmpAdapter.EntityGUIDMarshalPrefix = pOptions.EntityGUIDPrefix;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let tmpDataMap = pComprehension[tmpAdapterKey];
|
|
151
|
+
if (!tmpDataMap)
|
|
152
|
+
{
|
|
153
|
+
pFable.log.info(`No records to push for [${tmpAdapterKey}].`);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
tmpEntitiesPushed.push(tmpAdapterKey);
|
|
158
|
+
|
|
159
|
+
// Add source records
|
|
160
|
+
tmpAnticipate.anticipate(
|
|
161
|
+
(function(pAdapter, pDataMap)
|
|
162
|
+
{
|
|
163
|
+
return function(fRecordDone)
|
|
164
|
+
{
|
|
165
|
+
for (const tmpRecord in pDataMap)
|
|
166
|
+
{
|
|
167
|
+
pAdapter.addSourceRecord(pDataMap[tmpRecord]);
|
|
168
|
+
}
|
|
169
|
+
return fRecordDone();
|
|
170
|
+
};
|
|
171
|
+
})(tmpAdapter, tmpDataMap));
|
|
172
|
+
|
|
173
|
+
// Integrate records
|
|
174
|
+
tmpAnticipate.anticipate(
|
|
175
|
+
(function(pAdapter)
|
|
176
|
+
{
|
|
177
|
+
return function(fIntegrateDone)
|
|
178
|
+
{
|
|
179
|
+
pAdapter.integrateRecords(fIntegrateDone);
|
|
180
|
+
};
|
|
181
|
+
})(tmpAdapter));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch (pError)
|
|
185
|
+
{
|
|
186
|
+
pFable.log.error(`Error wiring up integration adapters: ${pError}`, pError);
|
|
187
|
+
return fDone(pError);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return fDone();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
tmpAnticipate.wait(
|
|
194
|
+
(pError) =>
|
|
195
|
+
{
|
|
196
|
+
if (pError)
|
|
197
|
+
{
|
|
198
|
+
pFable.log.error(`Error pushing comprehension.`, pError);
|
|
199
|
+
return fCallback(pError);
|
|
200
|
+
}
|
|
201
|
+
pFable.log.info(`Finished pushing comprehension for entities: [${tmpEntitiesPushed.join(', ')}].`);
|
|
202
|
+
return fCallback(null,
|
|
203
|
+
{
|
|
204
|
+
Success: true,
|
|
205
|
+
EntitiesPushed: tmpEntitiesPushed,
|
|
206
|
+
Message: `Pushed comprehension for ${tmpEntitiesPushed.length} entity(ies).`
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
}
|