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,98 @@
|
|
|
1
|
+
# Comprehensions
|
|
2
|
+
|
|
3
|
+
A Comprehension is the core data structure in meadow-integration. It is a JSON object that stores entity records keyed by their GUID, providing fast lookup and easy merging across data sources.
|
|
4
|
+
|
|
5
|
+
## Object Format
|
|
6
|
+
|
|
7
|
+
The standard comprehension format stores records as properties of an entity object:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"EntityName": {
|
|
12
|
+
"GUID-1": { "GUIDEntityName": "GUID-1", "Field1": "value", "Field2": "value" },
|
|
13
|
+
"GUID-2": { "GUIDEntityName": "GUID-2", "Field1": "value", "Field2": "value" }
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Benefits of Object Format
|
|
19
|
+
|
|
20
|
+
- **O(1) lookup** by GUID -- no scanning required
|
|
21
|
+
- **Natural deduplication** -- duplicate GUIDs merge automatically
|
|
22
|
+
- **Easy merging** -- `Object.assign()` combines records from multiple sources
|
|
23
|
+
- **Multi-entity support** -- one file can hold Books, Authors, and Joins
|
|
24
|
+
|
|
25
|
+
## Array Format
|
|
26
|
+
|
|
27
|
+
For export or consumption by other tools, comprehensions can be converted to arrays:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
[
|
|
31
|
+
{ "GUIDEntityName": "GUID-1", "Field1": "value", "Field2": "value" },
|
|
32
|
+
{ "GUIDEntityName": "GUID-2", "Field1": "value", "Field2": "value" }
|
|
33
|
+
]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Convert between formats using:
|
|
37
|
+
|
|
38
|
+
```shell
|
|
39
|
+
# Object -> Array
|
|
40
|
+
npx meadow-integration comprehensionarray input.json -e MyEntity -o output.json
|
|
41
|
+
|
|
42
|
+
# Array -> CSV
|
|
43
|
+
npx meadow-integration objectarraytocsv input.json -o output.csv
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Multi-Entity Comprehensions
|
|
47
|
+
|
|
48
|
+
A single comprehension file can contain records for multiple entity types. This is created by running `csvtransform` multiple times with different mapping files, passing the `-i` flag to merge into the existing comprehension:
|
|
49
|
+
|
|
50
|
+
```shell
|
|
51
|
+
# Create Book entities
|
|
52
|
+
npx meadow-integration csvtransform books.csv -m mapping_Book.json -o store.json
|
|
53
|
+
|
|
54
|
+
# Add Author entities to the same file
|
|
55
|
+
npx meadow-integration csvtransform books.csv -m mapping_Author.json -i store.json -o store.json
|
|
56
|
+
|
|
57
|
+
# Add BookAuthorJoin entities
|
|
58
|
+
npx meadow-integration csvtransform books.csv -m mapping_Join.json -i store.json -o store.json
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Result:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"Book": { "Book_1": {...}, "Book_2": {...} },
|
|
66
|
+
"Author": { "Author_SuzanneCollins": {...}, "Author_JKRowling": {...} },
|
|
67
|
+
"BookAuthorJoin": { "BAJ_A_SuzanneCollins_B_1": {...} }
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Merging Comprehensions
|
|
72
|
+
|
|
73
|
+
The `comprehensionintersect` command merges two comprehension files. Records with matching GUIDs have their fields merged (later values overwrite earlier ones):
|
|
74
|
+
|
|
75
|
+
```shell
|
|
76
|
+
npx meadow-integration comprehensionintersect file1.json -i file2.json -e MyEntity -o merged.json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This is particularly useful when the same entities have data spread across multiple source files (e.g. housing characteristics and housing costs for the same neighborhoods).
|
|
80
|
+
|
|
81
|
+
## GUID Design
|
|
82
|
+
|
|
83
|
+
GUIDs are the primary key for comprehension records. Good GUID design ensures:
|
|
84
|
+
|
|
85
|
+
- **Uniqueness** -- Each record gets a distinct key
|
|
86
|
+
- **Determinism** -- The same source data always generates the same GUID
|
|
87
|
+
- **Mergeability** -- Related data from different sources can be matched
|
|
88
|
+
|
|
89
|
+
### GUID Template Patterns
|
|
90
|
+
|
|
91
|
+
| Pattern | Use Case |
|
|
92
|
+
|---------|----------|
|
|
93
|
+
| `Entity_{~D:Record.id~}` | Single-column natural key |
|
|
94
|
+
| `{~D:Record.date~}_{~D:Record.seq~}` | Composite key |
|
|
95
|
+
| `{~PascalCaseIdentifier:Record.name~}` | Name-based key (normalized) |
|
|
96
|
+
| `{~D:Record.shared_key~}` | Cross-file merge key |
|
|
97
|
+
|
|
98
|
+
When the same GUID template is used across multiple csvtransform runs on different source files, the records are automatically merged in the comprehension.
|
package/docs/cover.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Meadow Integration <small>1.0</small>
|
|
2
|
+
|
|
3
|
+
> Data integration toolkit for transforming CSV, TSV, and JSON into Meadow entity comprehensions
|
|
4
|
+
|
|
5
|
+
- Transform tabular data into structured entity records
|
|
6
|
+
- Merge multiple data sources by shared keys
|
|
7
|
+
- Multi-entity extraction from single sources using Solvers
|
|
8
|
+
- CLI utility, programmatic API, and REST integration
|
|
9
|
+
|
|
10
|
+
[GitHub](https://github.com/stevenvelozo/meadow-integration)
|
|
11
|
+
[Get Started](#meadow-integration)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
Pict Docuserve - Base Styles
|
|
3
|
+
============================================================================ */
|
|
4
|
+
|
|
5
|
+
/* Reset and base */
|
|
6
|
+
*, *::before, *::after {
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
html, body {
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 0;
|
|
13
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
14
|
+
font-size: 16px;
|
|
15
|
+
line-height: 1.5;
|
|
16
|
+
color: #423D37;
|
|
17
|
+
background-color: #fff;
|
|
18
|
+
-webkit-font-smoothing: antialiased;
|
|
19
|
+
-moz-osx-font-smoothing: grayscale;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Typography */
|
|
23
|
+
h1, h2, h3, h4, h5, h6 {
|
|
24
|
+
margin-top: 0;
|
|
25
|
+
line-height: 1.3;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
a {
|
|
29
|
+
color: #2E7D74;
|
|
30
|
+
text-decoration: none;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
a:hover {
|
|
34
|
+
color: #256861;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Application container */
|
|
38
|
+
#Docuserve-Application-Container {
|
|
39
|
+
min-height: 100vh;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Utility: scrollbar styling */
|
|
43
|
+
::-webkit-scrollbar {
|
|
44
|
+
width: 8px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
::-webkit-scrollbar-track {
|
|
48
|
+
background: #F5F0E8;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
::-webkit-scrollbar-thumb {
|
|
52
|
+
background: #D4CCBE;
|
|
53
|
+
border-radius: 4px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
::-webkit-scrollbar-thumb:hover {
|
|
57
|
+
background: #B5AA9A;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Responsive adjustments */
|
|
61
|
+
@media (max-width: 768px) {
|
|
62
|
+
html {
|
|
63
|
+
font-size: 14px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
#Docuserve-Sidebar-Container {
|
|
67
|
+
display: none;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.docuserve-body {
|
|
71
|
+
flex-direction: column;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Examples Walkthrough
|
|
2
|
+
|
|
3
|
+
The `examples/` folder contains runnable scripts that demonstrate meadow-integration features. Each script is self-contained and writes output to `examples/output/`.
|
|
4
|
+
|
|
5
|
+
## Data Sources
|
|
6
|
+
|
|
7
|
+
The examples use data from `docs/examples/data/`:
|
|
8
|
+
|
|
9
|
+
- **books.csv** -- 10,000 book records (id, title, author, isbn, ratings, etc.)
|
|
10
|
+
- **seattle_neighborhoods/** -- Three Seattle census datasets sharing a Neighborhood Name column
|
|
11
|
+
|
|
12
|
+
## Example 001: CSV Check
|
|
13
|
+
|
|
14
|
+
Analyzes a CSV file to understand its structure before transforming it.
|
|
15
|
+
|
|
16
|
+
```shell
|
|
17
|
+
./examples/Example-001-CSV-Check.sh
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**What it does:** Runs `csvcheck` on books.csv and produces a statistics file showing row/column counts, headers, and per-column analysis (empty counts, numeric counts, first/last values).
|
|
21
|
+
|
|
22
|
+
**When to use:** As the first step with any new data source to understand what you are working with.
|
|
23
|
+
|
|
24
|
+
## Example 002: CSV Transform (Implicit)
|
|
25
|
+
|
|
26
|
+
Transforms a CSV with auto-detected settings.
|
|
27
|
+
|
|
28
|
+
```shell
|
|
29
|
+
./examples/Example-002-CSV-Transform-Implicit.sh
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**What it does:** Runs `csvtransform` with no mapping file or options. The entity name is derived from the filename, the GUID is generated from the first column, and all columns are mapped 1:1.
|
|
33
|
+
|
|
34
|
+
**When to use:** Quick exploration of what a comprehension looks like before writing a mapping file.
|
|
35
|
+
|
|
36
|
+
## Example 003: CSV Transform (CLI Options)
|
|
37
|
+
|
|
38
|
+
Controls the entity name and GUID template via command-line flags.
|
|
39
|
+
|
|
40
|
+
```shell
|
|
41
|
+
./examples/Example-003-CSV-Transform-CLI-Options.sh
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**What it does:** Uses `-e Book`, `-n GUIDBook`, and `-g "Book_{~D:Record.id~}"` to explicitly define the entity and GUID structure.
|
|
45
|
+
|
|
46
|
+
**When to use:** One-off transforms where you want specific entity naming but do not need column filtering.
|
|
47
|
+
|
|
48
|
+
## Example 004: CSV Transform (Mapping File)
|
|
49
|
+
|
|
50
|
+
Uses a JSON mapping file for precise column control.
|
|
51
|
+
|
|
52
|
+
```shell
|
|
53
|
+
./examples/Example-004-CSV-Transform-Mapping-File.sh
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**What it does:** Reads `mapping_books_Book.json` which maps only 7 fields from the 23-column CSV. Demonstrates computed fields (`PublicationYear` using `Math.roundPrecise`) and static values (`Genre: "Unknown"`).
|
|
57
|
+
|
|
58
|
+
**When to use:** Production data integration where you want to control exactly which fields are included and how they are named.
|
|
59
|
+
|
|
60
|
+
## Example 005: Multi-Entity Bookstore
|
|
61
|
+
|
|
62
|
+
Builds a complete relational data set from a single CSV.
|
|
63
|
+
|
|
64
|
+
```shell
|
|
65
|
+
./examples/Example-005-Multi-Entity-Bookstore.sh
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**What it does:** Runs three `csvtransform` passes on books.csv with different mapping files to create:
|
|
69
|
+
1. **Book** entities (one per book)
|
|
70
|
+
2. **Author** entities (unique authors, split from comma-separated values using Solvers)
|
|
71
|
+
3. **BookAuthorJoin** entities (many-to-many relationships)
|
|
72
|
+
|
|
73
|
+
Each pass uses `-i` to merge into the same comprehension file.
|
|
74
|
+
|
|
75
|
+
**When to use:** When a single data source needs to be decomposed into multiple related entity types.
|
|
76
|
+
|
|
77
|
+
## Example 006: Multi-CSV Intersect
|
|
78
|
+
|
|
79
|
+
Merges three CSV files that share a common key.
|
|
80
|
+
|
|
81
|
+
```shell
|
|
82
|
+
./examples/Example-006-Multi-CSV-Intersect.sh
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**What it does:** Transforms three Seattle neighborhood CSVs into separate comprehensions keyed on Neighborhood Name, then uses `comprehensionintersect` to merge them into one unified dataset.
|
|
86
|
+
|
|
87
|
+
**When to use:** When the same entities have data spread across multiple source files (e.g. different database tables or API responses).
|
|
88
|
+
|
|
89
|
+
## Example 007: Comprehension to Array
|
|
90
|
+
|
|
91
|
+
Converts from object-keyed format to a JSON array.
|
|
92
|
+
|
|
93
|
+
```shell
|
|
94
|
+
./examples/Example-007-Comprehension-To-Array.sh
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**What it does:** Creates a Book comprehension, then converts it from `{ "Book_1": {...} }` format to `[ {...}, {...} ]` format.
|
|
98
|
+
|
|
99
|
+
**When to use:** When downstream consumers need an array (UI tables, further processing, export).
|
|
100
|
+
|
|
101
|
+
## Example 008: Comprehension to CSV
|
|
102
|
+
|
|
103
|
+
Full round-trip from CSV through comprehension back to CSV.
|
|
104
|
+
|
|
105
|
+
```shell
|
|
106
|
+
./examples/Example-008-Comprehension-To-CSV.sh
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**What it does:** CSV -> Comprehension -> Array -> CSV export.
|
|
110
|
+
|
|
111
|
+
**When to use:** Reviewing transformed data in a spreadsheet, or creating filtered/cleaned CSVs.
|
|
112
|
+
|
|
113
|
+
## Example 009: JSON Array Transform
|
|
114
|
+
|
|
115
|
+
Transforms a JSON array file into a comprehension.
|
|
116
|
+
|
|
117
|
+
```shell
|
|
118
|
+
./examples/Example-009-JSON-Array-Transform.sh
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**What it does:** Creates a JSON array from CSV data, then uses `jsonarraytransform` with a mapping file to create a new comprehension.
|
|
122
|
+
|
|
123
|
+
**When to use:** When your source data comes as JSON (e.g. API responses) instead of CSV.
|
|
124
|
+
|
|
125
|
+
## Example 010: Programmatic API
|
|
126
|
+
|
|
127
|
+
Uses meadow-integration services directly in Node.js code.
|
|
128
|
+
|
|
129
|
+
```shell
|
|
130
|
+
node examples/Example-010-Programmatic-API.js
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**What it does:** Demonstrates three services without the CLI:
|
|
134
|
+
1. **TabularCheck** -- collecting statistics on parsed CSV records
|
|
135
|
+
2. **TabularTransform** -- building a comprehension with explicit configuration
|
|
136
|
+
3. **GUIDMap** -- tracking bidirectional GUID-to-ID mappings
|
|
137
|
+
|
|
138
|
+
**When to use:** When you are building data integration into a larger application and need programmatic control.
|
package/docs/index.html
CHANGED
|
@@ -1,22 +1,39 @@
|
|
|
1
|
-
<!
|
|
1
|
+
<!doctype html>
|
|
2
2
|
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
7
|
+
<meta name="description" content="Documentation powered by pict-docuserve">
|
|
8
|
+
|
|
9
|
+
<title>Documentation</title>
|
|
10
|
+
|
|
11
|
+
<!-- Application Stylesheet -->
|
|
12
|
+
<link href="css/docuserve.css" rel="stylesheet">
|
|
13
|
+
<!-- KaTeX stylesheet for LaTeX equation rendering -->
|
|
14
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css">
|
|
15
|
+
<!-- PICT Dynamic View CSS Container -->
|
|
16
|
+
<style id="PICT-CSS"></style>
|
|
17
|
+
|
|
18
|
+
<!-- Load the PICT library from jsDelivr CDN -->
|
|
19
|
+
<script src="https://cdn.jsdelivr.net/npm/pict@1/dist/pict.min.js" type="text/javascript"></script>
|
|
20
|
+
<!-- Bootstrap the Application -->
|
|
21
|
+
<script type="text/javascript">
|
|
22
|
+
//<![CDATA[
|
|
23
|
+
Pict.safeOnDocumentReady(() => { Pict.safeLoadPictApplication(PictDocuserve, 2)});
|
|
24
|
+
//]]>
|
|
25
|
+
</script>
|
|
26
|
+
</head>
|
|
27
|
+
<body>
|
|
28
|
+
<!-- The root container for the Pict application -->
|
|
29
|
+
<div id="Docuserve-Application-Container"></div>
|
|
30
|
+
|
|
31
|
+
<!-- Mermaid diagram rendering -->
|
|
32
|
+
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
|
|
33
|
+
<script>mermaid.initialize({ startOnLoad: false, theme: 'default' });</script>
|
|
34
|
+
<!-- KaTeX for LaTeX equation rendering -->
|
|
35
|
+
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.js"></script>
|
|
36
|
+
<!-- Load the Docuserve PICT Application Bundle from jsDelivr CDN -->
|
|
37
|
+
<script src="https://cdn.jsdelivr.net/npm/pict-docuserve@0/dist/pict-docuserve.min.js" type="text/javascript"></script>
|
|
38
|
+
</body>
|
|
22
39
|
</html>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Integration Adapter
|
|
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.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Source Records (from comprehension or external system)
|
|
9
|
+
|
|
|
10
|
+
v
|
|
11
|
+
addSourceRecord()
|
|
12
|
+
|
|
|
13
|
+
v
|
|
14
|
+
marshalRecord() -- map external GUIDs to Meadow GUIDs
|
|
15
|
+
-- validate against Meadow schema
|
|
16
|
+
-- truncate strings to schema limits
|
|
17
|
+
-- strip reserved fields (CreateDate, etc.)
|
|
18
|
+
|
|
|
19
|
+
v
|
|
20
|
+
pushRecordsToServer() -- single or bulk upsert
|
|
21
|
+
-- automatic retry on failure
|
|
22
|
+
|
|
|
23
|
+
v
|
|
24
|
+
GUIDMap updated -- Meadow IDs stored for cross-entity lookups
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## GUID Marshaling
|
|
28
|
+
|
|
29
|
+
External system GUIDs are transformed into deterministic Meadow GUIDs using this pattern:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
{AdapterSetGUIDMarshalPrefix}-{EntityGUIDMarshalPrefix}-{ExternalGUID}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
For example, with default settings and entity "Book":
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
External GUID: "12345"
|
|
39
|
+
Meadow GUID: "INTG-DEF-E-Book-12345"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This ensures GUIDs are unique across integration sets and entities.
|
|
43
|
+
|
|
44
|
+
### Cross-Entity GUID Resolution
|
|
45
|
+
|
|
46
|
+
When a source record contains GUID fields for other entities (e.g. `GUIDAuthor` on a BookAuthorJoin record), the adapter looks up the corresponding Meadow ID via the GUIDMap:
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
// Source record: { GUIDBookAuthorJoin: "BAJ_1", GUIDBook: "Book_1", GUIDAuthor: "Author_5" }
|
|
50
|
+
// Adapter marshals:
|
|
51
|
+
// - GUIDBookAuthorJoin -> "INTG-DEF-E-BookAuthorJoin-BAJ_1"
|
|
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
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This is why entity integration order matters -- entities that are referenced by other entities should be integrated first so their IDs are available in the GUIDMap.
|
|
57
|
+
|
|
58
|
+
## Batch Processing
|
|
59
|
+
|
|
60
|
+
The adapter automatically switches between single and bulk upsert modes:
|
|
61
|
+
|
|
62
|
+
- **Below threshold** (`< RecordThresholdForBulkUpsert`, default 1000): Records are upserted one at a time
|
|
63
|
+
- **Above threshold**: Records are batched into groups of `BulkUpsertBatchSize` (default 100) and sent as bulk upserts
|
|
64
|
+
|
|
65
|
+
## Retry Logic
|
|
66
|
+
|
|
67
|
+
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
|
+
|
|
69
|
+
1. The response contains the entity ID field
|
|
70
|
+
2. The ID is a positive number
|
|
71
|
+
3. The response GUID matches the sent GUID
|
|
72
|
+
|
|
73
|
+
If validation fails, the record is retried.
|
|
74
|
+
|
|
75
|
+
## Schema Validation
|
|
76
|
+
|
|
77
|
+
When `integrateRecords()` is called, the adapter fetches the entity schema from the Meadow API (`GET /Entity/Schema`). During marshaling:
|
|
78
|
+
|
|
79
|
+
- String fields are truncated to their schema-defined `size`
|
|
80
|
+
- Non-string fields are passed through
|
|
81
|
+
- Unknown fields (not in schema) are dropped
|
|
82
|
+
- Reserved fields (`CreateDate`, `UpdateDate`, `Deleted`, `DeleteDate`) are always stripped
|
|
83
|
+
|
|
84
|
+
## Delete Operations
|
|
85
|
+
|
|
86
|
+
Records with `Deleted: true` in the source are queued for deletion. The adapter looks up each record by GUID via the API, then issues a DELETE request using the Meadow ID.
|
|
87
|
+
|
|
88
|
+
## CLI Usage
|
|
89
|
+
|
|
90
|
+
The `load_comprehension` command wraps the Integration Adapter for CLI use:
|
|
91
|
+
|
|
92
|
+
```shell
|
|
93
|
+
npx meadow-integration load_comprehension ./my-comprehension.json \
|
|
94
|
+
-p "MY-PREFIX" \
|
|
95
|
+
-e "MY-ENTITY-PREFIX"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This automatically creates adapters for every entity in the comprehension and processes them in sequence.
|
|
99
|
+
|
|
100
|
+
## Static Helper
|
|
101
|
+
|
|
102
|
+
The `getAdapter()` static method provides a convenient way to get or create an adapter:
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
const libAdapter = require('meadow-integration/source/Meadow-Service-Integration-Adapter.js');
|
|
106
|
+
|
|
107
|
+
// Gets existing adapter for 'Book' or creates a new one
|
|
108
|
+
let tmpAdapter = libAdapter.getAdapter(myFable, 'Book', 'BK');
|
|
109
|
+
```
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Mapping Files
|
|
2
|
+
|
|
3
|
+
Mapping files give you precise control over how source data columns become comprehension fields. They are JSON files that define the entity name, GUID generation template, and field-by-field mappings using Pict templates.
|
|
4
|
+
|
|
5
|
+
## Basic Structure
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"Entity": "Book",
|
|
10
|
+
"GUIDTemplate": "Book_{~D:Record.id~}",
|
|
11
|
+
"Mappings": {
|
|
12
|
+
"Title": "{~D:Record.title~}",
|
|
13
|
+
"Language": "{~D:Record.language_code~}",
|
|
14
|
+
"ISBN": "{~D:Record.isbn~}",
|
|
15
|
+
"Genre": "Unknown"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Fields
|
|
21
|
+
|
|
22
|
+
| Field | Required | Description |
|
|
23
|
+
|-------|----------|-------------|
|
|
24
|
+
| `Entity` | Yes | The entity name. Determines the top-level key in the comprehension. |
|
|
25
|
+
| `GUIDTemplate` | Yes | Pict template that generates a unique GUID for each record. |
|
|
26
|
+
| `GUIDName` | No | Name of the GUID column (default: `GUID{Entity}`). |
|
|
27
|
+
| `Mappings` | Yes | Object mapping output field names to Pict template expressions. |
|
|
28
|
+
| `Solvers` | No | Array of fable expression strings to run before mapping. |
|
|
29
|
+
| `MultipleGUIDUniqueness` | No | When `true`, a single source row produces multiple output records. |
|
|
30
|
+
| `ManyfestAddresses` | No | When `true`, use Manyfest dot-notation for nested field assignment. |
|
|
31
|
+
|
|
32
|
+
## Pict Template Syntax
|
|
33
|
+
|
|
34
|
+
Templates use the `{~D:...~}` syntax to reference values from the current source record. Each record is available as `Record.<column>`.
|
|
35
|
+
|
|
36
|
+
| Template | Description |
|
|
37
|
+
|----------|-------------|
|
|
38
|
+
| `{~D:Record.title~}` | Direct column reference |
|
|
39
|
+
| `{~D:Record.first~} {~D:Record.last~}` | String concatenation |
|
|
40
|
+
| `"Unknown"` | Static/literal value |
|
|
41
|
+
| `{~D:Fable.Math.roundPrecise(Record.year,0)~}` | Function call on the value |
|
|
42
|
+
| `{~PascalCaseIdentifier:Record.name~}` | Template with format modifier |
|
|
43
|
+
|
|
44
|
+
The full Pict template engine is available, including format modifiers and function calls through the Fable service container.
|
|
45
|
+
|
|
46
|
+
## Example: Simple Column Mapping
|
|
47
|
+
|
|
48
|
+
Given a CSV with columns `id, name, email, age`:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"Entity": "User",
|
|
53
|
+
"GUIDTemplate": "User_{~D:Record.id~}",
|
|
54
|
+
"Mappings": {
|
|
55
|
+
"DisplayName": "{~D:Record.name~}",
|
|
56
|
+
"EmailAddress": "{~D:Record.email~}",
|
|
57
|
+
"Age": "{~D:Record.age~}"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
This produces records like:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"GUIDUser": "User_42",
|
|
67
|
+
"DisplayName": "Alice Smith",
|
|
68
|
+
"EmailAddress": "alice@example.com",
|
|
69
|
+
"Age": "30"
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Example: Computed GUID from Multiple Columns
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"Entity": "Transaction",
|
|
78
|
+
"GUIDTemplate": "TXN_{~D:Record.date~}_{~D:Record.account_id~}_{~D:Record.seq~}",
|
|
79
|
+
"Mappings": {
|
|
80
|
+
"Amount": "{~D:Record.amount~}",
|
|
81
|
+
"AccountID": "{~D:Record.account_id~}",
|
|
82
|
+
"TransactionDate": "{~D:Record.date~}"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Composite GUIDs are useful when the source data has no single unique key.
|
|
88
|
+
|
|
89
|
+
## Example: Multi-Record Generation with Solvers
|
|
90
|
+
|
|
91
|
+
When a single source row needs to produce multiple comprehension records, use `MultipleGUIDUniqueness` with a Solver expression that splits a value.
|
|
92
|
+
|
|
93
|
+
Given a books CSV where the `authors` column contains comma-separated names:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"Entity": "Author",
|
|
98
|
+
"MultipleGUIDUniqueness": true,
|
|
99
|
+
"Solvers": [
|
|
100
|
+
"NewRecordsGUIDUniqueness = STRINGGETSEGMENTS(IncomingRecord.authors,\",\")"
|
|
101
|
+
],
|
|
102
|
+
"GUIDTemplate": "Author_{~PascalCaseIdentifier:Record._GUIDUniqueness~}",
|
|
103
|
+
"Mappings": {
|
|
104
|
+
"Name": "{~D:Record._GUIDUniqueness~}"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The Solver splits the comma-separated author names into an array stored in `NewRecordsGUIDUniqueness`. For each entry, a record is created with `_GUIDUniqueness` set to that entry's value.
|
|
110
|
+
|
|
111
|
+
## Example: Join Table Mapping
|
|
112
|
+
|
|
113
|
+
To create many-to-many join records between Books and Authors:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"Entity": "BookAuthorJoin",
|
|
118
|
+
"MultipleGUIDUniqueness": true,
|
|
119
|
+
"Solvers": [
|
|
120
|
+
"NewRecordsGUIDUniqueness = STRINGGETSEGMENTS(IncomingRecord.authors,\",\")"
|
|
121
|
+
],
|
|
122
|
+
"GUIDTemplate": "BAJ_A_{~PascalCaseIdentifier:Record._GUIDUniqueness~}_B_{~D:Record.id~}",
|
|
123
|
+
"Mappings": {
|
|
124
|
+
"GUIDBook": "Book_{~D:Record.id~}",
|
|
125
|
+
"GUIDAuthor": "Author_{~PascalCaseIdentifier:Record._GUIDUniqueness~}"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
This generates one join record per book-author pair, with cross-reference GUIDs that match the Book and Author entities.
|
|
131
|
+
|
|
132
|
+
## Configuration Priority
|
|
133
|
+
|
|
134
|
+
When running a transform, three layers of configuration are merged:
|
|
135
|
+
|
|
136
|
+
1. **Implicit** -- Auto-detected from the first record's columns (entity name from filename, 1:1 column mappings)
|
|
137
|
+
2. **Explicit** -- Loaded from a mapping file via `-m`
|
|
138
|
+
3. **User** -- Command-line options (`-e`, `-n`, `-g`, `-c`)
|
|
139
|
+
|
|
140
|
+
Each layer overrides the previous, so CLI options always win.
|