@fluentcommerce/fc-connect-sdk 0.1.53 → 0.1.55
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 +30 -2
- package/README.md +39 -0
- package/dist/cjs/auth/index.d.ts +3 -0
- package/dist/cjs/auth/index.js +13 -0
- package/dist/cjs/auth/profile-loader.d.ts +18 -0
- package/dist/cjs/auth/profile-loader.js +208 -0
- package/dist/cjs/client-factory.d.ts +4 -0
- package/dist/cjs/client-factory.js +10 -0
- package/dist/cjs/clients/fluent-client.js +13 -6
- package/dist/cjs/index.d.ts +3 -1
- package/dist/cjs/index.js +8 -2
- package/dist/cjs/utils/pagination-helpers.js +38 -2
- package/dist/cjs/versori/fluent-versori-client.js +11 -5
- package/dist/esm/auth/index.d.ts +3 -0
- package/dist/esm/auth/index.js +2 -0
- package/dist/esm/auth/profile-loader.d.ts +18 -0
- package/dist/esm/auth/profile-loader.js +169 -0
- package/dist/esm/client-factory.d.ts +4 -0
- package/dist/esm/client-factory.js +9 -0
- package/dist/esm/clients/fluent-client.js +13 -6
- package/dist/esm/index.d.ts +3 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/utils/pagination-helpers.js +38 -2
- package/dist/esm/versori/fluent-versori-client.js +11 -5
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/tsconfig.types.tsbuildinfo +1 -1
- package/dist/types/auth/index.d.ts +3 -0
- package/dist/types/auth/profile-loader.d.ts +18 -0
- package/dist/types/client-factory.d.ts +4 -0
- package/dist/types/index.d.ts +3 -1
- package/docs/00-START-HERE/EXPORT-VALIDATION.md +158 -158
- package/docs/00-START-HERE/cli-analyze-source-structure-guide.md +655 -655
- package/docs/00-START-HERE/cli-documentation-index.md +202 -202
- package/docs/00-START-HERE/cli-quick-reference.md +252 -252
- package/docs/00-START-HERE/decision-tree.md +552 -552
- package/docs/00-START-HERE/getting-started.md +1070 -1070
- package/docs/00-START-HERE/mapper-quick-decision-guide.md +235 -235
- package/docs/00-START-HERE/readme.md +237 -237
- package/docs/00-START-HERE/retailerid-configuration.md +404 -404
- package/docs/00-START-HERE/sdk-philosophy.md +794 -794
- package/docs/00-START-HERE/troubleshooting-quick-reference.md +1086 -1086
- package/docs/01-TEMPLATES/faq.md +686 -686
- package/docs/01-TEMPLATES/patterns/pattern-templates-guide.md +68 -68
- package/docs/01-TEMPLATES/patterns/patterns-csv-schema-validation-and-rejection-report.md +233 -233
- package/docs/01-TEMPLATES/patterns/patterns-custom-resolvers.md +407 -407
- package/docs/01-TEMPLATES/patterns/patterns-error-handling-retry.md +511 -511
- package/docs/01-TEMPLATES/patterns/patterns-field-mapping-universal.md +701 -701
- package/docs/01-TEMPLATES/patterns/patterns-large-file-splitting.md +1430 -1430
- package/docs/01-TEMPLATES/patterns/patterns-master-data-etl.md +2399 -2399
- package/docs/01-TEMPLATES/patterns/patterns-pagination-streaming.md +447 -447
- package/docs/01-TEMPLATES/patterns/patterns-state-duplicate-prevention.md +385 -385
- package/docs/01-TEMPLATES/readme.md +957 -957
- package/docs/01-TEMPLATES/standalone/standalone-asn-inbound-processing.md +1209 -1209
- package/docs/01-TEMPLATES/standalone/standalone-graphql-query-export.md +1140 -1140
- package/docs/01-TEMPLATES/standalone/standalone-graphql-to-parquet-partitioned-s3.md +432 -432
- package/docs/01-TEMPLATES/standalone/standalone-multi-channel-inventory-sync.md +1185 -1185
- package/docs/01-TEMPLATES/standalone/standalone-multi-source-aggregation.md +1462 -1462
- package/docs/01-TEMPLATES/standalone/standalone-s3-csv-batch-api.md +1390 -1390
- package/docs/01-TEMPLATES/standalone/standalone-s3-csv-inventory-to-batch.md +330 -330
- package/docs/01-TEMPLATES/standalone/standalone-scripts-guide.md +87 -87
- package/docs/01-TEMPLATES/standalone/standalone-sftp-xml-graphql.md +1444 -1444
- package/docs/01-TEMPLATES/standalone/standalone-webhook-payload-processing.md +688 -688
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-dropship-order-routing.md +193 -193
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-graphql-parquet-extraction.md +518 -518
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-inter-location-transfers.md +2162 -2162
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-pre-order-allocation.md +2226 -2226
- package/docs/01-TEMPLATES/versori/business-examples/business-scenarios-guide.md +87 -87
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-connection-validation-pattern.md +656 -656
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-dual-workflow-connector.md +835 -835
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-guide.md +108 -108
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-kv-state-management.md +1533 -1533
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-xml-response-patterns.md +1160 -1160
- package/docs/01-TEMPLATES/versori/versori-platform-guide.md +201 -201
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-asn-purchase-order.md +1906 -1906
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-dropship-routing.md +1074 -1074
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-flash-sale-reserve.md +1395 -1395
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-generic-xml-order.md +888 -888
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-payment-gateway-integration.md +2478 -2478
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-rma-returns-comprehensive.md +2240 -2240
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-xml-order-ingestion.md +2029 -2029
- package/docs/01-TEMPLATES/versori/webhooks/webhook-templates-guide.md +140 -140
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/inventory-mapping.json +20 -20
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/products_2025-01-22.csv +11 -11
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/sample-data-guide.md +34 -34
- package/docs/01-TEMPLATES/versori/workflows/_examples/workflow-examples-guide.md +36 -36
- package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-modes-guide.md +1038 -1038
- package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-workflows-guide.md +138 -138
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/graphql-extraction-guide.md +63 -63
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-csv.md +2062 -2062
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-xml.md +2294 -2294
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-s3-csv.md +2461 -2461
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-sftp-xml.md +2529 -2529
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-csv.md +2464 -2464
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-json.md +1959 -1959
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-s3-csv.md +1953 -1953
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-sftp-xml.md +2541 -2541
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-s3-json.md +2384 -2384
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-sftp-xml.md +2445 -2445
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-csv.md +2355 -2355
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-json.md +2042 -2042
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-sftp-xml.md +2726 -2726
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/batch-api-guide.md +206 -206
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-cycle-count-reconciliation.md +2030 -2030
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-multi-channel-inventory-sync.md +1882 -1882
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-csv-inventory-batch.md +2827 -2827
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-json-inventory-batch.md +1952 -1952
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-xml-inventory-batch.md +3289 -3289
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-csv-inventory-batch.md +3064 -3064
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-json-inventory-batch.md +3238 -3238
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-xml-inventory-batch.md +2977 -2977
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/event-api-guide.md +321 -321
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-json-order-cancel-event.md +959 -959
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-xml-order-cancel-event.md +1170 -1170
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-csv-product-event.md +2312 -2312
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-json-product-event.md +2999 -2999
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-parquet-product-event.md +2836 -2836
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-xml-product-event.md +2395 -2395
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-csv-product-event.md +2295 -2295
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-json-product-event.md +2602 -2602
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-parquet-product-event.md +2589 -2589
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-xml-product-event.md +3578 -3578
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/graphql-mutations-guide.md +93 -93
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-json-order-update-graphql.md +1260 -1260
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-xml-order-update-graphql.md +1472 -1472
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-control-graphql.md +2417 -2417
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-location-graphql.md +2811 -2811
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-price-graphql.md +2619 -2619
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-json-location-graphql.md +2807 -2807
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-xml-location-graphql.md +2373 -2373
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-control-graphql.md +2740 -2740
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-location-graphql.md +2760 -2760
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-json-location-graphql.md +1710 -1710
- package/docs/01-TEMPLATES/versori/workflows/ingestion/ingestion-workflows-guide.md +136 -136
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/rubix-webhooks-guide.md +520 -520
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-inline.md +1418 -1418
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-universal-mapper.md +1785 -1785
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-order-attribute-update.md +824 -824
- package/docs/01-TEMPLATES/versori/workflows/workflows-overview-guide.md +646 -646
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-batch-archival.md +724 -724
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-job-tracker.md +627 -627
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-partial-batch-recovery.md +561 -561
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-quick-reference.md +367 -367
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-readme.md +407 -407
- package/docs/02-CORE-GUIDES/advanced-services/readme.md +49 -49
- package/docs/02-CORE-GUIDES/api-reference/api-reference-quick-reference.md +548 -548
- package/docs/02-CORE-GUIDES/api-reference/event-api-input-output-reference.md +702 -1171
- package/docs/02-CORE-GUIDES/api-reference/examples/client-initialization.ts +286 -286
- package/docs/02-CORE-GUIDES/api-reference/graphql-error-classification.md +337 -337
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-01-client-api.md +399 -482
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-03-authentication.md +199 -199
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-04-graphql-mapping.md +925 -925
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-05-services.md +1198 -1198
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-06-data-sources.md +1083 -1083
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-07-parsers.md +1097 -1097
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-pagination.md +513 -513
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-types.md +545 -597
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-error-handling.md +527 -527
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-webhook-validation.md +514 -514
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-extraction.md +557 -557
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-utilities.md +412 -412
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-cli-tools.md +423 -423
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-error-handling.md +716 -716
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-analyze-source-structure.md +518 -518
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-partial-responses.md +212 -212
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-testing.md +300 -300
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-13-resolver-builder.md +322 -322
- package/docs/02-CORE-GUIDES/api-reference/readme.md +279 -279
- package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-quick-reference.md +351 -351
- package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-readme.md +277 -277
- package/docs/02-CORE-GUIDES/auto-pagination/examples/auto-pagination-readme.md +178 -178
- package/docs/02-CORE-GUIDES/auto-pagination/examples/common-patterns.ts +351 -351
- package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-products.ts +384 -384
- package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-virtual-positions.ts +308 -308
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-01-foundations.md +470 -470
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-02-quick-start.md +713 -713
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-03-configuration.md +754 -754
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-04-advanced-patterns.md +732 -732
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-05-sdk-integration.md +847 -847
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-06-troubleshooting.md +359 -359
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-07-api-reference.md +462 -462
- package/docs/02-CORE-GUIDES/auto-pagination/readme.md +54 -54
- package/docs/02-CORE-GUIDES/data-sources/data-sources-file-operations-error-handling.md +1487 -1487
- package/docs/02-CORE-GUIDES/data-sources/data-sources-quick-reference.md +836 -836
- package/docs/02-CORE-GUIDES/data-sources/data-sources-readme.md +276 -276
- package/docs/02-CORE-GUIDES/data-sources/data-sources-sftp-credential-access-security.md +553 -553
- package/docs/02-CORE-GUIDES/data-sources/examples/common-patterns.ts +409 -409
- package/docs/02-CORE-GUIDES/data-sources/examples/data-sources-readme.md +178 -178
- package/docs/02-CORE-GUIDES/data-sources/examples/s3-operations.ts +308 -308
- package/docs/02-CORE-GUIDES/data-sources/examples/sftp-operations.ts +371 -371
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-01-foundations.md +735 -735
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-02-s3-operations.md +1302 -1302
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-03-sftp-operations.md +1379 -1379
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-04-file-patterns.md +941 -941
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-05-advanced-topics.md +813 -813
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-06-integration-patterns.md +486 -486
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-07-troubleshooting.md +387 -387
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-08-api-reference.md +417 -417
- package/docs/02-CORE-GUIDES/data-sources/readme.md +77 -77
- package/docs/02-CORE-GUIDES/error-handling-guide.md +936 -936
- package/docs/02-CORE-GUIDES/extraction/examples/02-core-guides-extraction-readme.md +116 -116
- package/docs/02-CORE-GUIDES/extraction/examples/common-patterns.ts +428 -428
- package/docs/02-CORE-GUIDES/extraction/examples/extract-inventory-basic.ts +187 -187
- package/docs/02-CORE-GUIDES/extraction/extraction-quick-reference.md +596 -596
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-01-foundations.md +514 -514
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-02-basic-extraction.md +823 -823
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-03-parquet-processing.md +507 -507
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-04-data-enrichment.md +546 -546
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-05-transformation.md +494 -494
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-export-formats.md +458 -458
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-performance.md +138 -138
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-api-reference.md +148 -148
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-optimization.md +692 -692
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-08-extraction-orchestrator.md +1008 -1008
- package/docs/02-CORE-GUIDES/extraction/readme.md +151 -151
- package/docs/02-CORE-GUIDES/ingestion/examples/_simple-kv-store.ts +40 -40
- package/docs/02-CORE-GUIDES/ingestion/examples/error-recovery.ts +728 -728
- package/docs/02-CORE-GUIDES/ingestion/examples/event-driven.ts +501 -501
- package/docs/02-CORE-GUIDES/ingestion/examples/local-file-ingestion.ts +88 -88
- package/docs/02-CORE-GUIDES/ingestion/examples/parquet-ingestion.ts +117 -117
- package/docs/02-CORE-GUIDES/ingestion/examples/performance-optimized.ts +647 -647
- package/docs/02-CORE-GUIDES/ingestion/examples/s3-csv-ingestion.ts +169 -169
- package/docs/02-CORE-GUIDES/ingestion/examples/sftp-csv-ingestion.ts +134 -134
- package/docs/02-CORE-GUIDES/ingestion/ingestion-quick-reference.md +546 -546
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-01-introduction.md +626 -626
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-02-quick-start.md +658 -658
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-03-data-sources.md +1052 -1052
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-04-field-mapping.md +763 -763
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-05-advanced-parsers.md +676 -676
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-06-batch-api.md +1295 -1295
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-api-reference.md +138 -138
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-state-management.md +1037 -1037
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-08-performance-optimization.md +1349 -1349
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-09-best-practices.md +1893 -1893
- package/docs/02-CORE-GUIDES/ingestion/readme.md +160 -160
- package/docs/02-CORE-GUIDES/logging-guide.md +585 -585
- package/docs/02-CORE-GUIDES/mapping/error-handling-patterns.md +401 -401
- package/docs/02-CORE-GUIDES/mapping/examples/02-core-guides-mapping-readme.md +128 -128
- package/docs/02-CORE-GUIDES/mapping/examples/common-patterns.ts +273 -273
- package/docs/02-CORE-GUIDES/mapping/examples/csv-location-ingestion.json +36 -36
- package/docs/02-CORE-GUIDES/mapping/examples/csv-mapping.ts +242 -242
- package/docs/02-CORE-GUIDES/mapping/examples/graphql-to-parquet-extraction.json +36 -36
- package/docs/02-CORE-GUIDES/mapping/examples/json-mapping.ts +213 -213
- package/docs/02-CORE-GUIDES/mapping/examples/json-product-to-mutation.json +48 -48
- package/docs/02-CORE-GUIDES/mapping/examples/xml-mapping.ts +291 -291
- package/docs/02-CORE-GUIDES/mapping/examples/xml-order-to-mutation.json +45 -45
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-quick-reference.md +463 -463
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-readme.md +227 -227
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-01-introduction.md +222 -222
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-02-quick-start.md +351 -351
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-03-schema-validation.md +569 -569
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-04-mapping-patterns.md +471 -471
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-05-configuration-reference.md +611 -611
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-advanced-xpath.md +148 -148
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-path-syntax.md +464 -464
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-api-reference.md +94 -94
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-array-handling.md +307 -307
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-08-custom-resolvers.md +544 -544
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-09-advanced-patterns.md +427 -427
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-10-hooks-and-variables.md +336 -336
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-11-error-handling.md +488 -488
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-12-arguments-vs-nodes.md +383 -383
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-13-best-practices.md +477 -477
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/readme.md +62 -62
- package/docs/02-CORE-GUIDES/mapping/mapping-format-decision-tree.md +480 -480
- package/docs/02-CORE-GUIDES/mapping/mapping-graphql-alias-batching-guide.md +820 -820
- package/docs/02-CORE-GUIDES/mapping/mapping-javascript-objects.md +2369 -2369
- package/docs/02-CORE-GUIDES/mapping/mapping-mapper-comparison-guide.md +682 -682
- package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-07-api-reference.md +1327 -1327
- package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-08-error-handling.md +1142 -1142
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-04-use-cases.md +891 -891
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-helpers-resolvers.md +1126 -1126
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-sdk-resolvers.md +199 -199
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-07-api-reference.md +1319 -1319
- package/docs/02-CORE-GUIDES/mapping/readme.md +178 -178
- package/docs/02-CORE-GUIDES/mapping/resolver-registration.md +410 -410
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/common-patterns.ts +226 -226
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/custom-resolvers.ts +227 -227
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/sdk-resolvers-usage.ts +203 -203
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-readme.md +274 -274
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-api-reference.md +679 -679
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-cookbook.md +826 -826
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-guide.md +1330 -1330
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-helpers-reference.md +1437 -1437
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-parameters-reference.md +553 -553
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-troubleshooting.md +854 -854
- package/docs/02-CORE-GUIDES/mapping/resolvers/readme.md +75 -75
- package/docs/02-CORE-GUIDES/parsers/examples/02-core-guides-parsers-readme.md +161 -161
- package/docs/02-CORE-GUIDES/parsers/examples/csv-parser-examples.ts +110 -110
- package/docs/02-CORE-GUIDES/parsers/examples/json-parser-examples.ts +33 -33
- package/docs/02-CORE-GUIDES/parsers/examples/parquet-parser-examples.ts +47 -47
- package/docs/02-CORE-GUIDES/parsers/examples/xml-parser-examples.ts +38 -38
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-01-foundations.md +355 -355
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-02-csv-parser.md +772 -772
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-03-json-parser.md +789 -789
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-04-xml-parser.md +857 -857
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-05-parquet-parser.md +603 -603
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-integration-patterns.md +702 -702
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-streaming.md +121 -121
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-api-reference.md +89 -89
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-troubleshooting.md +727 -727
- package/docs/02-CORE-GUIDES/parsers/parsers-quick-reference.md +482 -482
- package/docs/02-CORE-GUIDES/parsers/parsers-readme.md +258 -258
- package/docs/02-CORE-GUIDES/parsers/readme.md +65 -65
- package/docs/02-CORE-GUIDES/readme.md +194 -194
- package/docs/02-CORE-GUIDES/webhook-validation/examples/basic-validation.ts +108 -108
- package/docs/02-CORE-GUIDES/webhook-validation/examples/common-patterns.ts +316 -316
- package/docs/02-CORE-GUIDES/webhook-validation/examples/webhook-validation-readme.md +61 -61
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-01-foundations.md +440 -440
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-02-quick-start.md +525 -525
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-03-versori-integration.md +741 -741
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-04-platform-integration.md +629 -629
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-05-configuration.md +535 -535
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-error-handling.md +611 -611
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-troubleshooting.md +124 -124
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-07-api-reference.md +511 -511
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-08-rubix-webhooks.md +590 -590
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-09-rubix-event-vs-http-call.md +432 -432
- package/docs/02-CORE-GUIDES/webhook-validation/readme.md +239 -239
- package/docs/02-CORE-GUIDES/webhook-validation/webhook-validation-quick-reference.md +392 -392
- package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-quick-reference.md +498 -498
- package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-readme.md +313 -313
- package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/common-patterns.ts +612 -612
- package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/connector-scenarios-readme.md +253 -253
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-01-foundations.md +452 -452
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-02-simple-scenarios.md +681 -681
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-03-intermediate-scenarios.md +637 -637
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-04-advanced-scenarios.md +650 -650
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-05-bidirectional-sync.md +233 -233
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-06-production-patterns.md +442 -442
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-07-reference.md +445 -445
- package/docs/03-PATTERN-GUIDES/connector-scenarios/readme.md +31 -31
- package/docs/03-PATTERN-GUIDES/enterprise-integration-patterns.md +1528 -1528
- package/docs/03-PATTERN-GUIDES/error-handling/comprehensive-error-handling-guide.md +1437 -1437
- package/docs/03-PATTERN-GUIDES/error-handling/error-handling-quick-reference.md +390 -390
- package/docs/03-PATTERN-GUIDES/error-handling/examples/common-patterns.ts +438 -438
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-01-foundations.md +362 -362
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-02-error-types.md +850 -850
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-03-utf8-handling.md +456 -456
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-04-error-scenarios.md +658 -658
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-05-calling-patterns.md +671 -671
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-06-retry-strategies.md +1034 -1034
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-07-monitoring.md +653 -653
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-08-api-reference.md +847 -847
- package/docs/03-PATTERN-GUIDES/error-handling/readme.md +36 -36
- package/docs/03-PATTERN-GUIDES/examples/__tests__/readme.md +40 -40
- package/docs/03-PATTERN-GUIDES/examples/__tests__/resolver-examples.test.js +282 -282
- package/docs/03-PATTERN-GUIDES/examples/test-data/03-pattern-guides-readme.md +110 -110
- package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-inventory.json +123 -123
- package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-order.json +171 -171
- package/docs/03-PATTERN-GUIDES/examples/test-data/readme.md +28 -28
- package/docs/03-PATTERN-GUIDES/extraction/extraction-readme.md +15 -15
- package/docs/03-PATTERN-GUIDES/extraction/readme.md +25 -25
- package/docs/03-PATTERN-GUIDES/file-operations/examples/common-patterns.ts +407 -407
- package/docs/03-PATTERN-GUIDES/file-operations/examples/file-operations-readme.md +142 -142
- package/docs/03-PATTERN-GUIDES/file-operations/file-operations-quick-reference.md +462 -462
- package/docs/03-PATTERN-GUIDES/file-operations/file-operations-readme.md +379 -379
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-01-foundations.md +430 -430
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-02-quick-start.md +484 -484
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-03-s3-operations.md +507 -507
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-04-sftp-operations.md +963 -963
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-05-streaming-performance.md +503 -503
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-archive-patterns.md +386 -386
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-error-handling.md +117 -117
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-api-reference.md +78 -78
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-testing-troubleshooting.md +567 -567
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-08-api-reference.md +1055 -1055
- package/docs/03-PATTERN-GUIDES/file-operations/readme.md +32 -32
- package/docs/03-PATTERN-GUIDES/ingestion/ingestion-readme.md +15 -15
- package/docs/03-PATTERN-GUIDES/ingestion/readme.md +25 -25
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/batch-processing.ts +130 -130
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/common-patterns.ts +360 -360
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/delta-sync.ts +130 -130
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/integration-patterns-readme.md +100 -100
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/real-time-webhook.ts +398 -398
- package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-quick-reference.md +962 -962
- package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-readme.md +134 -134
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-01-real-time-processing.md +991 -991
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-02-batch-processing.md +1547 -1547
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-03-delta-sync.md +1108 -1108
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-04-webhook-patterns.md +1181 -1181
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-05-error-handling.md +1061 -1061
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-advanced-integration-services.md +1547 -1547
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-performance.md +109 -109
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-07-api-reference.md +34 -34
- package/docs/03-PATTERN-GUIDES/integration-patterns/readme.md +30 -30
- package/docs/03-PATTERN-GUIDES/logging-minimal-mode.md +128 -128
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/common-patterns.ts +380 -380
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/multiple-connections-readme.md +139 -139
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/parallel-root-connections.ts +149 -149
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/real-world-scenarios.ts +405 -405
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-01-foundations.md +378 -378
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-02-quick-start.md +566 -566
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-03-targeting-connections.md +659 -659
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-04-parallel-queries.md +656 -656
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-05-best-practices.md +624 -624
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-api-reference.md +824 -824
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-versori.md +119 -119
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-07-api-reference.md +87 -87
- package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-quick-reference.md +353 -353
- package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-readme.md +270 -270
- package/docs/03-PATTERN-GUIDES/multiple-connections/readme.md +30 -30
- package/docs/03-PATTERN-GUIDES/pagination/pagination-readme.md +14 -14
- package/docs/03-PATTERN-GUIDES/pagination/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/parquet/examples/common-patterns.ts +180 -180
- package/docs/03-PATTERN-GUIDES/parquet/examples/read-parquet.ts +48 -48
- package/docs/03-PATTERN-GUIDES/parquet/examples/write-parquet.ts +65 -65
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-01-introduction.md +393 -393
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-02-quick-start.md +572 -572
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-03-reading-parquet.md +525 -525
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-04-writing-parquet.md +554 -554
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-05-graphql-extraction.md +405 -405
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-performance.md +104 -104
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-s3-integration.md +511 -511
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-api-reference.md +90 -90
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-performance-optimization.md +525 -525
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-08-best-practices.md +712 -712
- package/docs/03-PATTERN-GUIDES/parquet/parquet-quick-reference.md +683 -683
- package/docs/03-PATTERN-GUIDES/parquet/parquet-readme.md +248 -248
- package/docs/03-PATTERN-GUIDES/parquet/readme.md +32 -32
- package/docs/03-PATTERN-GUIDES/parsers/parsers-readme.md +12 -12
- package/docs/03-PATTERN-GUIDES/parsers/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/readme.md +159 -159
- package/docs/03-PATTERN-GUIDES/webhooks/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/webhooks/webhooks-readme.md +8 -8
- package/docs/04-REFERENCE/architecture/architecture-01-overview.md +427 -427
- package/docs/04-REFERENCE/architecture/architecture-02-client-architecture.md +424 -424
- package/docs/04-REFERENCE/architecture/architecture-03-data-flow.md +690 -690
- package/docs/04-REFERENCE/architecture/architecture-04-service-layer.md +834 -834
- package/docs/04-REFERENCE/architecture/architecture-05-integration-architecture.md +655 -655
- package/docs/04-REFERENCE/architecture/architecture-06-state-management.md +653 -653
- package/docs/04-REFERENCE/architecture/architecture-adding-new-data-sources.md +686 -686
- package/docs/04-REFERENCE/architecture/readme.md +279 -279
- package/docs/04-REFERENCE/platforms/deno/readme.md +117 -117
- package/docs/04-REFERENCE/platforms/nodejs/readme.md +146 -146
- package/docs/04-REFERENCE/platforms/readme.md +135 -135
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-01-introduction.md +398 -398
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-02-quick-start.md +560 -560
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-03-authentication.md +757 -757
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-04-workflows.md +2476 -2476
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-05-connections.md +1167 -1167
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-kv-storage.md +990 -990
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-state-management.md +121 -121
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-api-reference.md +68 -68
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-deployment.md +731 -731
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-08-best-practices.md +1111 -1111
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-09-signature-reference.md +766 -766
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-readme.md +299 -299
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-s3-sftp-configuration-guide.md +1425 -1425
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-api-key-security.md +816 -816
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-connection-security.md +681 -681
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-workflow-task-types.md +708 -708
- package/docs/04-REFERENCE/platforms/versori/readme.md +108 -108
- package/docs/04-REFERENCE/readme.md +148 -148
- package/docs/04-REFERENCE/resolver-signature/examples/advanced-resolvers.ts +482 -482
- package/docs/04-REFERENCE/resolver-signature/examples/async-resolvers.ts +496 -496
- package/docs/04-REFERENCE/resolver-signature/examples/basic-resolvers.ts +343 -343
- package/docs/04-REFERENCE/resolver-signature/examples/resolver-signature-readme.md +188 -188
- package/docs/04-REFERENCE/resolver-signature/examples/testing-resolvers.ts +463 -463
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-01-foundations.md +286 -286
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-02-parameter-reference.md +643 -643
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-03-basic-examples.md +521 -521
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-04-advanced-patterns.md +739 -739
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-05-sdk-resolvers.md +531 -531
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-migration-guide.md +650 -650
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-testing.md +125 -125
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-07-api-reference.md +794 -794
- package/docs/04-REFERENCE/resolver-signature/readme.md +64 -64
- package/docs/04-REFERENCE/resolver-signature/resolver-signature-quick-reference.md +270 -270
- package/docs/04-REFERENCE/resolver-signature/resolver-signature-readme.md +351 -351
- package/docs/04-REFERENCE/schema/fluent-commerce-schema.json +764 -764
- package/docs/04-REFERENCE/schema/readme.md +141 -141
- package/docs/04-REFERENCE/testing/examples/04-reference-testing-readme.md +158 -158
- package/docs/04-REFERENCE/testing/examples/fluent-testing.ts +62 -62
- package/docs/04-REFERENCE/testing/examples/health-check.ts +155 -155
- package/docs/04-REFERENCE/testing/examples/integration-test.ts +119 -119
- package/docs/04-REFERENCE/testing/examples/performance-test.ts +183 -183
- package/docs/04-REFERENCE/testing/examples/s3-testing.ts +127 -127
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-01-foundations.md +267 -267
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-02-s3-testing.md +599 -599
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-03-fluent-testing.md +589 -589
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-04-integration-testing.md +699 -699
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-05-debugging.md +478 -478
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-cicd-integration.md +463 -463
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-preflight-validation.md +131 -131
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-best-practices.md +499 -499
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-coverage-ci.md +165 -165
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-08-api-reference.md +634 -634
- package/docs/04-REFERENCE/testing/readme.md +86 -86
- package/docs/04-REFERENCE/testing/testing-quick-reference.md +667 -667
- package/docs/04-REFERENCE/testing/testing-readme.md +286 -286
- package/docs/04-REFERENCE/troubleshooting/readme.md +144 -144
- package/docs/04-REFERENCE/troubleshooting/troubleshooting-deno-sftp-compatibility.md +392 -392
- package/docs/template-loading-matrix.md +242 -242
- package/package.json +5 -3
|
@@ -1,688 +1,688 @@
|
|
|
1
|
-
# Standalone: Webhook Payload Processing
|
|
2
|
-
|
|
3
|
-
**FC Connect SDK Use Case Guide**
|
|
4
|
-
|
|
5
|
-
> **SDK**: [@fluentcommerce/fc-connect-sdk](https://www.npmjs.com/package/@fluentcommerce/fc-connect-sdk)
|
|
6
|
-
> **Version**: Use latest - `npm install @fluentcommerce/fc-connect-sdk@latest`
|
|
7
|
-
|
|
8
|
-
**Context**: Node.js script that processes webhook payloads from external systems (3PL, WMS, marketplaces) and creates/updates entities in Fluent Commerce
|
|
9
|
-
|
|
10
|
-
**Complexity**: Low-Medium
|
|
11
|
-
|
|
12
|
-
**Runtime**: Node.js ≥18 / Deno
|
|
13
|
-
|
|
14
|
-
**Estimated Lines**: ~250 lines
|
|
15
|
-
|
|
16
|
-
## What You'll Build
|
|
17
|
-
|
|
18
|
-
- Simple webhook payload processor (not a server)
|
|
19
|
-
- Webhook signature validation using SDK
|
|
20
|
-
- Payload parsing with SDK utilities
|
|
21
|
-
- Data transformation using UniversalMapper
|
|
22
|
-
- Entity creation/update via GraphQL mutations
|
|
23
|
-
- File-based webhook queue (for testing/debugging)
|
|
24
|
-
- Error handling and logging
|
|
25
|
-
|
|
26
|
-
**Use Cases**:
|
|
27
|
-
- Processing stored webhook payloads from a message queue
|
|
28
|
-
- Testing webhook integrations offline
|
|
29
|
-
- Batch processing of webhook files
|
|
30
|
-
- Debugging webhook data transformations
|
|
31
|
-
|
|
32
|
-
## SDK Methods Used
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
// FC Connect SDK+
|
|
36
|
-
// Install: npm install @fluentcommerce/fc-connect-sdk@latest
|
|
37
|
-
// Docs: https://www.npmjs.com/package/@fluentcommerce/fc-connect-sdk
|
|
38
|
-
// GitHub: https://github.com/fluentcommerce/fc-connect-sdk
|
|
39
|
-
|
|
40
|
-
import {
|
|
41
|
-
createClient,
|
|
42
|
-
WebhookValidationService,
|
|
43
|
-
XMLParserService,
|
|
44
|
-
JSONParserService,
|
|
45
|
-
GraphQLMutationMapper,
|
|
46
|
-
UniversalMapper,
|
|
47
|
-
createConsoleLogger,
|
|
48
|
-
toStructuredLogger
|
|
49
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
50
|
-
|
|
51
|
-
// Key SDK methods
|
|
52
|
-
const client = await createClient({...}) // OAuth2 client
|
|
53
|
-
const validator = new WebhookValidationService(...) // Signature validation
|
|
54
|
-
await validator.validateWebhook(body, headers, key) // Validate webhook
|
|
55
|
-
const xmlParser = new XMLParserService() // Parse XML webhooks
|
|
56
|
-
const jsonParser = new JSONParserService() // Parse JSON webhooks
|
|
57
|
-
const mapper = new GraphQLMutationMapper(...) // Map to GraphQL
|
|
58
|
-
await client.graphql({ query, variables }) // Execute mutation
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Complete Working Implementation
|
|
64
|
-
|
|
65
|
-
### 1. Project Setup
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
mkdir webhook-payload-processor
|
|
69
|
-
cd webhook-payload-processor
|
|
70
|
-
|
|
71
|
-
npm init -y
|
|
72
|
-
npm install @fluentcommerce/fc-connect-sdk dotenv
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### 2. Environment Configuration (.env)
|
|
76
|
-
|
|
77
|
-
```env
|
|
78
|
-
# Fluent Commerce OAuth2
|
|
79
|
-
FLUENT_BASE_URL=https://api.fluentcommerce.com
|
|
80
|
-
FLUENT_CLIENT_ID=your-oauth2-client-id
|
|
81
|
-
FLUENT_CLIENT_SECRET=your-oauth2-client-secret
|
|
82
|
-
FLUENT_RETAILER_ID=your-retailer-id
|
|
83
|
-
|
|
84
|
-
# Webhook Validation
|
|
85
|
-
WEBHOOK_PUBLIC_KEY=-----BEGIN PUBLIC KEY-----\nMIIBIjAN...\n-----END PUBLIC KEY-----
|
|
86
|
-
|
|
87
|
-
# Processing
|
|
88
|
-
WEBHOOK_FORMAT=json
|
|
89
|
-
WEBHOOK_INPUT_DIR=./webhooks/incoming
|
|
90
|
-
WEBHOOK_PROCESSED_DIR=./webhooks/processed
|
|
91
|
-
WEBHOOK_ERROR_DIR=./webhooks/errors
|
|
92
|
-
|
|
93
|
-
# Logging
|
|
94
|
-
LOG_LEVEL=info
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### 3. Main Script Implementation (src/webhook-processor.ts)
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
import 'dotenv/config';
|
|
101
|
-
// FC Connect SDK+
|
|
102
|
-
import {
|
|
103
|
-
createClient,
|
|
104
|
-
WebhookValidationService,
|
|
105
|
-
XMLParserService,
|
|
106
|
-
JSONParserService,
|
|
107
|
-
GraphQLMutationMapper,
|
|
108
|
-
type FluentClient,
|
|
109
|
-
type StructuredLogger,
|
|
110
|
-
createConsoleLogger,
|
|
111
|
-
toStructuredLogger
|
|
112
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
113
|
-
|
|
114
|
-
import { readFileSync, readdirSync, renameSync, mkdirSync, existsSync } from 'fs';
|
|
115
|
-
import { join } from 'path';
|
|
116
|
-
|
|
117
|
-
// ============================================================================
|
|
118
|
-
// CONFIGURATION
|
|
119
|
-
// ============================================================================
|
|
120
|
-
|
|
121
|
-
const config = {
|
|
122
|
-
fluent: {
|
|
123
|
-
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
124
|
-
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
125
|
-
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
126
|
-
retailerId: process.env.FLUENT_RETAILER_ID!,
|
|
127
|
-
},
|
|
128
|
-
webhook: {
|
|
129
|
-
publicKey: process.env.WEBHOOK_PUBLIC_KEY!,
|
|
130
|
-
format: (process.env.WEBHOOK_FORMAT || 'json') as 'json' | 'xml',
|
|
131
|
-
inputDir: process.env.WEBHOOK_INPUT_DIR || './webhooks/incoming',
|
|
132
|
-
processedDir: process.env.WEBHOOK_PROCESSED_DIR || './webhooks/processed',
|
|
133
|
-
errorDir: process.env.WEBHOOK_ERROR_DIR || './webhooks/errors',
|
|
134
|
-
},
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
// ============================================================================
|
|
138
|
-
// GRAPHQL MAPPING CONFIGURATION
|
|
139
|
-
// ============================================================================
|
|
140
|
-
|
|
141
|
-
const orderWebhookMapping = {
|
|
142
|
-
version: '1.0',
|
|
143
|
-
name: 'External Order Webhook to Fluent',
|
|
144
|
-
mutation: 'createOrder',
|
|
145
|
-
sourceFormat: 'json',
|
|
146
|
-
arguments: {
|
|
147
|
-
input: {
|
|
148
|
-
_type: 'CreateOrderInput!',
|
|
149
|
-
ref: { source: 'order_id', required: true },
|
|
150
|
-
type: { value: 'HD' },
|
|
151
|
-
retailer: {
|
|
152
|
-
id: { value: config.fluent.retailerId },
|
|
153
|
-
},
|
|
154
|
-
customer: {
|
|
155
|
-
firstName: { source: 'customer.first_name', required: true },
|
|
156
|
-
lastName: { source: 'customer.last_name', required: true },
|
|
157
|
-
email: { source: 'customer.email', transform: 'toLowerCase', required: true },
|
|
158
|
-
},
|
|
159
|
-
items: {
|
|
160
|
-
_array: true,
|
|
161
|
-
source: 'line_items',
|
|
162
|
-
ref: { source: 'line_item_id', required: true },
|
|
163
|
-
productRef: { source: 'sku', required: true },
|
|
164
|
-
quantity: { source: 'quantity', transform: 'parseInt', required: true },
|
|
165
|
-
price: { source: 'unit_price', transform: 'parseFloat', required: true },
|
|
166
|
-
},
|
|
167
|
-
totalPrice: { source: 'total_amount', transform: 'parseFloat', required: true },
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
returnFields: ['id', 'ref', 'status', 'totalPrice'],
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
// ============================================================================
|
|
174
|
-
// WEBHOOK PROCESSOR
|
|
175
|
-
// ============================================================================
|
|
176
|
-
|
|
177
|
-
class WebhookProcessor {
|
|
178
|
-
private client: FluentClient;
|
|
179
|
-
private logger: StructuredLogger;
|
|
180
|
-
private validator: WebhookValidationService;
|
|
181
|
-
private xmlParser: XMLParserService;
|
|
182
|
-
private jsonParser: JSONParserService;
|
|
183
|
-
private mutationMapper: GraphQLMutationMapper;
|
|
184
|
-
|
|
185
|
-
constructor() {
|
|
186
|
-
this.logger = toStructuredLogger(createConsoleLogger(), { logLevel: 'info' });
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async initialize(): Promise<void> {
|
|
190
|
-
this.logger.info('Initializing webhook processor');
|
|
191
|
-
|
|
192
|
-
// Initialize Fluent client
|
|
193
|
-
this.client = await createClient({ config: config.fluent });
|
|
194
|
-
|
|
195
|
-
// Initialize webhook validator
|
|
196
|
-
this.validator = new WebhookValidationService(
|
|
197
|
-
{ strictValidation: true },
|
|
198
|
-
this.logger
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
// Initialize parsers
|
|
202
|
-
this.xmlParser = new XMLParserService();
|
|
203
|
-
this.jsonParser = new JSONParserService();
|
|
204
|
-
|
|
205
|
-
// Initialize mutation mapper
|
|
206
|
-
this.mutationMapper = new GraphQLMutationMapper(
|
|
207
|
-
orderWebhookMapping,
|
|
208
|
-
this.client,
|
|
209
|
-
this.logger
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
// Ensure directories exist
|
|
213
|
-
for (const dir of [config.webhook.inputDir, config.webhook.processedDir, config.webhook.errorDir]) {
|
|
214
|
-
if (!existsSync(dir)) {
|
|
215
|
-
mkdirSync(dir, { recursive: true });
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
this.logger.info('Webhook processor initialized');
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
async processWebhooks(): Promise<void> {
|
|
223
|
-
this.logger.info('Starting webhook processing cycle');
|
|
224
|
-
|
|
225
|
-
// List webhook files
|
|
226
|
-
const files = readdirSync(config.webhook.inputDir);
|
|
227
|
-
const webhookFiles = files.filter(f =>
|
|
228
|
-
f.endsWith('.json') || f.endsWith('.xml') || f.endsWith('.txt')
|
|
229
|
-
);
|
|
230
|
-
|
|
231
|
-
this.logger.info(`Found ${webhookFiles.length} webhook files`);
|
|
232
|
-
|
|
233
|
-
let processed = 0;
|
|
234
|
-
let failed = 0;
|
|
235
|
-
|
|
236
|
-
for (const file of webhookFiles) {
|
|
237
|
-
try {
|
|
238
|
-
await this.processWebhookFile(file);
|
|
239
|
-
processed++;
|
|
240
|
-
} catch (error: any) {
|
|
241
|
-
this.logger.error(`Failed to process webhook file: ${file}`, error);
|
|
242
|
-
failed++;
|
|
243
|
-
|
|
244
|
-
// Move to error directory
|
|
245
|
-
const errorPath = join(config.webhook.errorDir, file);
|
|
246
|
-
renameSync(join(config.webhook.inputDir, file), errorPath);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
this.logger.info('Webhook processing complete', {
|
|
251
|
-
processed,
|
|
252
|
-
failed,
|
|
253
|
-
total: webhookFiles.length,
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
private async processWebhookFile(fileName: string): Promise<void> {
|
|
258
|
-
this.logger.info(`Processing webhook file: ${fileName}`);
|
|
259
|
-
|
|
260
|
-
const filePath = join(config.webhook.inputDir, fileName);
|
|
261
|
-
|
|
262
|
-
// Read file
|
|
263
|
-
const content = readFileSync(filePath, 'utf8');
|
|
264
|
-
|
|
265
|
-
// Parse webhook metadata and payload
|
|
266
|
-
// Expected format: HEADERS\n\n<headers JSON>\n\nBODY\n\n<payload>
|
|
267
|
-
const { headers, body } = this.parseWebhookFile(content);
|
|
268
|
-
|
|
269
|
-
// Validate webhook signature (if headers present)
|
|
270
|
-
if (headers && config.webhook.publicKey) {
|
|
271
|
-
const validationResult = await this.validator.validateWebhook(
|
|
272
|
-
body,
|
|
273
|
-
headers,
|
|
274
|
-
config.webhook.publicKey
|
|
275
|
-
);
|
|
276
|
-
|
|
277
|
-
if (!validationResult.isValid) {
|
|
278
|
-
throw new Error(`Webhook signature validation failed: ${validationResult.error}`);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
this.logger.debug('Webhook signature validated', {
|
|
282
|
-
algorithm: validationResult.algorithm,
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Parse webhook payload
|
|
287
|
-
let parsedPayload: any;
|
|
288
|
-
if (config.webhook.format === 'xml') {
|
|
289
|
-
parsedPayload = await this.xmlParser.parse(body, {
|
|
290
|
-
includeAttributes: true,
|
|
291
|
-
parseNumbers: true,
|
|
292
|
-
});
|
|
293
|
-
} else {
|
|
294
|
-
parsedPayload = await this.jsonParser.parse(body);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
this.logger.debug('Webhook payload parsed', {
|
|
298
|
-
rootKeys: Object.keys(parsedPayload),
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
// Map to GraphQL mutation
|
|
302
|
-
const mutationPayload = await this.mutationMapper.map(parsedPayload);
|
|
303
|
-
|
|
304
|
-
this.logger.debug('Mapped to GraphQL mutation', {
|
|
305
|
-
variables: mutationPayload.variables,
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
// Execute mutation
|
|
309
|
-
const result = await this.client.graphql(mutationPayload);
|
|
310
|
-
|
|
311
|
-
if (result.errors) {
|
|
312
|
-
throw new Error(`GraphQL errors: ${JSON.stringify(result.errors)}`);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
this.logger.info('Webhook processed successfully', {
|
|
316
|
-
file: fileName,
|
|
317
|
-
orderId: (result.data as any)?.createOrder?.id,
|
|
318
|
-
orderRef: (result.data as any)?.createOrder?.ref,
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
// Move to processed directory
|
|
322
|
-
const processedPath = join(config.webhook.processedDir, fileName);
|
|
323
|
-
renameSync(filePath, processedPath);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
private parseWebhookFile(content: string): { headers?: Record<string, string>; body: string } {
|
|
327
|
-
// Check if file contains headers
|
|
328
|
-
if (content.startsWith('HEADERS\n')) {
|
|
329
|
-
const parts = content.split('\n\nBODY\n\n');
|
|
330
|
-
if (parts.length === 2) {
|
|
331
|
-
const headersJson = parts[0].replace('HEADERS\n\n', '');
|
|
332
|
-
const headers = JSON.parse(headersJson);
|
|
333
|
-
const body = parts[1];
|
|
334
|
-
return { headers, body };
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// No headers, entire file is body
|
|
339
|
-
return { body: content };
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// ============================================================================
|
|
344
|
-
// MAIN ENTRY POINT
|
|
345
|
-
// ============================================================================
|
|
346
|
-
|
|
347
|
-
async function main() {
|
|
348
|
-
const processor = new WebhookProcessor();
|
|
349
|
-
|
|
350
|
-
try {
|
|
351
|
-
await processor.initialize();
|
|
352
|
-
await processor.processWebhooks();
|
|
353
|
-
|
|
354
|
-
console.log('Webhook processing completed successfully');
|
|
355
|
-
process.exit(0);
|
|
356
|
-
} catch (error: any) {
|
|
357
|
-
console.error('Fatal error:', error);
|
|
358
|
-
process.exit(1);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// Run if executed directly
|
|
363
|
-
if (require.main === module) {
|
|
364
|
-
main();
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
export { WebhookProcessor };
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
---
|
|
371
|
-
|
|
372
|
-
## Key Patterns Explained
|
|
373
|
-
|
|
374
|
-
### Pattern 1: Webhook Signature Validation
|
|
375
|
-
|
|
376
|
-
**Purpose**: Verify webhook authenticity using cryptographic signatures
|
|
377
|
-
|
|
378
|
-
```typescript
|
|
379
|
-
// Initialize validator
|
|
380
|
-
const validator = new WebhookValidationService(
|
|
381
|
-
{ strictValidation: true }, // Enforce signature validation
|
|
382
|
-
logger
|
|
383
|
-
);
|
|
384
|
-
|
|
385
|
-
// Validate webhook
|
|
386
|
-
const result = await validator.validateWebhook(
|
|
387
|
-
rawBody, // Raw webhook body (string)
|
|
388
|
-
headers, // HTTP headers (contains signature)
|
|
389
|
-
publicKey // Public key from webhook provider
|
|
390
|
-
);
|
|
391
|
-
|
|
392
|
-
if (!result.isValid) {
|
|
393
|
-
throw new Error(`Validation failed: ${result.error}`);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// Signature valid - safe to process
|
|
397
|
-
console.log(`Validated with ${result.algorithm}`);
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
**Supported Algorithms**:
|
|
401
|
-
- `sha256`: HMAC-SHA256 signatures
|
|
402
|
-
- `rsa-sha256`: RSA-SHA256 signatures
|
|
403
|
-
- Auto-detection from headers
|
|
404
|
-
|
|
405
|
-
### Pattern 2: Webhook File Format
|
|
406
|
-
|
|
407
|
-
**With Headers** (for signature validation):
|
|
408
|
-
```
|
|
409
|
-
HEADERS
|
|
410
|
-
|
|
411
|
-
{"x-webhook-signature":"sha256=abc123...","content-type":"application/json"}
|
|
412
|
-
|
|
413
|
-
BODY
|
|
414
|
-
|
|
415
|
-
{"order_id":"ORD-123","customer":{"first_name":"John"...}}
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
**Without Headers** (no validation):
|
|
419
|
-
```json
|
|
420
|
-
{"order_id":"ORD-123","customer":{"first_name":"John"...}}
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
### Pattern 3: XML vs JSON Parsing
|
|
424
|
-
|
|
425
|
-
```typescript
|
|
426
|
-
// Detect format and parse accordingly
|
|
427
|
-
let parsedPayload: any;
|
|
428
|
-
|
|
429
|
-
if (config.webhook.format === 'xml') {
|
|
430
|
-
parsedPayload = await xmlParser.parse(body, {
|
|
431
|
-
includeAttributes: true, // Parse @id, @type attributes
|
|
432
|
-
parseNumbers: true, // Auto-convert numbers
|
|
433
|
-
parseBooleans: true, // Auto-convert booleans
|
|
434
|
-
});
|
|
435
|
-
} else {
|
|
436
|
-
parsedPayload = await jsonParser.parse(body);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// Now parsedPayload is a JavaScript object ready for mapping
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
### Pattern 4: GraphQL Mutation Mapping
|
|
443
|
-
|
|
444
|
-
```typescript
|
|
445
|
-
// Configure mapping once
|
|
446
|
-
const mapping = {
|
|
447
|
-
mutation: 'createOrder',
|
|
448
|
-
sourceFormat: 'json',
|
|
449
|
-
arguments: {
|
|
450
|
-
input: {
|
|
451
|
-
ref: { source: 'order_id', required: true },
|
|
452
|
-
customer: {
|
|
453
|
-
firstName: { source: 'customer.first_name' },
|
|
454
|
-
email: { source: 'customer.email', transform: 'toLowerCase' },
|
|
455
|
-
},
|
|
456
|
-
items: {
|
|
457
|
-
_array: true,
|
|
458
|
-
source: 'line_items',
|
|
459
|
-
productRef: { source: 'sku' },
|
|
460
|
-
quantity: { source: 'quantity', transform: 'parseInt' },
|
|
461
|
-
},
|
|
462
|
-
},
|
|
463
|
-
},
|
|
464
|
-
};
|
|
465
|
-
|
|
466
|
-
// Map webhook data to GraphQL mutation
|
|
467
|
-
const mapper = new GraphQLMutationMapper(mapping, logger, { fluentClient: client });
|
|
468
|
-
const payload = await mapper.map(parsedPayload);
|
|
469
|
-
|
|
470
|
-
// Execute mutation
|
|
471
|
-
const result = await client.graphql(payload);
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
---
|
|
475
|
-
|
|
476
|
-
## Testing
|
|
477
|
-
|
|
478
|
-
### 1. Create Test Webhook Files
|
|
479
|
-
|
|
480
|
-
**Test Order (webhooks/incoming/order-001.json)**:
|
|
481
|
-
```json
|
|
482
|
-
{
|
|
483
|
-
"order_id": "TEST-001",
|
|
484
|
-
"customer": {
|
|
485
|
-
"first_name": "John",
|
|
486
|
-
"last_name": "Doe",
|
|
487
|
-
"email": "JOHN.DOE@EXAMPLE.COM"
|
|
488
|
-
},
|
|
489
|
-
"line_items": [
|
|
490
|
-
{"line_item_id": "1", "sku": "PROD-001", "quantity": 2, "unit_price": 29.99},
|
|
491
|
-
{"line_item_id": "2", "sku": "PROD-002", "quantity": 1, "unit_price": 49.99}
|
|
492
|
-
],
|
|
493
|
-
"total_amount": 109.97
|
|
494
|
-
}
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
**With Signature Validation (webhooks/incoming/order-002.json)**:
|
|
498
|
-
```
|
|
499
|
-
HEADERS
|
|
500
|
-
|
|
501
|
-
{"x-webhook-signature":"sha256=abc123def456...","content-type":"application/json"}
|
|
502
|
-
|
|
503
|
-
BODY
|
|
504
|
-
|
|
505
|
-
{"order_id":"TEST-002","customer":{"first_name":"Jane","last_name":"Smith","email":"jane@example.com"},"line_items":[{"line_item_id":"1","sku":"PROD-003","quantity":1,"unit_price":99.99}],"total_amount":99.99}
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
### 2. Run Processor
|
|
509
|
-
|
|
510
|
-
```bash
|
|
511
|
-
# Process all webhooks in incoming directory
|
|
512
|
-
npm run dev
|
|
513
|
-
|
|
514
|
-
# Check processed webhooks
|
|
515
|
-
ls webhooks/processed/
|
|
516
|
-
|
|
517
|
-
# Check errors
|
|
518
|
-
ls webhooks/errors/
|
|
519
|
-
```
|
|
520
|
-
|
|
521
|
-
### 3. Manual Testing
|
|
522
|
-
|
|
523
|
-
```bash
|
|
524
|
-
# Test single webhook
|
|
525
|
-
cat webhooks/incoming/order-001.json | node dist/webhook-processor.js
|
|
526
|
-
|
|
527
|
-
# Test with debug logging
|
|
528
|
-
LOG_LEVEL=debug npm run dev
|
|
529
|
-
```
|
|
530
|
-
|
|
531
|
-
---
|
|
532
|
-
|
|
533
|
-
## Deployment Options
|
|
534
|
-
|
|
535
|
-
### Option 1: Cron Job (Scheduled Processing)
|
|
536
|
-
|
|
537
|
-
```bash
|
|
538
|
-
# Run every 5 minutes
|
|
539
|
-
*/5 * * * * cd /opt/webhook-processor && npm start >> /var/log/webhook-processor.log 2>&1
|
|
540
|
-
```
|
|
541
|
-
|
|
542
|
-
### Option 2: Message Queue Consumer
|
|
543
|
-
|
|
544
|
-
```typescript
|
|
545
|
-
// Integrate with SQS, RabbitMQ, etc.
|
|
546
|
-
import { SQS } from '@aws-sdk/client-sqs';
|
|
547
|
-
|
|
548
|
-
const sqs = new SQS({ region: 'us-east-1' });
|
|
549
|
-
|
|
550
|
-
while (true) {
|
|
551
|
-
const messages = await sqs.receiveMessage({
|
|
552
|
-
QueueUrl: process.env.QUEUE_URL,
|
|
553
|
-
MaxNumberOfMessages: 10,
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
for (const message of messages.Messages || []) {
|
|
557
|
-
// Save to file
|
|
558
|
-
const fileName = `webhook-${Date.now()}.json`;
|
|
559
|
-
writeFileSync(
|
|
560
|
-
join(config.webhook.inputDir, fileName),
|
|
561
|
-
message.Body
|
|
562
|
-
);
|
|
563
|
-
|
|
564
|
-
// Process
|
|
565
|
-
await processor.processWebhooks();
|
|
566
|
-
|
|
567
|
-
// Delete from queue
|
|
568
|
-
await sqs.deleteMessage({
|
|
569
|
-
QueueUrl: process.env.QUEUE_URL,
|
|
570
|
-
ReceiptHandle: message.ReceiptHandle,
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
575
|
-
}
|
|
576
|
-
```
|
|
577
|
-
|
|
578
|
-
### Option 3: AWS Lambda with S3 Trigger
|
|
579
|
-
|
|
580
|
-
```typescript
|
|
581
|
-
import { S3Event } from 'aws-lambda';
|
|
582
|
-
|
|
583
|
-
export const handler = async (event: S3Event) => {
|
|
584
|
-
const processor = new WebhookProcessor();
|
|
585
|
-
await processor.initialize();
|
|
586
|
-
|
|
587
|
-
for (const record of event.Records) {
|
|
588
|
-
const bucket = record.s3.bucket.name;
|
|
589
|
-
const key = record.s3.object.key;
|
|
590
|
-
|
|
591
|
-
// Download from S3 to input directory
|
|
592
|
-
// Process
|
|
593
|
-
// Move to processed S3 folder
|
|
594
|
-
}
|
|
595
|
-
};
|
|
596
|
-
```
|
|
597
|
-
|
|
598
|
-
---
|
|
599
|
-
|
|
600
|
-
## Common Issues
|
|
601
|
-
|
|
602
|
-
### Issue 1: Signature Validation Fails
|
|
603
|
-
|
|
604
|
-
**Symptom**: `Webhook signature validation failed`
|
|
605
|
-
|
|
606
|
-
**Solution**:
|
|
607
|
-
```typescript
|
|
608
|
-
// Ensure raw body is used (no JSON parsing beforehand)
|
|
609
|
-
const rawBody = readFileSync(filePath, 'utf8');
|
|
610
|
-
|
|
611
|
-
// Check public key format
|
|
612
|
-
const publicKey = `-----BEGIN PUBLIC KEY-----
|
|
613
|
-
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
|
|
614
|
-
-----END PUBLIC KEY-----`;
|
|
615
|
-
|
|
616
|
-
// Verify header name matches webhook provider
|
|
617
|
-
const signatureHeader = headers['x-webhook-signature'] ||
|
|
618
|
-
headers['x-signature'] ||
|
|
619
|
-
headers['signature'];
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
### Issue 2: Mapping Errors
|
|
623
|
-
|
|
624
|
-
**Symptom**: `Required field 'ref' is missing`
|
|
625
|
-
|
|
626
|
-
**Solution**:
|
|
627
|
-
```typescript
|
|
628
|
-
// Debug parsed payload structure
|
|
629
|
-
console.log('Parsed payload:', JSON.stringify(parsedPayload, null, 2));
|
|
630
|
-
|
|
631
|
-
// Check source path matches actual structure
|
|
632
|
-
ref: { source: 'order_id' } // ✅ Correct if payload has {order_id: "..."}
|
|
633
|
-
ref: { source: 'orderId' } // ❌ Wrong if payload uses snake_case
|
|
634
|
-
```
|
|
635
|
-
|
|
636
|
-
### Issue 3: XML Attribute Not Found
|
|
637
|
-
|
|
638
|
-
**Symptom**: `Cannot read property '@id' of undefined`
|
|
639
|
-
|
|
640
|
-
**Solution**:
|
|
641
|
-
```typescript
|
|
642
|
-
// Enable attribute parsing
|
|
643
|
-
const parsedXml = await xmlParser.parse(body, {
|
|
644
|
-
includeAttributes: true, // REQUIRED for @attributes
|
|
645
|
-
});
|
|
646
|
-
|
|
647
|
-
// Access attributes with @ prefix
|
|
648
|
-
ref: { source: 'order.@id' } // For <order id="123">
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
---
|
|
652
|
-
|
|
653
|
-
## Related Guides
|
|
654
|
-
|
|
655
|
-
- **[SFTP XML → GraphQL](./sftp-xml-graphql.md)** - Similar pattern with SFTP polling
|
|
656
|
-
- **[S3 CSV → Batch API](./s3-csv-batch-api.md)** - Batch processing pattern
|
|
657
|
-
- **[GraphQL Mutation Mapping](../../02-CORE-GUIDES/mapping/graphql-mutation-mapping/)** - Complete mapping reference
|
|
658
|
-
- **[Webhook Validation](../../02-CORE-GUIDES/advanced-services/advanced-services-readme.md)** - Security best practices
|
|
659
|
-
|
|
660
|
-
---
|
|
661
|
-
|
|
662
|
-
## Production Checklist
|
|
663
|
-
|
|
664
|
-
- [ ] Webhook signature validation enabled
|
|
665
|
-
- [ ] Public key secured (secrets manager, not hardcoded)
|
|
666
|
-
- [ ] Error webhooks moved to error directory (not deleted)
|
|
667
|
-
- [ ] Processed webhooks archived with retention policy
|
|
668
|
-
- [ ] Monitoring configured (file counts, processing rate)
|
|
669
|
-
- [ ] Dead letter queue for failed webhooks
|
|
670
|
-
- [ ] Retry logic for transient failures
|
|
671
|
-
- [ ] Logging includes correlation IDs
|
|
672
|
-
- [ ] Alerts configured for high error rates
|
|
673
|
-
- [ ] Documentation updated with webhook format examples
|
|
674
|
-
|
|
675
|
-
---
|
|
676
|
-
|
|
677
|
-
**Next Steps**:
|
|
678
|
-
|
|
679
|
-
1. Customize `orderWebhookMapping` for your webhook format
|
|
680
|
-
2. Add more mapping configurations for different webhook types
|
|
681
|
-
3. Integrate with message queue (SQS, RabbitMQ, Kafka)
|
|
682
|
-
4. Set up monitoring and alerting
|
|
683
|
-
5. Deploy to production environment
|
|
684
|
-
|
|
685
|
-
**Support**:
|
|
686
|
-
|
|
687
|
-
- SDK Issues: https://github.com/fluentcommerce/fc-connect-sdk/issues
|
|
688
|
-
- Fluent Commerce API: https://docs.fluentcommerce.com
|
|
1
|
+
# Standalone: Webhook Payload Processing
|
|
2
|
+
|
|
3
|
+
**FC Connect SDK Use Case Guide**
|
|
4
|
+
|
|
5
|
+
> **SDK**: [@fluentcommerce/fc-connect-sdk](https://www.npmjs.com/package/@fluentcommerce/fc-connect-sdk)
|
|
6
|
+
> **Version**: Use latest - `npm install @fluentcommerce/fc-connect-sdk@latest`
|
|
7
|
+
|
|
8
|
+
**Context**: Node.js script that processes webhook payloads from external systems (3PL, WMS, marketplaces) and creates/updates entities in Fluent Commerce
|
|
9
|
+
|
|
10
|
+
**Complexity**: Low-Medium
|
|
11
|
+
|
|
12
|
+
**Runtime**: Node.js ≥18 / Deno
|
|
13
|
+
|
|
14
|
+
**Estimated Lines**: ~250 lines
|
|
15
|
+
|
|
16
|
+
## What You'll Build
|
|
17
|
+
|
|
18
|
+
- Simple webhook payload processor (not a server)
|
|
19
|
+
- Webhook signature validation using SDK
|
|
20
|
+
- Payload parsing with SDK utilities
|
|
21
|
+
- Data transformation using UniversalMapper
|
|
22
|
+
- Entity creation/update via GraphQL mutations
|
|
23
|
+
- File-based webhook queue (for testing/debugging)
|
|
24
|
+
- Error handling and logging
|
|
25
|
+
|
|
26
|
+
**Use Cases**:
|
|
27
|
+
- Processing stored webhook payloads from a message queue
|
|
28
|
+
- Testing webhook integrations offline
|
|
29
|
+
- Batch processing of webhook files
|
|
30
|
+
- Debugging webhook data transformations
|
|
31
|
+
|
|
32
|
+
## SDK Methods Used
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// FC Connect SDK+
|
|
36
|
+
// Install: npm install @fluentcommerce/fc-connect-sdk@latest
|
|
37
|
+
// Docs: https://www.npmjs.com/package/@fluentcommerce/fc-connect-sdk
|
|
38
|
+
// GitHub: https://github.com/fluentcommerce/fc-connect-sdk
|
|
39
|
+
|
|
40
|
+
import {
|
|
41
|
+
createClient,
|
|
42
|
+
WebhookValidationService,
|
|
43
|
+
XMLParserService,
|
|
44
|
+
JSONParserService,
|
|
45
|
+
GraphQLMutationMapper,
|
|
46
|
+
UniversalMapper,
|
|
47
|
+
createConsoleLogger,
|
|
48
|
+
toStructuredLogger
|
|
49
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
50
|
+
|
|
51
|
+
// Key SDK methods
|
|
52
|
+
const client = await createClient({...}) // OAuth2 client
|
|
53
|
+
const validator = new WebhookValidationService(...) // Signature validation
|
|
54
|
+
await validator.validateWebhook(body, headers, key) // Validate webhook
|
|
55
|
+
const xmlParser = new XMLParserService() // Parse XML webhooks
|
|
56
|
+
const jsonParser = new JSONParserService() // Parse JSON webhooks
|
|
57
|
+
const mapper = new GraphQLMutationMapper(...) // Map to GraphQL
|
|
58
|
+
await client.graphql({ query, variables }) // Execute mutation
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Complete Working Implementation
|
|
64
|
+
|
|
65
|
+
### 1. Project Setup
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
mkdir webhook-payload-processor
|
|
69
|
+
cd webhook-payload-processor
|
|
70
|
+
|
|
71
|
+
npm init -y
|
|
72
|
+
npm install @fluentcommerce/fc-connect-sdk dotenv
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 2. Environment Configuration (.env)
|
|
76
|
+
|
|
77
|
+
```env
|
|
78
|
+
# Fluent Commerce OAuth2
|
|
79
|
+
FLUENT_BASE_URL=https://api.fluentcommerce.com
|
|
80
|
+
FLUENT_CLIENT_ID=your-oauth2-client-id
|
|
81
|
+
FLUENT_CLIENT_SECRET=your-oauth2-client-secret
|
|
82
|
+
FLUENT_RETAILER_ID=your-retailer-id
|
|
83
|
+
|
|
84
|
+
# Webhook Validation
|
|
85
|
+
WEBHOOK_PUBLIC_KEY=-----BEGIN PUBLIC KEY-----\nMIIBIjAN...\n-----END PUBLIC KEY-----
|
|
86
|
+
|
|
87
|
+
# Processing
|
|
88
|
+
WEBHOOK_FORMAT=json
|
|
89
|
+
WEBHOOK_INPUT_DIR=./webhooks/incoming
|
|
90
|
+
WEBHOOK_PROCESSED_DIR=./webhooks/processed
|
|
91
|
+
WEBHOOK_ERROR_DIR=./webhooks/errors
|
|
92
|
+
|
|
93
|
+
# Logging
|
|
94
|
+
LOG_LEVEL=info
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 3. Main Script Implementation (src/webhook-processor.ts)
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import 'dotenv/config';
|
|
101
|
+
// FC Connect SDK+
|
|
102
|
+
import {
|
|
103
|
+
createClient,
|
|
104
|
+
WebhookValidationService,
|
|
105
|
+
XMLParserService,
|
|
106
|
+
JSONParserService,
|
|
107
|
+
GraphQLMutationMapper,
|
|
108
|
+
type FluentClient,
|
|
109
|
+
type StructuredLogger,
|
|
110
|
+
createConsoleLogger,
|
|
111
|
+
toStructuredLogger
|
|
112
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
113
|
+
|
|
114
|
+
import { readFileSync, readdirSync, renameSync, mkdirSync, existsSync } from 'fs';
|
|
115
|
+
import { join } from 'path';
|
|
116
|
+
|
|
117
|
+
// ============================================================================
|
|
118
|
+
// CONFIGURATION
|
|
119
|
+
// ============================================================================
|
|
120
|
+
|
|
121
|
+
const config = {
|
|
122
|
+
fluent: {
|
|
123
|
+
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
124
|
+
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
125
|
+
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
126
|
+
retailerId: process.env.FLUENT_RETAILER_ID!,
|
|
127
|
+
},
|
|
128
|
+
webhook: {
|
|
129
|
+
publicKey: process.env.WEBHOOK_PUBLIC_KEY!,
|
|
130
|
+
format: (process.env.WEBHOOK_FORMAT || 'json') as 'json' | 'xml',
|
|
131
|
+
inputDir: process.env.WEBHOOK_INPUT_DIR || './webhooks/incoming',
|
|
132
|
+
processedDir: process.env.WEBHOOK_PROCESSED_DIR || './webhooks/processed',
|
|
133
|
+
errorDir: process.env.WEBHOOK_ERROR_DIR || './webhooks/errors',
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// GRAPHQL MAPPING CONFIGURATION
|
|
139
|
+
// ============================================================================
|
|
140
|
+
|
|
141
|
+
const orderWebhookMapping = {
|
|
142
|
+
version: '1.0',
|
|
143
|
+
name: 'External Order Webhook to Fluent',
|
|
144
|
+
mutation: 'createOrder',
|
|
145
|
+
sourceFormat: 'json',
|
|
146
|
+
arguments: {
|
|
147
|
+
input: {
|
|
148
|
+
_type: 'CreateOrderInput!',
|
|
149
|
+
ref: { source: 'order_id', required: true },
|
|
150
|
+
type: { value: 'HD' },
|
|
151
|
+
retailer: {
|
|
152
|
+
id: { value: config.fluent.retailerId },
|
|
153
|
+
},
|
|
154
|
+
customer: {
|
|
155
|
+
firstName: { source: 'customer.first_name', required: true },
|
|
156
|
+
lastName: { source: 'customer.last_name', required: true },
|
|
157
|
+
email: { source: 'customer.email', transform: 'toLowerCase', required: true },
|
|
158
|
+
},
|
|
159
|
+
items: {
|
|
160
|
+
_array: true,
|
|
161
|
+
source: 'line_items',
|
|
162
|
+
ref: { source: 'line_item_id', required: true },
|
|
163
|
+
productRef: { source: 'sku', required: true },
|
|
164
|
+
quantity: { source: 'quantity', transform: 'parseInt', required: true },
|
|
165
|
+
price: { source: 'unit_price', transform: 'parseFloat', required: true },
|
|
166
|
+
},
|
|
167
|
+
totalPrice: { source: 'total_amount', transform: 'parseFloat', required: true },
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
returnFields: ['id', 'ref', 'status', 'totalPrice'],
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// ============================================================================
|
|
174
|
+
// WEBHOOK PROCESSOR
|
|
175
|
+
// ============================================================================
|
|
176
|
+
|
|
177
|
+
class WebhookProcessor {
|
|
178
|
+
private client: FluentClient;
|
|
179
|
+
private logger: StructuredLogger;
|
|
180
|
+
private validator: WebhookValidationService;
|
|
181
|
+
private xmlParser: XMLParserService;
|
|
182
|
+
private jsonParser: JSONParserService;
|
|
183
|
+
private mutationMapper: GraphQLMutationMapper;
|
|
184
|
+
|
|
185
|
+
constructor() {
|
|
186
|
+
this.logger = toStructuredLogger(createConsoleLogger(), { logLevel: 'info' });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async initialize(): Promise<void> {
|
|
190
|
+
this.logger.info('Initializing webhook processor');
|
|
191
|
+
|
|
192
|
+
// Initialize Fluent client
|
|
193
|
+
this.client = await createClient({ config: config.fluent });
|
|
194
|
+
|
|
195
|
+
// Initialize webhook validator
|
|
196
|
+
this.validator = new WebhookValidationService(
|
|
197
|
+
{ strictValidation: true },
|
|
198
|
+
this.logger
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
// Initialize parsers
|
|
202
|
+
this.xmlParser = new XMLParserService();
|
|
203
|
+
this.jsonParser = new JSONParserService();
|
|
204
|
+
|
|
205
|
+
// Initialize mutation mapper
|
|
206
|
+
this.mutationMapper = new GraphQLMutationMapper(
|
|
207
|
+
orderWebhookMapping,
|
|
208
|
+
this.client,
|
|
209
|
+
this.logger
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// Ensure directories exist
|
|
213
|
+
for (const dir of [config.webhook.inputDir, config.webhook.processedDir, config.webhook.errorDir]) {
|
|
214
|
+
if (!existsSync(dir)) {
|
|
215
|
+
mkdirSync(dir, { recursive: true });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
this.logger.info('Webhook processor initialized');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async processWebhooks(): Promise<void> {
|
|
223
|
+
this.logger.info('Starting webhook processing cycle');
|
|
224
|
+
|
|
225
|
+
// List webhook files
|
|
226
|
+
const files = readdirSync(config.webhook.inputDir);
|
|
227
|
+
const webhookFiles = files.filter(f =>
|
|
228
|
+
f.endsWith('.json') || f.endsWith('.xml') || f.endsWith('.txt')
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
this.logger.info(`Found ${webhookFiles.length} webhook files`);
|
|
232
|
+
|
|
233
|
+
let processed = 0;
|
|
234
|
+
let failed = 0;
|
|
235
|
+
|
|
236
|
+
for (const file of webhookFiles) {
|
|
237
|
+
try {
|
|
238
|
+
await this.processWebhookFile(file);
|
|
239
|
+
processed++;
|
|
240
|
+
} catch (error: any) {
|
|
241
|
+
this.logger.error(`Failed to process webhook file: ${file}`, error);
|
|
242
|
+
failed++;
|
|
243
|
+
|
|
244
|
+
// Move to error directory
|
|
245
|
+
const errorPath = join(config.webhook.errorDir, file);
|
|
246
|
+
renameSync(join(config.webhook.inputDir, file), errorPath);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
this.logger.info('Webhook processing complete', {
|
|
251
|
+
processed,
|
|
252
|
+
failed,
|
|
253
|
+
total: webhookFiles.length,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private async processWebhookFile(fileName: string): Promise<void> {
|
|
258
|
+
this.logger.info(`Processing webhook file: ${fileName}`);
|
|
259
|
+
|
|
260
|
+
const filePath = join(config.webhook.inputDir, fileName);
|
|
261
|
+
|
|
262
|
+
// Read file
|
|
263
|
+
const content = readFileSync(filePath, 'utf8');
|
|
264
|
+
|
|
265
|
+
// Parse webhook metadata and payload
|
|
266
|
+
// Expected format: HEADERS\n\n<headers JSON>\n\nBODY\n\n<payload>
|
|
267
|
+
const { headers, body } = this.parseWebhookFile(content);
|
|
268
|
+
|
|
269
|
+
// Validate webhook signature (if headers present)
|
|
270
|
+
if (headers && config.webhook.publicKey) {
|
|
271
|
+
const validationResult = await this.validator.validateWebhook(
|
|
272
|
+
body,
|
|
273
|
+
headers,
|
|
274
|
+
config.webhook.publicKey
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
if (!validationResult.isValid) {
|
|
278
|
+
throw new Error(`Webhook signature validation failed: ${validationResult.error}`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
this.logger.debug('Webhook signature validated', {
|
|
282
|
+
algorithm: validationResult.algorithm,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Parse webhook payload
|
|
287
|
+
let parsedPayload: any;
|
|
288
|
+
if (config.webhook.format === 'xml') {
|
|
289
|
+
parsedPayload = await this.xmlParser.parse(body, {
|
|
290
|
+
includeAttributes: true,
|
|
291
|
+
parseNumbers: true,
|
|
292
|
+
});
|
|
293
|
+
} else {
|
|
294
|
+
parsedPayload = await this.jsonParser.parse(body);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
this.logger.debug('Webhook payload parsed', {
|
|
298
|
+
rootKeys: Object.keys(parsedPayload),
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// Map to GraphQL mutation
|
|
302
|
+
const mutationPayload = await this.mutationMapper.map(parsedPayload);
|
|
303
|
+
|
|
304
|
+
this.logger.debug('Mapped to GraphQL mutation', {
|
|
305
|
+
variables: mutationPayload.variables,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// Execute mutation
|
|
309
|
+
const result = await this.client.graphql(mutationPayload);
|
|
310
|
+
|
|
311
|
+
if (result.errors) {
|
|
312
|
+
throw new Error(`GraphQL errors: ${JSON.stringify(result.errors)}`);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
this.logger.info('Webhook processed successfully', {
|
|
316
|
+
file: fileName,
|
|
317
|
+
orderId: (result.data as any)?.createOrder?.id,
|
|
318
|
+
orderRef: (result.data as any)?.createOrder?.ref,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// Move to processed directory
|
|
322
|
+
const processedPath = join(config.webhook.processedDir, fileName);
|
|
323
|
+
renameSync(filePath, processedPath);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private parseWebhookFile(content: string): { headers?: Record<string, string>; body: string } {
|
|
327
|
+
// Check if file contains headers
|
|
328
|
+
if (content.startsWith('HEADERS\n')) {
|
|
329
|
+
const parts = content.split('\n\nBODY\n\n');
|
|
330
|
+
if (parts.length === 2) {
|
|
331
|
+
const headersJson = parts[0].replace('HEADERS\n\n', '');
|
|
332
|
+
const headers = JSON.parse(headersJson);
|
|
333
|
+
const body = parts[1];
|
|
334
|
+
return { headers, body };
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// No headers, entire file is body
|
|
339
|
+
return { body: content };
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ============================================================================
|
|
344
|
+
// MAIN ENTRY POINT
|
|
345
|
+
// ============================================================================
|
|
346
|
+
|
|
347
|
+
async function main() {
|
|
348
|
+
const processor = new WebhookProcessor();
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
await processor.initialize();
|
|
352
|
+
await processor.processWebhooks();
|
|
353
|
+
|
|
354
|
+
console.log('Webhook processing completed successfully');
|
|
355
|
+
process.exit(0);
|
|
356
|
+
} catch (error: any) {
|
|
357
|
+
console.error('Fatal error:', error);
|
|
358
|
+
process.exit(1);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Run if executed directly
|
|
363
|
+
if (require.main === module) {
|
|
364
|
+
main();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export { WebhookProcessor };
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Key Patterns Explained
|
|
373
|
+
|
|
374
|
+
### Pattern 1: Webhook Signature Validation
|
|
375
|
+
|
|
376
|
+
**Purpose**: Verify webhook authenticity using cryptographic signatures
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
// Initialize validator
|
|
380
|
+
const validator = new WebhookValidationService(
|
|
381
|
+
{ strictValidation: true }, // Enforce signature validation
|
|
382
|
+
logger
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
// Validate webhook
|
|
386
|
+
const result = await validator.validateWebhook(
|
|
387
|
+
rawBody, // Raw webhook body (string)
|
|
388
|
+
headers, // HTTP headers (contains signature)
|
|
389
|
+
publicKey // Public key from webhook provider
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
if (!result.isValid) {
|
|
393
|
+
throw new Error(`Validation failed: ${result.error}`);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Signature valid - safe to process
|
|
397
|
+
console.log(`Validated with ${result.algorithm}`);
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**Supported Algorithms**:
|
|
401
|
+
- `sha256`: HMAC-SHA256 signatures
|
|
402
|
+
- `rsa-sha256`: RSA-SHA256 signatures
|
|
403
|
+
- Auto-detection from headers
|
|
404
|
+
|
|
405
|
+
### Pattern 2: Webhook File Format
|
|
406
|
+
|
|
407
|
+
**With Headers** (for signature validation):
|
|
408
|
+
```
|
|
409
|
+
HEADERS
|
|
410
|
+
|
|
411
|
+
{"x-webhook-signature":"sha256=abc123...","content-type":"application/json"}
|
|
412
|
+
|
|
413
|
+
BODY
|
|
414
|
+
|
|
415
|
+
{"order_id":"ORD-123","customer":{"first_name":"John"...}}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Without Headers** (no validation):
|
|
419
|
+
```json
|
|
420
|
+
{"order_id":"ORD-123","customer":{"first_name":"John"...}}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Pattern 3: XML vs JSON Parsing
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
// Detect format and parse accordingly
|
|
427
|
+
let parsedPayload: any;
|
|
428
|
+
|
|
429
|
+
if (config.webhook.format === 'xml') {
|
|
430
|
+
parsedPayload = await xmlParser.parse(body, {
|
|
431
|
+
includeAttributes: true, // Parse @id, @type attributes
|
|
432
|
+
parseNumbers: true, // Auto-convert numbers
|
|
433
|
+
parseBooleans: true, // Auto-convert booleans
|
|
434
|
+
});
|
|
435
|
+
} else {
|
|
436
|
+
parsedPayload = await jsonParser.parse(body);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Now parsedPayload is a JavaScript object ready for mapping
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Pattern 4: GraphQL Mutation Mapping
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
// Configure mapping once
|
|
446
|
+
const mapping = {
|
|
447
|
+
mutation: 'createOrder',
|
|
448
|
+
sourceFormat: 'json',
|
|
449
|
+
arguments: {
|
|
450
|
+
input: {
|
|
451
|
+
ref: { source: 'order_id', required: true },
|
|
452
|
+
customer: {
|
|
453
|
+
firstName: { source: 'customer.first_name' },
|
|
454
|
+
email: { source: 'customer.email', transform: 'toLowerCase' },
|
|
455
|
+
},
|
|
456
|
+
items: {
|
|
457
|
+
_array: true,
|
|
458
|
+
source: 'line_items',
|
|
459
|
+
productRef: { source: 'sku' },
|
|
460
|
+
quantity: { source: 'quantity', transform: 'parseInt' },
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
// Map webhook data to GraphQL mutation
|
|
467
|
+
const mapper = new GraphQLMutationMapper(mapping, logger, { fluentClient: client });
|
|
468
|
+
const payload = await mapper.map(parsedPayload);
|
|
469
|
+
|
|
470
|
+
// Execute mutation
|
|
471
|
+
const result = await client.graphql(payload);
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Testing
|
|
477
|
+
|
|
478
|
+
### 1. Create Test Webhook Files
|
|
479
|
+
|
|
480
|
+
**Test Order (webhooks/incoming/order-001.json)**:
|
|
481
|
+
```json
|
|
482
|
+
{
|
|
483
|
+
"order_id": "TEST-001",
|
|
484
|
+
"customer": {
|
|
485
|
+
"first_name": "John",
|
|
486
|
+
"last_name": "Doe",
|
|
487
|
+
"email": "JOHN.DOE@EXAMPLE.COM"
|
|
488
|
+
},
|
|
489
|
+
"line_items": [
|
|
490
|
+
{"line_item_id": "1", "sku": "PROD-001", "quantity": 2, "unit_price": 29.99},
|
|
491
|
+
{"line_item_id": "2", "sku": "PROD-002", "quantity": 1, "unit_price": 49.99}
|
|
492
|
+
],
|
|
493
|
+
"total_amount": 109.97
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
**With Signature Validation (webhooks/incoming/order-002.json)**:
|
|
498
|
+
```
|
|
499
|
+
HEADERS
|
|
500
|
+
|
|
501
|
+
{"x-webhook-signature":"sha256=abc123def456...","content-type":"application/json"}
|
|
502
|
+
|
|
503
|
+
BODY
|
|
504
|
+
|
|
505
|
+
{"order_id":"TEST-002","customer":{"first_name":"Jane","last_name":"Smith","email":"jane@example.com"},"line_items":[{"line_item_id":"1","sku":"PROD-003","quantity":1,"unit_price":99.99}],"total_amount":99.99}
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### 2. Run Processor
|
|
509
|
+
|
|
510
|
+
```bash
|
|
511
|
+
# Process all webhooks in incoming directory
|
|
512
|
+
npm run dev
|
|
513
|
+
|
|
514
|
+
# Check processed webhooks
|
|
515
|
+
ls webhooks/processed/
|
|
516
|
+
|
|
517
|
+
# Check errors
|
|
518
|
+
ls webhooks/errors/
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### 3. Manual Testing
|
|
522
|
+
|
|
523
|
+
```bash
|
|
524
|
+
# Test single webhook
|
|
525
|
+
cat webhooks/incoming/order-001.json | node dist/webhook-processor.js
|
|
526
|
+
|
|
527
|
+
# Test with debug logging
|
|
528
|
+
LOG_LEVEL=debug npm run dev
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
## Deployment Options
|
|
534
|
+
|
|
535
|
+
### Option 1: Cron Job (Scheduled Processing)
|
|
536
|
+
|
|
537
|
+
```bash
|
|
538
|
+
# Run every 5 minutes
|
|
539
|
+
*/5 * * * * cd /opt/webhook-processor && npm start >> /var/log/webhook-processor.log 2>&1
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Option 2: Message Queue Consumer
|
|
543
|
+
|
|
544
|
+
```typescript
|
|
545
|
+
// Integrate with SQS, RabbitMQ, etc.
|
|
546
|
+
import { SQS } from '@aws-sdk/client-sqs';
|
|
547
|
+
|
|
548
|
+
const sqs = new SQS({ region: 'us-east-1' });
|
|
549
|
+
|
|
550
|
+
while (true) {
|
|
551
|
+
const messages = await sqs.receiveMessage({
|
|
552
|
+
QueueUrl: process.env.QUEUE_URL,
|
|
553
|
+
MaxNumberOfMessages: 10,
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
for (const message of messages.Messages || []) {
|
|
557
|
+
// Save to file
|
|
558
|
+
const fileName = `webhook-${Date.now()}.json`;
|
|
559
|
+
writeFileSync(
|
|
560
|
+
join(config.webhook.inputDir, fileName),
|
|
561
|
+
message.Body
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
// Process
|
|
565
|
+
await processor.processWebhooks();
|
|
566
|
+
|
|
567
|
+
// Delete from queue
|
|
568
|
+
await sqs.deleteMessage({
|
|
569
|
+
QueueUrl: process.env.QUEUE_URL,
|
|
570
|
+
ReceiptHandle: message.ReceiptHandle,
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
575
|
+
}
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
### Option 3: AWS Lambda with S3 Trigger
|
|
579
|
+
|
|
580
|
+
```typescript
|
|
581
|
+
import { S3Event } from 'aws-lambda';
|
|
582
|
+
|
|
583
|
+
export const handler = async (event: S3Event) => {
|
|
584
|
+
const processor = new WebhookProcessor();
|
|
585
|
+
await processor.initialize();
|
|
586
|
+
|
|
587
|
+
for (const record of event.Records) {
|
|
588
|
+
const bucket = record.s3.bucket.name;
|
|
589
|
+
const key = record.s3.object.key;
|
|
590
|
+
|
|
591
|
+
// Download from S3 to input directory
|
|
592
|
+
// Process
|
|
593
|
+
// Move to processed S3 folder
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
---
|
|
599
|
+
|
|
600
|
+
## Common Issues
|
|
601
|
+
|
|
602
|
+
### Issue 1: Signature Validation Fails
|
|
603
|
+
|
|
604
|
+
**Symptom**: `Webhook signature validation failed`
|
|
605
|
+
|
|
606
|
+
**Solution**:
|
|
607
|
+
```typescript
|
|
608
|
+
// Ensure raw body is used (no JSON parsing beforehand)
|
|
609
|
+
const rawBody = readFileSync(filePath, 'utf8');
|
|
610
|
+
|
|
611
|
+
// Check public key format
|
|
612
|
+
const publicKey = `-----BEGIN PUBLIC KEY-----
|
|
613
|
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
|
|
614
|
+
-----END PUBLIC KEY-----`;
|
|
615
|
+
|
|
616
|
+
// Verify header name matches webhook provider
|
|
617
|
+
const signatureHeader = headers['x-webhook-signature'] ||
|
|
618
|
+
headers['x-signature'] ||
|
|
619
|
+
headers['signature'];
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
### Issue 2: Mapping Errors
|
|
623
|
+
|
|
624
|
+
**Symptom**: `Required field 'ref' is missing`
|
|
625
|
+
|
|
626
|
+
**Solution**:
|
|
627
|
+
```typescript
|
|
628
|
+
// Debug parsed payload structure
|
|
629
|
+
console.log('Parsed payload:', JSON.stringify(parsedPayload, null, 2));
|
|
630
|
+
|
|
631
|
+
// Check source path matches actual structure
|
|
632
|
+
ref: { source: 'order_id' } // ✅ Correct if payload has {order_id: "..."}
|
|
633
|
+
ref: { source: 'orderId' } // ❌ Wrong if payload uses snake_case
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
### Issue 3: XML Attribute Not Found
|
|
637
|
+
|
|
638
|
+
**Symptom**: `Cannot read property '@id' of undefined`
|
|
639
|
+
|
|
640
|
+
**Solution**:
|
|
641
|
+
```typescript
|
|
642
|
+
// Enable attribute parsing
|
|
643
|
+
const parsedXml = await xmlParser.parse(body, {
|
|
644
|
+
includeAttributes: true, // REQUIRED for @attributes
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
// Access attributes with @ prefix
|
|
648
|
+
ref: { source: 'order.@id' } // For <order id="123">
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
## Related Guides
|
|
654
|
+
|
|
655
|
+
- **[SFTP XML → GraphQL](./sftp-xml-graphql.md)** - Similar pattern with SFTP polling
|
|
656
|
+
- **[S3 CSV → Batch API](./s3-csv-batch-api.md)** - Batch processing pattern
|
|
657
|
+
- **[GraphQL Mutation Mapping](../../02-CORE-GUIDES/mapping/graphql-mutation-mapping/)** - Complete mapping reference
|
|
658
|
+
- **[Webhook Validation](../../02-CORE-GUIDES/advanced-services/advanced-services-readme.md)** - Security best practices
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
|
|
662
|
+
## Production Checklist
|
|
663
|
+
|
|
664
|
+
- [ ] Webhook signature validation enabled
|
|
665
|
+
- [ ] Public key secured (secrets manager, not hardcoded)
|
|
666
|
+
- [ ] Error webhooks moved to error directory (not deleted)
|
|
667
|
+
- [ ] Processed webhooks archived with retention policy
|
|
668
|
+
- [ ] Monitoring configured (file counts, processing rate)
|
|
669
|
+
- [ ] Dead letter queue for failed webhooks
|
|
670
|
+
- [ ] Retry logic for transient failures
|
|
671
|
+
- [ ] Logging includes correlation IDs
|
|
672
|
+
- [ ] Alerts configured for high error rates
|
|
673
|
+
- [ ] Documentation updated with webhook format examples
|
|
674
|
+
|
|
675
|
+
---
|
|
676
|
+
|
|
677
|
+
**Next Steps**:
|
|
678
|
+
|
|
679
|
+
1. Customize `orderWebhookMapping` for your webhook format
|
|
680
|
+
2. Add more mapping configurations for different webhook types
|
|
681
|
+
3. Integrate with message queue (SQS, RabbitMQ, Kafka)
|
|
682
|
+
4. Set up monitoring and alerting
|
|
683
|
+
5. Deploy to production environment
|
|
684
|
+
|
|
685
|
+
**Support**:
|
|
686
|
+
|
|
687
|
+
- SDK Issues: https://github.com/fluentcommerce/fc-connect-sdk/issues
|
|
688
|
+
- Fluent Commerce API: https://docs.fluentcommerce.com
|