@fluentcommerce/fc-connect-sdk 0.1.54 → 0.1.56
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 +12 -0
- package/README.md +11 -0
- package/dist/cjs/clients/fluent-client.js +13 -6
- package/dist/cjs/utils/pagination-helpers.js +38 -2
- package/dist/cjs/versori/fluent-versori-client.js +11 -5
- package/dist/esm/clients/fluent-client.js +13 -6
- 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/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 -520
- 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
- package/docs/02-CORE-GUIDES/api-reference/cli-profile-integration.md +0 -377
|
@@ -1,824 +1,824 @@
|
|
|
1
|
-
---
|
|
2
|
-
template_id: tpl-webhook-rubix-order-update
|
|
3
|
-
canonical_filename: template-webhook-rubix-order-attribute-update.md
|
|
4
|
-
sdk_version: ^0.1.39
|
|
5
|
-
runtime: versori
|
|
6
|
-
direction: webhook
|
|
7
|
-
source: fluent-rubix-workflow
|
|
8
|
-
destination: fluent-graphql
|
|
9
|
-
entity: order
|
|
10
|
-
format: json
|
|
11
|
-
logging: versori
|
|
12
|
-
status: stable
|
|
13
|
-
features:
|
|
14
|
-
- webhook-validation
|
|
15
|
-
- fire-and-forget
|
|
16
|
-
- background-processing
|
|
17
|
-
- graphql-mutations
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
# Template: Rubix Order Webhook → Attribute Update
|
|
21
|
-
|
|
22
|
-
**SDK Version:** `^0.1.39` - `npm install @fluentcommerce/fc-connect-sdk@^0.1.39`
|
|
23
|
-
**Last Updated:** 2025-01-25
|
|
24
|
-
**Deployment Target:** Versori Platform
|
|
25
|
-
|
|
26
|
-
**Production Ready:**
|
|
27
|
-
- ✅ **Order-specific template** - Focused on attribute updates
|
|
28
|
-
- ✅ **Production-ready modular structure** - Separate workflows, services, types
|
|
29
|
-
- ✅ **GraphQL mutations** - Update order attributes
|
|
30
|
-
- ✅ **Lightweight** - No SFTP/XML complexity
|
|
31
|
-
- ✅ **Critical fixes applied** - retailerId validation
|
|
32
|
-
- ✅ **Comprehensive documentation** - Testing, troubleshooting, monitoring
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## Overview
|
|
37
|
-
|
|
38
|
-
Receive order webhooks from Rubix workflows and update order attributes via GraphQL mutations.
|
|
39
|
-
|
|
40
|
-
**Pattern:**
|
|
41
|
-
1. Validate webhook signature (~10-50ms)
|
|
42
|
-
2. Return HTTP 200 OK immediately (fast acknowledgment)
|
|
43
|
-
3. Background: Update order attributes via GraphQL mutation (~500ms)
|
|
44
|
-
|
|
45
|
-
**Business Use Case:**
|
|
46
|
-
- Rubix workflow sends webhook when order status changes
|
|
47
|
-
- Handler updates custom attributes for tracking
|
|
48
|
-
- Records processing timestamp
|
|
49
|
-
- No external system integration needed
|
|
50
|
-
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
## STEP 1: Understand This Template
|
|
54
|
-
|
|
55
|
-
### What This Template Does
|
|
56
|
-
|
|
57
|
-
**Core Functionality:**
|
|
58
|
-
- Receives ORDER webhooks from Fluent Commerce Rubix workflows
|
|
59
|
-
- Validates webhook signature for security
|
|
60
|
-
- Returns fast HTTP 200 OK response (<100ms)
|
|
61
|
-
- Background: Updates order attributes via GraphQL mutation
|
|
62
|
-
|
|
63
|
-
**Key SDK Components:**
|
|
64
|
-
- `WebhookValidationService` - Signature validation
|
|
65
|
-
- `createClient()` - Universal client factory
|
|
66
|
-
- `client.graphql()` - Execute mutations
|
|
67
|
-
- Native Versori `log` - Structured logging
|
|
68
|
-
|
|
69
|
-
**Critical Patterns:**
|
|
70
|
-
- **Sync + Fire-and-Forget**: Fast validation, background processing
|
|
71
|
-
- **Signature Validation**: Always validate webhook signatures
|
|
72
|
-
- **RetailerId Validation**: Check before `setRetailerId()`
|
|
73
|
-
- **GraphQL Mutations**: Update attributes, not create orders
|
|
74
|
-
|
|
75
|
-
### When to Use This Template
|
|
76
|
-
|
|
77
|
-
✅ **USE THIS TEMPLATE FOR:**
|
|
78
|
-
- Order status change webhooks from Rubix workflows
|
|
79
|
-
- Update custom order attributes for tracking
|
|
80
|
-
- Record processing timestamps
|
|
81
|
-
- Simple order updates without external systems
|
|
82
|
-
|
|
83
|
-
❌ **DON'T USE FOR:**
|
|
84
|
-
- Fulfilment webhooks (use fulfilment export template)
|
|
85
|
-
- Complex exports to SFTP/S3 (use extraction templates)
|
|
86
|
-
- Order creation (use GraphQL mutations directly)
|
|
87
|
-
- High-volume order ingestion (use Batch API templates)
|
|
88
|
-
|
|
89
|
-
### Business Use Cases
|
|
90
|
-
|
|
91
|
-
**Use Case 1: Order Processing Timestamp Tracking**
|
|
92
|
-
- Rubix workflow sends webhook when order moves to "PAID" status
|
|
93
|
-
- Handler updates `webhookProcessedAt` attribute
|
|
94
|
-
- Records processing timestamp
|
|
95
|
-
- No external system integration needed
|
|
96
|
-
|
|
97
|
-
**Use Case 2: Order Status Synchronization**
|
|
98
|
-
- Rubix workflow sends webhook on status change
|
|
99
|
-
- Handler updates custom attributes
|
|
100
|
-
- Enables cross-system status reporting
|
|
101
|
-
|
|
102
|
-
---
|
|
103
|
-
|
|
104
|
-
## STEP 2: Implementation Prompt for Claude Code
|
|
105
|
-
|
|
106
|
-
**Copy this prompt and send to Claude Code to generate the complete implementation:**
|
|
107
|
-
|
|
108
|
-
```
|
|
109
|
-
Create a Versori webhook handler for Fluent Commerce Rubix ORDER events with signature validation and GraphQL attribute updates.
|
|
110
|
-
|
|
111
|
-
REQUIREMENTS:
|
|
112
|
-
1. Runtime: Versori Platform (HTTP webhook)
|
|
113
|
-
2. Source: Rubix SendWebhook action (ORDER entity type only)
|
|
114
|
-
3. Destination: Fluent Commerce GraphQL API (mutations)
|
|
115
|
-
4. Format: JSON webhook payload
|
|
116
|
-
5. Entity: ORDER
|
|
117
|
-
|
|
118
|
-
KEY FEATURES:
|
|
119
|
-
- Webhook signature validation using WebhookValidationService
|
|
120
|
-
- Sync mode + fire-and-forget pattern for fast response
|
|
121
|
-
- Background processing: GraphQL mutation for attribute updates
|
|
122
|
-
- Modular architecture (workflows/, services/, types/)
|
|
123
|
-
- Native Versori logging (no LoggingService)
|
|
124
|
-
- Comprehensive error handling with SDK error types
|
|
125
|
-
- RetailerId validation before setRetailerId()
|
|
126
|
-
|
|
127
|
-
CRITICAL REQUIREMENTS:
|
|
128
|
-
1. Webhook Mode: response: { mode: 'sync' }
|
|
129
|
-
2. Signature Validation: WebhookValidationService with strictValidation: true
|
|
130
|
-
3. RetailerId Validation: Check exists before client.setRetailerId()
|
|
131
|
-
4. Background Processing: Fire-and-forget pattern (no await on background promise)
|
|
132
|
-
5. GraphQL Mutation: Update order attributes only (not creation)
|
|
133
|
-
6. Error Handling: Import and handle AuthenticationError, GraphQLError
|
|
134
|
-
7. Type Safety: Use Logger, VersoriContext types (not any)
|
|
135
|
-
|
|
136
|
-
SDK METHODS TO USE:
|
|
137
|
-
- import { Buffer } from 'node:buffer' (for consistency)
|
|
138
|
-
- createClient({ ...ctx, log })
|
|
139
|
-
- await client.setRetailerId(retailerId) (with validation first!)
|
|
140
|
-
- new WebhookValidationService({ strictValidation: true }, log)
|
|
141
|
-
- await validator.validateWebhook(rawBody, headers, publicKey)
|
|
142
|
-
- await client.graphql({ query, variables }) (mutation)
|
|
143
|
-
|
|
144
|
-
FORBIDDEN PATTERNS:
|
|
145
|
-
- ❌ LoggingService (use native log from context)
|
|
146
|
-
- ❌ await on background processing (use fire-and-forget)
|
|
147
|
-
- ❌ Missing retailerId validation before setRetailerId()
|
|
148
|
-
- ❌ Using 'any' type (use Logger, VersoriContext from SDK)
|
|
149
|
-
- ❌ Direct FluentClient instantiation (use createClient factory)
|
|
150
|
-
- ❌ Generic error handling (use SDK error types)
|
|
151
|
-
|
|
152
|
-
GRAPHQL MUTATION STRUCTURE:
|
|
153
|
-
mutation UpdateOrderAttributes($input: UpdateOrderInput!) {
|
|
154
|
-
updateOrder(input: $input) {
|
|
155
|
-
id
|
|
156
|
-
ref
|
|
157
|
-
attributes {
|
|
158
|
-
name
|
|
159
|
-
type
|
|
160
|
-
value
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
GENERATE:
|
|
166
|
-
1. package.json with dependencies
|
|
167
|
-
2. index.ts (entry point - exports workflow)
|
|
168
|
-
3. src/workflows/webhook/order-update-handler.ts (webhook entry)
|
|
169
|
-
4. src/services/order-mutation.service.ts (GraphQL mutations)
|
|
170
|
-
5. src/types/order.types.ts (TypeScript interfaces)
|
|
171
|
-
6. readme.md with setup instructions
|
|
172
|
-
|
|
173
|
-
Ensure all code is production-ready with proper error handling, signature validation, and type safety.
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
## STEP 3: Detailed Flow Documentation
|
|
179
|
-
|
|
180
|
-
### Complete Processing Flow
|
|
181
|
-
|
|
182
|
-
```
|
|
183
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
184
|
-
│ 1. RUBIX WEBHOOK RECEIVED │
|
|
185
|
-
│ POST https://{workspace}.versori.run/rubix-order │
|
|
186
|
-
│ Headers: fluent-signature │
|
|
187
|
-
│ Body: { entityRef: "ORD-001", entityType: "ORDER" } │
|
|
188
|
-
└────────────────────┬────────────────────────────────────────┘
|
|
189
|
-
│
|
|
190
|
-
▼
|
|
191
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
192
|
-
│ 2. SIGNATURE VALIDATION (Synchronous, ~10-50ms) │
|
|
193
|
-
│ - Extract rawBody and headers │
|
|
194
|
-
│ - Get fluentPublicKey from activation │
|
|
195
|
-
│ - WebhookValidationService.validateWebhook() │
|
|
196
|
-
│ - Return 400 if invalid │
|
|
197
|
-
└────────────────────┬────────────────────────────────────────┘
|
|
198
|
-
│
|
|
199
|
-
▼
|
|
200
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
201
|
-
│ 3. PAYLOAD VALIDATION (Synchronous, ~5ms) │
|
|
202
|
-
│ - Check entityRef exists │
|
|
203
|
-
│ - Check entityType === 'ORDER' │
|
|
204
|
-
│ - Check retailerId exists │
|
|
205
|
-
│ - Return 400 if missing required fields │
|
|
206
|
-
└────────────────────┬────────────────────────────────────────┘
|
|
207
|
-
│
|
|
208
|
-
▼
|
|
209
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
210
|
-
│ 4. HTTP 200 OK RESPONSE (Total: ~50ms) │
|
|
211
|
-
│ - Return success immediately │
|
|
212
|
-
│ - Background promise continues (fire-and-forget) │
|
|
213
|
-
└────────────────────┬────────────────────────────────────────┘
|
|
214
|
-
│
|
|
215
|
-
▼ (Background - doesn't block response)
|
|
216
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
217
|
-
│ 5. BACKGROUND PROCESSING (~300-500ms) │
|
|
218
|
-
│ ┌─────────────────────────────────────────────────────┐ │
|
|
219
|
-
│ │ 5a. Initialize Fluent Client (~100ms) │ │
|
|
220
|
-
│ │ - createClient({ ...ctx, log }) │ │
|
|
221
|
-
│ │ - Validate retailerId exists │ │
|
|
222
|
-
│ │ - client.setRetailerId(retailerId) │ │
|
|
223
|
-
│ └─────────────────────────────────────────────────────┘ │
|
|
224
|
-
│ ┌─────────────────────────────────────────────────────┐ │
|
|
225
|
-
│ │ 5b. Update Order Attributes (~200-400ms) │ │
|
|
226
|
-
│ │ - GraphQL mutation with attributes array │ │
|
|
227
|
-
│ │ - Handle GraphQLError if mutation fails │ │
|
|
228
|
-
│ └─────────────────────────────────────────────────────┘ │
|
|
229
|
-
│ ┌─────────────────────────────────────────────────────┐ │
|
|
230
|
-
│ │ 5c. Log Completion │ │
|
|
231
|
-
│ │ - Success: Log order ref and duration │ │
|
|
232
|
-
│ │ - Failure: Log error with category │ │
|
|
233
|
-
│ └─────────────────────────────────────────────────────┘ │
|
|
234
|
-
└─────────────────────────────────────────────────────────────┘
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### Response Timing
|
|
238
|
-
|
|
239
|
-
| Stage | Timing | Blocking |
|
|
240
|
-
|-------|--------|----------|
|
|
241
|
-
| **Signature Validation** | ~10-50ms | ✅ Yes (blocks response) |
|
|
242
|
-
| **Payload Validation** | ~5ms | ✅ Yes (blocks response) |
|
|
243
|
-
| **HTTP 200 Response** | ~50-100ms | ✅ Fast acknowledgment |
|
|
244
|
-
| **GraphQL Mutation** | ~200-400ms | ❌ No (background) |
|
|
245
|
-
| **Total Background** | ~300-500ms | ❌ Fire-and-forget |
|
|
246
|
-
|
|
247
|
-
---
|
|
248
|
-
|
|
249
|
-
## STEP 4: Production Modular Structure
|
|
250
|
-
|
|
251
|
-
### Complete Project Structure
|
|
252
|
-
|
|
253
|
-
```
|
|
254
|
-
rubix-order-update/
|
|
255
|
-
├── package.json # Dependencies and Versori config
|
|
256
|
-
├── tsconfig.json # TypeScript configuration
|
|
257
|
-
├── readme.md # Setup and deployment instructions
|
|
258
|
-
├── index.ts # Entry point - exports workflow
|
|
259
|
-
└── src/
|
|
260
|
-
├── workflows/
|
|
261
|
-
│ └── webhook/
|
|
262
|
-
│ └── order-update-handler.ts # Webhook entry point
|
|
263
|
-
│
|
|
264
|
-
├── services/
|
|
265
|
-
│ └── order-mutation.service.ts # GraphQL mutations
|
|
266
|
-
│
|
|
267
|
-
└── types/
|
|
268
|
-
└── order.types.ts # TypeScript interfaces
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
---
|
|
272
|
-
|
|
273
|
-
## SDK Imports (Verified - Versori Optimized)
|
|
274
|
-
|
|
275
|
-
```typescript
|
|
276
|
-
// CRITICAL: Buffer import for consistency (even though not used for SFTP)
|
|
277
|
-
import { Buffer } from 'node:buffer';
|
|
278
|
-
|
|
279
|
-
// FC Connect SDK imports
|
|
280
|
-
import {
|
|
281
|
-
createClient,
|
|
282
|
-
WebhookValidationService,
|
|
283
|
-
AuthenticationError,
|
|
284
|
-
GraphQLError,
|
|
285
|
-
type Logger,
|
|
286
|
-
type VersoriContext,
|
|
287
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
288
|
-
|
|
289
|
-
// Versori platform imports
|
|
290
|
-
import { webhook, http } from '@versori/run';
|
|
291
|
-
import type { Context } from '@versori/run';
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
---
|
|
295
|
-
|
|
296
|
-
## Configuration
|
|
297
|
-
|
|
298
|
-
### Activation Variables
|
|
299
|
-
|
|
300
|
-
```json
|
|
301
|
-
{
|
|
302
|
-
"fluentPublicKey": "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----"
|
|
303
|
-
}
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
### Connections
|
|
307
|
-
|
|
308
|
-
| Connection | Description | Required |
|
|
309
|
-
|------------|-------------|----------|
|
|
310
|
-
| `fluent_commerce` | OAuth2 connection to Fluent Commerce | Yes |
|
|
311
|
-
|
|
312
|
-
---
|
|
313
|
-
|
|
314
|
-
## Complete Working Code
|
|
315
|
-
|
|
316
|
-
### 1. Entry Point (index.ts)
|
|
317
|
-
|
|
318
|
-
```typescript
|
|
319
|
-
/**
|
|
320
|
-
* Entry point - Export webhook workflow for Versori platform
|
|
321
|
-
*/
|
|
322
|
-
|
|
323
|
-
export { rubixOrderUpdateHandler } from './src/workflows/webhook/order-update-handler';
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
### 2. Webhook Handler (src/workflows/webhook/order-update-handler.ts)
|
|
327
|
-
|
|
328
|
-
```typescript
|
|
329
|
-
/**
|
|
330
|
-
* Rubix Order Webhook Handler → Attribute Update
|
|
331
|
-
*
|
|
332
|
-
* Direction: Fluent Rubix Workflow (ORDER) → Versori → GraphQL Mutation
|
|
333
|
-
*/
|
|
334
|
-
|
|
335
|
-
import { Buffer } from 'node:buffer';
|
|
336
|
-
import { webhook } from '@versori/run';
|
|
337
|
-
import type { Context } from '@versori/run';
|
|
338
|
-
import {
|
|
339
|
-
WebhookValidationService,
|
|
340
|
-
type Logger,
|
|
341
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
342
|
-
import { processOrderUpdate } from '../../services/order-mutation.service';
|
|
343
|
-
|
|
344
|
-
export const rubixOrderUpdateHandler = webhook('rubix-order', {
|
|
345
|
-
response: { mode: 'sync' }, // ✅ Sync mode: response sent when handler returns
|
|
346
|
-
}, async (ctx: Context<any>) => {
|
|
347
|
-
const { log, activation } = ctx;
|
|
348
|
-
|
|
349
|
-
log.info('🚀 [WEBHOOK] Received Rubix order webhook', {
|
|
350
|
-
timestamp: new Date().toISOString(),
|
|
351
|
-
correlationId: activation?.id,
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
// ========================================
|
|
355
|
-
// STEP 1: VALIDATE SIGNATURE (Synchronous)
|
|
356
|
-
// ========================================
|
|
357
|
-
|
|
358
|
-
const publicKey = activation.getVariable('fluentPublicKey');
|
|
359
|
-
if (!publicKey) {
|
|
360
|
-
log.error('❌ [WEBHOOK] Missing fluentPublicKey configuration');
|
|
361
|
-
return {
|
|
362
|
-
status: 500,
|
|
363
|
-
body: {
|
|
364
|
-
success: false,
|
|
365
|
-
error: 'Missing fluentPublicKey configuration',
|
|
366
|
-
recommendation: 'Configure fluentPublicKey in activation variables',
|
|
367
|
-
},
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// Extract raw body (Versori provides parsed data in ctx.data)
|
|
372
|
-
const rawBody = typeof ctx.data === 'string' ? ctx.data : JSON.stringify(ctx.data);
|
|
373
|
-
|
|
374
|
-
// Extract headers from request (ctx.request() is a function call)
|
|
375
|
-
const req = ctx.request?.() || {};
|
|
376
|
-
const headersObj = req.headers || {};
|
|
377
|
-
const headers = headersObj instanceof Headers
|
|
378
|
-
? Object.fromEntries(headersObj.entries())
|
|
379
|
-
: headersObj;
|
|
380
|
-
|
|
381
|
-
log.info('🔍 [WEBHOOK] Validating webhook signature');
|
|
382
|
-
|
|
383
|
-
const validator = new WebhookValidationService({ strictValidation: true }, log as Logger);
|
|
384
|
-
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
385
|
-
|
|
386
|
-
if (!result.isValid) {
|
|
387
|
-
log.error('❌ [WEBHOOK] Invalid webhook signature', {
|
|
388
|
-
error: result.error,
|
|
389
|
-
algorithm: result.algorithm,
|
|
390
|
-
});
|
|
391
|
-
return {
|
|
392
|
-
status: 401,
|
|
393
|
-
body: {
|
|
394
|
-
success: false,
|
|
395
|
-
error: 'Invalid webhook signature',
|
|
396
|
-
recommendation: 'Verify fluentPublicKey matches Fluent environment',
|
|
397
|
-
},
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
log.info('✅ [WEBHOOK] Signature validated successfully', {
|
|
402
|
-
algorithm: result.algorithm,
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
// ========================================
|
|
406
|
-
// STEP 2: VALIDATE PAYLOAD (Synchronous)
|
|
407
|
-
// ========================================
|
|
408
|
-
|
|
409
|
-
const payload = typeof ctx.data === 'string' ? JSON.parse(ctx.data) : ctx.data;
|
|
410
|
-
const { entityRef, entityType, retailerId } = payload;
|
|
411
|
-
|
|
412
|
-
if (!entityRef) {
|
|
413
|
-
log.error('❌ [WEBHOOK] Missing entityRef in payload', { payload });
|
|
414
|
-
return {
|
|
415
|
-
status: 400,
|
|
416
|
-
body: {
|
|
417
|
-
success: false,
|
|
418
|
-
error: 'Missing entityRef in payload',
|
|
419
|
-
recommendation: 'Ensure Rubix SendWebhook includes order ref',
|
|
420
|
-
},
|
|
421
|
-
};
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if (entityType !== 'ORDER') {
|
|
425
|
-
log.error('❌ [WEBHOOK] Invalid entity type', { entityType, expected: 'ORDER' });
|
|
426
|
-
return {
|
|
427
|
-
status: 400,
|
|
428
|
-
body: {
|
|
429
|
-
success: false,
|
|
430
|
-
error: `Invalid entity type: ${entityType}. Expected: ORDER`,
|
|
431
|
-
recommendation: 'Use fulfilment-export template for FULFILMENT webhooks',
|
|
432
|
-
},
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
if (!retailerId) {
|
|
437
|
-
log.error('❌ [WEBHOOK] Missing retailerId in payload', { payload });
|
|
438
|
-
return {
|
|
439
|
-
status: 400,
|
|
440
|
-
body: {
|
|
441
|
-
success: false,
|
|
442
|
-
error: 'Missing retailerId in payload',
|
|
443
|
-
recommendation: 'Ensure Rubix SendWebhook includes retailerId',
|
|
444
|
-
},
|
|
445
|
-
};
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
log.info('✅ [WEBHOOK] Validation passed, starting background processing', {
|
|
449
|
-
entityRef,
|
|
450
|
-
entityType,
|
|
451
|
-
retailerId,
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
// ========================================
|
|
455
|
-
// STEP 3: FIRE-AND-FORGET BACKGROUND PROCESSING
|
|
456
|
-
// ========================================
|
|
457
|
-
|
|
458
|
-
// ✅ Fire-and-forget: Start background processing WITHOUT await
|
|
459
|
-
// The promise continues execution after we return the response
|
|
460
|
-
processOrderUpdate(ctx, payload)
|
|
461
|
-
.then(() => {
|
|
462
|
-
log.info('✅ [BACKGROUND] Order updated successfully', {
|
|
463
|
-
orderRef: entityRef,
|
|
464
|
-
});
|
|
465
|
-
})
|
|
466
|
-
.catch((error: unknown) => {
|
|
467
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
468
|
-
log.error('❌ [BACKGROUND] Order update failed', {
|
|
469
|
-
orderRef: entityRef,
|
|
470
|
-
error: errorMessage,
|
|
471
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
472
|
-
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
473
|
-
});
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
// Return immediately (response sent with this return value)
|
|
477
|
-
return {
|
|
478
|
-
status: 200,
|
|
479
|
-
body: {
|
|
480
|
-
success: true,
|
|
481
|
-
entityType,
|
|
482
|
-
orderRef: entityRef,
|
|
483
|
-
message: 'Webhook validated and update started in background',
|
|
484
|
-
timestamp: new Date().toISOString(),
|
|
485
|
-
correlationId: activation?.id,
|
|
486
|
-
},
|
|
487
|
-
};
|
|
488
|
-
});
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
### 3. Order Mutation Service (src/services/order-mutation.service.ts)
|
|
492
|
-
|
|
493
|
-
```typescript
|
|
494
|
-
/**
|
|
495
|
-
* Order Mutation Service
|
|
496
|
-
*
|
|
497
|
-
* Handles GraphQL mutations for order attribute updates
|
|
498
|
-
*/
|
|
499
|
-
|
|
500
|
-
import {
|
|
501
|
-
createClient,
|
|
502
|
-
AuthenticationError,
|
|
503
|
-
GraphQLError,
|
|
504
|
-
type Logger,
|
|
505
|
-
type VersoriContext,
|
|
506
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
507
|
-
import type { RubixOrderPayload } from '../types/order.types';
|
|
508
|
-
|
|
509
|
-
const UPDATE_ORDER_MUTATION = `
|
|
510
|
-
mutation UpdateOrderAttributes($input: UpdateOrderInput!) {
|
|
511
|
-
updateOrder(input: $input) {
|
|
512
|
-
id
|
|
513
|
-
ref
|
|
514
|
-
attributes {
|
|
515
|
-
name
|
|
516
|
-
type
|
|
517
|
-
value
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
`;
|
|
522
|
-
|
|
523
|
-
export async function processOrderUpdate(
|
|
524
|
-
ctx: VersoriContext,
|
|
525
|
-
payload: RubixOrderPayload
|
|
526
|
-
): Promise<void> {
|
|
527
|
-
const log = ctx.log as Logger;
|
|
528
|
-
const { entityRef, retailerId } = payload;
|
|
529
|
-
|
|
530
|
-
const processingStartTime = Date.now();
|
|
531
|
-
|
|
532
|
-
log.info('🔄 [BACKGROUND] Starting order attribute update', {
|
|
533
|
-
timestamp: new Date().toISOString(),
|
|
534
|
-
orderRef: entityRef,
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
try {
|
|
538
|
-
// ========================================
|
|
539
|
-
// STEP 1: VALIDATE RETAILER ID
|
|
540
|
-
// ========================================
|
|
541
|
-
|
|
542
|
-
if (!retailerId) {
|
|
543
|
-
log.error('❌ [BACKGROUND] Missing retailerId in payload', {
|
|
544
|
-
orderRef: entityRef,
|
|
545
|
-
});
|
|
546
|
-
throw new Error('retailerId is required for GraphQL operations');
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// ========================================
|
|
550
|
-
// STEP 2: INITIALIZE FLUENT CLIENT
|
|
551
|
-
// ========================================
|
|
552
|
-
|
|
553
|
-
const client = await createClient({ ...ctx, log });
|
|
554
|
-
await client.setRetailerId(retailerId);
|
|
555
|
-
|
|
556
|
-
// ========================================
|
|
557
|
-
// STEP 3: UPDATE ORDER ATTRIBUTES
|
|
558
|
-
// ========================================
|
|
559
|
-
|
|
560
|
-
log.info('📝 [BACKGROUND] Updating order attributes', { orderRef: entityRef });
|
|
561
|
-
|
|
562
|
-
await client.graphql({
|
|
563
|
-
query: UPDATE_ORDER_MUTATION,
|
|
564
|
-
variables: {
|
|
565
|
-
input: {
|
|
566
|
-
ref: entityRef,
|
|
567
|
-
attributes: [
|
|
568
|
-
{
|
|
569
|
-
name: 'webhookProcessedAt',
|
|
570
|
-
type: 'STRING',
|
|
571
|
-
value: new Date().toISOString(),
|
|
572
|
-
},
|
|
573
|
-
{
|
|
574
|
-
name: 'webhookSource',
|
|
575
|
-
type: 'STRING',
|
|
576
|
-
value: 'rubix-workflow',
|
|
577
|
-
},
|
|
578
|
-
],
|
|
579
|
-
},
|
|
580
|
-
},
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
const duration = Date.now() - processingStartTime;
|
|
584
|
-
log.info('✅ [BACKGROUND] Order attributes updated successfully', {
|
|
585
|
-
orderRef: entityRef,
|
|
586
|
-
duration: `${duration}ms`,
|
|
587
|
-
});
|
|
588
|
-
} catch (error: unknown) {
|
|
589
|
-
let errorMessage = 'Unknown error';
|
|
590
|
-
let recommendation: string | undefined;
|
|
591
|
-
let errorCategory = 'UNKNOWN';
|
|
592
|
-
|
|
593
|
-
if (error instanceof GraphQLError) {
|
|
594
|
-
errorMessage = error.message;
|
|
595
|
-
recommendation = 'Review GraphQL mutation syntax and field permissions';
|
|
596
|
-
errorCategory = 'GRAPHQL';
|
|
597
|
-
} else if (error instanceof AuthenticationError) {
|
|
598
|
-
errorMessage = error.message;
|
|
599
|
-
recommendation = 'Verify Fluent Commerce credentials and retailerId';
|
|
600
|
-
errorCategory = 'AUTHENTICATION';
|
|
601
|
-
} else if (error instanceof Error) {
|
|
602
|
-
errorMessage = error.message;
|
|
603
|
-
} else {
|
|
604
|
-
errorMessage = String(error);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
log.error('❌ [BACKGROUND] Order update failed', {
|
|
608
|
-
orderRef: entityRef,
|
|
609
|
-
error: errorMessage,
|
|
610
|
-
errorCategory,
|
|
611
|
-
recommendation,
|
|
612
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
613
|
-
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
614
|
-
});
|
|
615
|
-
|
|
616
|
-
throw error;
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
```
|
|
620
|
-
|
|
621
|
-
### 4. Type Definitions (src/types/order.types.ts)
|
|
622
|
-
|
|
623
|
-
```typescript
|
|
624
|
-
/**
|
|
625
|
-
* Type definitions for Rubix order webhooks
|
|
626
|
-
*/
|
|
627
|
-
|
|
628
|
-
export interface RubixOrderPayload {
|
|
629
|
-
id: string;
|
|
630
|
-
name: string;
|
|
631
|
-
accountId: string;
|
|
632
|
-
retailerId: string;
|
|
633
|
-
entityRef: string;
|
|
634
|
-
entityType: 'ORDER';
|
|
635
|
-
entityStatus?: string;
|
|
636
|
-
type: 'NORMAL' | string;
|
|
637
|
-
rootEntityId?: string;
|
|
638
|
-
rootEntityType?: string;
|
|
639
|
-
rootEntityRef?: string;
|
|
640
|
-
attributes?: Record<string, any>;
|
|
641
|
-
context?: Record<string, any>;
|
|
642
|
-
}
|
|
643
|
-
```
|
|
644
|
-
|
|
645
|
-
### 5. Package Configuration (package.json)
|
|
646
|
-
|
|
647
|
-
```json
|
|
648
|
-
{
|
|
649
|
-
"name": "rubix-order-update",
|
|
650
|
-
"version": "2.0.0",
|
|
651
|
-
"description": "Rubix order webhook handler with attribute updates",
|
|
652
|
-
"type": "module",
|
|
653
|
-
"main": "index.ts",
|
|
654
|
-
"scripts": {
|
|
655
|
-
"dev": "versori dev",
|
|
656
|
-
"build": "versori build",
|
|
657
|
-
"deploy": "versori deploy",
|
|
658
|
-
"test": "jest",
|
|
659
|
-
"lint": "eslint src/**/*.ts"
|
|
660
|
-
},
|
|
661
|
-
"dependencies": {
|
|
662
|
-
"@fluentcommerce/fc-connect-sdk": "^0.1.39",
|
|
663
|
-
"@versori/run": "latest"
|
|
664
|
-
},
|
|
665
|
-
"devDependencies": {
|
|
666
|
-
"@types/node": "^20.0.0",
|
|
667
|
-
"typescript": "^5.0.0",
|
|
668
|
-
"jest": "^29.0.0",
|
|
669
|
-
"@types/jest": "^29.0.0",
|
|
670
|
-
"eslint": "^8.0.0"
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
---
|
|
676
|
-
|
|
677
|
-
## Key Patterns Explained
|
|
678
|
-
|
|
679
|
-
### Pattern 1: RetailerId Validation Before setRetailerId()
|
|
680
|
-
|
|
681
|
-
```typescript
|
|
682
|
-
// ✅ CORRECT - Validate before use
|
|
683
|
-
const retailerId = payload.retailerId;
|
|
684
|
-
if (!retailerId) {
|
|
685
|
-
log.error('❌ [BACKGROUND] Missing retailerId in payload');
|
|
686
|
-
throw new Error('retailerId is required for GraphQL operations');
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
const client = await createClient({ ...ctx, log });
|
|
690
|
-
await client.setRetailerId(retailerId);
|
|
691
|
-
```
|
|
692
|
-
|
|
693
|
-
**Why:** Missing retailerId causes 401 authentication errors with unclear messages.
|
|
694
|
-
|
|
695
|
-
### Pattern 2: SDK Error Type Handling
|
|
696
|
-
|
|
697
|
-
```typescript
|
|
698
|
-
// ✅ CORRECT - Handle SDK error types
|
|
699
|
-
catch (error: unknown) {
|
|
700
|
-
if (error instanceof GraphQLError) {
|
|
701
|
-
errorCategory = 'GRAPHQL';
|
|
702
|
-
recommendation = 'Review mutation syntax';
|
|
703
|
-
} else if (error instanceof AuthenticationError) {
|
|
704
|
-
errorCategory = 'AUTHENTICATION';
|
|
705
|
-
recommendation = 'Verify credentials';
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
```
|
|
709
|
-
|
|
710
|
-
**Why:** SDK errors include recommendations for resolution.
|
|
711
|
-
|
|
712
|
-
---
|
|
713
|
-
|
|
714
|
-
## Testing
|
|
715
|
-
|
|
716
|
-
### 1. Unit Tests
|
|
717
|
-
|
|
718
|
-
```typescript
|
|
719
|
-
// src/services/__tests__/order-mutation.test.ts
|
|
720
|
-
import { processOrderUpdate } from '../order-mutation.service';
|
|
721
|
-
|
|
722
|
-
describe('Order Mutation Service', () => {
|
|
723
|
-
test('should update order successfully', async () => {
|
|
724
|
-
const mockCtx = {
|
|
725
|
-
log: { info: jest.fn(), error: jest.fn() },
|
|
726
|
-
activation: { getVariable: jest.fn() },
|
|
727
|
-
};
|
|
728
|
-
|
|
729
|
-
const payload = {
|
|
730
|
-
entityRef: 'ORD-001',
|
|
731
|
-
entityType: 'ORDER',
|
|
732
|
-
retailerId: '1',
|
|
733
|
-
};
|
|
734
|
-
|
|
735
|
-
await expect(processOrderUpdate(mockCtx, payload)).resolves.not.toThrow();
|
|
736
|
-
});
|
|
737
|
-
});
|
|
738
|
-
```
|
|
739
|
-
|
|
740
|
-
### 2. Integration Test
|
|
741
|
-
|
|
742
|
-
```bash
|
|
743
|
-
# Test webhook with valid signature
|
|
744
|
-
curl -X POST https://workspace.versori.run/rubix-order \
|
|
745
|
-
-H "Content-Type: application/json" \
|
|
746
|
-
-H "fluent-signature: <valid-signature>" \
|
|
747
|
-
-d '{
|
|
748
|
-
"entityType": "ORDER",
|
|
749
|
-
"entityRef": "ORD-001",
|
|
750
|
-
"retailerId": "1"
|
|
751
|
-
}'
|
|
752
|
-
|
|
753
|
-
# Expected: 200 OK with { success: true, message: "..." }
|
|
754
|
-
```
|
|
755
|
-
|
|
756
|
-
---
|
|
757
|
-
|
|
758
|
-
## Production Checklist
|
|
759
|
-
|
|
760
|
-
- [ ] Configure `fluentPublicKey` activation variable
|
|
761
|
-
- [ ] Configure Fluent Commerce connection in Versori
|
|
762
|
-
- [ ] Create Rubix workflow with SendWebhook action (ORDER events)
|
|
763
|
-
- [ ] Build Event payload with entityRef, entityType, retailerId
|
|
764
|
-
- [ ] Test webhook signature validation
|
|
765
|
-
- [ ] Test GraphQL mutation with real order ref
|
|
766
|
-
- [ ] Test error scenarios (invalid signature, missing retailerId)
|
|
767
|
-
- [ ] Monitor response times (<100ms for validation)
|
|
768
|
-
- [ ] Set up alerts for webhook/mutation failures
|
|
769
|
-
- [ ] Document custom attributes being set
|
|
770
|
-
|
|
771
|
-
---
|
|
772
|
-
|
|
773
|
-
## Troubleshooting Guide
|
|
774
|
-
|
|
775
|
-
| Issue | Cause | Solution |
|
|
776
|
-
|-------|-------|----------|
|
|
777
|
-
| "Invalid webhook signature" | Wrong public key | Verify fluentPublicKey matches Fluent environment |
|
|
778
|
-
| "Missing retailerId" | Rubix payload incomplete | Add retailerId to Rubix Event payload builder |
|
|
779
|
-
| "GraphQL mutation failed" | Invalid permissions | Verify user has UPDATE_ORDER permission |
|
|
780
|
-
| "401 authentication error" | Missing retailerId | Ensure retailerId validation before setRetailerId() |
|
|
781
|
-
|
|
782
|
-
---
|
|
783
|
-
|
|
784
|
-
## Monitoring & Alerting
|
|
785
|
-
|
|
786
|
-
### Success Response
|
|
787
|
-
|
|
788
|
-
```json
|
|
789
|
-
{
|
|
790
|
-
"success": true,
|
|
791
|
-
"entityType": "ORDER",
|
|
792
|
-
"orderRef": "ORD-001",
|
|
793
|
-
"message": "Webhook validated and update started in background",
|
|
794
|
-
"timestamp": "2025-01-25T14:30:00.000Z",
|
|
795
|
-
"correlationId": "act_123",
|
|
796
|
-
"processingMode": "async"
|
|
797
|
-
}
|
|
798
|
-
```
|
|
799
|
-
|
|
800
|
-
### Key Metrics
|
|
801
|
-
|
|
802
|
-
```typescript
|
|
803
|
-
const METRICS = {
|
|
804
|
-
signatureValidationMs: 45,
|
|
805
|
-
totalResponseTimeMs: 50,
|
|
806
|
-
graphqlMutationMs: 250,
|
|
807
|
-
totalBackgroundMs: 350,
|
|
808
|
-
};
|
|
809
|
-
```
|
|
810
|
-
|
|
811
|
-
---
|
|
812
|
-
|
|
813
|
-
## Related Documentation
|
|
814
|
-
|
|
815
|
-
- [Fulfilment Export Template](./template-webhook-rubix-fulfilment-to-sftp-xml.md) - For FULFILMENT webhooks
|
|
816
|
-
- [Webhook Validation Guide](../../../../02-CORE-GUIDES/webhook-validation/)
|
|
817
|
-
- [Versori Platform Guide](../../../../04-REFERENCE/platforms/versori/)
|
|
818
|
-
|
|
819
|
-
---
|
|
820
|
-
|
|
821
|
-
**Pattern**: Order webhook → GraphQL mutation → Attribute update
|
|
822
|
-
**✅ Production-Ready**: Lightweight template with all critical fixes applied
|
|
823
|
-
**Key Learning**: Always validate retailerId, handle SDK errors, fire-and-forget pattern
|
|
824
|
-
**Critical**: RetailerId validation, proper error categorization, fast response
|
|
1
|
+
---
|
|
2
|
+
template_id: tpl-webhook-rubix-order-update
|
|
3
|
+
canonical_filename: template-webhook-rubix-order-attribute-update.md
|
|
4
|
+
sdk_version: ^0.1.39
|
|
5
|
+
runtime: versori
|
|
6
|
+
direction: webhook
|
|
7
|
+
source: fluent-rubix-workflow
|
|
8
|
+
destination: fluent-graphql
|
|
9
|
+
entity: order
|
|
10
|
+
format: json
|
|
11
|
+
logging: versori
|
|
12
|
+
status: stable
|
|
13
|
+
features:
|
|
14
|
+
- webhook-validation
|
|
15
|
+
- fire-and-forget
|
|
16
|
+
- background-processing
|
|
17
|
+
- graphql-mutations
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# Template: Rubix Order Webhook → Attribute Update
|
|
21
|
+
|
|
22
|
+
**SDK Version:** `^0.1.39` - `npm install @fluentcommerce/fc-connect-sdk@^0.1.39`
|
|
23
|
+
**Last Updated:** 2025-01-25
|
|
24
|
+
**Deployment Target:** Versori Platform
|
|
25
|
+
|
|
26
|
+
**Production Ready:**
|
|
27
|
+
- ✅ **Order-specific template** - Focused on attribute updates
|
|
28
|
+
- ✅ **Production-ready modular structure** - Separate workflows, services, types
|
|
29
|
+
- ✅ **GraphQL mutations** - Update order attributes
|
|
30
|
+
- ✅ **Lightweight** - No SFTP/XML complexity
|
|
31
|
+
- ✅ **Critical fixes applied** - retailerId validation
|
|
32
|
+
- ✅ **Comprehensive documentation** - Testing, troubleshooting, monitoring
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Overview
|
|
37
|
+
|
|
38
|
+
Receive order webhooks from Rubix workflows and update order attributes via GraphQL mutations.
|
|
39
|
+
|
|
40
|
+
**Pattern:**
|
|
41
|
+
1. Validate webhook signature (~10-50ms)
|
|
42
|
+
2. Return HTTP 200 OK immediately (fast acknowledgment)
|
|
43
|
+
3. Background: Update order attributes via GraphQL mutation (~500ms)
|
|
44
|
+
|
|
45
|
+
**Business Use Case:**
|
|
46
|
+
- Rubix workflow sends webhook when order status changes
|
|
47
|
+
- Handler updates custom attributes for tracking
|
|
48
|
+
- Records processing timestamp
|
|
49
|
+
- No external system integration needed
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## STEP 1: Understand This Template
|
|
54
|
+
|
|
55
|
+
### What This Template Does
|
|
56
|
+
|
|
57
|
+
**Core Functionality:**
|
|
58
|
+
- Receives ORDER webhooks from Fluent Commerce Rubix workflows
|
|
59
|
+
- Validates webhook signature for security
|
|
60
|
+
- Returns fast HTTP 200 OK response (<100ms)
|
|
61
|
+
- Background: Updates order attributes via GraphQL mutation
|
|
62
|
+
|
|
63
|
+
**Key SDK Components:**
|
|
64
|
+
- `WebhookValidationService` - Signature validation
|
|
65
|
+
- `createClient()` - Universal client factory
|
|
66
|
+
- `client.graphql()` - Execute mutations
|
|
67
|
+
- Native Versori `log` - Structured logging
|
|
68
|
+
|
|
69
|
+
**Critical Patterns:**
|
|
70
|
+
- **Sync + Fire-and-Forget**: Fast validation, background processing
|
|
71
|
+
- **Signature Validation**: Always validate webhook signatures
|
|
72
|
+
- **RetailerId Validation**: Check before `setRetailerId()`
|
|
73
|
+
- **GraphQL Mutations**: Update attributes, not create orders
|
|
74
|
+
|
|
75
|
+
### When to Use This Template
|
|
76
|
+
|
|
77
|
+
✅ **USE THIS TEMPLATE FOR:**
|
|
78
|
+
- Order status change webhooks from Rubix workflows
|
|
79
|
+
- Update custom order attributes for tracking
|
|
80
|
+
- Record processing timestamps
|
|
81
|
+
- Simple order updates without external systems
|
|
82
|
+
|
|
83
|
+
❌ **DON'T USE FOR:**
|
|
84
|
+
- Fulfilment webhooks (use fulfilment export template)
|
|
85
|
+
- Complex exports to SFTP/S3 (use extraction templates)
|
|
86
|
+
- Order creation (use GraphQL mutations directly)
|
|
87
|
+
- High-volume order ingestion (use Batch API templates)
|
|
88
|
+
|
|
89
|
+
### Business Use Cases
|
|
90
|
+
|
|
91
|
+
**Use Case 1: Order Processing Timestamp Tracking**
|
|
92
|
+
- Rubix workflow sends webhook when order moves to "PAID" status
|
|
93
|
+
- Handler updates `webhookProcessedAt` attribute
|
|
94
|
+
- Records processing timestamp
|
|
95
|
+
- No external system integration needed
|
|
96
|
+
|
|
97
|
+
**Use Case 2: Order Status Synchronization**
|
|
98
|
+
- Rubix workflow sends webhook on status change
|
|
99
|
+
- Handler updates custom attributes
|
|
100
|
+
- Enables cross-system status reporting
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## STEP 2: Implementation Prompt for Claude Code
|
|
105
|
+
|
|
106
|
+
**Copy this prompt and send to Claude Code to generate the complete implementation:**
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
Create a Versori webhook handler for Fluent Commerce Rubix ORDER events with signature validation and GraphQL attribute updates.
|
|
110
|
+
|
|
111
|
+
REQUIREMENTS:
|
|
112
|
+
1. Runtime: Versori Platform (HTTP webhook)
|
|
113
|
+
2. Source: Rubix SendWebhook action (ORDER entity type only)
|
|
114
|
+
3. Destination: Fluent Commerce GraphQL API (mutations)
|
|
115
|
+
4. Format: JSON webhook payload
|
|
116
|
+
5. Entity: ORDER
|
|
117
|
+
|
|
118
|
+
KEY FEATURES:
|
|
119
|
+
- Webhook signature validation using WebhookValidationService
|
|
120
|
+
- Sync mode + fire-and-forget pattern for fast response
|
|
121
|
+
- Background processing: GraphQL mutation for attribute updates
|
|
122
|
+
- Modular architecture (workflows/, services/, types/)
|
|
123
|
+
- Native Versori logging (no LoggingService)
|
|
124
|
+
- Comprehensive error handling with SDK error types
|
|
125
|
+
- RetailerId validation before setRetailerId()
|
|
126
|
+
|
|
127
|
+
CRITICAL REQUIREMENTS:
|
|
128
|
+
1. Webhook Mode: response: { mode: 'sync' }
|
|
129
|
+
2. Signature Validation: WebhookValidationService with strictValidation: true
|
|
130
|
+
3. RetailerId Validation: Check exists before client.setRetailerId()
|
|
131
|
+
4. Background Processing: Fire-and-forget pattern (no await on background promise)
|
|
132
|
+
5. GraphQL Mutation: Update order attributes only (not creation)
|
|
133
|
+
6. Error Handling: Import and handle AuthenticationError, GraphQLError
|
|
134
|
+
7. Type Safety: Use Logger, VersoriContext types (not any)
|
|
135
|
+
|
|
136
|
+
SDK METHODS TO USE:
|
|
137
|
+
- import { Buffer } from 'node:buffer' (for consistency)
|
|
138
|
+
- createClient({ ...ctx, log })
|
|
139
|
+
- await client.setRetailerId(retailerId) (with validation first!)
|
|
140
|
+
- new WebhookValidationService({ strictValidation: true }, log)
|
|
141
|
+
- await validator.validateWebhook(rawBody, headers, publicKey)
|
|
142
|
+
- await client.graphql({ query, variables }) (mutation)
|
|
143
|
+
|
|
144
|
+
FORBIDDEN PATTERNS:
|
|
145
|
+
- ❌ LoggingService (use native log from context)
|
|
146
|
+
- ❌ await on background processing (use fire-and-forget)
|
|
147
|
+
- ❌ Missing retailerId validation before setRetailerId()
|
|
148
|
+
- ❌ Using 'any' type (use Logger, VersoriContext from SDK)
|
|
149
|
+
- ❌ Direct FluentClient instantiation (use createClient factory)
|
|
150
|
+
- ❌ Generic error handling (use SDK error types)
|
|
151
|
+
|
|
152
|
+
GRAPHQL MUTATION STRUCTURE:
|
|
153
|
+
mutation UpdateOrderAttributes($input: UpdateOrderInput!) {
|
|
154
|
+
updateOrder(input: $input) {
|
|
155
|
+
id
|
|
156
|
+
ref
|
|
157
|
+
attributes {
|
|
158
|
+
name
|
|
159
|
+
type
|
|
160
|
+
value
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
GENERATE:
|
|
166
|
+
1. package.json with dependencies
|
|
167
|
+
2. index.ts (entry point - exports workflow)
|
|
168
|
+
3. src/workflows/webhook/order-update-handler.ts (webhook entry)
|
|
169
|
+
4. src/services/order-mutation.service.ts (GraphQL mutations)
|
|
170
|
+
5. src/types/order.types.ts (TypeScript interfaces)
|
|
171
|
+
6. readme.md with setup instructions
|
|
172
|
+
|
|
173
|
+
Ensure all code is production-ready with proper error handling, signature validation, and type safety.
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## STEP 3: Detailed Flow Documentation
|
|
179
|
+
|
|
180
|
+
### Complete Processing Flow
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
184
|
+
│ 1. RUBIX WEBHOOK RECEIVED │
|
|
185
|
+
│ POST https://{workspace}.versori.run/rubix-order │
|
|
186
|
+
│ Headers: fluent-signature │
|
|
187
|
+
│ Body: { entityRef: "ORD-001", entityType: "ORDER" } │
|
|
188
|
+
└────────────────────┬────────────────────────────────────────┘
|
|
189
|
+
│
|
|
190
|
+
▼
|
|
191
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
192
|
+
│ 2. SIGNATURE VALIDATION (Synchronous, ~10-50ms) │
|
|
193
|
+
│ - Extract rawBody and headers │
|
|
194
|
+
│ - Get fluentPublicKey from activation │
|
|
195
|
+
│ - WebhookValidationService.validateWebhook() │
|
|
196
|
+
│ - Return 400 if invalid │
|
|
197
|
+
└────────────────────┬────────────────────────────────────────┘
|
|
198
|
+
│
|
|
199
|
+
▼
|
|
200
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
201
|
+
│ 3. PAYLOAD VALIDATION (Synchronous, ~5ms) │
|
|
202
|
+
│ - Check entityRef exists │
|
|
203
|
+
│ - Check entityType === 'ORDER' │
|
|
204
|
+
│ - Check retailerId exists │
|
|
205
|
+
│ - Return 400 if missing required fields │
|
|
206
|
+
└────────────────────┬────────────────────────────────────────┘
|
|
207
|
+
│
|
|
208
|
+
▼
|
|
209
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
210
|
+
│ 4. HTTP 200 OK RESPONSE (Total: ~50ms) │
|
|
211
|
+
│ - Return success immediately │
|
|
212
|
+
│ - Background promise continues (fire-and-forget) │
|
|
213
|
+
└────────────────────┬────────────────────────────────────────┘
|
|
214
|
+
│
|
|
215
|
+
▼ (Background - doesn't block response)
|
|
216
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
217
|
+
│ 5. BACKGROUND PROCESSING (~300-500ms) │
|
|
218
|
+
│ ┌─────────────────────────────────────────────────────┐ │
|
|
219
|
+
│ │ 5a. Initialize Fluent Client (~100ms) │ │
|
|
220
|
+
│ │ - createClient({ ...ctx, log }) │ │
|
|
221
|
+
│ │ - Validate retailerId exists │ │
|
|
222
|
+
│ │ - client.setRetailerId(retailerId) │ │
|
|
223
|
+
│ └─────────────────────────────────────────────────────┘ │
|
|
224
|
+
│ ┌─────────────────────────────────────────────────────┐ │
|
|
225
|
+
│ │ 5b. Update Order Attributes (~200-400ms) │ │
|
|
226
|
+
│ │ - GraphQL mutation with attributes array │ │
|
|
227
|
+
│ │ - Handle GraphQLError if mutation fails │ │
|
|
228
|
+
│ └─────────────────────────────────────────────────────┘ │
|
|
229
|
+
│ ┌─────────────────────────────────────────────────────┐ │
|
|
230
|
+
│ │ 5c. Log Completion │ │
|
|
231
|
+
│ │ - Success: Log order ref and duration │ │
|
|
232
|
+
│ │ - Failure: Log error with category │ │
|
|
233
|
+
│ └─────────────────────────────────────────────────────┘ │
|
|
234
|
+
└─────────────────────────────────────────────────────────────┘
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Response Timing
|
|
238
|
+
|
|
239
|
+
| Stage | Timing | Blocking |
|
|
240
|
+
|-------|--------|----------|
|
|
241
|
+
| **Signature Validation** | ~10-50ms | ✅ Yes (blocks response) |
|
|
242
|
+
| **Payload Validation** | ~5ms | ✅ Yes (blocks response) |
|
|
243
|
+
| **HTTP 200 Response** | ~50-100ms | ✅ Fast acknowledgment |
|
|
244
|
+
| **GraphQL Mutation** | ~200-400ms | ❌ No (background) |
|
|
245
|
+
| **Total Background** | ~300-500ms | ❌ Fire-and-forget |
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## STEP 4: Production Modular Structure
|
|
250
|
+
|
|
251
|
+
### Complete Project Structure
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
rubix-order-update/
|
|
255
|
+
├── package.json # Dependencies and Versori config
|
|
256
|
+
├── tsconfig.json # TypeScript configuration
|
|
257
|
+
├── readme.md # Setup and deployment instructions
|
|
258
|
+
├── index.ts # Entry point - exports workflow
|
|
259
|
+
└── src/
|
|
260
|
+
├── workflows/
|
|
261
|
+
│ └── webhook/
|
|
262
|
+
│ └── order-update-handler.ts # Webhook entry point
|
|
263
|
+
│
|
|
264
|
+
├── services/
|
|
265
|
+
│ └── order-mutation.service.ts # GraphQL mutations
|
|
266
|
+
│
|
|
267
|
+
└── types/
|
|
268
|
+
└── order.types.ts # TypeScript interfaces
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## SDK Imports (Verified - Versori Optimized)
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
// CRITICAL: Buffer import for consistency (even though not used for SFTP)
|
|
277
|
+
import { Buffer } from 'node:buffer';
|
|
278
|
+
|
|
279
|
+
// FC Connect SDK imports
|
|
280
|
+
import {
|
|
281
|
+
createClient,
|
|
282
|
+
WebhookValidationService,
|
|
283
|
+
AuthenticationError,
|
|
284
|
+
GraphQLError,
|
|
285
|
+
type Logger,
|
|
286
|
+
type VersoriContext,
|
|
287
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
288
|
+
|
|
289
|
+
// Versori platform imports
|
|
290
|
+
import { webhook, http } from '@versori/run';
|
|
291
|
+
import type { Context } from '@versori/run';
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Configuration
|
|
297
|
+
|
|
298
|
+
### Activation Variables
|
|
299
|
+
|
|
300
|
+
```json
|
|
301
|
+
{
|
|
302
|
+
"fluentPublicKey": "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----"
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Connections
|
|
307
|
+
|
|
308
|
+
| Connection | Description | Required |
|
|
309
|
+
|------------|-------------|----------|
|
|
310
|
+
| `fluent_commerce` | OAuth2 connection to Fluent Commerce | Yes |
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Complete Working Code
|
|
315
|
+
|
|
316
|
+
### 1. Entry Point (index.ts)
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
/**
|
|
320
|
+
* Entry point - Export webhook workflow for Versori platform
|
|
321
|
+
*/
|
|
322
|
+
|
|
323
|
+
export { rubixOrderUpdateHandler } from './src/workflows/webhook/order-update-handler';
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### 2. Webhook Handler (src/workflows/webhook/order-update-handler.ts)
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
/**
|
|
330
|
+
* Rubix Order Webhook Handler → Attribute Update
|
|
331
|
+
*
|
|
332
|
+
* Direction: Fluent Rubix Workflow (ORDER) → Versori → GraphQL Mutation
|
|
333
|
+
*/
|
|
334
|
+
|
|
335
|
+
import { Buffer } from 'node:buffer';
|
|
336
|
+
import { webhook } from '@versori/run';
|
|
337
|
+
import type { Context } from '@versori/run';
|
|
338
|
+
import {
|
|
339
|
+
WebhookValidationService,
|
|
340
|
+
type Logger,
|
|
341
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
342
|
+
import { processOrderUpdate } from '../../services/order-mutation.service';
|
|
343
|
+
|
|
344
|
+
export const rubixOrderUpdateHandler = webhook('rubix-order', {
|
|
345
|
+
response: { mode: 'sync' }, // ✅ Sync mode: response sent when handler returns
|
|
346
|
+
}, async (ctx: Context<any>) => {
|
|
347
|
+
const { log, activation } = ctx;
|
|
348
|
+
|
|
349
|
+
log.info('🚀 [WEBHOOK] Received Rubix order webhook', {
|
|
350
|
+
timestamp: new Date().toISOString(),
|
|
351
|
+
correlationId: activation?.id,
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// ========================================
|
|
355
|
+
// STEP 1: VALIDATE SIGNATURE (Synchronous)
|
|
356
|
+
// ========================================
|
|
357
|
+
|
|
358
|
+
const publicKey = activation.getVariable('fluentPublicKey');
|
|
359
|
+
if (!publicKey) {
|
|
360
|
+
log.error('❌ [WEBHOOK] Missing fluentPublicKey configuration');
|
|
361
|
+
return {
|
|
362
|
+
status: 500,
|
|
363
|
+
body: {
|
|
364
|
+
success: false,
|
|
365
|
+
error: 'Missing fluentPublicKey configuration',
|
|
366
|
+
recommendation: 'Configure fluentPublicKey in activation variables',
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Extract raw body (Versori provides parsed data in ctx.data)
|
|
372
|
+
const rawBody = typeof ctx.data === 'string' ? ctx.data : JSON.stringify(ctx.data);
|
|
373
|
+
|
|
374
|
+
// Extract headers from request (ctx.request() is a function call)
|
|
375
|
+
const req = ctx.request?.() || {};
|
|
376
|
+
const headersObj = req.headers || {};
|
|
377
|
+
const headers = headersObj instanceof Headers
|
|
378
|
+
? Object.fromEntries(headersObj.entries())
|
|
379
|
+
: headersObj;
|
|
380
|
+
|
|
381
|
+
log.info('🔍 [WEBHOOK] Validating webhook signature');
|
|
382
|
+
|
|
383
|
+
const validator = new WebhookValidationService({ strictValidation: true }, log as Logger);
|
|
384
|
+
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
385
|
+
|
|
386
|
+
if (!result.isValid) {
|
|
387
|
+
log.error('❌ [WEBHOOK] Invalid webhook signature', {
|
|
388
|
+
error: result.error,
|
|
389
|
+
algorithm: result.algorithm,
|
|
390
|
+
});
|
|
391
|
+
return {
|
|
392
|
+
status: 401,
|
|
393
|
+
body: {
|
|
394
|
+
success: false,
|
|
395
|
+
error: 'Invalid webhook signature',
|
|
396
|
+
recommendation: 'Verify fluentPublicKey matches Fluent environment',
|
|
397
|
+
},
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
log.info('✅ [WEBHOOK] Signature validated successfully', {
|
|
402
|
+
algorithm: result.algorithm,
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// ========================================
|
|
406
|
+
// STEP 2: VALIDATE PAYLOAD (Synchronous)
|
|
407
|
+
// ========================================
|
|
408
|
+
|
|
409
|
+
const payload = typeof ctx.data === 'string' ? JSON.parse(ctx.data) : ctx.data;
|
|
410
|
+
const { entityRef, entityType, retailerId } = payload;
|
|
411
|
+
|
|
412
|
+
if (!entityRef) {
|
|
413
|
+
log.error('❌ [WEBHOOK] Missing entityRef in payload', { payload });
|
|
414
|
+
return {
|
|
415
|
+
status: 400,
|
|
416
|
+
body: {
|
|
417
|
+
success: false,
|
|
418
|
+
error: 'Missing entityRef in payload',
|
|
419
|
+
recommendation: 'Ensure Rubix SendWebhook includes order ref',
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (entityType !== 'ORDER') {
|
|
425
|
+
log.error('❌ [WEBHOOK] Invalid entity type', { entityType, expected: 'ORDER' });
|
|
426
|
+
return {
|
|
427
|
+
status: 400,
|
|
428
|
+
body: {
|
|
429
|
+
success: false,
|
|
430
|
+
error: `Invalid entity type: ${entityType}. Expected: ORDER`,
|
|
431
|
+
recommendation: 'Use fulfilment-export template for FULFILMENT webhooks',
|
|
432
|
+
},
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (!retailerId) {
|
|
437
|
+
log.error('❌ [WEBHOOK] Missing retailerId in payload', { payload });
|
|
438
|
+
return {
|
|
439
|
+
status: 400,
|
|
440
|
+
body: {
|
|
441
|
+
success: false,
|
|
442
|
+
error: 'Missing retailerId in payload',
|
|
443
|
+
recommendation: 'Ensure Rubix SendWebhook includes retailerId',
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
log.info('✅ [WEBHOOK] Validation passed, starting background processing', {
|
|
449
|
+
entityRef,
|
|
450
|
+
entityType,
|
|
451
|
+
retailerId,
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
// ========================================
|
|
455
|
+
// STEP 3: FIRE-AND-FORGET BACKGROUND PROCESSING
|
|
456
|
+
// ========================================
|
|
457
|
+
|
|
458
|
+
// ✅ Fire-and-forget: Start background processing WITHOUT await
|
|
459
|
+
// The promise continues execution after we return the response
|
|
460
|
+
processOrderUpdate(ctx, payload)
|
|
461
|
+
.then(() => {
|
|
462
|
+
log.info('✅ [BACKGROUND] Order updated successfully', {
|
|
463
|
+
orderRef: entityRef,
|
|
464
|
+
});
|
|
465
|
+
})
|
|
466
|
+
.catch((error: unknown) => {
|
|
467
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
468
|
+
log.error('❌ [BACKGROUND] Order update failed', {
|
|
469
|
+
orderRef: entityRef,
|
|
470
|
+
error: errorMessage,
|
|
471
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
472
|
+
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
// Return immediately (response sent with this return value)
|
|
477
|
+
return {
|
|
478
|
+
status: 200,
|
|
479
|
+
body: {
|
|
480
|
+
success: true,
|
|
481
|
+
entityType,
|
|
482
|
+
orderRef: entityRef,
|
|
483
|
+
message: 'Webhook validated and update started in background',
|
|
484
|
+
timestamp: new Date().toISOString(),
|
|
485
|
+
correlationId: activation?.id,
|
|
486
|
+
},
|
|
487
|
+
};
|
|
488
|
+
});
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### 3. Order Mutation Service (src/services/order-mutation.service.ts)
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
/**
|
|
495
|
+
* Order Mutation Service
|
|
496
|
+
*
|
|
497
|
+
* Handles GraphQL mutations for order attribute updates
|
|
498
|
+
*/
|
|
499
|
+
|
|
500
|
+
import {
|
|
501
|
+
createClient,
|
|
502
|
+
AuthenticationError,
|
|
503
|
+
GraphQLError,
|
|
504
|
+
type Logger,
|
|
505
|
+
type VersoriContext,
|
|
506
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
507
|
+
import type { RubixOrderPayload } from '../types/order.types';
|
|
508
|
+
|
|
509
|
+
const UPDATE_ORDER_MUTATION = `
|
|
510
|
+
mutation UpdateOrderAttributes($input: UpdateOrderInput!) {
|
|
511
|
+
updateOrder(input: $input) {
|
|
512
|
+
id
|
|
513
|
+
ref
|
|
514
|
+
attributes {
|
|
515
|
+
name
|
|
516
|
+
type
|
|
517
|
+
value
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
`;
|
|
522
|
+
|
|
523
|
+
export async function processOrderUpdate(
|
|
524
|
+
ctx: VersoriContext,
|
|
525
|
+
payload: RubixOrderPayload
|
|
526
|
+
): Promise<void> {
|
|
527
|
+
const log = ctx.log as Logger;
|
|
528
|
+
const { entityRef, retailerId } = payload;
|
|
529
|
+
|
|
530
|
+
const processingStartTime = Date.now();
|
|
531
|
+
|
|
532
|
+
log.info('🔄 [BACKGROUND] Starting order attribute update', {
|
|
533
|
+
timestamp: new Date().toISOString(),
|
|
534
|
+
orderRef: entityRef,
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
try {
|
|
538
|
+
// ========================================
|
|
539
|
+
// STEP 1: VALIDATE RETAILER ID
|
|
540
|
+
// ========================================
|
|
541
|
+
|
|
542
|
+
if (!retailerId) {
|
|
543
|
+
log.error('❌ [BACKGROUND] Missing retailerId in payload', {
|
|
544
|
+
orderRef: entityRef,
|
|
545
|
+
});
|
|
546
|
+
throw new Error('retailerId is required for GraphQL operations');
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// ========================================
|
|
550
|
+
// STEP 2: INITIALIZE FLUENT CLIENT
|
|
551
|
+
// ========================================
|
|
552
|
+
|
|
553
|
+
const client = await createClient({ ...ctx, log });
|
|
554
|
+
await client.setRetailerId(retailerId);
|
|
555
|
+
|
|
556
|
+
// ========================================
|
|
557
|
+
// STEP 3: UPDATE ORDER ATTRIBUTES
|
|
558
|
+
// ========================================
|
|
559
|
+
|
|
560
|
+
log.info('📝 [BACKGROUND] Updating order attributes', { orderRef: entityRef });
|
|
561
|
+
|
|
562
|
+
await client.graphql({
|
|
563
|
+
query: UPDATE_ORDER_MUTATION,
|
|
564
|
+
variables: {
|
|
565
|
+
input: {
|
|
566
|
+
ref: entityRef,
|
|
567
|
+
attributes: [
|
|
568
|
+
{
|
|
569
|
+
name: 'webhookProcessedAt',
|
|
570
|
+
type: 'STRING',
|
|
571
|
+
value: new Date().toISOString(),
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
name: 'webhookSource',
|
|
575
|
+
type: 'STRING',
|
|
576
|
+
value: 'rubix-workflow',
|
|
577
|
+
},
|
|
578
|
+
],
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
const duration = Date.now() - processingStartTime;
|
|
584
|
+
log.info('✅ [BACKGROUND] Order attributes updated successfully', {
|
|
585
|
+
orderRef: entityRef,
|
|
586
|
+
duration: `${duration}ms`,
|
|
587
|
+
});
|
|
588
|
+
} catch (error: unknown) {
|
|
589
|
+
let errorMessage = 'Unknown error';
|
|
590
|
+
let recommendation: string | undefined;
|
|
591
|
+
let errorCategory = 'UNKNOWN';
|
|
592
|
+
|
|
593
|
+
if (error instanceof GraphQLError) {
|
|
594
|
+
errorMessage = error.message;
|
|
595
|
+
recommendation = 'Review GraphQL mutation syntax and field permissions';
|
|
596
|
+
errorCategory = 'GRAPHQL';
|
|
597
|
+
} else if (error instanceof AuthenticationError) {
|
|
598
|
+
errorMessage = error.message;
|
|
599
|
+
recommendation = 'Verify Fluent Commerce credentials and retailerId';
|
|
600
|
+
errorCategory = 'AUTHENTICATION';
|
|
601
|
+
} else if (error instanceof Error) {
|
|
602
|
+
errorMessage = error.message;
|
|
603
|
+
} else {
|
|
604
|
+
errorMessage = String(error);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
log.error('❌ [BACKGROUND] Order update failed', {
|
|
608
|
+
orderRef: entityRef,
|
|
609
|
+
error: errorMessage,
|
|
610
|
+
errorCategory,
|
|
611
|
+
recommendation,
|
|
612
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
613
|
+
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
throw error;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### 4. Type Definitions (src/types/order.types.ts)
|
|
622
|
+
|
|
623
|
+
```typescript
|
|
624
|
+
/**
|
|
625
|
+
* Type definitions for Rubix order webhooks
|
|
626
|
+
*/
|
|
627
|
+
|
|
628
|
+
export interface RubixOrderPayload {
|
|
629
|
+
id: string;
|
|
630
|
+
name: string;
|
|
631
|
+
accountId: string;
|
|
632
|
+
retailerId: string;
|
|
633
|
+
entityRef: string;
|
|
634
|
+
entityType: 'ORDER';
|
|
635
|
+
entityStatus?: string;
|
|
636
|
+
type: 'NORMAL' | string;
|
|
637
|
+
rootEntityId?: string;
|
|
638
|
+
rootEntityType?: string;
|
|
639
|
+
rootEntityRef?: string;
|
|
640
|
+
attributes?: Record<string, any>;
|
|
641
|
+
context?: Record<string, any>;
|
|
642
|
+
}
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### 5. Package Configuration (package.json)
|
|
646
|
+
|
|
647
|
+
```json
|
|
648
|
+
{
|
|
649
|
+
"name": "rubix-order-update",
|
|
650
|
+
"version": "2.0.0",
|
|
651
|
+
"description": "Rubix order webhook handler with attribute updates",
|
|
652
|
+
"type": "module",
|
|
653
|
+
"main": "index.ts",
|
|
654
|
+
"scripts": {
|
|
655
|
+
"dev": "versori dev",
|
|
656
|
+
"build": "versori build",
|
|
657
|
+
"deploy": "versori deploy",
|
|
658
|
+
"test": "jest",
|
|
659
|
+
"lint": "eslint src/**/*.ts"
|
|
660
|
+
},
|
|
661
|
+
"dependencies": {
|
|
662
|
+
"@fluentcommerce/fc-connect-sdk": "^0.1.39",
|
|
663
|
+
"@versori/run": "latest"
|
|
664
|
+
},
|
|
665
|
+
"devDependencies": {
|
|
666
|
+
"@types/node": "^20.0.0",
|
|
667
|
+
"typescript": "^5.0.0",
|
|
668
|
+
"jest": "^29.0.0",
|
|
669
|
+
"@types/jest": "^29.0.0",
|
|
670
|
+
"eslint": "^8.0.0"
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
---
|
|
676
|
+
|
|
677
|
+
## Key Patterns Explained
|
|
678
|
+
|
|
679
|
+
### Pattern 1: RetailerId Validation Before setRetailerId()
|
|
680
|
+
|
|
681
|
+
```typescript
|
|
682
|
+
// ✅ CORRECT - Validate before use
|
|
683
|
+
const retailerId = payload.retailerId;
|
|
684
|
+
if (!retailerId) {
|
|
685
|
+
log.error('❌ [BACKGROUND] Missing retailerId in payload');
|
|
686
|
+
throw new Error('retailerId is required for GraphQL operations');
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
const client = await createClient({ ...ctx, log });
|
|
690
|
+
await client.setRetailerId(retailerId);
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
**Why:** Missing retailerId causes 401 authentication errors with unclear messages.
|
|
694
|
+
|
|
695
|
+
### Pattern 2: SDK Error Type Handling
|
|
696
|
+
|
|
697
|
+
```typescript
|
|
698
|
+
// ✅ CORRECT - Handle SDK error types
|
|
699
|
+
catch (error: unknown) {
|
|
700
|
+
if (error instanceof GraphQLError) {
|
|
701
|
+
errorCategory = 'GRAPHQL';
|
|
702
|
+
recommendation = 'Review mutation syntax';
|
|
703
|
+
} else if (error instanceof AuthenticationError) {
|
|
704
|
+
errorCategory = 'AUTHENTICATION';
|
|
705
|
+
recommendation = 'Verify credentials';
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
**Why:** SDK errors include recommendations for resolution.
|
|
711
|
+
|
|
712
|
+
---
|
|
713
|
+
|
|
714
|
+
## Testing
|
|
715
|
+
|
|
716
|
+
### 1. Unit Tests
|
|
717
|
+
|
|
718
|
+
```typescript
|
|
719
|
+
// src/services/__tests__/order-mutation.test.ts
|
|
720
|
+
import { processOrderUpdate } from '../order-mutation.service';
|
|
721
|
+
|
|
722
|
+
describe('Order Mutation Service', () => {
|
|
723
|
+
test('should update order successfully', async () => {
|
|
724
|
+
const mockCtx = {
|
|
725
|
+
log: { info: jest.fn(), error: jest.fn() },
|
|
726
|
+
activation: { getVariable: jest.fn() },
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
const payload = {
|
|
730
|
+
entityRef: 'ORD-001',
|
|
731
|
+
entityType: 'ORDER',
|
|
732
|
+
retailerId: '1',
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
await expect(processOrderUpdate(mockCtx, payload)).resolves.not.toThrow();
|
|
736
|
+
});
|
|
737
|
+
});
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
### 2. Integration Test
|
|
741
|
+
|
|
742
|
+
```bash
|
|
743
|
+
# Test webhook with valid signature
|
|
744
|
+
curl -X POST https://workspace.versori.run/rubix-order \
|
|
745
|
+
-H "Content-Type: application/json" \
|
|
746
|
+
-H "fluent-signature: <valid-signature>" \
|
|
747
|
+
-d '{
|
|
748
|
+
"entityType": "ORDER",
|
|
749
|
+
"entityRef": "ORD-001",
|
|
750
|
+
"retailerId": "1"
|
|
751
|
+
}'
|
|
752
|
+
|
|
753
|
+
# Expected: 200 OK with { success: true, message: "..." }
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
---
|
|
757
|
+
|
|
758
|
+
## Production Checklist
|
|
759
|
+
|
|
760
|
+
- [ ] Configure `fluentPublicKey` activation variable
|
|
761
|
+
- [ ] Configure Fluent Commerce connection in Versori
|
|
762
|
+
- [ ] Create Rubix workflow with SendWebhook action (ORDER events)
|
|
763
|
+
- [ ] Build Event payload with entityRef, entityType, retailerId
|
|
764
|
+
- [ ] Test webhook signature validation
|
|
765
|
+
- [ ] Test GraphQL mutation with real order ref
|
|
766
|
+
- [ ] Test error scenarios (invalid signature, missing retailerId)
|
|
767
|
+
- [ ] Monitor response times (<100ms for validation)
|
|
768
|
+
- [ ] Set up alerts for webhook/mutation failures
|
|
769
|
+
- [ ] Document custom attributes being set
|
|
770
|
+
|
|
771
|
+
---
|
|
772
|
+
|
|
773
|
+
## Troubleshooting Guide
|
|
774
|
+
|
|
775
|
+
| Issue | Cause | Solution |
|
|
776
|
+
|-------|-------|----------|
|
|
777
|
+
| "Invalid webhook signature" | Wrong public key | Verify fluentPublicKey matches Fluent environment |
|
|
778
|
+
| "Missing retailerId" | Rubix payload incomplete | Add retailerId to Rubix Event payload builder |
|
|
779
|
+
| "GraphQL mutation failed" | Invalid permissions | Verify user has UPDATE_ORDER permission |
|
|
780
|
+
| "401 authentication error" | Missing retailerId | Ensure retailerId validation before setRetailerId() |
|
|
781
|
+
|
|
782
|
+
---
|
|
783
|
+
|
|
784
|
+
## Monitoring & Alerting
|
|
785
|
+
|
|
786
|
+
### Success Response
|
|
787
|
+
|
|
788
|
+
```json
|
|
789
|
+
{
|
|
790
|
+
"success": true,
|
|
791
|
+
"entityType": "ORDER",
|
|
792
|
+
"orderRef": "ORD-001",
|
|
793
|
+
"message": "Webhook validated and update started in background",
|
|
794
|
+
"timestamp": "2025-01-25T14:30:00.000Z",
|
|
795
|
+
"correlationId": "act_123",
|
|
796
|
+
"processingMode": "async"
|
|
797
|
+
}
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
### Key Metrics
|
|
801
|
+
|
|
802
|
+
```typescript
|
|
803
|
+
const METRICS = {
|
|
804
|
+
signatureValidationMs: 45,
|
|
805
|
+
totalResponseTimeMs: 50,
|
|
806
|
+
graphqlMutationMs: 250,
|
|
807
|
+
totalBackgroundMs: 350,
|
|
808
|
+
};
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
---
|
|
812
|
+
|
|
813
|
+
## Related Documentation
|
|
814
|
+
|
|
815
|
+
- [Fulfilment Export Template](./template-webhook-rubix-fulfilment-to-sftp-xml.md) - For FULFILMENT webhooks
|
|
816
|
+
- [Webhook Validation Guide](../../../../02-CORE-GUIDES/webhook-validation/)
|
|
817
|
+
- [Versori Platform Guide](../../../../04-REFERENCE/platforms/versori/)
|
|
818
|
+
|
|
819
|
+
---
|
|
820
|
+
|
|
821
|
+
**Pattern**: Order webhook → GraphQL mutation → Attribute update
|
|
822
|
+
**✅ Production-Ready**: Lightweight template with all critical fixes applied
|
|
823
|
+
**Key Learning**: Always validate retailerId, handle SDK errors, fire-and-forget pattern
|
|
824
|
+
**Critical**: RetailerId validation, proper error categorization, fast response
|