meadow-integration 1.0.18 → 1.0.20
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/docs/README.md +1 -0
- package/docs/_sidebar.md +6 -1
- package/docs/comprehension-push/configuration.md +308 -0
- package/docs/integration-adapter.md +164 -15
- package/examples/example-comprehension-push.meadow.config.json +23 -0
- package/package.json +4 -2
- package/source/Meadow-Integration.js +21 -1
- package/source/Meadow-Service-Integration-Adapter.js +678 -245
- package/source/Meadow-Service-Integration-GUIDMap.js +19 -2
- package/source/cli/commands/Meadow-Integration-Command-ComprehensionPush.js +210 -38
- package/source/services/clone/Meadow-Service-RestClient.js +46 -0
- package/source/services/parser/Service-FileParser-CSV.js +263 -0
- package/source/services/parser/Service-FileParser-FixedWidth.js +158 -0
- package/source/services/parser/Service-FileParser-JSON.js +255 -0
- package/source/services/parser/Service-FileParser-XLSX.js +194 -0
- package/source/services/parser/Service-FileParser-XML.js +190 -0
- package/source/services/parser/Service-FileParser.js +142 -0
- package/test/Meadow-Integration-ComprehensionPush_test.js +580 -0
package/docs/README.md
CHANGED
|
@@ -158,5 +158,6 @@ Both pipelines share the Fable service provider pattern for dependency injection
|
|
|
158
158
|
- [Comprehensions](comprehensions.md) -- The comprehension data format in detail
|
|
159
159
|
- [Programmatic API](programmatic-api.md) -- Using services directly in your code
|
|
160
160
|
- [Integration Adapter](integration-adapter.md) -- Pushing data to Meadow REST APIs
|
|
161
|
+
- [Comprehension Push Configuration](comprehension-push/configuration.md) -- CLI options and config file for pushing comprehensions
|
|
161
162
|
- [Data Clone Overview](data-clone/overview.md) -- Synchronizing remote APIs to local databases
|
|
162
163
|
- [Examples](examples-walkthrough.md) -- Walkthrough of all runnable examples
|
package/docs/_sidebar.md
CHANGED
|
@@ -14,12 +14,17 @@
|
|
|
14
14
|
- [Comprehensions](comprehensions.md)
|
|
15
15
|
- [Mapping Files](mapping-files.md)
|
|
16
16
|
|
|
17
|
+
- Comprehension Push
|
|
18
|
+
|
|
19
|
+
- [Integration Adapter](integration-adapter.md)
|
|
20
|
+
- [Push Configuration](comprehension-push/configuration.md)
|
|
21
|
+
|
|
17
22
|
- Data Synchronization
|
|
18
23
|
|
|
19
24
|
- [Data Clone Overview](data-clone/overview.md)
|
|
20
25
|
- [Connection Manager](data-clone/connection-manager.md)
|
|
21
26
|
- [Sync Modes](data-clone/sync-modes.md)
|
|
22
|
-
- [Configuration](data-clone/configuration.md)
|
|
27
|
+
- [Clone Configuration](data-clone/configuration.md)
|
|
23
28
|
- [Docker Deployment](data-clone/docker.md)
|
|
24
29
|
|
|
25
30
|
- CLI Reference
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# Comprehension Push Configuration Reference
|
|
2
|
+
|
|
3
|
+
The `load_comprehension` command (aliases: `load`, `push`) pushes a comprehension JSON file to a Meadow REST API using the Integration Adapter service. It is configured through a `.meadow.config.json` file and/or CLI flags.
|
|
4
|
+
|
|
5
|
+
## Configuration File
|
|
6
|
+
|
|
7
|
+
The configuration file is named `.meadow.config.json`. The CLI toolkit automatically searches for this file using the same cascading strategy as the data-clone command.
|
|
8
|
+
|
|
9
|
+
### Cascading Resolution Order
|
|
10
|
+
|
|
11
|
+
1. **Current working directory** -- `.meadow.config.json` in the directory where the command is run.
|
|
12
|
+
2. **Parent directories** -- Walks up the directory tree searching for `.meadow.config.json`.
|
|
13
|
+
3. **Home directory** -- `~/.meadow.config.json` as a fallback.
|
|
14
|
+
|
|
15
|
+
Settings from the configuration file are merged with the built-in defaults. CLI flags override configuration file values.
|
|
16
|
+
|
|
17
|
+
## Full Configuration Schema
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"Source": {
|
|
22
|
+
"ServerURL": "https://api.example.com/1.0/",
|
|
23
|
+
"UserID": "integration_user",
|
|
24
|
+
"Password": "integration_password"
|
|
25
|
+
},
|
|
26
|
+
"SessionManager": {
|
|
27
|
+
"Sessions": {
|
|
28
|
+
"Default": {
|
|
29
|
+
"ServerURL": "https://api.example.com/1.0/",
|
|
30
|
+
"Credentials": {
|
|
31
|
+
"username": "session_user",
|
|
32
|
+
"password": "session_password"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The `load_comprehension` command uses the same `Source` block as the `data-clone` command, and the same `SessionManager` block. The `Destination`, `SchemaPath`, and `Sync` blocks are not used by this command.
|
|
41
|
+
|
|
42
|
+
## Section Reference
|
|
43
|
+
|
|
44
|
+
### Source
|
|
45
|
+
|
|
46
|
+
Configuration for the Meadow REST API server to push comprehension data to.
|
|
47
|
+
|
|
48
|
+
| Property | Type | Default | Description |
|
|
49
|
+
|----------|------|---------|-------------|
|
|
50
|
+
| `ServerURL` | string | `"https://localhost:8080/1.0/"` | Base URL of the Meadow API (must include trailing `/1.0/`) |
|
|
51
|
+
| `UserID` | string or false | `false` | Username for API authentication; set to `false` to skip built-in auth |
|
|
52
|
+
| `Password` | string or false | `false` | Password for API authentication; set to `false` to skip built-in auth |
|
|
53
|
+
|
|
54
|
+
When `UserID` and `Password` are both `false`, the command skips built-in authentication. You can still authenticate via `SessionManager` (see below).
|
|
55
|
+
|
|
56
|
+
### SessionManager
|
|
57
|
+
|
|
58
|
+
Optional session-based authentication using `pict-sessionmanager`. This is the same mechanism used by the `data-clone` command, so the same `.meadow.config.json` file can drive both operations against the same server.
|
|
59
|
+
|
|
60
|
+
| Property | Type | Default | Description |
|
|
61
|
+
|----------|------|---------|-------------|
|
|
62
|
+
| `Sessions` | object | `{}` | Map of session name to session configuration |
|
|
63
|
+
|
|
64
|
+
Each session is an object with:
|
|
65
|
+
|
|
66
|
+
| Property | Type | Description |
|
|
67
|
+
|----------|------|-------------|
|
|
68
|
+
| `ServerURL` | string | The server URL for this session |
|
|
69
|
+
| `Credentials` | object | Credential key-value pairs to pass to the authenticator |
|
|
70
|
+
|
|
71
|
+
When `SessionManager.Sessions` is empty or omitted, session-based authentication is skipped.
|
|
72
|
+
|
|
73
|
+
#### Authentication Order
|
|
74
|
+
|
|
75
|
+
The command authenticates in this order:
|
|
76
|
+
|
|
77
|
+
1. **SessionManager** -- If `SessionManager.Sessions` is configured, each session is authenticated first. The SessionManager is connected to the REST client so credentials are auto-injected into all subsequent requests.
|
|
78
|
+
2. **Built-in credentials** -- If `Source.UserID` and `Source.Password` are set, the command authenticates with the API using the standard `Authenticate` endpoint.
|
|
79
|
+
3. **No authentication** -- If neither is configured, requests are made unauthenticated.
|
|
80
|
+
|
|
81
|
+
Both mechanisms can be used together. SessionManager handles token/cookie injection while built-in auth provides a direct login.
|
|
82
|
+
|
|
83
|
+
## CLI Flag Overrides
|
|
84
|
+
|
|
85
|
+
All configuration values can be overridden via CLI flags. CLI flags take precedence over values from `.meadow.config.json`.
|
|
86
|
+
|
|
87
|
+
### Source (API) Flags
|
|
88
|
+
|
|
89
|
+
| Flag | Long Form | Description |
|
|
90
|
+
|------|-----------|-------------|
|
|
91
|
+
| `-a` | `--api_server` | API server URL (maps to `Source.ServerURL`) |
|
|
92
|
+
| `-u` | `--api_username` | API username (maps to `Source.UserID`) |
|
|
93
|
+
| `-w` | `--api_password` | API password (maps to `Source.Password`) |
|
|
94
|
+
|
|
95
|
+
### GUID Flags
|
|
96
|
+
|
|
97
|
+
| Flag | Long Form | Default | Description |
|
|
98
|
+
|------|-----------|---------|-------------|
|
|
99
|
+
| `-p` | `--prefix` | `"INTG-DEF"` | Adapter-set GUID marshal prefix. Applied to all entities. |
|
|
100
|
+
| `-e` | `--entityguidprefix` | Auto-generated | Per-entity GUID marshal prefix. Overrides the auto-generated prefix for all entities. |
|
|
101
|
+
| | `--allowguidtruncation` | `false` | Allow automatic prefix truncation when a generated GUID exceeds the server's column size. Without this flag, oversized GUIDs cause an error. |
|
|
102
|
+
|
|
103
|
+
The generated Meadow GUID follows this pattern:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
{prefix}-{entityguidprefix}-{ExternalGUID}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
For example, with `--prefix "MYAPP"` and entity `Book` with a comprehension GUID of `Book_1`:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
MYAPP-E-Book-Book_1
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
When `--entityguidprefix` is not specified, the auto-generated prefix is the capital letters of the entity name (e.g. `Book` -> `B`, `BookAuthorJoin` -> `BAJ`).
|
|
116
|
+
|
|
117
|
+
### Batch and Performance Flags
|
|
118
|
+
|
|
119
|
+
| Flag | Long Form | Default | Description |
|
|
120
|
+
|------|-----------|---------|-------------|
|
|
121
|
+
| | `--bulkupsert` | `"true"` | Enable bulk upsert mode. Set to `"false"` to force single-record upserts. |
|
|
122
|
+
| | `--batchsize` | `100` | Number of records per bulk upsert batch. |
|
|
123
|
+
| | `--progressinterval` | `100` | How often (in records) to log per-entity progress. |
|
|
124
|
+
| | `--metaprogressinterval` | `0` | How often (in records) to log cross-entity meta progress. `0` disables meta progress logging. |
|
|
125
|
+
|
|
126
|
+
### Other Flags
|
|
127
|
+
|
|
128
|
+
| Flag | Long Form | Default | Description |
|
|
129
|
+
|------|-----------|---------|-------------|
|
|
130
|
+
| | `--logfile` | None | Path to write log output to a file. |
|
|
131
|
+
|
|
132
|
+
## Complete Working Examples
|
|
133
|
+
|
|
134
|
+
### Example 1: Minimal -- Push with CLI Credentials
|
|
135
|
+
|
|
136
|
+
No config file needed. Everything on the command line:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
mdwint load_comprehension ./my-data.json \
|
|
140
|
+
--api_server "https://api.example.com/1.0/" \
|
|
141
|
+
--api_username "admin" \
|
|
142
|
+
--api_password "admin_password" \
|
|
143
|
+
--prefix "MYAPP"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Example 2: Config File with Built-in Authentication
|
|
147
|
+
|
|
148
|
+
`.meadow.config.json`:
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"Source": {
|
|
153
|
+
"ServerURL": "https://api.example.com/1.0/",
|
|
154
|
+
"UserID": "integration_user",
|
|
155
|
+
"Password": "integration_password"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Run:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
mdwint load_comprehension ./entities.json
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Example 3: Config File with SessionManager (Same as Clone)
|
|
167
|
+
|
|
168
|
+
This is the key use case: the same `.meadow.config.json` used for `data-clone` also works for `load_comprehension`, so you can clone data from a server and push comprehensions to the same server using the same credentials.
|
|
169
|
+
|
|
170
|
+
`.meadow.config.json`:
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"Source": {
|
|
175
|
+
"ServerURL": "https://api.example.com/1.0/",
|
|
176
|
+
"UserID": "admin",
|
|
177
|
+
"Password": "admin_password"
|
|
178
|
+
},
|
|
179
|
+
"SessionManager": {
|
|
180
|
+
"Sessions": {
|
|
181
|
+
"Default": {
|
|
182
|
+
"ServerURL": "https://api.example.com/1.0/",
|
|
183
|
+
"Credentials": {
|
|
184
|
+
"username": "admin",
|
|
185
|
+
"password": "admin_password"
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
"Destination": {
|
|
191
|
+
"Provider": "MySQL",
|
|
192
|
+
"MySQL": {
|
|
193
|
+
"server": "127.0.0.1",
|
|
194
|
+
"database": "meadow_clone"
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
"SchemaPath": "./schema/Model-Extended.json"
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Clone data, then push a comprehension -- both use the same config:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
mdwint data-clone
|
|
205
|
+
mdwint load_comprehension ./new-records.json --prefix "IMPORT-2026"
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Example 4: Large Import with Progress Tracking
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
mdwint load_comprehension ./large-dataset.json \
|
|
212
|
+
--api_server "https://api.example.com/1.0/" \
|
|
213
|
+
--api_username "admin" \
|
|
214
|
+
--api_password "admin_password" \
|
|
215
|
+
--prefix "BULK" \
|
|
216
|
+
--batchsize 200 \
|
|
217
|
+
--metaprogressinterval 500 \
|
|
218
|
+
--progressinterval 50
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
This logs per-entity progress every 50 records and overall progress every 500 records.
|
|
222
|
+
|
|
223
|
+
### Example 5: Allow GUID Truncation for One-Time Import
|
|
224
|
+
|
|
225
|
+
When the generated GUID (prefix + external GUID) exceeds the server's GUID column size, the command errors by default. For one-time imports where GUID stability is not critical, you can allow automatic prefix truncation:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
mdwint load_comprehension ./legacy-import.json \
|
|
229
|
+
--api_server "https://api.example.com/1.0/" \
|
|
230
|
+
--api_username "admin" \
|
|
231
|
+
--api_password "admin_password" \
|
|
232
|
+
--prefix "LEGACY-IMPORT-2026" \
|
|
233
|
+
--allowguidtruncation
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
The adapter truncates the prefix to fit the column size while preserving the full external GUID.
|
|
237
|
+
|
|
238
|
+
### Example 6: Single-Record Upserts (Disable Bulk)
|
|
239
|
+
|
|
240
|
+
For debugging or when the server does not support bulk upserts:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
mdwint load_comprehension ./debug-data.json \
|
|
244
|
+
--api_server "https://localhost:8080/1.0/" \
|
|
245
|
+
--bulkupsert false
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Comprehension File Format
|
|
249
|
+
|
|
250
|
+
The comprehension file is a JSON object where top-level keys are entity names and values are objects keyed by GUID containing record data:
|
|
251
|
+
|
|
252
|
+
```json
|
|
253
|
+
{
|
|
254
|
+
"Book": {
|
|
255
|
+
"Book_1": {
|
|
256
|
+
"GUIDBook": "Book_1",
|
|
257
|
+
"Title": "The Hunger Games",
|
|
258
|
+
"Language": "eng"
|
|
259
|
+
},
|
|
260
|
+
"Book_2": {
|
|
261
|
+
"GUIDBook": "Book_2",
|
|
262
|
+
"Title": "Harry Potter",
|
|
263
|
+
"Language": "eng"
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
"Author": {
|
|
267
|
+
"Author_SC": {
|
|
268
|
+
"GUIDAuthor": "Author_SC",
|
|
269
|
+
"Name": "Suzanne Collins"
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Entities are processed in the order they appear in the file. Entities referenced by other entities (via foreign key GUIDs) should appear first so their Meadow IDs are available for cross-entity lookups.
|
|
276
|
+
|
|
277
|
+
### Cross-Entity GUID Resolution
|
|
278
|
+
|
|
279
|
+
Source records can reference other entities using GUID fields. The adapter resolves these to Meadow IDs during marshaling:
|
|
280
|
+
|
|
281
|
+
- **`GUIDEntityName`** -- External system GUID. The adapter looks up the corresponding Meadow ID from the session's local GUID map (populated when that entity was pushed earlier in the same session).
|
|
282
|
+
- **`_GUIDEntityName`** -- Meadow GUID (underscore prefix). The adapter performs an async server API lookup (`GET /Entity/By/GUIDEntity/{GUID}`) to resolve the Meadow ID. Use this for records that reference entities already on the server but not pushed in the current session.
|
|
283
|
+
|
|
284
|
+
For example, a BookAuthorJoin record referencing an existing Author already on the server:
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"BookAuthorJoin": {
|
|
289
|
+
"BAJ_1": {
|
|
290
|
+
"GUIDBookAuthorJoin": "BAJ_1",
|
|
291
|
+
"GUIDBook": "Book_1",
|
|
292
|
+
"_GUIDAuthor": "Author_SC"
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Here `GUIDBook` is resolved from the session map (Book was pushed earlier), while `_GUIDAuthor` triggers a server lookup.
|
|
299
|
+
|
|
300
|
+
## Using the Explanation Command
|
|
301
|
+
|
|
302
|
+
The CLI includes a built-in command to display the resolved configuration:
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
mdwint config
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
This shows the merged result of default values, configuration file values, and CLI overrides, helping you verify what settings will be used before running a push.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Integration Adapter
|
|
2
2
|
|
|
3
|
-
The Integration Adapter is the bridge between comprehension data and a running Meadow REST API. It handles record marshaling, GUID generation, schema validation, batch operations, and retry logic.
|
|
3
|
+
The Integration Adapter is the bridge between comprehension data and a running Meadow REST API. It handles record marshaling, GUID generation, schema validation, batch operations, progress tracking, and retry logic.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
@@ -12,6 +12,7 @@ Source Records (from comprehension or external system)
|
|
|
12
12
|
|
|
|
13
13
|
v
|
|
14
14
|
marshalRecord() -- map external GUIDs to Meadow GUIDs
|
|
15
|
+
-- resolve _GUID fields via async server lookup
|
|
15
16
|
-- validate against Meadow schema
|
|
16
17
|
-- truncate strings to schema limits
|
|
17
18
|
-- strip reserved fields (CreateDate, etc.)
|
|
@@ -19,11 +20,28 @@ Source Records (from comprehension or external system)
|
|
|
19
20
|
v
|
|
20
21
|
pushRecordsToServer() -- single or bulk upsert
|
|
21
22
|
-- automatic retry on failure
|
|
23
|
+
-- per-entity progress tracking
|
|
24
|
+
-- meta (cross-entity) progress tracking
|
|
22
25
|
|
|
|
23
26
|
v
|
|
24
27
|
GUIDMap updated -- Meadow IDs stored for cross-entity lookups
|
|
25
28
|
```
|
|
26
29
|
|
|
30
|
+
## REST Client Injection
|
|
31
|
+
|
|
32
|
+
The adapter supports flexible REST client injection. You can provide a client in three ways:
|
|
33
|
+
|
|
34
|
+
1. **Explicit injection** via `setRestClient(pClient)` or `options.Client`
|
|
35
|
+
2. **Auto-resolved** from `fable.MeadowCloneRestClient` (used by the CLI command)
|
|
36
|
+
3. **Legacy fallback** from `fable.EntityProvider` (backwards compatible)
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
const adapter = libAdapter.getAdapter(myFable, 'Book', 'BK');
|
|
40
|
+
adapter.setRestClient(myFable.MeadowCloneRestClient);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The client should expose: `upsertEntity`, `upsertEntities`, `getEntityByGUID`, `getEntity`, `deleteEntity`, and `getJSON`.
|
|
44
|
+
|
|
27
45
|
## GUID Marshaling
|
|
28
46
|
|
|
29
47
|
External system GUIDs are transformed into deterministic Meadow GUIDs using this pattern:
|
|
@@ -41,19 +59,60 @@ Meadow GUID: "INTG-DEF-E-Book-12345"
|
|
|
41
59
|
|
|
42
60
|
This ensures GUIDs are unique across integration sets and entities.
|
|
43
61
|
|
|
62
|
+
### GUID Length Validation
|
|
63
|
+
|
|
64
|
+
The adapter validates that generated GUIDs do not exceed the server's GUID column size. The maximum length is resolved from (in order):
|
|
65
|
+
|
|
66
|
+
1. **Explicit `GUIDMaxLength` option** -- positive integer overrides everything
|
|
67
|
+
2. **`GUIDColumnSizes` map** -- per-entity sizes passed in options (e.g. from a schema build step)
|
|
68
|
+
3. **Live server schema** -- fetched via `GET /Entity/Schema` during `integrateRecords()`
|
|
69
|
+
4. **`DefaultGUIDColumnSize`** -- fallback (default: 36)
|
|
70
|
+
|
|
71
|
+
When a GUID exceeds the maximum length:
|
|
72
|
+
|
|
73
|
+
- **Default behavior** (`AllowGUIDTruncation: false`): Throws an error with a clear message showing the GUID, prefix, and limit.
|
|
74
|
+
- **Truncation mode** (`AllowGUIDTruncation: true`): The prefix is progressively truncated to fit while preserving the full external GUID. Use this for one-time imports where GUID stability is not required.
|
|
75
|
+
|
|
44
76
|
### Cross-Entity GUID Resolution
|
|
45
77
|
|
|
46
|
-
When a source record contains GUID fields for other entities
|
|
78
|
+
When a source record contains GUID fields for other entities, the adapter resolves them to Meadow IDs:
|
|
79
|
+
|
|
80
|
+
#### External GUIDs (`GUIDEntityName`)
|
|
81
|
+
|
|
82
|
+
Fields starting with `GUID` (without underscore) are treated as external system GUIDs. The adapter looks up the corresponding Meadow ID from the session's local External GUID Map:
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
// Source: { GUIDBookAuthorJoin: "BAJ_1", GUIDBook: "Book_1", GUIDAuthor: "Author_5" }
|
|
86
|
+
// Adapter resolves:
|
|
87
|
+
// GUIDBook "Book_1" -> ExternalGUIDMap -> Meadow GUID -> GUIDMap -> IDBook: 42
|
|
88
|
+
// GUIDAuthor "Author_5" -> ExternalGUIDMap -> Meadow GUID -> GUIDMap -> IDAuthor: 17
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
This requires the referenced entity to have been integrated earlier in the same session.
|
|
92
|
+
|
|
93
|
+
#### Meadow GUIDs (`_GUIDEntityName`)
|
|
94
|
+
|
|
95
|
+
Fields starting with `_GUID` (underscore prefix) are treated as Meadow GUIDs that may already exist on the server. The adapter performs an **async server API lookup** via `getIDFromGUIDAsync`:
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
// Source: { GUIDProduct: "Prod_1", _GUIDMaterial: "LADOTD-Material-9999M99999" }
|
|
99
|
+
// Adapter resolves:
|
|
100
|
+
// _GUIDMaterial -> async GET /Material/By/GUIDMaterial/LADOTD-Material-9999M99999 -> IDMaterial: 5012
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Use this for records that reference entities already on the server but not pushed in the current session.
|
|
104
|
+
|
|
105
|
+
#### Destination Field Overrides (`_Dest_IDEntity_*_Via_*`)
|
|
106
|
+
|
|
107
|
+
For explicit control over which ID field a resolved GUID maps to:
|
|
47
108
|
|
|
48
109
|
```javascript
|
|
49
|
-
// Source
|
|
50
|
-
// Adapter
|
|
51
|
-
//
|
|
52
|
-
// - GUIDBook -> looks up Meadow ID for external GUID "Book_1" -> IDBook: 42
|
|
53
|
-
// - GUIDAuthor -> looks up Meadow ID for external GUID "Author_5" -> IDAuthor: 17
|
|
110
|
+
// Source: { "_Dest_IDEntity_IDCustomField_Via_GUIDMaterial": "some-guid-value" }
|
|
111
|
+
// Adapter resolves the GUID via server lookup and sets:
|
|
112
|
+
// record.IDCustomField = resolvedID
|
|
54
113
|
```
|
|
55
114
|
|
|
56
|
-
This is
|
|
115
|
+
This is useful when the foreign key column name doesn't follow the standard `ID{EntityName}` pattern.
|
|
57
116
|
|
|
58
117
|
## Batch Processing
|
|
59
118
|
|
|
@@ -64,13 +123,36 @@ The adapter automatically switches between single and bulk upsert modes:
|
|
|
64
123
|
|
|
65
124
|
## Retry Logic
|
|
66
125
|
|
|
67
|
-
Failed upsert operations are retried up to `RecordPushRetryThreshold` times (default 5), with a hard cap of 50 retries.
|
|
126
|
+
Failed upsert operations are retried up to `RecordPushRetryThreshold` times (default 5), with a hard cap of 50 retries. The adapter validates server responses to confirm:
|
|
68
127
|
|
|
69
128
|
1. The response contains the entity ID field
|
|
70
129
|
2. The ID is a positive number
|
|
71
130
|
3. The response GUID matches the sent GUID
|
|
72
131
|
|
|
73
|
-
If validation fails, the record is retried.
|
|
132
|
+
If validation fails, the record is retried. Specific error types are handled without retry:
|
|
133
|
+
|
|
134
|
+
- **Duplicate entry errors** -- Logged as warnings and skipped
|
|
135
|
+
- **GUID length rejections** -- Logged as errors and skipped
|
|
136
|
+
- **Server create rejections** -- Logged as errors and skipped
|
|
137
|
+
|
|
138
|
+
When the retry threshold is exhausted, the adapter attempts a fallback read of the record by GUID to populate the GUID-to-ID mapping even if the upsert failed.
|
|
139
|
+
|
|
140
|
+
## Progress Tracking
|
|
141
|
+
|
|
142
|
+
### Per-Entity Progress
|
|
143
|
+
|
|
144
|
+
Every entity push creates a progress tracker via `fable.ProgressTrackerSet`. The tracker logs status at each record (single mode) or each batch (bulk mode).
|
|
145
|
+
|
|
146
|
+
### Meta Progress
|
|
147
|
+
|
|
148
|
+
For cross-entity progress tracking across an entire comprehension push, callers can set:
|
|
149
|
+
|
|
150
|
+
```javascript
|
|
151
|
+
adapter.MetaProgressTrackerHash = myTrackerHash;
|
|
152
|
+
adapter.MetaProgressTrackerLogInterval = 500; // log every ~500 records
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The meta tracker uses a threshold-crossing interval check rather than exact modulo, so bulk increments (e.g. +100 per batch) reliably trigger logging when they cross an interval boundary.
|
|
74
156
|
|
|
75
157
|
## Schema Validation
|
|
76
158
|
|
|
@@ -78,7 +160,8 @@ When `integrateRecords()` is called, the adapter fetches the entity schema from
|
|
|
78
160
|
|
|
79
161
|
- String fields are truncated to their schema-defined `size`
|
|
80
162
|
- Non-string fields are passed through
|
|
81
|
-
-
|
|
163
|
+
- When `SimpleMarshal` is true, all schema-matched fields are passed through without type coercion
|
|
164
|
+
- When `ForceMarshal` is true, fields not found in the schema are still included
|
|
82
165
|
- Reserved fields (`CreateDate`, `UpdateDate`, `Deleted`, `DeleteDate`) are always stripped
|
|
83
166
|
|
|
84
167
|
## Delete Operations
|
|
@@ -91,19 +174,85 @@ The `load_comprehension` command wraps the Integration Adapter for CLI use:
|
|
|
91
174
|
|
|
92
175
|
```shell
|
|
93
176
|
npx meadow-integration load_comprehension ./my-comprehension.json \
|
|
94
|
-
|
|
95
|
-
|
|
177
|
+
--api_server "https://api.example.com/1.0/" \
|
|
178
|
+
--api_username "admin" \
|
|
179
|
+
--api_password "admin_password" \
|
|
180
|
+
--prefix "MY-PREFIX" \
|
|
181
|
+
--batchsize 200 \
|
|
182
|
+
--metaprogressinterval 500
|
|
96
183
|
```
|
|
97
184
|
|
|
98
|
-
This automatically creates adapters for every entity in the comprehension and processes
|
|
185
|
+
This automatically creates adapters for every entity in the comprehension, injects the REST client and SessionManager credentials, and processes entities in sequence.
|
|
186
|
+
|
|
187
|
+
See [Comprehension Push Configuration](comprehension-push/configuration.md) for full CLI options and configuration details.
|
|
188
|
+
|
|
189
|
+
## Programmatic Usage
|
|
190
|
+
|
|
191
|
+
```javascript
|
|
192
|
+
const libMeadowIntegration = require('meadow-integration');
|
|
193
|
+
const libFable = require('fable');
|
|
194
|
+
|
|
195
|
+
let myFable = new libFable({ /* ... */ });
|
|
196
|
+
|
|
197
|
+
// Register the adapter service type
|
|
198
|
+
myFable.serviceManager.addServiceType('IntegrationAdapter',
|
|
199
|
+
libMeadowIntegration.IntegrationAdapter);
|
|
200
|
+
|
|
201
|
+
// Get or create an adapter for 'Book'
|
|
202
|
+
let bookAdapter = libMeadowIntegration.IntegrationAdapter.getAdapter(
|
|
203
|
+
myFable, 'Book', 'BK', { SimpleMarshal: true, ForceMarshal: true });
|
|
204
|
+
|
|
205
|
+
// Inject a REST client
|
|
206
|
+
bookAdapter.setRestClient(myRestClient);
|
|
207
|
+
|
|
208
|
+
// Add source records
|
|
209
|
+
bookAdapter.addSourceRecord({ GUIDBook: "Book_1", Title: "The Hunger Games" });
|
|
210
|
+
bookAdapter.addSourceRecord({ GUIDBook: "Book_2", Title: "Harry Potter" });
|
|
211
|
+
|
|
212
|
+
// Integrate (fetch schema, marshal, push)
|
|
213
|
+
bookAdapter.integrateRecords((pError) =>
|
|
214
|
+
{
|
|
215
|
+
if (pError)
|
|
216
|
+
{
|
|
217
|
+
console.error('Integration failed:', pError);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
console.log('Books integrated successfully.');
|
|
221
|
+
});
|
|
222
|
+
```
|
|
99
223
|
|
|
100
224
|
## Static Helper
|
|
101
225
|
|
|
102
226
|
The `getAdapter()` static method provides a convenient way to get or create an adapter:
|
|
103
227
|
|
|
104
228
|
```javascript
|
|
105
|
-
const libAdapter = require('meadow-integration
|
|
229
|
+
const libAdapter = require('meadow-integration').IntegrationAdapter;
|
|
106
230
|
|
|
107
231
|
// Gets existing adapter for 'Book' or creates a new one
|
|
108
232
|
let tmpAdapter = libAdapter.getAdapter(myFable, 'Book', 'BK');
|
|
233
|
+
|
|
234
|
+
// With custom options (e.g. SimpleMarshal, ForceMarshal, AllowGUIDTruncation)
|
|
235
|
+
let tmpAdapter = libAdapter.getAdapter(myFable, 'Book', 'BK',
|
|
236
|
+
{ SimpleMarshal: true, ForceMarshal: true, AllowGUIDTruncation: true });
|
|
109
237
|
```
|
|
238
|
+
|
|
239
|
+
## Adapter Options Reference
|
|
240
|
+
|
|
241
|
+
| Option | Type | Default | Description |
|
|
242
|
+
|--------|------|---------|-------------|
|
|
243
|
+
| `Entity` | string | `"DefaultEntity"` | The Meadow entity name |
|
|
244
|
+
| `AdapterSetGUIDMarshalPrefix` | string or false | `false` (falls back to `"INTG-DEF"`) | Prefix for the adapter set in generated GUIDs |
|
|
245
|
+
| `EntityGUIDMarshalPrefix` | string or false | `false` (auto: `"E-{Entity}"`) | Per-entity prefix in generated GUIDs |
|
|
246
|
+
| `GUIDMaxLength` | number | `0` (auto-detect) | Maximum GUID length; 0 = resolve from schema |
|
|
247
|
+
| `GUIDColumnSizes` | object | `{}` | Per-entity GUID column sizes (entity name -> max size) |
|
|
248
|
+
| `DefaultGUIDColumnSize` | number | `36` | Fallback GUID column size |
|
|
249
|
+
| `AllowGUIDTruncation` | boolean | `false` | Allow prefix truncation for oversized GUIDs |
|
|
250
|
+
| `SimpleMarshal` | boolean | `false` | Pass through schema-matched fields without type coercion |
|
|
251
|
+
| `ForceMarshal` | boolean | `false` | Include fields not found in schema |
|
|
252
|
+
| `PerformUpserts` | boolean | `true` | Enable upsert operations |
|
|
253
|
+
| `PerformDeletes` | boolean | `true` | Enable delete operations |
|
|
254
|
+
| `RecordPushRetryThreshold` | number | `5` | Max retries per record/batch |
|
|
255
|
+
| `RecordThresholdForBulkUpsert` | number | `1000` | Record count above which bulk mode is used |
|
|
256
|
+
| `BulkUpsertBatchSize` | number | `100` | Records per bulk upsert batch |
|
|
257
|
+
| `ProgressLogInterval` | number | `100` | Per-entity progress log interval |
|
|
258
|
+
| `Client` | object | `null` | REST client instance (alternative to `setRestClient()`) |
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Source":
|
|
3
|
+
{
|
|
4
|
+
"ServerURL": "https://api.example.com/1.0/",
|
|
5
|
+
"UserID": "integration_user",
|
|
6
|
+
"Password": "integration_password"
|
|
7
|
+
},
|
|
8
|
+
"SessionManager":
|
|
9
|
+
{
|
|
10
|
+
"Sessions":
|
|
11
|
+
{
|
|
12
|
+
"Default":
|
|
13
|
+
{
|
|
14
|
+
"ServerURL": "https://api.example.com/1.0/",
|
|
15
|
+
"Credentials":
|
|
16
|
+
{
|
|
17
|
+
"username": "session_user",
|
|
18
|
+
"password": "session_password"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "meadow-integration",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.20",
|
|
4
4
|
"description": "Meadow Data Integration",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mdwint": "source/cli/Meadow-Integration-CLI-Run.js"
|
|
@@ -39,12 +39,14 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"fable": "^3.1.63",
|
|
41
41
|
"fable-serviceproviderbase": "^3.0.19",
|
|
42
|
+
"fast-xml-parser": "^4.4.1",
|
|
42
43
|
"meadow": "^2.0.33",
|
|
43
44
|
"meadow-connection-mssql": "^1.0.16",
|
|
44
45
|
"meadow-connection-mysql": "^1.0.14",
|
|
45
46
|
"orator": "^6.0.4",
|
|
46
47
|
"orator-serviceserver-restify": "^2.0.9",
|
|
47
48
|
"pict-service-commandlineutility": "^1.0.19",
|
|
48
|
-
"pict-sessionmanager": "^1.0.2"
|
|
49
|
+
"pict-sessionmanager": "^1.0.2",
|
|
50
|
+
"xlsx": "^0.18.5"
|
|
49
51
|
}
|
|
50
52
|
}
|
|
@@ -8,6 +8,16 @@ const libSyncEntityInitial = require(`./services/clone/Meadow-Service-Sync-Entit
|
|
|
8
8
|
const libSyncEntityOngoing = require(`./services/clone/Meadow-Service-Sync-Entity-Ongoing.js`);
|
|
9
9
|
const libOperation = require(`./services/clone/Meadow-Service-Operation.js`);
|
|
10
10
|
|
|
11
|
+
const libIntegrationAdapter = require(`./Meadow-Service-Integration-Adapter.js`);
|
|
12
|
+
const libGUIDMap = require(`./Meadow-Service-Integration-GUIDMap.js`);
|
|
13
|
+
|
|
14
|
+
const libFileParser = require(`./services/parser/Service-FileParser.js`);
|
|
15
|
+
const libFileParserCSV = require(`./services/parser/Service-FileParser-CSV.js`);
|
|
16
|
+
const libFileParserJSON = require(`./services/parser/Service-FileParser-JSON.js`);
|
|
17
|
+
const libFileParserXLSX = require(`./services/parser/Service-FileParser-XLSX.js`);
|
|
18
|
+
const libFileParserXML = require(`./services/parser/Service-FileParser-XML.js`);
|
|
19
|
+
const libFileParserFixedWidth = require(`./services/parser/Service-FileParser-FixedWidth.js`);
|
|
20
|
+
|
|
11
21
|
module.exports = (
|
|
12
22
|
{
|
|
13
23
|
TabularCheck: libTabularCheck,
|
|
@@ -18,5 +28,15 @@ module.exports = (
|
|
|
18
28
|
Sync: libSync,
|
|
19
29
|
SyncEntityInitial: libSyncEntityInitial,
|
|
20
30
|
SyncEntityOngoing: libSyncEntityOngoing,
|
|
21
|
-
Operation: libOperation
|
|
31
|
+
Operation: libOperation,
|
|
32
|
+
|
|
33
|
+
IntegrationAdapter: libIntegrationAdapter,
|
|
34
|
+
GUIDMap: libGUIDMap,
|
|
35
|
+
|
|
36
|
+
FileParser: libFileParser,
|
|
37
|
+
FileParserCSV: libFileParserCSV,
|
|
38
|
+
FileParserJSON: libFileParserJSON,
|
|
39
|
+
FileParserXLSX: libFileParserXLSX,
|
|
40
|
+
FileParserXML: libFileParserXML,
|
|
41
|
+
FileParserFixedWidth: libFileParserFixedWidth
|
|
22
42
|
});
|