meadow-integration 1.0.5 → 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 +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 +6 -3
- 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
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# MeadowCloneRestClient
|
|
2
|
+
|
|
3
|
+
REST client for communicating with a remote Meadow API server. Provides authentication, session management, CRUD operations, and paginated entity set downloads for the data-clone pipeline.
|
|
4
|
+
|
|
5
|
+
**Source:** `source/services/clone/Meadow-Service-RestClient.js`
|
|
6
|
+
|
|
7
|
+
**Extends:** `fable-serviceproviderbase`
|
|
8
|
+
|
|
9
|
+
**Service Type:** `MeadowCloneRestClient`
|
|
10
|
+
|
|
11
|
+
## Constructor
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
const restClient = fable.serviceManager.instantiateServiceProvider('MeadowCloneRestClient', pOptions);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Options
|
|
18
|
+
|
|
19
|
+
| Option | Type | Default | Description |
|
|
20
|
+
|--------|------|---------|-------------|
|
|
21
|
+
| `DownloadBatchSize` | `number` | `100` | Number of records per page when downloading entity sets. |
|
|
22
|
+
| `ServerURL` | `string` | `'https://localhost:8080/1.0/'` | Base URL for all API requests. |
|
|
23
|
+
| `UserID` | `string\|false` | `false` | Username for authentication. If `false`, `authenticate()` is a no-op. |
|
|
24
|
+
| `Password` | `string\|false` | `false` | Password for authentication. If `false`, `authenticate()` is a no-op. |
|
|
25
|
+
| `SessionToken` | `string` | *(none)* | Optional pre-existing session token to use instead of authenticating. |
|
|
26
|
+
|
|
27
|
+
## Properties
|
|
28
|
+
|
|
29
|
+
### `session`
|
|
30
|
+
|
|
31
|
+
*Getter* -- Returns the current session data object, or `false` if not authenticated.
|
|
32
|
+
|
|
33
|
+
### `loggedIn`
|
|
34
|
+
|
|
35
|
+
*Getter* -- Returns `boolean`. `true` after a successful call to `authenticate()`, `false` after `deauthenticate()` or before login.
|
|
36
|
+
|
|
37
|
+
### `serverURL`
|
|
38
|
+
|
|
39
|
+
The base URL string used for all API requests.
|
|
40
|
+
|
|
41
|
+
### `cache`
|
|
42
|
+
|
|
43
|
+
Object map of per-entity `ObjectCache` instances. Populated automatically by `getEntity()` and `getEntitySet()`.
|
|
44
|
+
|
|
45
|
+
## Methods
|
|
46
|
+
|
|
47
|
+
### `authenticate(fCallback)`
|
|
48
|
+
|
|
49
|
+
Authenticates with the Meadow API server using the configured `UserID` and `Password`.
|
|
50
|
+
|
|
51
|
+
| Parameter | Type | Description |
|
|
52
|
+
|-----------|------|-------------|
|
|
53
|
+
| `fCallback` | `function(pError, pSessionData)` | Callback with the session data on success. |
|
|
54
|
+
|
|
55
|
+
Posts to `{ServerURL}/Authenticate` with `{ UserName, Password }`. On success, stores session data and sets a `UserSession` cookie for subsequent requests.
|
|
56
|
+
|
|
57
|
+
If `UserID` or `Password` is falsy, authentication is skipped and the callback is invoked immediately.
|
|
58
|
+
|
|
59
|
+
### `deauthenticate(fCallback)`
|
|
60
|
+
|
|
61
|
+
Logs out from the Meadow API server and clears session data.
|
|
62
|
+
|
|
63
|
+
| Parameter | Type | Description |
|
|
64
|
+
|-----------|------|-------------|
|
|
65
|
+
| `fCallback` | `function(pError, pSessionData)` | Callback invoked after logout. `pSessionData` will be `false`. |
|
|
66
|
+
|
|
67
|
+
### `getJSON(pURL, fCallback)`
|
|
68
|
+
|
|
69
|
+
Performs a GET request for JSON data at the given URL path (appended to `ServerURL`).
|
|
70
|
+
|
|
71
|
+
| Parameter | Type | Description |
|
|
72
|
+
|-----------|------|-------------|
|
|
73
|
+
| `pURL` | `string` | URL path appended to `ServerURL`. |
|
|
74
|
+
| `fCallback` | `function(pError, pResponse, pBody)` | Standard REST callback. |
|
|
75
|
+
|
|
76
|
+
### `createEntity(pEntity, pRecord, fCallback)`
|
|
77
|
+
|
|
78
|
+
Creates a new entity record on the server via POST.
|
|
79
|
+
|
|
80
|
+
| Parameter | Type | Description |
|
|
81
|
+
|-----------|------|-------------|
|
|
82
|
+
| `pEntity` | `string` | Entity name (e.g. `'Animal'`). |
|
|
83
|
+
| `pRecord` | `object` | Record data to create. |
|
|
84
|
+
| `fCallback` | `function(pError, pBody)` | Callback with the created record body. |
|
|
85
|
+
|
|
86
|
+
### `updateEntity(pEntity, pRecord, fCallback)`
|
|
87
|
+
|
|
88
|
+
Updates an existing entity record on the server via PUT.
|
|
89
|
+
|
|
90
|
+
| Parameter | Type | Description |
|
|
91
|
+
|-----------|------|-------------|
|
|
92
|
+
| `pEntity` | `string` | Entity name. |
|
|
93
|
+
| `pRecord` | `object` | Record data to update (must include the identifier). |
|
|
94
|
+
| `fCallback` | `function(pError, pBody)` | Callback with the updated record body. |
|
|
95
|
+
|
|
96
|
+
### `upsertEntity(pEntity, pRecord, fCallback)`
|
|
97
|
+
|
|
98
|
+
Creates or updates an entity record on the server via PUT to the `/Upsert` endpoint.
|
|
99
|
+
|
|
100
|
+
| Parameter | Type | Description |
|
|
101
|
+
|-----------|------|-------------|
|
|
102
|
+
| `pEntity` | `string` | Entity name. |
|
|
103
|
+
| `pRecord` | `object` | Record data to upsert. |
|
|
104
|
+
| `fCallback` | `function(pError, pBody)` | Callback with the upserted record body. |
|
|
105
|
+
|
|
106
|
+
### `deleteEntity(pEntity, pIDRecord, fCallback)`
|
|
107
|
+
|
|
108
|
+
Deletes an entity record by ID from the server via DELETE.
|
|
109
|
+
|
|
110
|
+
| Parameter | Type | Description |
|
|
111
|
+
|-----------|------|-------------|
|
|
112
|
+
| `pEntity` | `string` | Entity name. |
|
|
113
|
+
| `pIDRecord` | `number\|string` | The ID of the record to delete. |
|
|
114
|
+
| `fCallback` | `function(pError, pBody)` | Callback with the response body. |
|
|
115
|
+
|
|
116
|
+
### `getEntity(pEntity, pIDRecord, fCallback)`
|
|
117
|
+
|
|
118
|
+
Retrieves a single entity record by ID. Uses an in-memory cache (max age 30s, max 10000 entries) to avoid redundant requests.
|
|
119
|
+
|
|
120
|
+
| Parameter | Type | Description |
|
|
121
|
+
|-----------|------|-------------|
|
|
122
|
+
| `pEntity` | `string` | Entity name. |
|
|
123
|
+
| `pIDRecord` | `number\|string` | The ID of the record to retrieve. |
|
|
124
|
+
| `fCallback` | `function(pError, pBody)` | Callback with the record body (from cache or server). |
|
|
125
|
+
|
|
126
|
+
### `getEntitySet(pEntity, pMeadowFilterExpression, fCallback)`
|
|
127
|
+
|
|
128
|
+
Downloads a full set of entity records matching a Meadow filter expression. Automatically paginates using `DownloadBatchSize`.
|
|
129
|
+
|
|
130
|
+
| Parameter | Type | Description |
|
|
131
|
+
|-----------|------|-------------|
|
|
132
|
+
| `pEntity` | `string` | Entity name. |
|
|
133
|
+
| `pMeadowFilterExpression` | `string` | Meadow filter expression (e.g. `'FBV~Deleted~EQ~0'`). |
|
|
134
|
+
| `fCallback` | `function(pError, pEntitySet)` | Callback with the full array of records. |
|
|
135
|
+
|
|
136
|
+
**Algorithm:**
|
|
137
|
+
1. Requests the count from `{Entity}s/Count/FilteredTo/{Filter}`.
|
|
138
|
+
2. Generates paginated URL fragments based on `DownloadBatchSize`.
|
|
139
|
+
3. Downloads each page sequentially and concatenates results.
|
|
140
|
+
|
|
141
|
+
### `setSessionToken(pSessionToken)`
|
|
142
|
+
|
|
143
|
+
Manually sets the session token for subsequent requests. The token is appended as a `SessionToken` query parameter when no session data cookie is present.
|
|
144
|
+
|
|
145
|
+
| Parameter | Type | Description |
|
|
146
|
+
|-----------|------|-------------|
|
|
147
|
+
| `pSessionToken` | `string` | The session token string. |
|
|
148
|
+
|
|
149
|
+
### `setSessionData(pSessionData)`
|
|
150
|
+
|
|
151
|
+
Sets session data and configures the `UserSession` cookie on the underlying REST client.
|
|
152
|
+
|
|
153
|
+
| Parameter | Type | Description |
|
|
154
|
+
|-----------|------|-------------|
|
|
155
|
+
| `pSessionData` | `object` | Session data object. If it has a `SessionID` property, that value is used as the cookie. |
|
|
156
|
+
|
|
157
|
+
### `resetSessionData()`
|
|
158
|
+
|
|
159
|
+
Clears all session data and cookies. Called internally by `deauthenticate()`.
|
|
160
|
+
|
|
161
|
+
## Usage Examples
|
|
162
|
+
|
|
163
|
+
### Authentication and Reading Entities
|
|
164
|
+
|
|
165
|
+
```js
|
|
166
|
+
const libFable = require('fable');
|
|
167
|
+
const libRestClient = require('meadow-integration/source/services/clone/Meadow-Service-RestClient');
|
|
168
|
+
|
|
169
|
+
const fable = new libFable({ Product: 'CloneApp' });
|
|
170
|
+
|
|
171
|
+
fable.serviceManager.addServiceType('MeadowCloneRestClient', libRestClient);
|
|
172
|
+
const restClient = fable.serviceManager.instantiateServiceProvider('MeadowCloneRestClient',
|
|
173
|
+
{
|
|
174
|
+
ServerURL: 'https://api.example.com/1.0/',
|
|
175
|
+
UserID: 'sync_user',
|
|
176
|
+
Password: 'sync_password',
|
|
177
|
+
DownloadBatchSize: 250
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
restClient.authenticate(
|
|
181
|
+
(pError, pSessionData) =>
|
|
182
|
+
{
|
|
183
|
+
if (pError)
|
|
184
|
+
{
|
|
185
|
+
console.error('Auth failed:', pError.message);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
console.log('Logged in:', restClient.loggedIn);
|
|
189
|
+
console.log('Session ID:', pSessionData.SessionID);
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Reading a Single Entity
|
|
194
|
+
|
|
195
|
+
```js
|
|
196
|
+
restClient.getEntity('Animal', 42,
|
|
197
|
+
(pError, pRecord) =>
|
|
198
|
+
{
|
|
199
|
+
if (pError)
|
|
200
|
+
{
|
|
201
|
+
console.error('Read failed:', pError.message);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
console.log('Animal name:', pRecord.Name);
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Downloading a Filtered Entity Set
|
|
209
|
+
|
|
210
|
+
```js
|
|
211
|
+
restClient.getEntitySet('Animal', 'FBV~Deleted~EQ~0',
|
|
212
|
+
(pError, pRecords) =>
|
|
213
|
+
{
|
|
214
|
+
if (pError)
|
|
215
|
+
{
|
|
216
|
+
console.error('Download failed:', pError.message);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
console.log(`Downloaded ${pRecords.length} animals.`);
|
|
220
|
+
|
|
221
|
+
for (const tmpRecord of pRecords)
|
|
222
|
+
{
|
|
223
|
+
console.log(` ${tmpRecord.IDAnimal}: ${tmpRecord.Name}`);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Using a Pre-existing Session Token
|
|
229
|
+
|
|
230
|
+
```js
|
|
231
|
+
const restClient = fable.serviceManager.instantiateServiceProvider('MeadowCloneRestClient',
|
|
232
|
+
{
|
|
233
|
+
ServerURL: 'https://api.example.com/1.0/',
|
|
234
|
+
SessionToken: 'abc-123-def-456'
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// No need to call authenticate() -- the token is appended automatically
|
|
238
|
+
restClient.getJSON('Animal/1',
|
|
239
|
+
(pError, pResponse, pBody) =>
|
|
240
|
+
{
|
|
241
|
+
console.log('Record:', pBody);
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Upserting and Deleting Records
|
|
246
|
+
|
|
247
|
+
```js
|
|
248
|
+
// Upsert
|
|
249
|
+
restClient.upsertEntity('Animal', { GUIDAnimal: 'ANIMAL-001', Name: 'Felix', Type: 'Cat' },
|
|
250
|
+
(pError, pBody) =>
|
|
251
|
+
{
|
|
252
|
+
console.log('Upserted:', pBody);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Delete
|
|
256
|
+
restClient.deleteEntity('Animal', 42,
|
|
257
|
+
(pError, pBody) =>
|
|
258
|
+
{
|
|
259
|
+
console.log('Deleted:', pBody);
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Deauthentication
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
restClient.deauthenticate(
|
|
267
|
+
(pError) =>
|
|
268
|
+
{
|
|
269
|
+
console.log('Logged out. Session:', restClient.session); // false
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Related Services
|
|
274
|
+
|
|
275
|
+
- [MeadowConnectionManager](./connection-manager.md) -- Manages the local database connection pool.
|
|
276
|
+
- [MeadowSync](./sync.md) -- Orchestrator that uses this REST client to download records from the server.
|
|
277
|
+
- [MeadowSyncEntityInitial](./sync-entity-initial.md) -- Uses this client to fetch max IDs, counts, and paginated record sets.
|
|
278
|
+
- [MeadowSyncEntityOngoing](./sync-entity-ongoing.md) -- Uses this client for update-based differential sync.
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# MeadowConnectionManager
|
|
2
|
+
|
|
3
|
+
Database connection manager supporting MySQL and MSSQL providers for the meadow data-clone services.
|
|
4
|
+
|
|
5
|
+
**Source:** `source/services/clone/Meadow-Service-ConnectionManager.js`
|
|
6
|
+
|
|
7
|
+
**Extends:** `fable-serviceproviderbase`
|
|
8
|
+
|
|
9
|
+
**Service Type:** `MeadowConnectionManager`
|
|
10
|
+
|
|
11
|
+
## Constructor
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
const connectionManager = fable.serviceManager.instantiateServiceProvider('MeadowConnectionManager', pOptions);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Options
|
|
18
|
+
|
|
19
|
+
| Option | Type | Default | Description |
|
|
20
|
+
|--------|------|---------|-------------|
|
|
21
|
+
| `Provider` | `string` | `'MySQL'` | Database provider to use. Supported values: `'MySQL'`, `'MSSQL'`. |
|
|
22
|
+
| `MySQL` | `object` | *(see below)* | MySQL-specific connection configuration. |
|
|
23
|
+
| `MySQL.server` | `string` | `'127.0.0.1'` | MySQL server hostname. |
|
|
24
|
+
| `MySQL.port` | `number` | `3306` | MySQL server port. |
|
|
25
|
+
| `MySQL.user` | `string` | `'root'` | MySQL authentication user. |
|
|
26
|
+
| `MySQL.password` | `string` | `''` | MySQL authentication password. |
|
|
27
|
+
| `MySQL.database` | `string` | `'meadow'` | MySQL database name. |
|
|
28
|
+
| `MySQL.connectionLimit` | `number` | `20` | Maximum number of connections in the MySQL pool. |
|
|
29
|
+
| `MSSQL` | `object` | *(see below)* | MSSQL-specific connection configuration. |
|
|
30
|
+
| `MSSQL.server` | `string` | `'127.0.0.1'` | MSSQL server hostname. |
|
|
31
|
+
| `MSSQL.port` | `number` | `1433` | MSSQL server port. |
|
|
32
|
+
| `MSSQL.user` | `string` | `'sa'` | MSSQL authentication user. |
|
|
33
|
+
| `MSSQL.password` | `string` | `''` | MSSQL authentication password. |
|
|
34
|
+
| `MSSQL.database` | `string` | `'meadow'` | MSSQL database name. |
|
|
35
|
+
| `MSSQL.ConnectionPoolLimit` | `number` | `20` | Maximum number of connections in the MSSQL pool. |
|
|
36
|
+
|
|
37
|
+
## Properties
|
|
38
|
+
|
|
39
|
+
### `connected`
|
|
40
|
+
|
|
41
|
+
*Getter* -- Returns `boolean`.
|
|
42
|
+
|
|
43
|
+
Indicates whether the connection manager has successfully established a connection to the database.
|
|
44
|
+
|
|
45
|
+
### `ConnectionPool`
|
|
46
|
+
|
|
47
|
+
The underlying database connection pool object. Set to `false` before `connect()` is called. After a successful connection, holds the provider-specific pool instance.
|
|
48
|
+
|
|
49
|
+
### `Provider`
|
|
50
|
+
|
|
51
|
+
The active database provider string (`'MySQL'` or `'MSSQL'`).
|
|
52
|
+
|
|
53
|
+
## Methods
|
|
54
|
+
|
|
55
|
+
### `connect(fCallback)`
|
|
56
|
+
|
|
57
|
+
Establishes a connection to the database using the configured provider.
|
|
58
|
+
|
|
59
|
+
| Parameter | Type | Description |
|
|
60
|
+
|-----------|------|-------------|
|
|
61
|
+
| `fCallback` | `function(pError, pConnectionPool)` | Callback invoked when the connection attempt completes. On success, `pConnectionPool` is the active pool. |
|
|
62
|
+
|
|
63
|
+
**Behavior:**
|
|
64
|
+
- For `MySQL`: Requires the `meadow-connection-mysql` package. Registers and instantiates a `MeadowMySQLProvider` service, then calls `connectAsync`. Also applies `connectionLimit` to `fable.settings`.
|
|
65
|
+
- For `MSSQL`: Requires the `meadow-connection-mssql` package. Registers and instantiates a `MeadowMSSQLProvider` service, then calls `connectAsync`.
|
|
66
|
+
- For unsupported providers: Calls back with an `Error`.
|
|
67
|
+
|
|
68
|
+
### `createIndex(pEntitySchema, pColumn, pIsUnique, fCallback)`
|
|
69
|
+
|
|
70
|
+
Creates a database index on the specified column for the given entity, if the index does not already exist.
|
|
71
|
+
|
|
72
|
+
| Parameter | Type | Description |
|
|
73
|
+
|-----------|------|-------------|
|
|
74
|
+
| `pEntitySchema` | `object` | Entity schema object. Must have a `TableName` property. |
|
|
75
|
+
| `pColumn` | `object` | Column descriptor object. Must have a `Column` property (string). |
|
|
76
|
+
| `pIsUnique` | `boolean` | If `true`, creates a unique index. |
|
|
77
|
+
| `fCallback` | `function(pError)` | Callback invoked when the index creation attempt completes. |
|
|
78
|
+
|
|
79
|
+
**Index naming convention (MySQL):** `AK_{TableName}_{ColumnName}`
|
|
80
|
+
|
|
81
|
+
**Provider-specific behavior:**
|
|
82
|
+
- **MySQL:** Checks `INFORMATION_SCHEMA.STATISTICS` for an existing index before creating. Silently ignores `ER_DUP_KEYNAME` errors.
|
|
83
|
+
- **MSSQL:** Uses `IF NOT EXISTS(SELECT * FROM sys.indexes ...)` to conditionally create. Errors are logged but not passed to the callback (the callback always succeeds).
|
|
84
|
+
|
|
85
|
+
## Usage Examples
|
|
86
|
+
|
|
87
|
+
### MySQL Connection
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
const libFable = require('fable');
|
|
91
|
+
const libConnectionManager = require('meadow-integration/source/services/clone/Meadow-Service-ConnectionManager');
|
|
92
|
+
|
|
93
|
+
const fable = new libFable({ Product: 'MyApp' });
|
|
94
|
+
|
|
95
|
+
fable.serviceManager.addServiceType('MeadowConnectionManager', libConnectionManager);
|
|
96
|
+
const connectionManager = fable.serviceManager.instantiateServiceProvider('MeadowConnectionManager',
|
|
97
|
+
{
|
|
98
|
+
Provider: 'MySQL',
|
|
99
|
+
MySQL:
|
|
100
|
+
{
|
|
101
|
+
server: 'db.example.com',
|
|
102
|
+
port: 3306,
|
|
103
|
+
user: 'app_user',
|
|
104
|
+
password: 'secret',
|
|
105
|
+
database: 'production',
|
|
106
|
+
connectionLimit: 10
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
connectionManager.connect(
|
|
111
|
+
(pError, pConnectionPool) =>
|
|
112
|
+
{
|
|
113
|
+
if (pError)
|
|
114
|
+
{
|
|
115
|
+
console.error('Connection failed:', pError.message);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
console.log('Connected:', connectionManager.connected); // true
|
|
119
|
+
// pConnectionPool is now available for queries
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### MSSQL Connection
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
const fable = new libFable({ Product: 'MyApp' });
|
|
127
|
+
|
|
128
|
+
fable.serviceManager.addServiceType('MeadowConnectionManager', libConnectionManager);
|
|
129
|
+
const connectionManager = fable.serviceManager.instantiateServiceProvider('MeadowConnectionManager',
|
|
130
|
+
{
|
|
131
|
+
Provider: 'MSSQL',
|
|
132
|
+
MSSQL:
|
|
133
|
+
{
|
|
134
|
+
server: 'sql.example.com',
|
|
135
|
+
port: 1433,
|
|
136
|
+
user: 'sa',
|
|
137
|
+
password: 'secret',
|
|
138
|
+
database: 'production',
|
|
139
|
+
ConnectionPoolLimit: 20
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
connectionManager.connect(
|
|
144
|
+
(pError, pConnectionPool) =>
|
|
145
|
+
{
|
|
146
|
+
if (pError)
|
|
147
|
+
{
|
|
148
|
+
console.error('MSSQL connection failed:', pError.message);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
console.log('Connected to MSSQL:', connectionManager.connected);
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Creating an Index
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
const entitySchema = { TableName: 'Animal' };
|
|
159
|
+
const guidColumn = { Column: 'GUIDAnimal', DataType: 'GUID' };
|
|
160
|
+
|
|
161
|
+
connectionManager.createIndex(entitySchema, guidColumn, true,
|
|
162
|
+
(pError) =>
|
|
163
|
+
{
|
|
164
|
+
if (pError)
|
|
165
|
+
{
|
|
166
|
+
console.error('Index creation failed:', pError.message);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
// Index AK_Animal_GUIDAnimal now exists
|
|
170
|
+
console.log('Index created successfully.');
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Related Services
|
|
175
|
+
|
|
176
|
+
- [MeadowCloneRestClient](./clone-rest-client.md) -- REST client for communicating with the remote Meadow API server.
|
|
177
|
+
- [MeadowSync](./sync.md) -- Orchestrates full entity synchronization using a ConnectionManager pool.
|
|
178
|
+
- [MeadowSyncEntityInitial](./sync-entity-initial.md) -- Initial sync uses the connection pool for table and index creation.
|
|
179
|
+
- [MeadowSyncEntityOngoing](./sync-entity-ongoing.md) -- Ongoing sync uses the connection pool for table and index creation.
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# MeadowGUIDMap
|
|
2
|
+
|
|
3
|
+
In-memory bidirectional mapping service between Meadow GUIDs, Meadow IDs, and external system GUIDs. Used by the integration adapter to resolve entity references across system boundaries.
|
|
4
|
+
|
|
5
|
+
**Source:** `source/Meadow-Service-Integration-GUIDMap.js`
|
|
6
|
+
|
|
7
|
+
**Extends:** `fable-serviceproviderbase`
|
|
8
|
+
|
|
9
|
+
**Service Type:** `MeadowGUIDMap`
|
|
10
|
+
|
|
11
|
+
## Constructor
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
const guidMap = fable.addAndInstantiateServiceType('MeadowGUIDMap', libMeadowGUIDMap);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The `MeadowGUIDMap` is typically instantiated automatically by `MeadowIntegrationAdapter` if one does not already exist on the Fable instance.
|
|
18
|
+
|
|
19
|
+
### Options
|
|
20
|
+
|
|
21
|
+
The default options object is empty. No configuration is currently required.
|
|
22
|
+
|
|
23
|
+
## Internal Data Structures
|
|
24
|
+
|
|
25
|
+
The GUIDMap maintains three parallel in-memory maps, each keyed by entity name:
|
|
26
|
+
|
|
27
|
+
| Map | Description |
|
|
28
|
+
|-----|-------------|
|
|
29
|
+
| `_GUIDMap` | `{ Entity: { MeadowGUID: MeadowID } }` -- Maps Meadow GUIDs to numeric IDs. |
|
|
30
|
+
| `_IDMap` | `{ Entity: { MeadowID: MeadowGUID } }` -- Reverse map of numeric IDs to Meadow GUIDs. |
|
|
31
|
+
| `_ExternalGUIDMap` | `{ Entity: { ExternalGUID: MeadowGUID } }` -- Maps external system GUIDs to Meadow GUIDs. |
|
|
32
|
+
|
|
33
|
+
## Methods
|
|
34
|
+
|
|
35
|
+
### `addEntity(pEntity)`
|
|
36
|
+
|
|
37
|
+
Initializes the mapping tables for a given entity if they do not already exist.
|
|
38
|
+
|
|
39
|
+
| Parameter | Type | Description |
|
|
40
|
+
|-----------|------|-------------|
|
|
41
|
+
| `pEntity` | `string` | Entity name (e.g. `'Customer'`). |
|
|
42
|
+
|
|
43
|
+
**Returns:** `true`
|
|
44
|
+
|
|
45
|
+
Called automatically by other methods when an entity is encountered for the first time.
|
|
46
|
+
|
|
47
|
+
### `mapGUIDToID(pEntity, pGUID, pID)`
|
|
48
|
+
|
|
49
|
+
Stores a bidirectional mapping between a Meadow GUID and its numeric ID.
|
|
50
|
+
|
|
51
|
+
| Parameter | Type | Description |
|
|
52
|
+
|-----------|------|-------------|
|
|
53
|
+
| `pEntity` | `string` | Entity name. |
|
|
54
|
+
| `pGUID` | `string` | The Meadow GUID value. |
|
|
55
|
+
| `pID` | `number\|string` | The numeric ID value. |
|
|
56
|
+
|
|
57
|
+
**Returns:** `true`
|
|
58
|
+
|
|
59
|
+
Updates both `_GUIDMap[Entity][GUID] = ID` and `_IDMap[Entity][ID] = GUID`.
|
|
60
|
+
|
|
61
|
+
### `getIDFromGUID(pEntity, pGUID)`
|
|
62
|
+
|
|
63
|
+
Looks up the numeric ID for a given Meadow GUID.
|
|
64
|
+
|
|
65
|
+
| Parameter | Type | Description |
|
|
66
|
+
|-----------|------|-------------|
|
|
67
|
+
| `pEntity` | `string` | Entity name. |
|
|
68
|
+
| `pGUID` | `string` | The Meadow GUID to look up. |
|
|
69
|
+
|
|
70
|
+
**Returns:** `number|string|false` -- The ID if found, or `false` if the GUID is not mapped.
|
|
71
|
+
|
|
72
|
+
### `getIDFromGUIDAsync(pEntity, pGUID, fCallback)`
|
|
73
|
+
|
|
74
|
+
Asynchronous version of `getIDFromGUID`. If the GUID is not in the local map, attempts to fetch the record from the server via `fable.MeadowRestClient.getEntityByGUID()`.
|
|
75
|
+
|
|
76
|
+
| Parameter | Type | Description |
|
|
77
|
+
|-----------|------|-------------|
|
|
78
|
+
| `pEntity` | `string` | Entity name. |
|
|
79
|
+
| `pGUID` | `string` | The Meadow GUID to look up. |
|
|
80
|
+
| `fCallback` | `function(pError, pID)` | Callback with the ID if found, or `false`. |
|
|
81
|
+
|
|
82
|
+
If the record is fetched from the server, the GUID-to-ID mapping is stored for future lookups.
|
|
83
|
+
|
|
84
|
+
### `getGUIDFromID(pEntity, pID)`
|
|
85
|
+
|
|
86
|
+
Looks up the Meadow GUID for a given numeric ID.
|
|
87
|
+
|
|
88
|
+
| Parameter | Type | Description |
|
|
89
|
+
|-----------|------|-------------|
|
|
90
|
+
| `pEntity` | `string` | Entity name. |
|
|
91
|
+
| `pID` | `number\|string` | The numeric ID to look up. |
|
|
92
|
+
|
|
93
|
+
**Returns:** `string|false` -- The GUID if found, or `false` if the ID is not mapped.
|
|
94
|
+
|
|
95
|
+
### `mapExternalGUIDtoMeadowGUID(pEntity, pExternalGUID, pMeadowGUID)`
|
|
96
|
+
|
|
97
|
+
Stores a one-directional mapping from an external system GUID to a Meadow GUID.
|
|
98
|
+
|
|
99
|
+
| Parameter | Type | Description |
|
|
100
|
+
|-----------|------|-------------|
|
|
101
|
+
| `pEntity` | `string` | Entity name. |
|
|
102
|
+
| `pExternalGUID` | `string` | The external system's GUID value. |
|
|
103
|
+
| `pMeadowGUID` | `string` | The corresponding Meadow GUID. |
|
|
104
|
+
|
|
105
|
+
**Returns:** `true`
|
|
106
|
+
|
|
107
|
+
### `getMeadowGUIDFromExternalGUID(pEntity, pExternalGUID)`
|
|
108
|
+
|
|
109
|
+
Looks up the Meadow GUID for a given external system GUID.
|
|
110
|
+
|
|
111
|
+
| Parameter | Type | Description |
|
|
112
|
+
|-----------|------|-------------|
|
|
113
|
+
| `pEntity` | `string` | Entity name. |
|
|
114
|
+
| `pExternalGUID` | `string` | The external system's GUID to look up. |
|
|
115
|
+
|
|
116
|
+
**Returns:** `string|false` -- The Meadow GUID if found, or `false`.
|
|
117
|
+
|
|
118
|
+
### `getMeadowIDFromExternalGUID(pEntity, pExternalGUID)`
|
|
119
|
+
|
|
120
|
+
Resolves an external system GUID all the way to a Meadow numeric ID by chaining `getMeadowGUIDFromExternalGUID()` and `getIDFromGUID()`.
|
|
121
|
+
|
|
122
|
+
| Parameter | Type | Description |
|
|
123
|
+
|-----------|------|-------------|
|
|
124
|
+
| `pEntity` | `string` | Entity name. |
|
|
125
|
+
| `pExternalGUID` | `string` | The external system's GUID to resolve. |
|
|
126
|
+
|
|
127
|
+
**Returns:** `number|string|false` -- The Meadow ID if both mappings exist, or `false`.
|
|
128
|
+
|
|
129
|
+
## Usage Examples
|
|
130
|
+
|
|
131
|
+
### Basic GUID-to-ID Mapping
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
const libFable = require('fable');
|
|
135
|
+
const libGUIDMap = require('meadow-integration/source/Meadow-Service-Integration-GUIDMap');
|
|
136
|
+
|
|
137
|
+
const fable = new libFable({ Product: 'Mapper' });
|
|
138
|
+
const guidMap = fable.addAndInstantiateServiceType('MeadowGUIDMap', libGUIDMap);
|
|
139
|
+
|
|
140
|
+
// Store a mapping
|
|
141
|
+
guidMap.mapGUIDToID('Customer', 'INTG-CUST-001', 42);
|
|
142
|
+
|
|
143
|
+
// Look up by GUID
|
|
144
|
+
const id = guidMap.getIDFromGUID('Customer', 'INTG-CUST-001');
|
|
145
|
+
console.log(id); // 42
|
|
146
|
+
|
|
147
|
+
// Look up by ID (reverse)
|
|
148
|
+
const guid = guidMap.getGUIDFromID('Customer', 42);
|
|
149
|
+
console.log(guid); // 'INTG-CUST-001'
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### External GUID Resolution Chain
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
// Step 1: Map external GUID to Meadow GUID
|
|
156
|
+
guidMap.mapExternalGUIDtoMeadowGUID('Customer', 'CRM-42', 'INTG-DEF-E-Customer-CRM-42');
|
|
157
|
+
|
|
158
|
+
// Step 2: Map Meadow GUID to Meadow ID (done by the adapter after server upsert)
|
|
159
|
+
guidMap.mapGUIDToID('Customer', 'INTG-DEF-E-Customer-CRM-42', 100);
|
|
160
|
+
|
|
161
|
+
// Step 3: Resolve external GUID directly to Meadow GUID
|
|
162
|
+
const meadowGUID = guidMap.getMeadowGUIDFromExternalGUID('Customer', 'CRM-42');
|
|
163
|
+
console.log(meadowGUID); // 'INTG-DEF-E-Customer-CRM-42'
|
|
164
|
+
|
|
165
|
+
// Step 4: Resolve external GUID all the way to Meadow ID
|
|
166
|
+
const meadowID = guidMap.getMeadowIDFromExternalGUID('Customer', 'CRM-42');
|
|
167
|
+
console.log(meadowID); // 100
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Cross-Entity Reference Resolution
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
// After integrating Customers, their mappings exist
|
|
174
|
+
guidMap.mapExternalGUIDtoMeadowGUID('Customer', 'CRM-42', 'INTG-DEF-E-Customer-CRM-42');
|
|
175
|
+
guidMap.mapGUIDToID('Customer', 'INTG-DEF-E-Customer-CRM-42', 100);
|
|
176
|
+
|
|
177
|
+
// When integrating Orders, resolve the Customer reference
|
|
178
|
+
const customerID = guidMap.getMeadowIDFromExternalGUID('Customer', 'CRM-42');
|
|
179
|
+
console.log(customerID); // 100
|
|
180
|
+
|
|
181
|
+
// The adapter uses this to set IDCustomer on the Order record
|
|
182
|
+
const orderRecord = {
|
|
183
|
+
GUIDOrder: 'INTG-DEF-E-Order-ORD-500',
|
|
184
|
+
IDCustomer: customerID,
|
|
185
|
+
Total: 99.99
|
|
186
|
+
};
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Async GUID Lookup (Server Fallback)
|
|
190
|
+
|
|
191
|
+
```js
|
|
192
|
+
guidMap.getIDFromGUIDAsync('Customer', 'INTG-DEF-E-Customer-CRM-99',
|
|
193
|
+
(pError, pID) =>
|
|
194
|
+
{
|
|
195
|
+
if (pError)
|
|
196
|
+
{
|
|
197
|
+
console.error('Lookup failed:', pError.message);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (pID)
|
|
201
|
+
{
|
|
202
|
+
console.log('Found Customer ID:', pID);
|
|
203
|
+
}
|
|
204
|
+
else
|
|
205
|
+
{
|
|
206
|
+
console.log('Customer not found on server.');
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Mapping Flow Diagram
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
External System MeadowGUIDMap Meadow Server
|
|
215
|
+
--------------- ------------- -------------
|
|
216
|
+
CRM-42 --mapExternalGUIDtoMeadowGUID-->
|
|
217
|
+
INTG-DEF-E-Customer-CRM-42
|
|
218
|
+
--mapGUIDToID-->
|
|
219
|
+
GUID: INTG-DEF-E-Customer-CRM-42 => ID: 100
|
|
220
|
+
|
|
221
|
+
Later lookups:
|
|
222
|
+
getMeadowGUIDFromExternalGUID('Customer', 'CRM-42')
|
|
223
|
+
=> 'INTG-DEF-E-Customer-CRM-42'
|
|
224
|
+
|
|
225
|
+
getMeadowIDFromExternalGUID('Customer', 'CRM-42')
|
|
226
|
+
=> 100
|
|
227
|
+
|
|
228
|
+
getGUIDFromID('Customer', 100)
|
|
229
|
+
=> 'INTG-DEF-E-Customer-CRM-42'
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Related Services
|
|
233
|
+
|
|
234
|
+
- [MeadowIntegrationAdapter](./integration-adapter.md) -- The primary consumer; uses GUIDMap for all GUID resolution during record marshaling and upsert.
|