meadow-integration 1.0.4 → 1.0.6
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/.dockerignore +11 -0
- package/Docker-Build.sh +2 -0
- package/Docker-Compose.sh +2 -0
- package/Docker-Push.sh +2 -0
- package/Docker-Tag.sh +2 -0
- package/Dockerfile +28 -0
- package/Dockerfile_LUXURYCode +23 -0
- package/README.md +139 -25
- package/docker-compose.yml +16 -0
- package/docs/README.md +65 -18
- package/docs/{cover.md → _cover.md} +3 -2
- package/docs/_sidebar.md +52 -7
- package/docs/_topbar.md +2 -0
- package/docs/api/clone-rest-client.md +278 -0
- package/docs/api/connection-manager.md +179 -0
- package/docs/api/guid-map.md +234 -0
- package/docs/api/integration-adapter.md +283 -0
- package/docs/api/operation.md +241 -0
- package/docs/api/sync-entity-initial.md +227 -0
- package/docs/api/sync-entity-ongoing.md +244 -0
- package/docs/api/sync.md +213 -0
- package/docs/api/tabular-check.md +213 -0
- package/docs/api/tabular-transform.md +316 -0
- package/docs/architecture.md +423 -0
- package/docs/cli/comprehensionarray.md +111 -0
- package/docs/cli/comprehensionintersect.md +132 -0
- package/docs/cli/csvcheck.md +111 -0
- package/docs/cli/csvtransform.md +170 -0
- package/docs/cli/data-clone.md +277 -0
- package/docs/cli/jsonarraytransform.md +166 -0
- package/docs/cli/load-comprehension.md +129 -0
- package/docs/cli/objectarraytocsv.md +159 -0
- package/docs/cli/overview.md +96 -0
- package/docs/cli/serve.md +102 -0
- package/docs/cli/tsvtransform.md +144 -0
- package/docs/data-clone/configuration.md +357 -0
- package/docs/data-clone/connection-manager.md +206 -0
- package/docs/data-clone/docker.md +290 -0
- package/docs/data-clone/overview.md +173 -0
- package/docs/data-clone/sync-modes.md +186 -0
- package/docs/implementation-reference.md +311 -0
- package/docs/overview.md +156 -0
- package/docs/quickstart.md +233 -0
- package/docs/rest/comprehension-push.md +209 -0
- package/docs/rest/comprehension.md +506 -0
- package/docs/rest/csv.md +255 -0
- package/docs/rest/entity-generation.md +158 -0
- package/docs/rest/json-array.md +243 -0
- package/docs/rest/overview.md +120 -0
- package/docs/rest/status.md +63 -0
- package/docs/rest/tsv.md +241 -0
- package/docs/retold-catalog.json +93 -3
- package/docs/retold-keyword-index.json +23683 -1901
- package/package.json +13 -10
- package/scripts/run.sh +18 -0
- package/source/Meadow-Integration.js +15 -1
- package/source/cli/Default-Meadow-Integration-Configuration.json +37 -2
- package/source/cli/Meadow-Integration-CLI-Program.js +4 -1
- package/source/cli/commands/Meadow-Integration-Command-DataClone.js +284 -0
- package/source/services/clone/Meadow-Service-ConnectionManager.js +251 -0
- package/source/services/clone/Meadow-Service-Operation.js +196 -0
- package/source/services/clone/Meadow-Service-RestClient.js +364 -0
- package/source/services/clone/Meadow-Service-Sync-Entity-Initial.js +367 -0
- package/source/services/clone/Meadow-Service-Sync-Entity-Ongoing.js +457 -0
- package/source/services/clone/Meadow-Service-Sync.js +142 -0
- /package/docs/examples/bookstore/{mapping_books_Author.json → mapping_books_author.json} +0 -0
- /package/docs/examples/bookstore/{mapping_books_Book.json → mapping_books_book.json} +0 -0
package/docs/rest/csv.md
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# CSV Endpoints
|
|
2
|
+
|
|
3
|
+
## POST /1.0/CSV/Check
|
|
4
|
+
|
|
5
|
+
Analyze a CSV file and return statistics about its structure and content.
|
|
6
|
+
|
|
7
|
+
### Request Body
|
|
8
|
+
|
|
9
|
+
| Property | Type | Required | Default | Description |
|
|
10
|
+
|----------|------|----------|---------|-------------|
|
|
11
|
+
| `File` | string | Yes | - | Absolute path to the CSV file to analyze |
|
|
12
|
+
| `Records` | boolean | No | `false` | When `true`, include all parsed records in the response |
|
|
13
|
+
| `QuoteDelimiter` | string | No | `"` | Character used for quoting fields |
|
|
14
|
+
|
|
15
|
+
### Response
|
|
16
|
+
|
|
17
|
+
A statistics object containing row count, column count, column names, and optionally the full parsed records.
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"File": "/data/books.csv",
|
|
22
|
+
"RowCount": 42,
|
|
23
|
+
"ColumnCount": 5,
|
|
24
|
+
"Columns": ["id", "title", "author", "year", "genre"],
|
|
25
|
+
"Records": []
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Error Responses
|
|
30
|
+
|
|
31
|
+
| Status | Condition |
|
|
32
|
+
|--------|-----------|
|
|
33
|
+
| 400 | No valid `File` path provided |
|
|
34
|
+
| 404 | File does not exist |
|
|
35
|
+
| 500 | Error reading the CSV file |
|
|
36
|
+
|
|
37
|
+
### curl Example
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
curl -s -X POST http://localhost:8086/1.0/CSV/Check \
|
|
41
|
+
-H "Content-Type: application/json" \
|
|
42
|
+
-d '{
|
|
43
|
+
"File": "/data/books.csv"
|
|
44
|
+
}'
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
With records included:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
curl -s -X POST http://localhost:8086/1.0/CSV/Check \
|
|
51
|
+
-H "Content-Type: application/json" \
|
|
52
|
+
-d '{
|
|
53
|
+
"File": "/data/books.csv",
|
|
54
|
+
"Records": true
|
|
55
|
+
}'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### JavaScript Example (dependency-free)
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
const http = require('http');
|
|
62
|
+
|
|
63
|
+
const requestBody = JSON.stringify({
|
|
64
|
+
File: '/data/books.csv',
|
|
65
|
+
Records: false
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const options = {
|
|
69
|
+
hostname: 'localhost',
|
|
70
|
+
port: 8086,
|
|
71
|
+
path: '/1.0/CSV/Check',
|
|
72
|
+
method: 'POST',
|
|
73
|
+
headers: {
|
|
74
|
+
'Content-Type': 'application/json',
|
|
75
|
+
'Content-Length': Buffer.byteLength(requestBody)
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const req = http.request(options, (res) => {
|
|
80
|
+
let data = '';
|
|
81
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
82
|
+
res.on('end', () => {
|
|
83
|
+
const stats = JSON.parse(data);
|
|
84
|
+
console.log(`Rows: ${stats.RowCount}, Columns: ${stats.ColumnCount}`);
|
|
85
|
+
console.log('Column names:', stats.Columns);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
req.on('error', (err) => { console.error('Request error:', err.message); });
|
|
89
|
+
req.write(requestBody);
|
|
90
|
+
req.end();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## POST /1.0/CSV/Transform
|
|
96
|
+
|
|
97
|
+
Transform a CSV file into a comprehension object. A comprehension is a GUID-keyed hash of entity records used throughout the Meadow ecosystem for data integration.
|
|
98
|
+
|
|
99
|
+
### Request Body
|
|
100
|
+
|
|
101
|
+
| Property | Type | Required | Default | Description |
|
|
102
|
+
|----------|------|----------|---------|-------------|
|
|
103
|
+
| `File` | string | Yes | - | Absolute path to the CSV file to transform |
|
|
104
|
+
| `Entity` | string | No | Derived from filename | Entity name for the comprehension |
|
|
105
|
+
| `GUIDName` | string | No | `GUID{Entity}` | Name of the GUID column in the output |
|
|
106
|
+
| `GUIDTemplate` | string | No | Auto-generated | Pict template expression for generating GUIDs (e.g. `{~D:Record.id~}`) |
|
|
107
|
+
| `Mappings` | object | No | - | Column-level mapping overrides (`{ "OutputCol": "{~D:Record.input_col~}" }`) |
|
|
108
|
+
| `MappingConfiguration` | object | No | - | Full explicit mapping configuration object |
|
|
109
|
+
| `IncomingComprehension` | object | No | `{}` | Existing comprehension to merge new records into |
|
|
110
|
+
| `Extended` | boolean | No | `false` | When `true`, return the full mapping outcome state instead of just the comprehension |
|
|
111
|
+
| `QuoteDelimiter` | string | No | `"` | Character used for quoting fields |
|
|
112
|
+
|
|
113
|
+
### Response
|
|
114
|
+
|
|
115
|
+
By default, returns the comprehension object:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"Book": {
|
|
120
|
+
"0x42": {
|
|
121
|
+
"GUIDBook": "0x42",
|
|
122
|
+
"id": "1",
|
|
123
|
+
"title": "The Great Gatsby",
|
|
124
|
+
"author": "F. Scott Fitzgerald"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
When `Extended` is `true`, the response includes the full mapping outcome with `Comprehension`, `Configuration`, `ImplicitConfiguration`, `UserConfiguration`, `ParsedRowCount`, and more.
|
|
131
|
+
|
|
132
|
+
### Error Responses
|
|
133
|
+
|
|
134
|
+
| Status | Condition |
|
|
135
|
+
|--------|-----------|
|
|
136
|
+
| 400 | No valid `File` path provided |
|
|
137
|
+
| 404 | File does not exist |
|
|
138
|
+
| 500 | Error reading the CSV file |
|
|
139
|
+
|
|
140
|
+
### curl Example
|
|
141
|
+
|
|
142
|
+
Basic transform:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
curl -s -X POST http://localhost:8086/1.0/CSV/Transform \
|
|
146
|
+
-H "Content-Type: application/json" \
|
|
147
|
+
-d '{
|
|
148
|
+
"File": "/data/books.csv"
|
|
149
|
+
}'
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
With custom entity name and GUID template:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
curl -s -X POST http://localhost:8086/1.0/CSV/Transform \
|
|
156
|
+
-H "Content-Type: application/json" \
|
|
157
|
+
-d '{
|
|
158
|
+
"File": "/data/books.csv",
|
|
159
|
+
"Entity": "Book",
|
|
160
|
+
"GUIDTemplate": "{~D:Record.id~}",
|
|
161
|
+
"Extended": true
|
|
162
|
+
}'
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
With column mappings:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
curl -s -X POST http://localhost:8086/1.0/CSV/Transform \
|
|
169
|
+
-H "Content-Type: application/json" \
|
|
170
|
+
-d '{
|
|
171
|
+
"File": "/data/books.csv",
|
|
172
|
+
"Entity": "Book",
|
|
173
|
+
"Mappings": {
|
|
174
|
+
"BookTitle": "{~D:Record.title~}",
|
|
175
|
+
"AuthorName": "{~D:Record.author~}"
|
|
176
|
+
}
|
|
177
|
+
}'
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### JavaScript Example (dependency-free)
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
const http = require('http');
|
|
184
|
+
|
|
185
|
+
const requestBody = JSON.stringify({
|
|
186
|
+
File: '/data/books.csv',
|
|
187
|
+
Entity: 'Book',
|
|
188
|
+
GUIDTemplate: '{~D:Record.id~}'
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const options = {
|
|
192
|
+
hostname: 'localhost',
|
|
193
|
+
port: 8086,
|
|
194
|
+
path: '/1.0/CSV/Transform',
|
|
195
|
+
method: 'POST',
|
|
196
|
+
headers: {
|
|
197
|
+
'Content-Type': 'application/json',
|
|
198
|
+
'Content-Length': Buffer.byteLength(requestBody)
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const req = http.request(options, (res) => {
|
|
203
|
+
let data = '';
|
|
204
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
205
|
+
res.on('end', () => {
|
|
206
|
+
const comprehension = JSON.parse(data);
|
|
207
|
+
const entityKeys = Object.keys(comprehension);
|
|
208
|
+
console.log('Entities:', entityKeys);
|
|
209
|
+
entityKeys.forEach((entity) => {
|
|
210
|
+
const recordCount = Object.keys(comprehension[entity]).length;
|
|
211
|
+
console.log(` ${entity}: ${recordCount} records`);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
req.on('error', (err) => { console.error('Request error:', err.message); });
|
|
216
|
+
req.write(requestBody);
|
|
217
|
+
req.end();
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Extended Response Example
|
|
221
|
+
|
|
222
|
+
```javascript
|
|
223
|
+
const http = require('http');
|
|
224
|
+
|
|
225
|
+
const requestBody = JSON.stringify({
|
|
226
|
+
File: '/data/books.csv',
|
|
227
|
+
Entity: 'Book',
|
|
228
|
+
Extended: true
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const options = {
|
|
232
|
+
hostname: 'localhost',
|
|
233
|
+
port: 8086,
|
|
234
|
+
path: '/1.0/CSV/Transform',
|
|
235
|
+
method: 'POST',
|
|
236
|
+
headers: {
|
|
237
|
+
'Content-Type': 'application/json',
|
|
238
|
+
'Content-Length': Buffer.byteLength(requestBody)
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const req = http.request(options, (res) => {
|
|
243
|
+
let data = '';
|
|
244
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
245
|
+
res.on('end', () => {
|
|
246
|
+
const outcome = JSON.parse(data);
|
|
247
|
+
console.log('Parsed rows:', outcome.ParsedRowCount);
|
|
248
|
+
console.log('Configuration:', JSON.stringify(outcome.Configuration, null, 2));
|
|
249
|
+
console.log('Comprehension keys:', Object.keys(outcome.Comprehension));
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
req.on('error', (err) => { console.error('Request error:', err.message); });
|
|
253
|
+
req.write(requestBody);
|
|
254
|
+
req.end();
|
|
255
|
+
```
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Entity Generation Endpoint
|
|
2
|
+
|
|
3
|
+
## POST /1.0/Entity/FromTabularFolder
|
|
4
|
+
|
|
5
|
+
Generate a combined comprehension from all tabular data files (CSV, TSV, and JSON) found in a folder. Each file becomes an entity in the output comprehension, with the entity name derived from the filename (without extension) unless overridden.
|
|
6
|
+
|
|
7
|
+
### Request Body
|
|
8
|
+
|
|
9
|
+
| Property | Type | Required | Default | Description |
|
|
10
|
+
|----------|------|----------|---------|-------------|
|
|
11
|
+
| `Folder` | string | Yes | - | Absolute path to a folder containing tabular files |
|
|
12
|
+
| `Entity` | string | No | Derived from each filename | Force all files to use the same entity name |
|
|
13
|
+
| `MappingConfiguration` | object | No | - | Mapping hints configuration applied to all files |
|
|
14
|
+
|
|
15
|
+
### Supported File Types
|
|
16
|
+
|
|
17
|
+
| Extension | Format |
|
|
18
|
+
|-----------|--------|
|
|
19
|
+
| `.csv` | Comma-separated values |
|
|
20
|
+
| `.tsv` | Tab-separated values |
|
|
21
|
+
| `.json` | JSON array of objects |
|
|
22
|
+
|
|
23
|
+
Files with other extensions are silently ignored.
|
|
24
|
+
|
|
25
|
+
### Response
|
|
26
|
+
|
|
27
|
+
Returns a comprehension object containing all entities generated from files in the folder:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"housing_costs_Neighborhoods": {
|
|
32
|
+
"0x1": { "GUIDhousing_costs_Neighborhoods": "0x1", "Neighborhood": "Ballard", "MedianRent": "1450" }
|
|
33
|
+
},
|
|
34
|
+
"race_ethnicity_Neighborhoods": {
|
|
35
|
+
"0x1": { "GUIDrace_ethnicity_Neighborhoods": "0x1", "Neighborhood": "Ballard", "White": "78.2" }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Error Responses
|
|
41
|
+
|
|
42
|
+
| Status | Condition |
|
|
43
|
+
|--------|-----------|
|
|
44
|
+
| 400 | No valid `Folder` path provided, path is not a directory, or no tabular files found |
|
|
45
|
+
| 404 | Folder does not exist |
|
|
46
|
+
| 500 | Error processing files |
|
|
47
|
+
|
|
48
|
+
### curl Example
|
|
49
|
+
|
|
50
|
+
Basic folder processing:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
curl -s -X POST http://localhost:8086/1.0/Entity/FromTabularFolder \
|
|
54
|
+
-H "Content-Type: application/json" \
|
|
55
|
+
-d '{
|
|
56
|
+
"Folder": "/data/seattle_neighborhoods/"
|
|
57
|
+
}'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
With a forced entity name:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
curl -s -X POST http://localhost:8086/1.0/Entity/FromTabularFolder \
|
|
64
|
+
-H "Content-Type: application/json" \
|
|
65
|
+
-d '{
|
|
66
|
+
"Folder": "/data/imports/",
|
|
67
|
+
"Entity": "ImportedRecord"
|
|
68
|
+
}'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### JavaScript Example (dependency-free)
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
const http = require('http');
|
|
75
|
+
|
|
76
|
+
const requestBody = JSON.stringify({
|
|
77
|
+
Folder: '/data/seattle_neighborhoods/'
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const options = {
|
|
81
|
+
hostname: 'localhost',
|
|
82
|
+
port: 8086,
|
|
83
|
+
path: '/1.0/Entity/FromTabularFolder',
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers: {
|
|
86
|
+
'Content-Type': 'application/json',
|
|
87
|
+
'Content-Length': Buffer.byteLength(requestBody)
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const req = http.request(options, (res) => {
|
|
92
|
+
let data = '';
|
|
93
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
94
|
+
res.on('end', () => {
|
|
95
|
+
const comprehension = JSON.parse(data);
|
|
96
|
+
const entities = Object.keys(comprehension);
|
|
97
|
+
console.log(`Generated ${entities.length} entity(ies):`);
|
|
98
|
+
entities.forEach((entity) => {
|
|
99
|
+
const recordCount = Object.keys(comprehension[entity]).length;
|
|
100
|
+
console.log(` ${entity}: ${recordCount} records`);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
req.on('error', (err) => { console.error('Request error:', err.message); });
|
|
105
|
+
req.write(requestBody);
|
|
106
|
+
req.end();
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### With Mapping Configuration
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
const http = require('http');
|
|
113
|
+
|
|
114
|
+
const requestBody = JSON.stringify({
|
|
115
|
+
Folder: '/data/imports/',
|
|
116
|
+
Entity: 'Product',
|
|
117
|
+
MappingConfiguration: {
|
|
118
|
+
GUIDTemplate: '{~D:Record.sku~}',
|
|
119
|
+
Mappings: {
|
|
120
|
+
ProductName: '{~D:Record.name~}',
|
|
121
|
+
UnitPrice: '{~D:Record.price~}'
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const options = {
|
|
127
|
+
hostname: 'localhost',
|
|
128
|
+
port: 8086,
|
|
129
|
+
path: '/1.0/Entity/FromTabularFolder',
|
|
130
|
+
method: 'POST',
|
|
131
|
+
headers: {
|
|
132
|
+
'Content-Type': 'application/json',
|
|
133
|
+
'Content-Length': Buffer.byteLength(requestBody)
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const req = http.request(options, (res) => {
|
|
138
|
+
let data = '';
|
|
139
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
140
|
+
res.on('end', () => {
|
|
141
|
+
const comprehension = JSON.parse(data);
|
|
142
|
+
console.log('Products generated:', JSON.stringify(comprehension, null, 2));
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
req.on('error', (err) => { console.error('Request error:', err.message); });
|
|
146
|
+
req.write(requestBody);
|
|
147
|
+
req.end();
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### How It Works
|
|
151
|
+
|
|
152
|
+
1. The endpoint reads all files in the specified folder.
|
|
153
|
+
2. Files are filtered to supported extensions (`.csv`, `.tsv`, `.json`).
|
|
154
|
+
3. Each file is processed asynchronously using the `anticipate` pattern.
|
|
155
|
+
4. For CSV and TSV files, a fresh parser is created per file with the appropriate delimiter.
|
|
156
|
+
5. For JSON files, the contents are parsed and validated as an array.
|
|
157
|
+
6. Entity names are derived from the filename (without extension) unless `Entity` is provided.
|
|
158
|
+
7. Results from all files are merged into a single comprehension object.
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# JSON Array Endpoints
|
|
2
|
+
|
|
3
|
+
## POST /1.0/JSONArray/Transform
|
|
4
|
+
|
|
5
|
+
Transform a JSON array file into a comprehension object. The file must contain a valid JSON array of objects.
|
|
6
|
+
|
|
7
|
+
### Request Body
|
|
8
|
+
|
|
9
|
+
| Property | Type | Required | Default | Description |
|
|
10
|
+
|----------|------|----------|---------|-------------|
|
|
11
|
+
| `File` | string | Yes | - | Absolute path to a JSON file containing an array of objects |
|
|
12
|
+
| `Entity` | string | No | Derived from filename | Entity name for the comprehension |
|
|
13
|
+
| `GUIDName` | string | No | `GUID{Entity}` | Name of the GUID column in the output |
|
|
14
|
+
| `GUIDTemplate` | string | No | Auto-generated | Pict template expression for generating GUIDs |
|
|
15
|
+
| `Mappings` | object | No | - | Column-level mapping overrides |
|
|
16
|
+
| `MappingConfiguration` | object | No | - | Full explicit mapping configuration object |
|
|
17
|
+
| `IncomingComprehension` | object | No | `{}` | Existing comprehension to merge new records into |
|
|
18
|
+
| `Extended` | boolean | No | `false` | Return full mapping outcome state |
|
|
19
|
+
|
|
20
|
+
### Response
|
|
21
|
+
|
|
22
|
+
Returns a comprehension object:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"Customer": {
|
|
27
|
+
"CUST-100": {
|
|
28
|
+
"GUIDCustomer": "CUST-100",
|
|
29
|
+
"id": 100,
|
|
30
|
+
"name": "Acme Corp",
|
|
31
|
+
"email": "contact@acme.com"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Error Responses
|
|
38
|
+
|
|
39
|
+
| Status | Condition |
|
|
40
|
+
|--------|-----------|
|
|
41
|
+
| 400 | No valid `File` path provided, file is not valid JSON, or file does not contain an array |
|
|
42
|
+
| 404 | File does not exist |
|
|
43
|
+
|
|
44
|
+
### curl Example
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
curl -s -X POST http://localhost:8086/1.0/JSONArray/Transform \
|
|
48
|
+
-H "Content-Type: application/json" \
|
|
49
|
+
-d '{
|
|
50
|
+
"File": "/data/customers.json",
|
|
51
|
+
"Entity": "Customer",
|
|
52
|
+
"GUIDTemplate": "{~D:Record.id~}"
|
|
53
|
+
}'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### JavaScript Example (dependency-free)
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
const http = require('http');
|
|
60
|
+
|
|
61
|
+
const requestBody = JSON.stringify({
|
|
62
|
+
File: '/data/customers.json',
|
|
63
|
+
Entity: 'Customer',
|
|
64
|
+
GUIDTemplate: '{~D:Record.id~}'
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const options = {
|
|
68
|
+
hostname: 'localhost',
|
|
69
|
+
port: 8086,
|
|
70
|
+
path: '/1.0/JSONArray/Transform',
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: {
|
|
73
|
+
'Content-Type': 'application/json',
|
|
74
|
+
'Content-Length': Buffer.byteLength(requestBody)
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const req = http.request(options, (res) => {
|
|
79
|
+
let data = '';
|
|
80
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
81
|
+
res.on('end', () => {
|
|
82
|
+
const comprehension = JSON.parse(data);
|
|
83
|
+
const entityKeys = Object.keys(comprehension);
|
|
84
|
+
console.log('Entities:', entityKeys);
|
|
85
|
+
entityKeys.forEach((entity) => {
|
|
86
|
+
const recordCount = Object.keys(comprehension[entity]).length;
|
|
87
|
+
console.log(` ${entity}: ${recordCount} records`);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
req.on('error', (err) => { console.error('Request error:', err.message); });
|
|
92
|
+
req.write(requestBody);
|
|
93
|
+
req.end();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## POST /1.0/JSONArray/TransformRecords
|
|
99
|
+
|
|
100
|
+
Transform an in-memory JSON array of records into a comprehension object. Unlike `/JSONArray/Transform`, this endpoint does not require a file on disk -- the records are passed directly in the request body.
|
|
101
|
+
|
|
102
|
+
### Request Body
|
|
103
|
+
|
|
104
|
+
| Property | Type | Required | Default | Description |
|
|
105
|
+
|----------|------|----------|---------|-------------|
|
|
106
|
+
| `Records` | array | Yes | - | Array of record objects to transform |
|
|
107
|
+
| `Entity` | string | No | `"Records"` | Entity name for the comprehension |
|
|
108
|
+
| `GUIDName` | string | No | `GUID{Entity}` | Name of the GUID column in the output |
|
|
109
|
+
| `GUIDTemplate` | string | No | Auto-generated | Pict template expression for generating GUIDs |
|
|
110
|
+
| `Mappings` | object | No | - | Column-level mapping overrides |
|
|
111
|
+
| `MappingConfiguration` | object | No | - | Full explicit mapping configuration object |
|
|
112
|
+
| `IncomingComprehension` | object | No | `{}` | Existing comprehension to merge new records into |
|
|
113
|
+
| `Extended` | boolean | No | `false` | Return full mapping outcome state |
|
|
114
|
+
|
|
115
|
+
### Response
|
|
116
|
+
|
|
117
|
+
Returns a comprehension object:
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"Order": {
|
|
122
|
+
"ORD-001": {
|
|
123
|
+
"GUIDOrder": "ORD-001",
|
|
124
|
+
"order_id": "ORD-001",
|
|
125
|
+
"customer": "Acme Corp",
|
|
126
|
+
"total": 250.00
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Error Responses
|
|
133
|
+
|
|
134
|
+
| Status | Condition |
|
|
135
|
+
|--------|-----------|
|
|
136
|
+
| 400 | No valid `Records` array provided or array is empty |
|
|
137
|
+
|
|
138
|
+
### curl Example
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
curl -s -X POST http://localhost:8086/1.0/JSONArray/TransformRecords \
|
|
142
|
+
-H "Content-Type: application/json" \
|
|
143
|
+
-d '{
|
|
144
|
+
"Records": [
|
|
145
|
+
{ "order_id": "ORD-001", "customer": "Acme Corp", "total": 250.00 },
|
|
146
|
+
{ "order_id": "ORD-002", "customer": "Globex Inc", "total": 175.50 }
|
|
147
|
+
],
|
|
148
|
+
"Entity": "Order",
|
|
149
|
+
"GUIDTemplate": "{~D:Record.order_id~}"
|
|
150
|
+
}'
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### JavaScript Example (dependency-free)
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
const http = require('http');
|
|
157
|
+
|
|
158
|
+
const requestBody = JSON.stringify({
|
|
159
|
+
Records: [
|
|
160
|
+
{ order_id: 'ORD-001', customer: 'Acme Corp', total: 250.00 },
|
|
161
|
+
{ order_id: 'ORD-002', customer: 'Globex Inc', total: 175.50 },
|
|
162
|
+
{ order_id: 'ORD-003', customer: 'Initech', total: 320.00 }
|
|
163
|
+
],
|
|
164
|
+
Entity: 'Order',
|
|
165
|
+
GUIDTemplate: '{~D:Record.order_id~}'
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const options = {
|
|
169
|
+
hostname: 'localhost',
|
|
170
|
+
port: 8086,
|
|
171
|
+
path: '/1.0/JSONArray/TransformRecords',
|
|
172
|
+
method: 'POST',
|
|
173
|
+
headers: {
|
|
174
|
+
'Content-Type': 'application/json',
|
|
175
|
+
'Content-Length': Buffer.byteLength(requestBody)
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const req = http.request(options, (res) => {
|
|
180
|
+
let data = '';
|
|
181
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
182
|
+
res.on('end', () => {
|
|
183
|
+
const comprehension = JSON.parse(data);
|
|
184
|
+
console.log('Order comprehension:');
|
|
185
|
+
const orders = comprehension.Order;
|
|
186
|
+
Object.keys(orders).forEach((guid) => {
|
|
187
|
+
console.log(` ${guid}: ${orders[guid].customer} - $${orders[guid].total}`);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
req.on('error', (err) => { console.error('Request error:', err.message); });
|
|
192
|
+
req.write(requestBody);
|
|
193
|
+
req.end();
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### In-Memory Pipeline Example
|
|
197
|
+
|
|
198
|
+
This example shows how to use `TransformRecords` to convert data already in memory (e.g. from an API response) into a comprehension without writing to disk:
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
const http = require('http');
|
|
202
|
+
|
|
203
|
+
// Simulate records from an external API
|
|
204
|
+
const externalData = [
|
|
205
|
+
{ id: 1, name: 'Alice', department: 'Engineering' },
|
|
206
|
+
{ id: 2, name: 'Bob', department: 'Marketing' },
|
|
207
|
+
{ id: 3, name: 'Carol', department: 'Engineering' }
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
const requestBody = JSON.stringify({
|
|
211
|
+
Records: externalData,
|
|
212
|
+
Entity: 'Employee',
|
|
213
|
+
GUIDTemplate: '{~D:Record.id~}',
|
|
214
|
+
Mappings: {
|
|
215
|
+
FullName: '{~D:Record.name~}',
|
|
216
|
+
Dept: '{~D:Record.department~}'
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const options = {
|
|
221
|
+
hostname: 'localhost',
|
|
222
|
+
port: 8086,
|
|
223
|
+
path: '/1.0/JSONArray/TransformRecords',
|
|
224
|
+
method: 'POST',
|
|
225
|
+
headers: {
|
|
226
|
+
'Content-Type': 'application/json',
|
|
227
|
+
'Content-Length': Buffer.byteLength(requestBody)
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const req = http.request(options, (res) => {
|
|
232
|
+
let data = '';
|
|
233
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
234
|
+
res.on('end', () => {
|
|
235
|
+
const comprehension = JSON.parse(data);
|
|
236
|
+
console.log('Employee comprehension created with',
|
|
237
|
+
Object.keys(comprehension.Employee).length, 'records');
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
req.on('error', (err) => { console.error('Request error:', err.message); });
|
|
241
|
+
req.write(requestBody);
|
|
242
|
+
req.end();
|
|
243
|
+
```
|