@fluentcommerce/fc-connect-sdk 0.1.48 → 0.1.52
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/CHANGELOG.md +506 -379
- package/README.md +343 -0
- package/dist/cjs/clients/fluent-client.js +110 -14
- package/dist/cjs/data-sources/s3-data-source.js +1 -1
- package/dist/cjs/data-sources/sftp-data-source.js +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/services/extraction/extraction-orchestrator.d.ts +4 -1
- package/dist/cjs/services/extraction/extraction-orchestrator.js +84 -11
- package/dist/cjs/types/index.d.ts +79 -10
- package/dist/cjs/versori/fluent-versori-client.d.ts +4 -1
- package/dist/cjs/versori/fluent-versori-client.js +131 -13
- package/dist/esm/clients/fluent-client.js +110 -14
- package/dist/esm/data-sources/s3-data-source.js +1 -1
- package/dist/esm/data-sources/sftp-data-source.js +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/services/extraction/extraction-orchestrator.d.ts +4 -1
- package/dist/esm/services/extraction/extraction-orchestrator.js +84 -11
- package/dist/esm/types/index.d.ts +79 -10
- package/dist/esm/versori/fluent-versori-client.d.ts +4 -1
- package/dist/esm/versori/fluent-versori-client.js +131 -13
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/tsconfig.types.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/services/extraction/extraction-orchestrator.d.ts +4 -1
- package/dist/types/types/index.d.ts +79 -10
- package/dist/types/versori/fluent-versori-client.d.ts +4 -1
- package/docs/02-CORE-GUIDES/api-reference/event-api-input-output-reference.md +478 -18
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-01-client-api.md +83 -0
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-types.md +52 -0
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-partial-responses.md +212 -0
- package/docs/02-CORE-GUIDES/api-reference/readme.md +1 -1
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-08-extraction-orchestrator.md +68 -4
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-01-foundations.md +450 -448
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-02-quick-start.md +476 -474
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-03-schema-validation.md +464 -462
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-05-advanced-patterns.md +1366 -1364
- package/docs/readme.md +245 -245
- package/package.json +17 -6
- package/docs/versori-apis/ACTIVATIONS-AND-VARIABLES-GUIDE.md +0 -60
- package/docs/versori-apis/JWT-GENERATION-GUIDE.md +0 -94
- package/docs/versori-apis/QUICK-WORKFLOW.md +0 -293
- package/docs/versori-apis/README.md +0 -73
- package/docs/versori-apis/VERSORI-PLATFORM-ARCHITECTURE.md +0 -880
- package/docs/versori-apis/Versori-Platform-API.postman_collection.json +0 -2925
- package/docs/versori-apis/Versori-Platform-API.postman_environment.example.json +0 -62
- package/docs/versori-apis/Versori-Platform-API.postman_environment.json +0 -178
|
@@ -1,462 +1,464 @@
|
|
|
1
|
-
# Module 3: Schema Validation
|
|
2
|
-
|
|
3
|
-
**Ensure mapping correctness before deployment**
|
|
4
|
-
|
|
5
|
-
**Level:** Intermediate
|
|
6
|
-
**Time:** 30 minutes
|
|
7
|
-
**Prerequisites:** [Module 2: Quick Start](./mapping-02-quick-start.md)
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Learning Objectives
|
|
12
|
-
|
|
13
|
-
After completing this module, you will:
|
|
14
|
-
|
|
15
|
-
- ✅ Introspect your Fluent Commerce GraphQL schema
|
|
16
|
-
- ✅ Validate mappings against the schema
|
|
17
|
-
- ✅ Identify and fix common schema violations
|
|
18
|
-
- ✅ Understand schema validation best practices
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## Why Schema Validation Matters
|
|
23
|
-
|
|
24
|
-
All GraphQL examples in this documentation are validated against the Fluent Commerce schema documented in [`docs/schema/fluent-commerce-schema.json`](../../../04-REFERENCE/schema/fluent-commerce-schema.json). However, **your specific Fluent Commerce environment may have different field names, types, or requirements**.
|
|
25
|
-
|
|
26
|
-
Using incorrect field names leads to:
|
|
27
|
-
|
|
28
|
-
- ❌ Mapping failures at runtime
|
|
29
|
-
- ❌ Silent data loss (fields not saved)
|
|
30
|
-
- ❌ Cryptic error messages from Fluent API
|
|
31
|
-
- ❌ Failed integrations
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## Step 1: Introspect Your Schema
|
|
36
|
-
|
|
37
|
-
Generate a schema file specific to your environment:
|
|
38
|
-
|
|
39
|
-
### Using npx (No Installation)
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
npx fc-connect introspect-schema \
|
|
43
|
-
--endpoint https://YOUR_ACCOUNT.api.fluentcommerce.com/graphql \
|
|
44
|
-
--output schema.json
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### What You Get
|
|
48
|
-
|
|
49
|
-
The generated `schema.json` contains:
|
|
50
|
-
|
|
51
|
-
- ✅ All available mutations (`createOrder`, `upsertInventoryPosition`, etc.)
|
|
52
|
-
- ✅ Required vs optional fields for each mutation input
|
|
53
|
-
- ✅ Field types (`String!`, `Int`, `Float`, `DateTime`, etc.)
|
|
54
|
-
- ✅ Available query fields
|
|
55
|
-
- ✅ Enum values (`OrderType`, `EntityType`, etc.)
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
## Step 2: Validate Your Mapping
|
|
60
|
-
|
|
61
|
-
Before deploying your connector, validate your mapping against the schema:
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
# Validate ingestion mapping
|
|
65
|
-
npx fc-connect validate-schema \
|
|
66
|
-
--mapping config/field-mappings.json \
|
|
67
|
-
--schema schema.json
|
|
68
|
-
|
|
69
|
-
# Validate extraction mapping
|
|
70
|
-
npx fc-connect validate-schema \
|
|
71
|
-
--mapping config/extraction-mappings.json \
|
|
72
|
-
--schema schema.json
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### What Gets Validated
|
|
76
|
-
|
|
77
|
-
The validator checks:
|
|
78
|
-
|
|
79
|
-
- ✅ All target fields exist in the mutation input
|
|
80
|
-
- ✅ Required fields are mapped
|
|
81
|
-
- ✅ Field types match (`String` → `String!`, `Int` → `Int!`)
|
|
82
|
-
- ✅ Source paths are valid
|
|
83
|
-
- ✅ Resolvers are compatible with target types
|
|
84
|
-
|
|
85
|
-
---
|
|
86
|
-
|
|
87
|
-
## Example: Schema-Validated Order Mapping
|
|
88
|
-
|
|
89
|
-
### ✅ CORRECT - Validated Against Schema
|
|
90
|
-
|
|
91
|
-
```json
|
|
92
|
-
{
|
|
93
|
-
"direction": "ingest",
|
|
94
|
-
"sourceFormat": "xml",
|
|
95
|
-
"mutation": "createOrder",
|
|
96
|
-
"comment": "✅ Validated against Fluent Commerce schema",
|
|
97
|
-
|
|
98
|
-
"fields": {
|
|
99
|
-
"ref": {
|
|
100
|
-
"source": "order@id",
|
|
101
|
-
"required": true,
|
|
102
|
-
"comment": "✅ Maps to CreateOrderInput.ref (String! required)"
|
|
103
|
-
},
|
|
104
|
-
"type": {
|
|
105
|
-
"source": "order@type",
|
|
106
|
-
"resolver": "custom.mapOrderType",
|
|
107
|
-
"comment": "✅ Maps to CreateOrderInput.type (OrderType! enum required)"
|
|
108
|
-
},
|
|
109
|
-
"retailer": {
|
|
110
|
-
"fields": {
|
|
111
|
-
"id": {
|
|
112
|
-
"value": "1",
|
|
113
|
-
"comment": "✅ CreateOrderInput.retailer (RetailerInput! required)"
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
"totalPrice": {
|
|
118
|
-
"source": "order.total",
|
|
119
|
-
"resolver": "sdk.parseFloat",
|
|
120
|
-
"required": true,
|
|
121
|
-
"comment": "✅ Maps to CreateOrderInput.totalPrice (Float! required)"
|
|
122
|
-
},
|
|
123
|
-
"customer": {
|
|
124
|
-
"fields": {
|
|
125
|
-
"id": {
|
|
126
|
-
"resolver": "custom.lookupCustomerByEmail",
|
|
127
|
-
"comment": "✅ CreateOrderInput.customer (CustomerInput! required - lookup by email)"
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
"items": {
|
|
132
|
-
"source": "order.items.item",
|
|
133
|
-
"isArray": true,
|
|
134
|
-
"comment": "✅ Maps to CreateOrderInput.items ([OrderItemInput]! required)",
|
|
135
|
-
"fields": {
|
|
136
|
-
"ref": {
|
|
137
|
-
"source": "$.@id",
|
|
138
|
-
"required": true,
|
|
139
|
-
"comment": "✅ OrderItemInput.ref (String! required)"
|
|
140
|
-
},
|
|
141
|
-
"productRef": {
|
|
142
|
-
"source": "$.productId",
|
|
143
|
-
"required": true,
|
|
144
|
-
"comment": "✅ OrderItemInput.productRef (String! required)"
|
|
145
|
-
},
|
|
146
|
-
"quantity": {
|
|
147
|
-
"source": "$.qty",
|
|
148
|
-
"resolver": "sdk.parseInt",
|
|
149
|
-
"required": true,
|
|
150
|
-
"comment": "✅ OrderItemInput.quantity (Int! required)"
|
|
151
|
-
},
|
|
152
|
-
"price": {
|
|
153
|
-
"source": "$.unitPrice",
|
|
154
|
-
"resolver": "sdk.parseFloat",
|
|
155
|
-
"required": true,
|
|
156
|
-
"comment": "✅ OrderItemInput.price (Float! required)"
|
|
157
|
-
},
|
|
158
|
-
"totalPrice": {
|
|
159
|
-
"resolver": "custom.calculateItemTotal",
|
|
160
|
-
"required": true,
|
|
161
|
-
"comment": "✅ OrderItemInput.totalPrice (Float! required)"
|
|
162
|
-
},
|
|
163
|
-
"currency": {
|
|
164
|
-
"source": "order.currency",
|
|
165
|
-
"required": true,
|
|
166
|
-
"comment": "✅ OrderItemInput.currency (String! required)"
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
### ❌ WRONG - Not Validated
|
|
175
|
-
|
|
176
|
-
```json
|
|
177
|
-
{
|
|
178
|
-
"direction": "ingest",
|
|
179
|
-
"mutation": "createOrder",
|
|
180
|
-
"fields": {
|
|
181
|
-
"orderRef": {
|
|
182
|
-
// ❌ Field doesn't exist - should be "ref"
|
|
183
|
-
"source": "order@id"
|
|
184
|
-
},
|
|
185
|
-
"customerEmail": {
|
|
186
|
-
// ❌ Wrong path - should be "customer.id" not nested email
|
|
187
|
-
"source": "customer@email"
|
|
188
|
-
},
|
|
189
|
-
"amount": {
|
|
190
|
-
// ❌ Field doesn't exist - should be "totalPrice"
|
|
191
|
-
"source": "total"
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### Validation Errors
|
|
198
|
-
|
|
199
|
-
```
|
|
200
|
-
❌ Field "orderRef" does not exist in CreateOrderInput
|
|
201
|
-
Expected: "ref" (String! required)
|
|
202
|
-
|
|
203
|
-
❌ Field "customerEmail" does not exist in CreateOrderInput
|
|
204
|
-
Expected: "customer.id" (ID! required) or "customer.email" (String)
|
|
205
|
-
|
|
206
|
-
❌ Field "amount" does not exist in CreateOrderInput
|
|
207
|
-
Expected: "totalPrice" (Float! required)
|
|
208
|
-
|
|
209
|
-
❌ Missing required field: "type" (OrderType! required)
|
|
210
|
-
❌ Missing required field: "retailer.id" (ID! required)
|
|
211
|
-
❌ Missing required field: "customer.id" (ID! required)
|
|
212
|
-
❌ Missing required field: "items" ([OrderItemInput]! required)
|
|
213
|
-
❌ Missing required field: "currency" (String! required)
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
---
|
|
217
|
-
|
|
218
|
-
## Best Practices
|
|
219
|
-
|
|
220
|
-
### 1. Always Add Schema Comments
|
|
221
|
-
|
|
222
|
-
Document which schema field each mapping targets:
|
|
223
|
-
|
|
224
|
-
```json
|
|
225
|
-
{
|
|
226
|
-
"productRef": {
|
|
227
|
-
"source": "sku",
|
|
228
|
-
"comment": "✅ Maps to InventoryPositionInput.productRef (String! required)"
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
### 2. Validate Before Deployment
|
|
234
|
-
|
|
235
|
-
Run schema validation as part of your CI/CD:
|
|
236
|
-
|
|
237
|
-
```bash
|
|
238
|
-
# Add to package.json scripts
|
|
239
|
-
{
|
|
240
|
-
"scripts": {
|
|
241
|
-
"validate-mappings": "fc-connect-validate-schema --mapping config/field-mappings.json --schema schema.json"
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
# Run before deployment
|
|
246
|
-
npm run validate-mappings
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### 3. Keep Schema Updated
|
|
250
|
-
|
|
251
|
-
Re-introspect when Fluent Commerce API updates:
|
|
252
|
-
|
|
253
|
-
```bash
|
|
254
|
-
# Quarterly or when API changes
|
|
255
|
-
npx fc-connect introspect-schema --endpoint $FLUENT_URL --output schema.json
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
### 4. Reference Schema in Documentation
|
|
259
|
-
|
|
260
|
-
Link to schema for each example:
|
|
261
|
-
|
|
262
|
-
```markdown
|
|
263
|
-
See `docs/schema/fluent-commerce-schema.json` → types.Mutation.createOrder
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### 5. Test with Sample Data
|
|
267
|
-
|
|
268
|
-
Validate mappings work before processing real data:
|
|
269
|
-
|
|
270
|
-
```typescript
|
|
271
|
-
// Test mapping with canonical example
|
|
272
|
-
const testData = require('./docs/guides/examples/test-data/canonical-order.json');
|
|
273
|
-
const result = await mapper.mapWithNodes(testData);
|
|
274
|
-
assert(result.success, 'Mapping should succeed with canonical data');
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
---
|
|
278
|
-
|
|
279
|
-
## Schema Reference Files
|
|
280
|
-
|
|
281
|
-
This repository includes:
|
|
282
|
-
|
|
283
|
-
### 📄 fluent-commerce-schema.json
|
|
284
|
-
|
|
285
|
-
**Location:** `docs/schema/fluent-commerce-schema.json`
|
|
286
|
-
|
|
287
|
-
**Contains:**
|
|
288
|
-
|
|
289
|
-
- All mutations and queries
|
|
290
|
-
- Input types and required fields
|
|
291
|
-
- Enum values
|
|
292
|
-
- Connection types for pagination
|
|
293
|
-
- Scalar types (DateTime, JSON)
|
|
294
|
-
- Example queries validated against schema
|
|
295
|
-
|
|
296
|
-
### 📄 canonical-order.json
|
|
297
|
-
|
|
298
|
-
**Location:** `docs/guides/examples/test-data/canonical-order.json`
|
|
299
|
-
|
|
300
|
-
**Contains:**
|
|
301
|
-
|
|
302
|
-
- Validated structure for order ingestion
|
|
303
|
-
- Real-world field names (ORD-2025-001, SKU-WM-001)
|
|
304
|
-
- Complete with customer, items, payments
|
|
305
|
-
|
|
306
|
-
### 📄 canonical-inventory.json
|
|
307
|
-
|
|
308
|
-
**Location:** `docs/guides/examples/test-data/canonical-inventory.json`
|
|
309
|
-
|
|
310
|
-
**Contains:**
|
|
311
|
-
|
|
312
|
-
- Validated structure for inventory sync
|
|
313
|
-
- WMS-style quantity breakdown (on-hand, available, reserved)
|
|
314
|
-
- Real location (WH-001 - Springfield Distribution Center)
|
|
315
|
-
|
|
316
|
-
---
|
|
317
|
-
|
|
318
|
-
## Common Schema Pitfalls
|
|
319
|
-
|
|
320
|
-
| Issue | ❌ Wrong | ✅ Correct |
|
|
321
|
-
| -------------------------- | ------------------------------- | -------------------------------------- |
|
|
322
|
-
| **Field name typo** | `customerEmail` (doesn't exist) | `customer.email` (exists) |
|
|
323
|
-
| **Wrong field type** | String → Int | Use `resolver: "sdk.parseInt"` |
|
|
324
|
-
| **Missing required field** | Omit `retailer.id` | Include all required fields |
|
|
325
|
-
| **Wrong enum value** | `type: "REGULAR"` (not in enum) | `type: "STANDARD"` (in OrderType enum) |
|
|
326
|
-
| **Incorrect nesting** | `items.productRef` | Use `isArray: true` with nested `fields` config |
|
|
327
|
-
| **Missing array marker** | `items.fields` | Use `"isArray": true` in field config |
|
|
328
|
-
|
|
329
|
-
---
|
|
330
|
-
|
|
331
|
-
## Validation Workflow
|
|
332
|
-
|
|
333
|
-
```mermaid
|
|
334
|
-
graph TD
|
|
335
|
-
A[Create Mapping Config] --> B[Introspect Schema]
|
|
336
|
-
B --> C[Validate Mapping]
|
|
337
|
-
C --> D{Valid?}
|
|
338
|
-
D -->|Yes| E[Deploy Connector]
|
|
339
|
-
D -->|No| F[Fix Errors]
|
|
340
|
-
F --> C
|
|
341
|
-
E --> G[Test with Sample Data]
|
|
342
|
-
G --> H{Success?}
|
|
343
|
-
H -->|Yes| I[Production Deployment]
|
|
344
|
-
H -->|No| J[Debug & Fix]
|
|
345
|
-
J --> G
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
---
|
|
349
|
-
|
|
350
|
-
## Practice Exercise
|
|
351
|
-
|
|
352
|
-
**Task:** Validate this inventory mapping configuration:
|
|
353
|
-
|
|
354
|
-
```json
|
|
355
|
-
{
|
|
356
|
-
"direction": "ingest",
|
|
357
|
-
"mutation": "upsertInventoryPosition",
|
|
358
|
-
"fields": {
|
|
359
|
-
"skuRef": {
|
|
360
|
-
"source": "sku",
|
|
361
|
-
"required": true
|
|
362
|
-
},
|
|
363
|
-
"warehouse": {
|
|
364
|
-
"source": "location",
|
|
365
|
-
"required": true
|
|
366
|
-
},
|
|
367
|
-
"quantity": {
|
|
368
|
-
"source": "qty",
|
|
369
|
-
"resolver": "sdk.parseInt",
|
|
370
|
-
"required": true
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
**Questions:**
|
|
377
|
-
|
|
378
|
-
1. What schema errors will validation find?
|
|
379
|
-
2. How would you fix them?
|
|
380
|
-
|
|
381
|
-
<details>
|
|
382
|
-
<summary>Click to see answers</summary>
|
|
383
|
-
|
|
384
|
-
**Errors Found:**
|
|
385
|
-
|
|
386
|
-
1. ❌ Field "skuRef" doesn't exist → Should be "productRef"
|
|
387
|
-
2. ❌ Field "warehouse" doesn't exist → Should be "locationRef"
|
|
388
|
-
3. ❌ Field "quantity" doesn't exist → Should be "qty"
|
|
389
|
-
4. ❌ Missing required field "ref"
|
|
390
|
-
|
|
391
|
-
**Fixed Mapping:**
|
|
392
|
-
|
|
393
|
-
```json
|
|
394
|
-
{
|
|
395
|
-
"direction": "ingest",
|
|
396
|
-
"mutation": "upsertInventoryPosition",
|
|
397
|
-
"fields": {
|
|
398
|
-
"ref": {
|
|
399
|
-
"source": "sku",
|
|
400
|
-
"required": true,
|
|
401
|
-
"comment": "✅ UpsertInventoryPositionInput.ref (String! required)"
|
|
402
|
-
},
|
|
403
|
-
"productRef": {
|
|
404
|
-
"source": "sku",
|
|
405
|
-
"required": true,
|
|
406
|
-
"comment": "✅ UpsertInventoryPositionInput.productRef (String! required)"
|
|
407
|
-
},
|
|
408
|
-
"locationRef": {
|
|
409
|
-
"source": "location",
|
|
410
|
-
"required": true,
|
|
411
|
-
"comment": "✅ UpsertInventoryPositionInput.locationRef (String! required)"
|
|
412
|
-
},
|
|
413
|
-
"qty": {
|
|
414
|
-
"source": "qty",
|
|
415
|
-
"resolver": "sdk.parseInt",
|
|
416
|
-
"required": true,
|
|
417
|
-
"comment": "✅ UpsertInventoryPositionInput.qty (Int! required)"
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
</details>
|
|
424
|
-
|
|
425
|
-
---
|
|
426
|
-
|
|
427
|
-
## Key Takeaways
|
|
428
|
-
|
|
429
|
-
1. **Always validate before deployment** - Catch errors early
|
|
430
|
-
2. **Use schema comments** - Document field mappings
|
|
431
|
-
3. **Keep schema updated** - Re-introspect quarterly
|
|
432
|
-
4. **Test with canonical data** - Validate mappings work
|
|
433
|
-
5. **Reference schema files** - Link to schema documentation
|
|
434
|
-
|
|
435
|
-
---
|
|
436
|
-
|
|
437
|
-
## Next Steps
|
|
438
|
-
|
|
439
|
-
Now that you can validate mappings, you're ready to see real-world examples:
|
|
440
|
-
|
|
441
|
-
→ Continue to [Module 4: Use Cases](./mapping-04-use-cases.md)
|
|
442
|
-
|
|
443
|
-
**Alternative paths:**
|
|
444
|
-
|
|
445
|
-
- Jump to [Advanced Patterns](./mapping-05-advanced-patterns.md) for complex transformations
|
|
446
|
-
- Review [API Reference](./mapping-07-api-reference.md) for complete documentation
|
|
447
|
-
|
|
448
|
-
---
|
|
449
|
-
|
|
450
|
-
[← Back to Quick Start](./mapping-02-quick-start.md) | [Next: Use Cases →](./mapping-04-use-cases.md)
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
1
|
+
# Module 3: Schema Validation
|
|
2
|
+
|
|
3
|
+
**Ensure mapping correctness before deployment**
|
|
4
|
+
|
|
5
|
+
**Level:** Intermediate
|
|
6
|
+
**Time:** 30 minutes
|
|
7
|
+
**Prerequisites:** [Module 2: Quick Start](./mapping-02-quick-start.md)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Learning Objectives
|
|
12
|
+
|
|
13
|
+
After completing this module, you will:
|
|
14
|
+
|
|
15
|
+
- ✅ Introspect your Fluent Commerce GraphQL schema
|
|
16
|
+
- ✅ Validate mappings against the schema
|
|
17
|
+
- ✅ Identify and fix common schema violations
|
|
18
|
+
- ✅ Understand schema validation best practices
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Why Schema Validation Matters
|
|
23
|
+
|
|
24
|
+
All GraphQL examples in this documentation are validated against the Fluent Commerce schema documented in [`docs/schema/fluent-commerce-schema.json`](../../../04-REFERENCE/schema/fluent-commerce-schema.json). However, **your specific Fluent Commerce environment may have different field names, types, or requirements**.
|
|
25
|
+
|
|
26
|
+
Using incorrect field names leads to:
|
|
27
|
+
|
|
28
|
+
- ❌ Mapping failures at runtime
|
|
29
|
+
- ❌ Silent data loss (fields not saved)
|
|
30
|
+
- ❌ Cryptic error messages from Fluent API
|
|
31
|
+
- ❌ Failed integrations
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Step 1: Introspect Your Schema
|
|
36
|
+
|
|
37
|
+
Generate a schema file specific to your environment:
|
|
38
|
+
|
|
39
|
+
### Using npx (No Installation)
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx fc-connect introspect-schema \
|
|
43
|
+
--endpoint https://YOUR_ACCOUNT.api.fluentcommerce.com/graphql \
|
|
44
|
+
--output schema.json
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### What You Get
|
|
48
|
+
|
|
49
|
+
The generated `schema.json` contains:
|
|
50
|
+
|
|
51
|
+
- ✅ All available mutations (`createOrder`, `upsertInventoryPosition`, etc.)
|
|
52
|
+
- ✅ Required vs optional fields for each mutation input
|
|
53
|
+
- ✅ Field types (`String!`, `Int`, `Float`, `DateTime`, etc.)
|
|
54
|
+
- ✅ Available query fields
|
|
55
|
+
- ✅ Enum values (`OrderType`, `EntityType`, etc.)
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Step 2: Validate Your Mapping
|
|
60
|
+
|
|
61
|
+
Before deploying your connector, validate your mapping against the schema:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Validate ingestion mapping
|
|
65
|
+
npx fc-connect validate-schema \
|
|
66
|
+
--mapping config/field-mappings.json \
|
|
67
|
+
--schema schema.json
|
|
68
|
+
|
|
69
|
+
# Validate extraction mapping
|
|
70
|
+
npx fc-connect validate-schema \
|
|
71
|
+
--mapping config/extraction-mappings.json \
|
|
72
|
+
--schema schema.json
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### What Gets Validated
|
|
76
|
+
|
|
77
|
+
The validator checks:
|
|
78
|
+
|
|
79
|
+
- ✅ All target fields exist in the mutation input
|
|
80
|
+
- ✅ Required fields are mapped
|
|
81
|
+
- ✅ Field types match (`String` → `String!`, `Int` → `Int!`)
|
|
82
|
+
- ✅ Source paths are valid
|
|
83
|
+
- ✅ Resolvers are compatible with target types
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Example: Schema-Validated Order Mapping
|
|
88
|
+
|
|
89
|
+
### ✅ CORRECT - Validated Against Schema
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"direction": "ingest",
|
|
94
|
+
"sourceFormat": "xml",
|
|
95
|
+
"mutation": "createOrder",
|
|
96
|
+
"comment": "✅ Validated against Fluent Commerce schema",
|
|
97
|
+
|
|
98
|
+
"fields": {
|
|
99
|
+
"ref": {
|
|
100
|
+
"source": "order@id",
|
|
101
|
+
"required": true,
|
|
102
|
+
"comment": "✅ Maps to CreateOrderInput.ref (String! required)"
|
|
103
|
+
},
|
|
104
|
+
"type": {
|
|
105
|
+
"source": "order@type",
|
|
106
|
+
"resolver": "custom.mapOrderType",
|
|
107
|
+
"comment": "✅ Maps to CreateOrderInput.type (OrderType! enum required)"
|
|
108
|
+
},
|
|
109
|
+
"retailer": {
|
|
110
|
+
"fields": {
|
|
111
|
+
"id": {
|
|
112
|
+
"value": "1",
|
|
113
|
+
"comment": "✅ CreateOrderInput.retailer (RetailerInput! required)"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"totalPrice": {
|
|
118
|
+
"source": "order.total",
|
|
119
|
+
"resolver": "sdk.parseFloat",
|
|
120
|
+
"required": true,
|
|
121
|
+
"comment": "✅ Maps to CreateOrderInput.totalPrice (Float! required)"
|
|
122
|
+
},
|
|
123
|
+
"customer": {
|
|
124
|
+
"fields": {
|
|
125
|
+
"id": {
|
|
126
|
+
"resolver": "custom.lookupCustomerByEmail",
|
|
127
|
+
"comment": "✅ CreateOrderInput.customer (CustomerInput! required - lookup by email)"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"items": {
|
|
132
|
+
"source": "order.items.item",
|
|
133
|
+
"isArray": true,
|
|
134
|
+
"comment": "✅ Maps to CreateOrderInput.items ([OrderItemInput]! required)",
|
|
135
|
+
"fields": {
|
|
136
|
+
"ref": {
|
|
137
|
+
"source": "$.@id",
|
|
138
|
+
"required": true,
|
|
139
|
+
"comment": "✅ OrderItemInput.ref (String! required)"
|
|
140
|
+
},
|
|
141
|
+
"productRef": {
|
|
142
|
+
"source": "$.productId",
|
|
143
|
+
"required": true,
|
|
144
|
+
"comment": "✅ OrderItemInput.productRef (String! required)"
|
|
145
|
+
},
|
|
146
|
+
"quantity": {
|
|
147
|
+
"source": "$.qty",
|
|
148
|
+
"resolver": "sdk.parseInt",
|
|
149
|
+
"required": true,
|
|
150
|
+
"comment": "✅ OrderItemInput.quantity (Int! required)"
|
|
151
|
+
},
|
|
152
|
+
"price": {
|
|
153
|
+
"source": "$.unitPrice",
|
|
154
|
+
"resolver": "sdk.parseFloat",
|
|
155
|
+
"required": true,
|
|
156
|
+
"comment": "✅ OrderItemInput.price (Float! required)"
|
|
157
|
+
},
|
|
158
|
+
"totalPrice": {
|
|
159
|
+
"resolver": "custom.calculateItemTotal",
|
|
160
|
+
"required": true,
|
|
161
|
+
"comment": "✅ OrderItemInput.totalPrice (Float! required)"
|
|
162
|
+
},
|
|
163
|
+
"currency": {
|
|
164
|
+
"source": "order.currency",
|
|
165
|
+
"required": true,
|
|
166
|
+
"comment": "✅ OrderItemInput.currency (String! required)"
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### ❌ WRONG - Not Validated
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"direction": "ingest",
|
|
179
|
+
"mutation": "createOrder",
|
|
180
|
+
"fields": {
|
|
181
|
+
"orderRef": {
|
|
182
|
+
// ❌ Field doesn't exist - should be "ref"
|
|
183
|
+
"source": "order@id"
|
|
184
|
+
},
|
|
185
|
+
"customerEmail": {
|
|
186
|
+
// ❌ Wrong path - should be "customer.id" not nested email
|
|
187
|
+
"source": "customer@email"
|
|
188
|
+
},
|
|
189
|
+
"amount": {
|
|
190
|
+
// ❌ Field doesn't exist - should be "totalPrice"
|
|
191
|
+
"source": "total"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Validation Errors
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
❌ Field "orderRef" does not exist in CreateOrderInput
|
|
201
|
+
Expected: "ref" (String! required)
|
|
202
|
+
|
|
203
|
+
❌ Field "customerEmail" does not exist in CreateOrderInput
|
|
204
|
+
Expected: "customer.id" (ID! required) or "customer.email" (String)
|
|
205
|
+
|
|
206
|
+
❌ Field "amount" does not exist in CreateOrderInput
|
|
207
|
+
Expected: "totalPrice" (Float! required)
|
|
208
|
+
|
|
209
|
+
❌ Missing required field: "type" (OrderType! required)
|
|
210
|
+
❌ Missing required field: "retailer.id" (ID! required)
|
|
211
|
+
❌ Missing required field: "customer.id" (ID! required)
|
|
212
|
+
❌ Missing required field: "items" ([OrderItemInput]! required)
|
|
213
|
+
❌ Missing required field: "currency" (String! required)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Best Practices
|
|
219
|
+
|
|
220
|
+
### 1. Always Add Schema Comments
|
|
221
|
+
|
|
222
|
+
Document which schema field each mapping targets:
|
|
223
|
+
|
|
224
|
+
```json
|
|
225
|
+
{
|
|
226
|
+
"productRef": {
|
|
227
|
+
"source": "sku",
|
|
228
|
+
"comment": "✅ Maps to InventoryPositionInput.productRef (String! required)"
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### 2. Validate Before Deployment
|
|
234
|
+
|
|
235
|
+
Run schema validation as part of your CI/CD:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
# Add to package.json scripts
|
|
239
|
+
{
|
|
240
|
+
"scripts": {
|
|
241
|
+
"validate-mappings": "fc-connect-validate-schema --mapping config/field-mappings.json --schema schema.json"
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
# Run before deployment
|
|
246
|
+
npm run validate-mappings
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### 3. Keep Schema Updated
|
|
250
|
+
|
|
251
|
+
Re-introspect when Fluent Commerce API updates:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
# Quarterly or when API changes
|
|
255
|
+
npx fc-connect introspect-schema --endpoint $FLUENT_URL --output schema.json
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### 4. Reference Schema in Documentation
|
|
259
|
+
|
|
260
|
+
Link to schema for each example:
|
|
261
|
+
|
|
262
|
+
```markdown
|
|
263
|
+
See `docs/schema/fluent-commerce-schema.json` → types.Mutation.createOrder
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### 5. Test with Sample Data
|
|
267
|
+
|
|
268
|
+
Validate mappings work before processing real data:
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
// Test mapping with canonical example
|
|
272
|
+
const testData = require('./docs/guides/examples/test-data/canonical-order.json');
|
|
273
|
+
const result = await mapper.mapWithNodes(testData);
|
|
274
|
+
assert(result.success, 'Mapping should succeed with canonical data');
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Schema Reference Files
|
|
280
|
+
|
|
281
|
+
This repository includes:
|
|
282
|
+
|
|
283
|
+
### 📄 fluent-commerce-schema.json
|
|
284
|
+
|
|
285
|
+
**Location:** `docs/schema/fluent-commerce-schema.json`
|
|
286
|
+
|
|
287
|
+
**Contains:**
|
|
288
|
+
|
|
289
|
+
- All mutations and queries
|
|
290
|
+
- Input types and required fields
|
|
291
|
+
- Enum values
|
|
292
|
+
- Connection types for pagination
|
|
293
|
+
- Scalar types (DateTime, JSON)
|
|
294
|
+
- Example queries validated against schema
|
|
295
|
+
|
|
296
|
+
### 📄 canonical-order.json
|
|
297
|
+
|
|
298
|
+
**Location:** `docs/guides/examples/test-data/canonical-order.json`
|
|
299
|
+
|
|
300
|
+
**Contains:**
|
|
301
|
+
|
|
302
|
+
- Validated structure for order ingestion
|
|
303
|
+
- Real-world field names (ORD-2025-001, SKU-WM-001)
|
|
304
|
+
- Complete with customer, items, payments
|
|
305
|
+
|
|
306
|
+
### 📄 canonical-inventory.json
|
|
307
|
+
|
|
308
|
+
**Location:** `docs/guides/examples/test-data/canonical-inventory.json`
|
|
309
|
+
|
|
310
|
+
**Contains:**
|
|
311
|
+
|
|
312
|
+
- Validated structure for inventory sync
|
|
313
|
+
- WMS-style quantity breakdown (on-hand, available, reserved)
|
|
314
|
+
- Real location (WH-001 - Springfield Distribution Center)
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Common Schema Pitfalls
|
|
319
|
+
|
|
320
|
+
| Issue | ❌ Wrong | ✅ Correct |
|
|
321
|
+
| -------------------------- | ------------------------------- | -------------------------------------- |
|
|
322
|
+
| **Field name typo** | `customerEmail` (doesn't exist) | `customer.email` (exists) |
|
|
323
|
+
| **Wrong field type** | String → Int | Use `resolver: "sdk.parseInt"` |
|
|
324
|
+
| **Missing required field** | Omit `retailer.id` | Include all required fields |
|
|
325
|
+
| **Wrong enum value** | `type: "REGULAR"` (not in enum) | `type: "STANDARD"` (in OrderType enum) |
|
|
326
|
+
| **Incorrect nesting** | `items.productRef` | Use `isArray: true` with nested `fields` config |
|
|
327
|
+
| **Missing array marker** | `items.fields` | Use `"isArray": true` in field config |
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Validation Workflow
|
|
332
|
+
|
|
333
|
+
```mermaid
|
|
334
|
+
graph TD
|
|
335
|
+
A[Create Mapping Config] --> B[Introspect Schema]
|
|
336
|
+
B --> C[Validate Mapping]
|
|
337
|
+
C --> D{Valid?}
|
|
338
|
+
D -->|Yes| E[Deploy Connector]
|
|
339
|
+
D -->|No| F[Fix Errors]
|
|
340
|
+
F --> C
|
|
341
|
+
E --> G[Test with Sample Data]
|
|
342
|
+
G --> H{Success?}
|
|
343
|
+
H -->|Yes| I[Production Deployment]
|
|
344
|
+
H -->|No| J[Debug & Fix]
|
|
345
|
+
J --> G
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Practice Exercise
|
|
351
|
+
|
|
352
|
+
**Task:** Validate this inventory mapping configuration:
|
|
353
|
+
|
|
354
|
+
```json
|
|
355
|
+
{
|
|
356
|
+
"direction": "ingest",
|
|
357
|
+
"mutation": "upsertInventoryPosition",
|
|
358
|
+
"fields": {
|
|
359
|
+
"skuRef": {
|
|
360
|
+
"source": "sku",
|
|
361
|
+
"required": true
|
|
362
|
+
},
|
|
363
|
+
"warehouse": {
|
|
364
|
+
"source": "location",
|
|
365
|
+
"required": true
|
|
366
|
+
},
|
|
367
|
+
"quantity": {
|
|
368
|
+
"source": "qty",
|
|
369
|
+
"resolver": "sdk.parseInt",
|
|
370
|
+
"required": true
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
**Questions:**
|
|
377
|
+
|
|
378
|
+
1. What schema errors will validation find?
|
|
379
|
+
2. How would you fix them?
|
|
380
|
+
|
|
381
|
+
<details>
|
|
382
|
+
<summary>Click to see answers</summary>
|
|
383
|
+
|
|
384
|
+
**Errors Found:**
|
|
385
|
+
|
|
386
|
+
1. ❌ Field "skuRef" doesn't exist → Should be "productRef"
|
|
387
|
+
2. ❌ Field "warehouse" doesn't exist → Should be "locationRef"
|
|
388
|
+
3. ❌ Field "quantity" doesn't exist → Should be "qty"
|
|
389
|
+
4. ❌ Missing required field "ref"
|
|
390
|
+
|
|
391
|
+
**Fixed Mapping:**
|
|
392
|
+
|
|
393
|
+
```json
|
|
394
|
+
{
|
|
395
|
+
"direction": "ingest",
|
|
396
|
+
"mutation": "upsertInventoryPosition",
|
|
397
|
+
"fields": {
|
|
398
|
+
"ref": {
|
|
399
|
+
"source": "sku",
|
|
400
|
+
"required": true,
|
|
401
|
+
"comment": "✅ UpsertInventoryPositionInput.ref (String! required)"
|
|
402
|
+
},
|
|
403
|
+
"productRef": {
|
|
404
|
+
"source": "sku",
|
|
405
|
+
"required": true,
|
|
406
|
+
"comment": "✅ UpsertInventoryPositionInput.productRef (String! required)"
|
|
407
|
+
},
|
|
408
|
+
"locationRef": {
|
|
409
|
+
"source": "location",
|
|
410
|
+
"required": true,
|
|
411
|
+
"comment": "✅ UpsertInventoryPositionInput.locationRef (String! required)"
|
|
412
|
+
},
|
|
413
|
+
"qty": {
|
|
414
|
+
"source": "qty",
|
|
415
|
+
"resolver": "sdk.parseInt",
|
|
416
|
+
"required": true,
|
|
417
|
+
"comment": "✅ UpsertInventoryPositionInput.qty (Int! required)"
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
</details>
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Key Takeaways
|
|
428
|
+
|
|
429
|
+
1. **Always validate before deployment** - Catch errors early
|
|
430
|
+
2. **Use schema comments** - Document field mappings
|
|
431
|
+
3. **Keep schema updated** - Re-introspect quarterly
|
|
432
|
+
4. **Test with canonical data** - Validate mappings work
|
|
433
|
+
5. **Reference schema files** - Link to schema documentation
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## Next Steps
|
|
438
|
+
|
|
439
|
+
Now that you can validate mappings, you're ready to see real-world examples:
|
|
440
|
+
|
|
441
|
+
→ Continue to [Module 4: Use Cases](./mapping-04-use-cases.md)
|
|
442
|
+
|
|
443
|
+
**Alternative paths:**
|
|
444
|
+
|
|
445
|
+
- Jump to [Advanced Patterns](./mapping-05-advanced-patterns.md) for complex transformations
|
|
446
|
+
- Review [API Reference](./mapping-07-api-reference.md) for complete documentation
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
[← Back to Quick Start](./mapping-02-quick-start.md) | [Next: Use Cases →](./mapping-04-use-cases.md)
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
|