@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,1437 +1,1437 @@
|
|
|
1
|
-
# Comprehensive Error Handling Guide
|
|
2
|
-
|
|
3
|
-
> **Complete guide** to error handling across GraphQL, Event API, Batch API, and Job API with live examples
|
|
4
|
-
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
1. [Error Types Overview](#error-types-overview)
|
|
8
|
-
2. [Where is the Status Code?](#where-is-the-status-code)
|
|
9
|
-
3. [Anatomy of an API Call](#anatomy-of-an-api-call)
|
|
10
|
-
4. [Event API Error Handling](#event-api-error-handling)
|
|
11
|
-
5. [GraphQL Error Handling](#graphql-error-handling)
|
|
12
|
-
6. [Batch API Error Handling](#batch-api-error-handling)
|
|
13
|
-
7. [Job API Error Handling](#job-api-error-handling)
|
|
14
|
-
8. [Common Patterns](#common-patterns)
|
|
15
|
-
9. [Live Examples](#live-examples)
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Error Types Overview
|
|
20
|
-
|
|
21
|
-
> **📚 For complete error type reference**, see [Module 2: Error Types & Codes](./modules/error-handling-02-error-types.md)
|
|
22
|
-
|
|
23
|
-
**Quick Summary:**
|
|
24
|
-
|
|
25
|
-
| Error Type | When Thrown | HTTP Status | Auto-Retry |
|
|
26
|
-
|------------|-------------|-------------|------------|
|
|
27
|
-
| **FluentAPIError** | HTTP errors (4xx, 5xx) | 400, 404, 500, etc. | ✅ Yes (401, 5xx) |
|
|
28
|
-
| **AuthenticationError** | OAuth2 auth fails after 3 retries | 401 | N/A (already retried) |
|
|
29
|
-
| **GraphQLExecutionError** | GraphQL returns errors in response | 200 (with errors) | ❌ No |
|
|
30
|
-
|
|
31
|
-
### ⚡ How to Capture the Full Error Object
|
|
32
|
-
|
|
33
|
-
Standard logging (like `JSON.stringify(error)`) often misses custom fields. Use this helper to capture the full structure for your logs:
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
/**
|
|
37
|
-
* Helper to extract full error details including HTTP status and response body
|
|
38
|
-
*/
|
|
39
|
-
function extractError(error: any) {
|
|
40
|
-
// 1. Use toJSON() if available (e.g. GraphQLExecutionError, IngestionError)
|
|
41
|
-
if (typeof error.toJSON === 'function') {
|
|
42
|
-
return error.toJSON();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// 2. Manual extraction for FluentAPIError / Standard Errors
|
|
46
|
-
return {
|
|
47
|
-
name: error.name || 'Error',
|
|
48
|
-
message: error.message,
|
|
49
|
-
// Critical fields for FluentAPIError
|
|
50
|
-
statusCode: error.statusCode,
|
|
51
|
-
details: error.details,
|
|
52
|
-
code: error.code,
|
|
53
|
-
// Optional: stack trace
|
|
54
|
-
stack: error.stack
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Usage Example
|
|
59
|
-
const event = {
|
|
60
|
-
name: "UPSERT_PRODUCT",
|
|
61
|
-
retailerId: "YOUR_RETAILER_ID",
|
|
62
|
-
entityRef: "PC:MASTER:2",
|
|
63
|
-
entityType: "PRODUCT_CATALOGUE",
|
|
64
|
-
entitySubtype: "MASTER",
|
|
65
|
-
rootEntityRef: "PC:MASTER:2",
|
|
66
|
-
rootEntityType: "PRODUCT_CATALOGUE",
|
|
67
|
-
attributes: {
|
|
68
|
-
ref: "G_PROD_WITH_NO_STANDARD",
|
|
69
|
-
type: "VARIANT",
|
|
70
|
-
status: "ACTIVE",
|
|
71
|
-
gtin: "MH01-XS-Orange",
|
|
72
|
-
name: "Chaz Kangeroo Hoodie-XS-Orange main",
|
|
73
|
-
summary: "<p>test short description</p>",
|
|
74
|
-
categoryRefs: ["STANDARD_CATEGORY"],
|
|
75
|
-
price: [
|
|
76
|
-
{ type: "DEFAULT", currency: "USD", value: "52.000000" },
|
|
77
|
-
{ type: "SPECIAL", currency: "USD", value: "9.000000" }
|
|
78
|
-
],
|
|
79
|
-
taxType: {
|
|
80
|
-
country: "AU",
|
|
81
|
-
group: "Tax Group",
|
|
82
|
-
tariff: "Tax Tariff"
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
await client.sendEvent(event);
|
|
89
|
-
} catch (error) {
|
|
90
|
-
const fullError = extractError(error);
|
|
91
|
-
console.error('Error Thrown:', JSON.stringify(fullError, null, 2));
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
**Resulting Output:**
|
|
96
|
-
|
|
97
|
-
```json
|
|
98
|
-
{
|
|
99
|
-
"name": "FluentAPIError",
|
|
100
|
-
"message": "Request failed: 404",
|
|
101
|
-
"statusCode": 404,
|
|
102
|
-
"details": "No workflow rule matched this event"
|
|
103
|
-
}
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
[Full error type documentation →](./modules/error-handling-02-error-types.md)
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
## ❓ Where is the Status Code?
|
|
111
|
-
|
|
112
|
-
The location of the HTTP status code depends on whether the call **Succeeded** or **Failed**.
|
|
113
|
-
|
|
114
|
-
### Event API (sendEvent)
|
|
115
|
-
|
|
116
|
-
| Outcome | Where is the Status Code? | Example Value |
|
|
117
|
-
| :--- | :--- | :--- |
|
|
118
|
-
| **✅ Success** | `response.statusCode` | `200`, `201` |
|
|
119
|
-
| **❌ Failure** | `error.statusCode` | `400`, `404`, `500` |
|
|
120
|
-
|
|
121
|
-
### GraphQL API (graphql)
|
|
122
|
-
|
|
123
|
-
| Outcome | Where is the Status Code? | Example Value |
|
|
124
|
-
| :--- | :--- | :--- |
|
|
125
|
-
| **✅ Success** | **Not returned** (Implicit 200 OK) | N/A |
|
|
126
|
-
| **⚠️ Execution Error** | **Not returned** (Implicit 200 OK) | N/A |
|
|
127
|
-
| **❌ HTTP Failure** | `error.statusCode` | `500`, `503` |
|
|
128
|
-
|
|
129
|
-
---
|
|
130
|
-
|
|
131
|
-
## 🔍 Anatomy of an API Call
|
|
132
|
-
|
|
133
|
-
This section visualizes exactly what goes **IN** and what comes **OUT** of SDK operations.
|
|
134
|
-
|
|
135
|
-
### 1. Event API Anatomy
|
|
136
|
-
|
|
137
|
-
**Scenario:** Sending a Product Upsert event.
|
|
138
|
-
|
|
139
|
-
#### A. The Input (Event Payload)
|
|
140
|
-
```typescript
|
|
141
|
-
const event = {
|
|
142
|
-
name: "UPSERT_PRODUCT",
|
|
143
|
-
retailerId: "2",
|
|
144
|
-
entityRef: "PC:MASTER:2",
|
|
145
|
-
entityType: "PRODUCT_CATALOGUE",
|
|
146
|
-
// ...
|
|
147
|
-
};
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
#### B. The Call
|
|
151
|
-
```typescript
|
|
152
|
-
try {
|
|
153
|
-
const response = await client.sendEvent(event);
|
|
154
|
-
console.log('Success:', response);
|
|
155
|
-
} catch (error) {
|
|
156
|
-
console.error('Error:', extractError(error));
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
#### C. The Output (Two Possible Outcomes)
|
|
161
|
-
|
|
162
|
-
| Outcome | Structure Returned / Thrown | Status Code Location |
|
|
163
|
-
| :--- | :--- | :--- |
|
|
164
|
-
| **✅ SUCCESS** | **`EventResponse` Object**<br>Returned by `await` | **`response.statusCode`**<br>(e.g. 200) |
|
|
165
|
-
| **❌ FAILURE** | **`FluentAPIError` Object**<br>Caught in `catch` block | **`error.statusCode`**<br>(e.g. 400) |
|
|
166
|
-
|
|
167
|
-
---
|
|
168
|
-
|
|
169
|
-
### 2. GraphQL API Anatomy
|
|
170
|
-
|
|
171
|
-
**Scenario:** Querying an Order by Reference.
|
|
172
|
-
|
|
173
|
-
#### A. The Input (Query & Variables)
|
|
174
|
-
```typescript
|
|
175
|
-
const query = `
|
|
176
|
-
query GetOrder($ref: String!) {
|
|
177
|
-
order(ref: $ref) {
|
|
178
|
-
id
|
|
179
|
-
ref
|
|
180
|
-
status
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
`;
|
|
184
|
-
|
|
185
|
-
const variables = {
|
|
186
|
-
ref: "ORD-123"
|
|
187
|
-
};
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
#### B. The Call
|
|
191
|
-
```typescript
|
|
192
|
-
try {
|
|
193
|
-
const response = await client.graphql({ query, variables });
|
|
194
|
-
console.log('Success:', response.data);
|
|
195
|
-
} catch (error) {
|
|
196
|
-
console.error('Error:', extractError(error));
|
|
197
|
-
}
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
#### C. The Output (Three Possible Outcomes)
|
|
201
|
-
|
|
202
|
-
| Outcome | Structure Returned / Thrown | Status Code Location |
|
|
203
|
-
| :--- | :--- | :--- |
|
|
204
|
-
| **✅ SUCCESS** | **`GraphQLResponse` Object**<br>Returned by `await` | **None**<br>(Implicit 200 OK) |
|
|
205
|
-
| **❌ HTTP FAILURE** | **`FluentAPIError` Object**<br>Caught in `catch` block | **`error.statusCode`**<br>(e.g. 500) |
|
|
206
|
-
| **⚠️ EXECUTION ERROR** | **`GraphQLExecutionError` Object**<br>Caught in `catch` block | **None**<br>(Implicit 200 OK) |
|
|
207
|
-
|
|
208
|
-
---
|
|
209
|
-
|
|
210
|
-
## Event API Error Handling
|
|
211
|
-
|
|
212
|
-
### Success Response Structure
|
|
213
|
-
|
|
214
|
-
```typescript
|
|
215
|
-
interface EventResponse {
|
|
216
|
-
id?: string; // Event ID (if returned)
|
|
217
|
-
success: boolean; // Always true for success
|
|
218
|
-
status?: number; // Event status from Fluent API (CREATED, COMPLETED, etc.)
|
|
219
|
-
statusCode?: number; // HTTP status code (200, 201, 202)
|
|
220
|
-
message?: string; // Optional message
|
|
221
|
-
}
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
### Basic Try-Catch Pattern
|
|
225
|
-
|
|
226
|
-
```typescript
|
|
227
|
-
import { FluentClient, FluentAPIError, AuthenticationError } from '@fluentcommerce/fc-connect-sdk';
|
|
228
|
-
|
|
229
|
-
const client = new FluentClient({
|
|
230
|
-
baseUrl: 'https://api.fluentcommerce.com',
|
|
231
|
-
clientId: 'YOUR_CLIENT_ID',
|
|
232
|
-
clientSecret: 'YOUR_CLIENT_SECRET',
|
|
233
|
-
username: 'YOUR_USERNAME',
|
|
234
|
-
password: 'YOUR_PASSWORD',
|
|
235
|
-
retailerId: 'YOUR_RETAILER_ID'
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
try {
|
|
239
|
-
// SUCCESS CASE
|
|
240
|
-
const result = await client.sendEvent({
|
|
241
|
-
name: 'OrderCancel1',
|
|
242
|
-
entityRef: 'G_TEST_FC_POSTMAN_34',
|
|
243
|
-
entityType: 'ORDER',
|
|
244
|
-
retailerId: '2'
|
|
245
|
-
}, 'async');
|
|
246
|
-
|
|
247
|
-
console.log('✅ Event sent successfully');
|
|
248
|
-
console.log('HTTP Status:', result.statusCode); // 200
|
|
249
|
-
console.log('Success:', result.success); // true
|
|
250
|
-
console.log('Event ID:', result.id); // evt-123 (if available)
|
|
251
|
-
|
|
252
|
-
} catch (error) {
|
|
253
|
-
if (error instanceof FluentAPIError) {
|
|
254
|
-
// API Error (400, 404, 500, etc.)
|
|
255
|
-
console.error('❌ API Error');
|
|
256
|
-
console.error('Status:', error.statusCode); // 400, 404, 500, etc.
|
|
257
|
-
console.error('Message:', error.message); // "Request failed: 400"
|
|
258
|
-
console.error('Details:', error.details); // Response body
|
|
259
|
-
|
|
260
|
-
} else if (error instanceof AuthenticationError) {
|
|
261
|
-
// Auth failed after 3 retries
|
|
262
|
-
console.error('❌ Authentication Error');
|
|
263
|
-
console.error('Message:', error.message);
|
|
264
|
-
console.error('Status:', error.statusCode); // 401
|
|
265
|
-
|
|
266
|
-
} else {
|
|
267
|
-
// Unknown error
|
|
268
|
-
console.error('❌ Unexpected Error:', error);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
### Async Mode - Success Examples
|
|
274
|
-
|
|
275
|
-
**Input:**
|
|
276
|
-
```json
|
|
277
|
-
{
|
|
278
|
-
"name": "OrderCancel1",
|
|
279
|
-
"entityRef": "G_TEST_FC_POSTMAN_34",
|
|
280
|
-
"entityType": "ORDER",
|
|
281
|
-
"retailerId": "2"
|
|
282
|
-
}
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
**Output (Success):**
|
|
286
|
-
```json
|
|
287
|
-
{
|
|
288
|
-
"success": true,
|
|
289
|
-
"statusCode": 200
|
|
290
|
-
}
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
**Code:**
|
|
294
|
-
```typescript
|
|
295
|
-
const result = await client.sendEvent({
|
|
296
|
-
name: 'OrderCancel1',
|
|
297
|
-
entityRef: 'G_TEST_FC_POSTMAN_34',
|
|
298
|
-
entityType: 'ORDER',
|
|
299
|
-
retailerId: '2'
|
|
300
|
-
}, 'async');
|
|
301
|
-
|
|
302
|
-
// result.success = true
|
|
303
|
-
// result.statusCode = 200
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
---
|
|
307
|
-
|
|
308
|
-
### ⚡ SDK Automatic Retry Behavior
|
|
309
|
-
|
|
310
|
-
> **IMPORTANT:** The SDK automatically retries certain errors WITHOUT your intervention
|
|
311
|
-
|
|
312
|
-
**✅ Automatically Retried (3 attempts with exponential backoff):**
|
|
313
|
-
- `401 Unauthorized` - Token refresh attempt
|
|
314
|
-
- `500 Internal Server Error` - Transient server issue
|
|
315
|
-
- `503 Service Unavailable` - API temporarily overloaded
|
|
316
|
-
- Network errors - Connection failures
|
|
317
|
-
|
|
318
|
-
**❌ NOT Retried (Fail Immediately):**
|
|
319
|
-
- `400 Bad Request` - Invalid event payload
|
|
320
|
-
- `404 Not Found` - No workflow matched (sync mode)
|
|
321
|
-
- `422 Unprocessable Entity` - Validation failed
|
|
322
|
-
- All other 4xx errors
|
|
323
|
-
|
|
324
|
-
**Example:**
|
|
325
|
-
```typescript
|
|
326
|
-
try {
|
|
327
|
-
await client.sendEvent(event);
|
|
328
|
-
} catch (error) {
|
|
329
|
-
if (error instanceof FluentAPIError && error.statusCode === 500) {
|
|
330
|
-
// ⚠️ SDK already retried 3 times with exponential backoff
|
|
331
|
-
// This error means all retries failed
|
|
332
|
-
console.error('Server error after 3 automatic retries');
|
|
333
|
-
|
|
334
|
-
// You may want to implement additional retry logic here
|
|
335
|
-
// or send to a dead letter queue
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
---
|
|
341
|
-
|
|
342
|
-
### Async Mode - Error Examples
|
|
343
|
-
|
|
344
|
-
#### Error 1: Invalid Event Name (400)
|
|
345
|
-
|
|
346
|
-
**Input:**
|
|
347
|
-
```json
|
|
348
|
-
{
|
|
349
|
-
"name": "InvalidEventName",
|
|
350
|
-
"entityRef": "TEST-123",
|
|
351
|
-
"entityType": "ORDER",
|
|
352
|
-
"retailerId": "2"
|
|
353
|
-
}
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
**Error Thrown:**
|
|
357
|
-
```typescript
|
|
358
|
-
FluentAPIError {
|
|
359
|
-
name: "FluentAPIError",
|
|
360
|
-
message: "Request failed: 400",
|
|
361
|
-
statusCode: 400,
|
|
362
|
-
details: "Bad Request - Invalid event configuration"
|
|
363
|
-
}
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
**Handling:**
|
|
367
|
-
```typescript
|
|
368
|
-
try {
|
|
369
|
-
await client.sendEvent({
|
|
370
|
-
name: 'InvalidEventName', // ❌ No workflow configured
|
|
371
|
-
entityRef: 'TEST-123',
|
|
372
|
-
entityType: 'ORDER',
|
|
373
|
-
retailerId: '2'
|
|
374
|
-
}, 'async');
|
|
375
|
-
} catch (error) {
|
|
376
|
-
if (error instanceof FluentAPIError && error.statusCode === 400) {
|
|
377
|
-
console.error('Invalid event configuration');
|
|
378
|
-
// ⚠️ SDK did NOT retry (4xx = client error)
|
|
379
|
-
// Fix: Check event name in Rubix workflow configuration
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
---
|
|
385
|
-
|
|
386
|
-
#### Error 2: Missing retailerId (SDK Validation)
|
|
387
|
-
|
|
388
|
-
**Input:**
|
|
389
|
-
```json
|
|
390
|
-
{
|
|
391
|
-
"name": "TEST.EVENT",
|
|
392
|
-
"entityRef": "TEST-123",
|
|
393
|
-
"entityType": "ORDER"
|
|
394
|
-
// ❌ Missing retailerId
|
|
395
|
-
}
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
**Error Thrown:**
|
|
399
|
-
```typescript
|
|
400
|
-
FluentValidationError {
|
|
401
|
-
name: "FluentValidationError",
|
|
402
|
-
message: "retailerId is required for Event API. Provide it in the event payload or client config.",
|
|
403
|
-
code: "VALIDATION_ERROR",
|
|
404
|
-
statusCode: 400
|
|
405
|
-
}
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
**Key Point**: This error is thrown BEFORE the API call (fail-fast).
|
|
409
|
-
|
|
410
|
-
**Handling:**
|
|
411
|
-
```typescript
|
|
412
|
-
try {
|
|
413
|
-
await client.sendEvent({
|
|
414
|
-
name: 'TEST.EVENT',
|
|
415
|
-
entityRef: 'TEST-123',
|
|
416
|
-
entityType: 'ORDER'
|
|
417
|
-
// ❌ Missing retailerId
|
|
418
|
-
}, 'async');
|
|
419
|
-
} catch (error) {
|
|
420
|
-
if (error instanceof Error && error.message.includes('retailerId is required')) {
|
|
421
|
-
console.error('❌ retailerId validation failed (caught BEFORE API call)');
|
|
422
|
-
// Fix: Add retailerId to event or client config
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
---
|
|
428
|
-
|
|
429
|
-
### Sync Mode - Success and Error Examples
|
|
430
|
-
|
|
431
|
-
#### Sync Mode Success
|
|
432
|
-
|
|
433
|
-
**Input:**
|
|
434
|
-
```json
|
|
435
|
-
{
|
|
436
|
-
"name": "OrderCancel1",
|
|
437
|
-
"entityRef": "ORDER-SYNC-123",
|
|
438
|
-
"entityType": "ORDER",
|
|
439
|
-
"retailerId": "2"
|
|
440
|
-
}
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
**Output (if workflow configured and succeeds):**
|
|
444
|
-
```json
|
|
445
|
-
{
|
|
446
|
-
"success": true,
|
|
447
|
-
"status": "COMPLETED",
|
|
448
|
-
"statusCode": 201,
|
|
449
|
-
"message": "Workflow completed successfully"
|
|
450
|
-
}
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
**Code:**
|
|
454
|
-
```typescript
|
|
455
|
-
const result = await client.sendEvent({
|
|
456
|
-
name: 'OrderCancel1',
|
|
457
|
-
entityRef: 'ORDER-SYNC-123',
|
|
458
|
-
entityType: 'ORDER',
|
|
459
|
-
retailerId: '2'
|
|
460
|
-
}, 'sync'); // ← Sync mode waits for workflow
|
|
461
|
-
|
|
462
|
-
console.log('Workflow completed:', result.status);
|
|
463
|
-
console.log('HTTP Status:', result.statusCode);
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
---
|
|
467
|
-
|
|
468
|
-
#### Sync Mode Error: No Workflow Match (404)
|
|
469
|
-
|
|
470
|
-
**Input:**
|
|
471
|
-
```json
|
|
472
|
-
{
|
|
473
|
-
"name": "OrderCancel1",
|
|
474
|
-
"entityRef": "SYNC-NO-MATCH",
|
|
475
|
-
"entityType": "ORDER",
|
|
476
|
-
"retailerId": "2"
|
|
477
|
-
}
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
**Error Thrown:**
|
|
481
|
-
```typescript
|
|
482
|
-
FluentAPIError {
|
|
483
|
-
name: "FluentAPIError",
|
|
484
|
-
message: "Request failed: 404",
|
|
485
|
-
statusCode: 404,
|
|
486
|
-
details: "No workflow rule matched this event"
|
|
487
|
-
}
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
**Handling:**
|
|
491
|
-
```typescript
|
|
492
|
-
try {
|
|
493
|
-
await client.sendEvent({
|
|
494
|
-
name: 'OrderCancel1',
|
|
495
|
-
entityRef: 'SYNC-NO-MATCH',
|
|
496
|
-
entityType: 'ORDER',
|
|
497
|
-
retailerId: '2'
|
|
498
|
-
}, 'sync');
|
|
499
|
-
} catch (error) {
|
|
500
|
-
if (error instanceof FluentAPIError && error.statusCode === 404) {
|
|
501
|
-
console.error('No workflow rule matched (sync mode detected this)');
|
|
502
|
-
// ⚠️ Async mode would return 200 (doesn't wait for workflow)
|
|
503
|
-
// ✅ Sync mode detected the failure immediately
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
---
|
|
509
|
-
|
|
510
|
-
### Batch Processing with Per-Event Error Handling
|
|
511
|
-
|
|
512
|
-
```typescript
|
|
513
|
-
interface EventResult {
|
|
514
|
-
sku: string;
|
|
515
|
-
success: boolean;
|
|
516
|
-
statusCode?: number;
|
|
517
|
-
error?: string;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
async function processProducts(products: Product[]): Promise<EventResult[]> {
|
|
521
|
-
const results: EventResult[] = [];
|
|
522
|
-
|
|
523
|
-
for (const product of products) {
|
|
524
|
-
try {
|
|
525
|
-
const response = await client.sendEvent({
|
|
526
|
-
name: 'PRODUCT.UPSERT',
|
|
527
|
-
entityType: 'PRODUCT',
|
|
528
|
-
entityRef: product.sku,
|
|
529
|
-
retailerId: '2',
|
|
530
|
-
attributes: product
|
|
531
|
-
}, 'async');
|
|
532
|
-
|
|
533
|
-
// SUCCESS
|
|
534
|
-
results.push({
|
|
535
|
-
sku: product.sku,
|
|
536
|
-
success: true,
|
|
537
|
-
statusCode: response.statusCode
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
console.log(`✅ ${product.sku} - HTTP ${response.statusCode}`);
|
|
541
|
-
|
|
542
|
-
} catch (error) {
|
|
543
|
-
if (error instanceof FluentAPIError) {
|
|
544
|
-
// API ERROR (SDK already retried 401/5xx)
|
|
545
|
-
results.push({
|
|
546
|
-
sku: product.sku,
|
|
547
|
-
success: false,
|
|
548
|
-
error: `HTTP ${error.statusCode}: ${error.message}`
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
console.error(`❌ ${product.sku} - ${error.statusCode}: ${error.message}`);
|
|
552
|
-
|
|
553
|
-
// Handle specific error codes
|
|
554
|
-
if (error.statusCode === 400) {
|
|
555
|
-
console.error(' → Invalid product data');
|
|
556
|
-
} else if (error.statusCode === 422) {
|
|
557
|
-
console.error(' → Validation failed');
|
|
558
|
-
} else if (error.statusCode === 500) {
|
|
559
|
-
console.error(' → Server error (SDK already retried 3x)');
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
} else if (error instanceof AuthenticationError) {
|
|
563
|
-
// AUTH FAILED (after 3 retries)
|
|
564
|
-
console.error(`❌ Auth failed: ${error.message}`);
|
|
565
|
-
// Stop processing - credentials are invalid
|
|
566
|
-
break;
|
|
567
|
-
|
|
568
|
-
} else {
|
|
569
|
-
// UNEXPECTED ERROR
|
|
570
|
-
results.push({
|
|
571
|
-
sku: product.sku,
|
|
572
|
-
success: false,
|
|
573
|
-
error: error.message
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
return results;
|
|
580
|
-
}
|
|
581
|
-
```
|
|
582
|
-
|
|
583
|
-
---
|
|
584
|
-
|
|
585
|
-
## GraphQL Error Handling
|
|
586
|
-
|
|
587
|
-
### Success Response Structure
|
|
588
|
-
|
|
589
|
-
```typescript
|
|
590
|
-
interface GraphQLResponse {
|
|
591
|
-
data: any; // Query/mutation result
|
|
592
|
-
errors?: undefined; // No errors on success
|
|
593
|
-
}
|
|
594
|
-
```
|
|
595
|
-
|
|
596
|
-
### Error Response Structure
|
|
597
|
-
|
|
598
|
-
When GraphQL returns errors, SDK throws `GraphQLExecutionError`:
|
|
599
|
-
|
|
600
|
-
```typescript
|
|
601
|
-
{
|
|
602
|
-
name: 'GraphQLExecutionError',
|
|
603
|
-
message: string, // First error message
|
|
604
|
-
graphqlErrors: Array<{
|
|
605
|
-
message: string,
|
|
606
|
-
locations?: Array<{ line: number, column: number }>,
|
|
607
|
-
path?: Array<string | number>,
|
|
608
|
-
extensions?: { ... }
|
|
609
|
-
}>,
|
|
610
|
-
query: string,
|
|
611
|
-
variables?: { ... }
|
|
612
|
-
}
|
|
613
|
-
```
|
|
614
|
-
|
|
615
|
-
### Basic Try-Catch Pattern
|
|
616
|
-
|
|
617
|
-
```typescript
|
|
618
|
-
try {
|
|
619
|
-
// SUCCESS CASE
|
|
620
|
-
const result = await client.graphql({
|
|
621
|
-
query: `
|
|
622
|
-
query GetOrder($ref: String!) {
|
|
623
|
-
order(ref: $ref) {
|
|
624
|
-
id
|
|
625
|
-
ref
|
|
626
|
-
status
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
`,
|
|
630
|
-
variables: { ref: 'ORDER-123' }
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
console.log('✅ GraphQL query succeeded');
|
|
634
|
-
console.log('Data:', result.data);
|
|
635
|
-
console.log('Order:', result.data.order);
|
|
636
|
-
|
|
637
|
-
} catch (error) {
|
|
638
|
-
if (error instanceof GraphQLExecutionError) {
|
|
639
|
-
// GraphQL returned errors
|
|
640
|
-
console.error('❌ GraphQL Execution Error');
|
|
641
|
-
console.error('Message:', error.message);
|
|
642
|
-
console.error('All errors:', error.getErrorMessages());
|
|
643
|
-
console.error('Query:', error.query);
|
|
644
|
-
|
|
645
|
-
// Check for specific errors
|
|
646
|
-
const errorMessages = error.getErrorMessages();
|
|
647
|
-
if (errorMessages.some(msg => msg.includes('invalidField'))) {
|
|
648
|
-
console.error(' → Invalid field in query');
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
} else if (error instanceof FluentAPIError) {
|
|
652
|
-
// HTTP error (400, 500, etc.)
|
|
653
|
-
console.error('❌ API Error');
|
|
654
|
-
console.error('Status:', error.statusCode);
|
|
655
|
-
console.error('Message:', error.message);
|
|
656
|
-
|
|
657
|
-
} else if (error instanceof AuthenticationError) {
|
|
658
|
-
// Auth failed
|
|
659
|
-
console.error('❌ Authentication failed');
|
|
660
|
-
|
|
661
|
-
} else {
|
|
662
|
-
console.error('❌ Unexpected error:', error);
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
```
|
|
666
|
-
|
|
667
|
-
### GraphQL Success Example
|
|
668
|
-
|
|
669
|
-
**Input:**
|
|
670
|
-
```graphql
|
|
671
|
-
query GetOrder($ref: String!) {
|
|
672
|
-
order(ref: $ref) {
|
|
673
|
-
id
|
|
674
|
-
ref
|
|
675
|
-
status
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
```
|
|
679
|
-
|
|
680
|
-
**Variables:**
|
|
681
|
-
```json
|
|
682
|
-
{
|
|
683
|
-
"ref": "ORDER-123"
|
|
684
|
-
}
|
|
685
|
-
```
|
|
686
|
-
|
|
687
|
-
**Output (Success):**
|
|
688
|
-
```json
|
|
689
|
-
{
|
|
690
|
-
"data": {
|
|
691
|
-
"order": {
|
|
692
|
-
"id": "ORDER-123",
|
|
693
|
-
"ref": "ORD-2024-123",
|
|
694
|
-
"status": "CREATED"
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
```
|
|
699
|
-
|
|
700
|
-
**Code:**
|
|
701
|
-
```typescript
|
|
702
|
-
const result = await client.graphql({
|
|
703
|
-
query: `query GetOrder($ref: String!) { order(ref: $ref) { id ref status } }`,
|
|
704
|
-
variables: { ref: 'ORDER-123' }
|
|
705
|
-
});
|
|
706
|
-
|
|
707
|
-
console.log('Order ID:', result.data.order.id);
|
|
708
|
-
console.log('Order Ref:', result.data.order.ref);
|
|
709
|
-
console.log('Order Status:', result.data.order.status);
|
|
710
|
-
```
|
|
711
|
-
|
|
712
|
-
---
|
|
713
|
-
|
|
714
|
-
### GraphQL Error Examples
|
|
715
|
-
|
|
716
|
-
#### Error 1: Invalid Field
|
|
717
|
-
|
|
718
|
-
**Input:**
|
|
719
|
-
```graphql
|
|
720
|
-
query GetOrder {
|
|
721
|
-
order(ref: "123") {
|
|
722
|
-
id
|
|
723
|
-
invalidField # ❌ Field doesn't exist
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
```
|
|
727
|
-
|
|
728
|
-
**Error Thrown:**
|
|
729
|
-
```typescript
|
|
730
|
-
GraphQLExecutionError {
|
|
731
|
-
name: "GraphQLExecutionError",
|
|
732
|
-
message: "Field \"invalidField\" doesn't exist on type \"Order\"",
|
|
733
|
-
graphqlErrors: [
|
|
734
|
-
{
|
|
735
|
-
message: "Field \"invalidField\" doesn't exist on type \"Order\"",
|
|
736
|
-
locations: [{ line: 4, column: 5 }],
|
|
737
|
-
extensions: { ... }
|
|
738
|
-
}
|
|
739
|
-
],
|
|
740
|
-
query: "query GetOrder { ... }"
|
|
741
|
-
}
|
|
742
|
-
```
|
|
743
|
-
|
|
744
|
-
**Handling:**
|
|
745
|
-
```typescript
|
|
746
|
-
try {
|
|
747
|
-
await client.graphql({
|
|
748
|
-
query: `query GetOrder { order(ref: "123") { id invalidField } }`
|
|
749
|
-
});
|
|
750
|
-
} catch (error) {
|
|
751
|
-
if (error instanceof GraphQLExecutionError) {
|
|
752
|
-
console.error('GraphQL Error:', error.message);
|
|
753
|
-
|
|
754
|
-
// Get all error messages
|
|
755
|
-
const messages = error.getErrorMessages();
|
|
756
|
-
console.error('All errors:', messages);
|
|
757
|
-
|
|
758
|
-
// Check for specific error
|
|
759
|
-
const errorMessages = error.getErrorMessages();
|
|
760
|
-
if (errorMessages.some(msg => msg.includes('invalidField'))) {
|
|
761
|
-
console.error('Invalid field in query - check schema');
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
```
|
|
766
|
-
|
|
767
|
-
---
|
|
768
|
-
|
|
769
|
-
#### Error 2: Syntax Error
|
|
770
|
-
|
|
771
|
-
**Input:**
|
|
772
|
-
```graphql
|
|
773
|
-
query GetOrder {
|
|
774
|
-
order(ref: "123" # ❌ Missing closing parenthesis
|
|
775
|
-
id
|
|
776
|
-
ref
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
```
|
|
780
|
-
|
|
781
|
-
**Error Thrown:**
|
|
782
|
-
```typescript
|
|
783
|
-
FluentAPIError {
|
|
784
|
-
name: "FluentAPIError",
|
|
785
|
-
message: "Request failed: 400",
|
|
786
|
-
statusCode: 400,
|
|
787
|
-
details: {
|
|
788
|
-
errors: [
|
|
789
|
-
{
|
|
790
|
-
message: "Syntax Error: Expected ')', found Name 'id'",
|
|
791
|
-
locations: [{ line: 3, column: 5 }]
|
|
792
|
-
}
|
|
793
|
-
]
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
```
|
|
797
|
-
|
|
798
|
-
**Note**: Syntax errors throw `FluentAPIError` (400), not `GraphQLExecutionError`.
|
|
799
|
-
|
|
800
|
-
---
|
|
801
|
-
|
|
802
|
-
### GraphQL with Pagination
|
|
803
|
-
|
|
804
|
-
```typescript
|
|
805
|
-
try {
|
|
806
|
-
const result = await client.graphql({
|
|
807
|
-
query: `
|
|
808
|
-
query GetOrders {
|
|
809
|
-
orders(first: 50) {
|
|
810
|
-
edges {
|
|
811
|
-
node {
|
|
812
|
-
id
|
|
813
|
-
ref
|
|
814
|
-
status
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
pageInfo {
|
|
818
|
-
hasNextPage
|
|
819
|
-
endCursor
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
`,
|
|
824
|
-
pagination: {
|
|
825
|
-
enabled: true,
|
|
826
|
-
maxPages: 10,
|
|
827
|
-
maxRecords: 500
|
|
828
|
-
}
|
|
829
|
-
});
|
|
830
|
-
|
|
831
|
-
console.log('✅ Fetched', result.data.orders.edges.length, 'orders');
|
|
832
|
-
console.log('Total pages fetched:', result.extensions?.autoPagination?.totalPages);
|
|
833
|
-
|
|
834
|
-
} catch (error) {
|
|
835
|
-
if (error instanceof GraphQLExecutionError) {
|
|
836
|
-
console.error('GraphQL error during pagination:', error.message);
|
|
837
|
-
} else if (error instanceof FluentAPIError) {
|
|
838
|
-
console.error('API error during pagination:', error.statusCode);
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
```
|
|
842
|
-
|
|
843
|
-
---
|
|
844
|
-
|
|
845
|
-
## Batch API Error Handling
|
|
846
|
-
|
|
847
|
-
### Success Response Structure
|
|
848
|
-
|
|
849
|
-
```typescript
|
|
850
|
-
interface FluentBatchResponse {
|
|
851
|
-
id: string; // Batch ID
|
|
852
|
-
jobId: string; // Job ID
|
|
853
|
-
status: string; // Batch status (CREATED, SUBMITTED, PROCESSING, COMPLETED, FAILED)
|
|
854
|
-
processedEntities?: number; // Number of entities processed
|
|
855
|
-
totalEntities?: number; // Total entities in batch
|
|
856
|
-
createdAt?: string; // ISO timestamp
|
|
857
|
-
updatedAt?: string; // ISO timestamp
|
|
858
|
-
errors?: Array<{ // Failed entity details (if any)
|
|
859
|
-
code: string;
|
|
860
|
-
message: string;
|
|
861
|
-
entityIndex?: number;
|
|
862
|
-
}>;
|
|
863
|
-
}
|
|
864
|
-
```
|
|
865
|
-
|
|
866
|
-
### Basic Try-Catch Pattern
|
|
867
|
-
|
|
868
|
-
```typescript
|
|
869
|
-
try {
|
|
870
|
-
// SUCCESS CASE
|
|
871
|
-
const batch = await client.sendBatch('job-123', {
|
|
872
|
-
entities: [
|
|
873
|
-
{ id: '1', sku: 'SKU-001', name: 'Product 1' },
|
|
874
|
-
{ id: '2', sku: 'SKU-002', name: 'Product 2' }
|
|
875
|
-
],
|
|
876
|
-
action: 'UPSERT'
|
|
877
|
-
});
|
|
878
|
-
|
|
879
|
-
console.log('✅ Batch sent successfully');
|
|
880
|
-
console.log('Batch ID:', batch.id);
|
|
881
|
-
console.log('Status:', batch.status);
|
|
882
|
-
|
|
883
|
-
} catch (error) {
|
|
884
|
-
if (error instanceof FluentAPIError) {
|
|
885
|
-
console.error('❌ Batch Error');
|
|
886
|
-
console.error('Status:', error.statusCode);
|
|
887
|
-
console.error('Message:', error.message);
|
|
888
|
-
|
|
889
|
-
// Common errors
|
|
890
|
-
if (error.statusCode === 404) {
|
|
891
|
-
console.error(' → Job not found or expired');
|
|
892
|
-
} else if (error.statusCode === 400) {
|
|
893
|
-
console.error(' → Invalid batch payload');
|
|
894
|
-
} else if (error.statusCode === 500) {
|
|
895
|
-
console.error(' → Server error (SDK already retried 3x)');
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
```
|
|
900
|
-
|
|
901
|
-
### Batch Success Example
|
|
902
|
-
|
|
903
|
-
**Input:**
|
|
904
|
-
```typescript
|
|
905
|
-
await client.sendBatch('job-123', {
|
|
906
|
-
entities: [
|
|
907
|
-
{ id: '1', sku: 'SKU-001', name: 'Product 1', price: 19.99 },
|
|
908
|
-
{ id: '2', sku: 'SKU-002', name: 'Product 2', price: 29.99 }
|
|
909
|
-
],
|
|
910
|
-
action: 'UPSERT'
|
|
911
|
-
});
|
|
912
|
-
```
|
|
913
|
-
|
|
914
|
-
**Output (Success):**
|
|
915
|
-
```json
|
|
916
|
-
{
|
|
917
|
-
"id": "batch-456",
|
|
918
|
-
"jobId": "job-123",
|
|
919
|
-
"status": "CREATED",
|
|
920
|
-
"processedEntities": 2,
|
|
921
|
-
"totalEntities": 2
|
|
922
|
-
}
|
|
923
|
-
```
|
|
924
|
-
|
|
925
|
-
---
|
|
926
|
-
|
|
927
|
-
### Batch Error Examples
|
|
928
|
-
|
|
929
|
-
#### Error 1: Job Expired (404)
|
|
930
|
-
|
|
931
|
-
**Input:**
|
|
932
|
-
```typescript
|
|
933
|
-
await client.sendBatch('job-expired-123', {
|
|
934
|
-
entities: [...],
|
|
935
|
-
action: 'UPSERT'
|
|
936
|
-
});
|
|
937
|
-
```
|
|
938
|
-
|
|
939
|
-
**Error Thrown:**
|
|
940
|
-
```typescript
|
|
941
|
-
FluentAPIError {
|
|
942
|
-
name: "FluentAPIError",
|
|
943
|
-
message: "Request failed: 404",
|
|
944
|
-
statusCode: 404,
|
|
945
|
-
details: "Job not found or expired"
|
|
946
|
-
}
|
|
947
|
-
```
|
|
948
|
-
|
|
949
|
-
**Handling:**
|
|
950
|
-
```typescript
|
|
951
|
-
try {
|
|
952
|
-
await client.sendBatch(jobId, { entities, action: 'UPSERT' });
|
|
953
|
-
} catch (error) {
|
|
954
|
-
if (error instanceof FluentAPIError && error.statusCode === 404) {
|
|
955
|
-
console.error('Job expired - recreating...');
|
|
956
|
-
|
|
957
|
-
// Recreate job and retry
|
|
958
|
-
const newJob = await client.createJob({
|
|
959
|
-
name: 'inventory-sync',
|
|
960
|
-
retailerId: '2'
|
|
961
|
-
});
|
|
962
|
-
|
|
963
|
-
await client.sendBatch(newJob.id, { entities, action: 'UPSERT' });
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
```
|
|
967
|
-
|
|
968
|
-
---
|
|
969
|
-
|
|
970
|
-
#### Error 2: Invalid Batch Payload (400)
|
|
971
|
-
|
|
972
|
-
**Input:**
|
|
973
|
-
```typescript
|
|
974
|
-
await client.sendBatch('job-123', {
|
|
975
|
-
entities: [], // ❌ Empty entities array
|
|
976
|
-
action: 'UPSERT'
|
|
977
|
-
});
|
|
978
|
-
```
|
|
979
|
-
|
|
980
|
-
**Error Thrown:**
|
|
981
|
-
```typescript
|
|
982
|
-
FluentAPIError {
|
|
983
|
-
name: "FluentAPIError",
|
|
984
|
-
message: "Request failed: 400",
|
|
985
|
-
statusCode: 400,
|
|
986
|
-
details: "Batch must contain at least one entity"
|
|
987
|
-
}
|
|
988
|
-
```
|
|
989
|
-
|
|
990
|
-
---
|
|
991
|
-
|
|
992
|
-
### Batch Processing with Retry
|
|
993
|
-
|
|
994
|
-
```typescript
|
|
995
|
-
async function sendBatchWithRetry(
|
|
996
|
-
jobId: string,
|
|
997
|
-
entities: any[],
|
|
998
|
-
maxRetries: number = 3
|
|
999
|
-
): Promise<FluentBatchResponse> {
|
|
1000
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1001
|
-
try {
|
|
1002
|
-
const batch = await client.sendBatch(jobId, {
|
|
1003
|
-
entities,
|
|
1004
|
-
action: 'UPSERT'
|
|
1005
|
-
});
|
|
1006
|
-
|
|
1007
|
-
console.log(`✅ Batch sent (attempt ${attempt + 1})`);
|
|
1008
|
-
return batch;
|
|
1009
|
-
|
|
1010
|
-
} catch (error) {
|
|
1011
|
-
if (error instanceof FluentAPIError) {
|
|
1012
|
-
if (error.statusCode === 404 && attempt < maxRetries) {
|
|
1013
|
-
// Job expired - recreate
|
|
1014
|
-
console.log(`⚠️ Job expired, recreating... (attempt ${attempt + 1})`);
|
|
1015
|
-
|
|
1016
|
-
const newJob = await client.createJob({
|
|
1017
|
-
name: 'inventory-sync',
|
|
1018
|
-
retailerId: '2'
|
|
1019
|
-
});
|
|
1020
|
-
|
|
1021
|
-
jobId = newJob.id;
|
|
1022
|
-
// Retry with new job ID
|
|
1023
|
-
continue;
|
|
1024
|
-
|
|
1025
|
-
} else if (error.statusCode === 500 && attempt < maxRetries) {
|
|
1026
|
-
// Server error - SDK already retried 3x
|
|
1027
|
-
console.log(`⚠️ Server error, waiting before retry...`);
|
|
1028
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
1029
|
-
continue;
|
|
1030
|
-
|
|
1031
|
-
} else {
|
|
1032
|
-
// Non-retryable error or max retries reached
|
|
1033
|
-
throw error;
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
throw error;
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
throw new Error('Max retries exceeded');
|
|
1041
|
-
}
|
|
1042
|
-
```
|
|
1043
|
-
|
|
1044
|
-
---
|
|
1045
|
-
|
|
1046
|
-
## Job API Error Handling
|
|
1047
|
-
|
|
1048
|
-
### Success Response Structure
|
|
1049
|
-
|
|
1050
|
-
```typescript
|
|
1051
|
-
interface FluentJobResponse {
|
|
1052
|
-
id: string; // Job ID
|
|
1053
|
-
name: string; // Job name
|
|
1054
|
-
retailerId: string;
|
|
1055
|
-
status?: string; // Job status (CREATED, ACTIVE, PROCESSING, COMPLETED, FAILED)
|
|
1056
|
-
meta?: FluentJobMetadata; // Job metadata (preprocessing, source, etc.)
|
|
1057
|
-
createdAt?: string; // ISO timestamp
|
|
1058
|
-
updatedAt?: string; // ISO timestamp
|
|
1059
|
-
}
|
|
1060
|
-
```
|
|
1061
|
-
|
|
1062
|
-
### Basic Try-Catch Pattern
|
|
1063
|
-
|
|
1064
|
-
```typescript
|
|
1065
|
-
try {
|
|
1066
|
-
// SUCCESS CASE
|
|
1067
|
-
const job = await client.createJob({
|
|
1068
|
-
name: 'inventory-sync',
|
|
1069
|
-
retailerId: '2'
|
|
1070
|
-
});
|
|
1071
|
-
|
|
1072
|
-
console.log('✅ Job created successfully');
|
|
1073
|
-
console.log('Job ID:', job.id);
|
|
1074
|
-
console.log('Status:', job.status);
|
|
1075
|
-
console.log('Created:', job.createdAt);
|
|
1076
|
-
|
|
1077
|
-
} catch (error) {
|
|
1078
|
-
if (error instanceof FluentAPIError) {
|
|
1079
|
-
console.error('❌ Job Creation Error');
|
|
1080
|
-
console.error('Status:', error.statusCode);
|
|
1081
|
-
console.error('Message:', error.message);
|
|
1082
|
-
|
|
1083
|
-
if (error.statusCode === 400) {
|
|
1084
|
-
console.error(' → Invalid job payload');
|
|
1085
|
-
} else if (error.statusCode === 403) {
|
|
1086
|
-
console.error(' → Permission denied');
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
```
|
|
1091
|
-
|
|
1092
|
-
### Job Success Example
|
|
1093
|
-
|
|
1094
|
-
**Input:**
|
|
1095
|
-
```typescript
|
|
1096
|
-
await client.createJob({
|
|
1097
|
-
name: 'product-import-batch',
|
|
1098
|
-
retailerId: '2'
|
|
1099
|
-
});
|
|
1100
|
-
```
|
|
1101
|
-
|
|
1102
|
-
**Output (Success):**
|
|
1103
|
-
```json
|
|
1104
|
-
{
|
|
1105
|
-
"id": "job-789",
|
|
1106
|
-
"name": "product-import-batch",
|
|
1107
|
-
"status": "CREATED",
|
|
1108
|
-
"retailerId": "2",
|
|
1109
|
-
"createdAt": "2025-01-19T10:30:00Z",
|
|
1110
|
-
"updatedAt": "2025-01-19T10:30:00Z"
|
|
1111
|
-
}
|
|
1112
|
-
```
|
|
1113
|
-
|
|
1114
|
-
---
|
|
1115
|
-
|
|
1116
|
-
### Job Status Checking
|
|
1117
|
-
|
|
1118
|
-
**Response Structure:**
|
|
1119
|
-
```typescript
|
|
1120
|
-
interface FluentJobStatus {
|
|
1121
|
-
id: string;
|
|
1122
|
-
status: string; // Job status (CREATED, ACTIVE, PROCESSING, COMPLETED, FAILED)
|
|
1123
|
-
progress?: number; // Progress percentage (0-100)
|
|
1124
|
-
message?: string; // Status message
|
|
1125
|
-
completedAt?: string; // ISO timestamp when completed
|
|
1126
|
-
failedAt?: string; // ISO timestamp when failed
|
|
1127
|
-
errors?: Array<{ // Error details (if failed)
|
|
1128
|
-
code: string;
|
|
1129
|
-
message: string;
|
|
1130
|
-
}>;
|
|
1131
|
-
}
|
|
1132
|
-
```
|
|
1133
|
-
|
|
1134
|
-
**Example:**
|
|
1135
|
-
```typescript
|
|
1136
|
-
try {
|
|
1137
|
-
const status = await client.getJobStatus('job-789');
|
|
1138
|
-
|
|
1139
|
-
console.log('Job Status:', status.status);
|
|
1140
|
-
console.log('Progress:', status.progress);
|
|
1141
|
-
|
|
1142
|
-
if (status.status === 'FAILED') {
|
|
1143
|
-
console.warn('⚠️ Job failed - check errors');
|
|
1144
|
-
console.error('Errors:', status.errors);
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
} catch (error) {
|
|
1148
|
-
if (error instanceof FluentAPIError && error.statusCode === 404) {
|
|
1149
|
-
console.error('Job not found');
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
```
|
|
1153
|
-
|
|
1154
|
-
---
|
|
1155
|
-
|
|
1156
|
-
## Common Patterns
|
|
1157
|
-
|
|
1158
|
-
### Pattern 1: Centralized Error Handler
|
|
1159
|
-
|
|
1160
|
-
```typescript
|
|
1161
|
-
function handleSDKError(error: unknown, context: string): void {
|
|
1162
|
-
if (error instanceof FluentAPIError) {
|
|
1163
|
-
console.error(`[${context}] API Error:`, {
|
|
1164
|
-
status: error.statusCode,
|
|
1165
|
-
message: error.message,
|
|
1166
|
-
details: error.details
|
|
1167
|
-
});
|
|
1168
|
-
|
|
1169
|
-
// Log to monitoring service
|
|
1170
|
-
logger.error('Fluent API Error', {
|
|
1171
|
-
context,
|
|
1172
|
-
status: error.statusCode,
|
|
1173
|
-
message: error.message
|
|
1174
|
-
});
|
|
1175
|
-
|
|
1176
|
-
} else if (error instanceof GraphQLExecutionError) {
|
|
1177
|
-
console.error(`[${context}] GraphQL Error:`, {
|
|
1178
|
-
message: error.message,
|
|
1179
|
-
errors: error.getErrorMessages(),
|
|
1180
|
-
query: error.query
|
|
1181
|
-
});
|
|
1182
|
-
|
|
1183
|
-
logger.error('GraphQL Execution Error', {
|
|
1184
|
-
context,
|
|
1185
|
-
errors: error.getErrorMessages()
|
|
1186
|
-
});
|
|
1187
|
-
|
|
1188
|
-
} else if (error instanceof AuthenticationError) {
|
|
1189
|
-
console.error(`[${context}] Auth Error:`, {
|
|
1190
|
-
message: error.message,
|
|
1191
|
-
status: error.statusCode
|
|
1192
|
-
});
|
|
1193
|
-
|
|
1194
|
-
// Alert - credentials may be invalid
|
|
1195
|
-
alertService.send('Fluent Auth Failure', error.message);
|
|
1196
|
-
|
|
1197
|
-
} else {
|
|
1198
|
-
console.error(`[${context}] Unexpected Error:`, error);
|
|
1199
|
-
logger.error('Unexpected SDK Error', { context, error });
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
// Usage
|
|
1204
|
-
try {
|
|
1205
|
-
await client.sendEvent(event);
|
|
1206
|
-
} catch (error) {
|
|
1207
|
-
handleSDKError(error, 'Event API');
|
|
1208
|
-
throw error; // Re-throw if needed
|
|
1209
|
-
}
|
|
1210
|
-
```
|
|
1211
|
-
|
|
1212
|
-
---
|
|
1213
|
-
|
|
1214
|
-
### Pattern 2: Retry with Backoff
|
|
1215
|
-
|
|
1216
|
-
```typescript
|
|
1217
|
-
async function retryWithBackoff<T>(
|
|
1218
|
-
operation: () => Promise<T>,
|
|
1219
|
-
maxRetries: number = 3,
|
|
1220
|
-
baseDelay: number = 1000
|
|
1221
|
-
): Promise<T> {
|
|
1222
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1223
|
-
try {
|
|
1224
|
-
return await operation();
|
|
1225
|
-
} catch (error) {
|
|
1226
|
-
if (error instanceof FluentAPIError) {
|
|
1227
|
-
// Only retry 5xx server errors
|
|
1228
|
-
if (error.statusCode >= 500 && attempt < maxRetries) {
|
|
1229
|
-
const delay = baseDelay * Math.pow(2, attempt);
|
|
1230
|
-
console.log(`⚠️ Server error, retrying in ${delay}ms...`);
|
|
1231
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
1232
|
-
continue;
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
throw error;
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
throw new Error('Max retries exceeded');
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1241
|
-
// Usage
|
|
1242
|
-
const result = await retryWithBackoff(
|
|
1243
|
-
() => client.sendEvent(event),
|
|
1244
|
-
3,
|
|
1245
|
-
1000
|
|
1246
|
-
);
|
|
1247
|
-
```
|
|
1248
|
-
|
|
1249
|
-
---
|
|
1250
|
-
|
|
1251
|
-
### Pattern 3: Batch with Dead Letter Queue
|
|
1252
|
-
|
|
1253
|
-
```typescript
|
|
1254
|
-
interface ProcessingResult {
|
|
1255
|
-
successful: any[];
|
|
1256
|
-
failed: Array<{ item: any; error: string }>;
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
async function processBatchWithDLQ(
|
|
1260
|
-
items: any[]
|
|
1261
|
-
): Promise<ProcessingResult> {
|
|
1262
|
-
const result: ProcessingResult = {
|
|
1263
|
-
successful: [],
|
|
1264
|
-
failed: []
|
|
1265
|
-
};
|
|
1266
|
-
|
|
1267
|
-
for (const item of items) {
|
|
1268
|
-
try {
|
|
1269
|
-
const response = await client.sendEvent({
|
|
1270
|
-
name: 'PRODUCT.UPSERT',
|
|
1271
|
-
entityType: 'PRODUCT',
|
|
1272
|
-
entityRef: item.sku,
|
|
1273
|
-
retailerId: '2',
|
|
1274
|
-
attributes: item
|
|
1275
|
-
});
|
|
1276
|
-
|
|
1277
|
-
result.successful.push(item);
|
|
1278
|
-
|
|
1279
|
-
} catch (error) {
|
|
1280
|
-
if (error instanceof FluentAPIError) {
|
|
1281
|
-
// Add to dead letter queue
|
|
1282
|
-
result.failed.push({
|
|
1283
|
-
item,
|
|
1284
|
-
error: `HTTP ${error.statusCode}: ${error.message}`
|
|
1285
|
-
});
|
|
1286
|
-
|
|
1287
|
-
// Log to DLQ for manual review
|
|
1288
|
-
await deadLetterQueue.add({
|
|
1289
|
-
item,
|
|
1290
|
-
error: error.message,
|
|
1291
|
-
status: error.statusCode,
|
|
1292
|
-
timestamp: new Date().toISOString()
|
|
1293
|
-
});
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
|
-
return result;
|
|
1299
|
-
}
|
|
1300
|
-
```
|
|
1301
|
-
|
|
1302
|
-
---
|
|
1303
|
-
|
|
1304
|
-
## Live Examples
|
|
1305
|
-
|
|
1306
|
-
### Test Environment
|
|
1307
|
-
|
|
1308
|
-
```yaml
|
|
1309
|
-
Base URL: https://api.fluentcommerce.com
|
|
1310
|
-
Retailer ID: YOUR_RETAILER_ID
|
|
1311
|
-
Client ID: YOUR_CLIENT_ID
|
|
1312
|
-
```
|
|
1313
|
-
|
|
1314
|
-
### Example 1: Event API Success
|
|
1315
|
-
|
|
1316
|
-
**Code:**
|
|
1317
|
-
```typescript
|
|
1318
|
-
const client = new FluentClient({
|
|
1319
|
-
baseUrl: 'https://api.fluentcommerce.com',
|
|
1320
|
-
clientId: 'YOUR_CLIENT_ID',
|
|
1321
|
-
clientSecret: 'YOUR_CLIENT_SECRET',
|
|
1322
|
-
username: 'YOUR_USERNAME',
|
|
1323
|
-
password: 'YOUR_PASSWORD',
|
|
1324
|
-
retailerId: 'YOUR_RETAILER_ID'
|
|
1325
|
-
});
|
|
1326
|
-
|
|
1327
|
-
const result = await client.sendEvent({
|
|
1328
|
-
name: 'OrderCancel1',
|
|
1329
|
-
entityRef: 'G_TEST_FC_POSTMAN_34',
|
|
1330
|
-
entityType: 'ORDER',
|
|
1331
|
-
retailerId: '2'
|
|
1332
|
-
}, 'async');
|
|
1333
|
-
|
|
1334
|
-
console.log('Success:', result.success); // true
|
|
1335
|
-
console.log('HTTP Status:', result.statusCode); // 200
|
|
1336
|
-
```
|
|
1337
|
-
|
|
1338
|
-
**Console Output:**
|
|
1339
|
-
```
|
|
1340
|
-
Success: true
|
|
1341
|
-
HTTP Status: 200
|
|
1342
|
-
```
|
|
1343
|
-
|
|
1344
|
-
---
|
|
1345
|
-
|
|
1346
|
-
### Example 2: Event API Error (400)
|
|
1347
|
-
|
|
1348
|
-
**Code:**
|
|
1349
|
-
```typescript
|
|
1350
|
-
try {
|
|
1351
|
-
await client.sendEvent({
|
|
1352
|
-
name: 'InvalidEventName',
|
|
1353
|
-
entityRef: 'TEST-123',
|
|
1354
|
-
entityType: 'ORDER',
|
|
1355
|
-
retailerId: '2'
|
|
1356
|
-
}, 'async');
|
|
1357
|
-
} catch (error) {
|
|
1358
|
-
if (error instanceof FluentAPIError) {
|
|
1359
|
-
console.error('Error Status:', error.statusCode);
|
|
1360
|
-
console.error('Error Message:', error.message);
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
```
|
|
1364
|
-
|
|
1365
|
-
**Console Output:**
|
|
1366
|
-
```
|
|
1367
|
-
Error Status: 400
|
|
1368
|
-
Error Message: Request failed: 400
|
|
1369
|
-
```
|
|
1370
|
-
|
|
1371
|
-
---
|
|
1372
|
-
|
|
1373
|
-
### Example 3: Sync Mode - No Workflow Match (404)
|
|
1374
|
-
|
|
1375
|
-
**Code:**
|
|
1376
|
-
```typescript
|
|
1377
|
-
try {
|
|
1378
|
-
await client.sendEvent({
|
|
1379
|
-
name: 'OrderCancel1',
|
|
1380
|
-
entityRef: 'SYNC-NO-MATCH',
|
|
1381
|
-
entityType: 'ORDER',
|
|
1382
|
-
retailerId: '2'
|
|
1383
|
-
}, 'sync');
|
|
1384
|
-
} catch (error) {
|
|
1385
|
-
if (error instanceof FluentAPIError) {
|
|
1386
|
-
console.error('Sync mode detected workflow failure');
|
|
1387
|
-
console.error('Status:', error.statusCode); // 404
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
```
|
|
1391
|
-
|
|
1392
|
-
**Console Output:**
|
|
1393
|
-
```
|
|
1394
|
-
Sync mode detected workflow failure
|
|
1395
|
-
Status: 404
|
|
1396
|
-
```
|
|
1397
|
-
|
|
1398
|
-
---
|
|
1399
|
-
|
|
1400
|
-
## Summary Table
|
|
1401
|
-
|
|
1402
|
-
| API | Success Type | Error Type | SDK Retries | Notes |
|
|
1403
|
-
|-----|-------------|------------|-------------|-------|
|
|
1404
|
-
| **Event API** | `EventResponse` | `FluentAPIError`, `AuthenticationError` | ✅ 401 (3x), 5xx (3x) | Async = fast, Sync = detects workflow errors |
|
|
1405
|
-
| **GraphQL** | `GraphQLResponse` | `GraphQLExecutionError`, `FluentAPIError`, `AuthenticationError` | ✅ 401 (3x), 5xx (3x) | GraphQL errors vs HTTP errors |
|
|
1406
|
-
| **Batch API** | `FluentBatchResponse` | `FluentAPIError`, `AuthenticationError` | ✅ 401 (3x), 5xx (3x) | Watch for job expiry (404) |
|
|
1407
|
-
| **Job API** | `FluentJobResponse` | `FluentAPIError`, `AuthenticationError` | ✅ 401 (3x), 5xx (3x) | Jobs expire after 1 hour |
|
|
1408
|
-
|
|
1409
|
-
---
|
|
1410
|
-
|
|
1411
|
-
## Quick Reference
|
|
1412
|
-
|
|
1413
|
-
### When to Use Try-Catch
|
|
1414
|
-
|
|
1415
|
-
✅ **Always use try-catch** for:
|
|
1416
|
-
- `sendEvent()`
|
|
1417
|
-
- `graphql()`
|
|
1418
|
-
- `sendBatch()`
|
|
1419
|
-
- `createJob()`
|
|
1420
|
-
- `getJobStatus()`
|
|
1421
|
-
- Any SDK method that makes API calls
|
|
1422
|
-
|
|
1423
|
-
### Error Handling Checklist
|
|
1424
|
-
|
|
1425
|
-
- [ ] Catch `FluentAPIError` for HTTP errors
|
|
1426
|
-
- [ ] Catch `AuthenticationError` for auth failures
|
|
1427
|
-
- [ ] Catch `GraphQLExecutionError` for GraphQL errors
|
|
1428
|
-
- [ ] Check `error.statusCode` to determine error type
|
|
1429
|
-
- [ ] Log errors with context for debugging
|
|
1430
|
-
- [ ] Implement retry logic for transient errors (5xx)
|
|
1431
|
-
- [ ] Use dead letter queue for persistent failures
|
|
1432
|
-
- [ ] Alert on authentication failures
|
|
1433
|
-
|
|
1434
|
-
---
|
|
1435
|
-
|
|
1436
|
-
**SDK Version**: 0.1.46
|
|
1437
|
-
**Date**: 2025-01-19
|
|
1
|
+
# Comprehensive Error Handling Guide
|
|
2
|
+
|
|
3
|
+
> **Complete guide** to error handling across GraphQL, Event API, Batch API, and Job API with live examples
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Error Types Overview](#error-types-overview)
|
|
8
|
+
2. [Where is the Status Code?](#where-is-the-status-code)
|
|
9
|
+
3. [Anatomy of an API Call](#anatomy-of-an-api-call)
|
|
10
|
+
4. [Event API Error Handling](#event-api-error-handling)
|
|
11
|
+
5. [GraphQL Error Handling](#graphql-error-handling)
|
|
12
|
+
6. [Batch API Error Handling](#batch-api-error-handling)
|
|
13
|
+
7. [Job API Error Handling](#job-api-error-handling)
|
|
14
|
+
8. [Common Patterns](#common-patterns)
|
|
15
|
+
9. [Live Examples](#live-examples)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Error Types Overview
|
|
20
|
+
|
|
21
|
+
> **📚 For complete error type reference**, see [Module 2: Error Types & Codes](./modules/error-handling-02-error-types.md)
|
|
22
|
+
|
|
23
|
+
**Quick Summary:**
|
|
24
|
+
|
|
25
|
+
| Error Type | When Thrown | HTTP Status | Auto-Retry |
|
|
26
|
+
|------------|-------------|-------------|------------|
|
|
27
|
+
| **FluentAPIError** | HTTP errors (4xx, 5xx) | 400, 404, 500, etc. | ✅ Yes (401, 5xx) |
|
|
28
|
+
| **AuthenticationError** | OAuth2 auth fails after 3 retries | 401 | N/A (already retried) |
|
|
29
|
+
| **GraphQLExecutionError** | GraphQL returns errors in response | 200 (with errors) | ❌ No |
|
|
30
|
+
|
|
31
|
+
### ⚡ How to Capture the Full Error Object
|
|
32
|
+
|
|
33
|
+
Standard logging (like `JSON.stringify(error)`) often misses custom fields. Use this helper to capture the full structure for your logs:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
/**
|
|
37
|
+
* Helper to extract full error details including HTTP status and response body
|
|
38
|
+
*/
|
|
39
|
+
function extractError(error: any) {
|
|
40
|
+
// 1. Use toJSON() if available (e.g. GraphQLExecutionError, IngestionError)
|
|
41
|
+
if (typeof error.toJSON === 'function') {
|
|
42
|
+
return error.toJSON();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 2. Manual extraction for FluentAPIError / Standard Errors
|
|
46
|
+
return {
|
|
47
|
+
name: error.name || 'Error',
|
|
48
|
+
message: error.message,
|
|
49
|
+
// Critical fields for FluentAPIError
|
|
50
|
+
statusCode: error.statusCode,
|
|
51
|
+
details: error.details,
|
|
52
|
+
code: error.code,
|
|
53
|
+
// Optional: stack trace
|
|
54
|
+
stack: error.stack
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Usage Example
|
|
59
|
+
const event = {
|
|
60
|
+
name: "UPSERT_PRODUCT",
|
|
61
|
+
retailerId: "YOUR_RETAILER_ID",
|
|
62
|
+
entityRef: "PC:MASTER:2",
|
|
63
|
+
entityType: "PRODUCT_CATALOGUE",
|
|
64
|
+
entitySubtype: "MASTER",
|
|
65
|
+
rootEntityRef: "PC:MASTER:2",
|
|
66
|
+
rootEntityType: "PRODUCT_CATALOGUE",
|
|
67
|
+
attributes: {
|
|
68
|
+
ref: "G_PROD_WITH_NO_STANDARD",
|
|
69
|
+
type: "VARIANT",
|
|
70
|
+
status: "ACTIVE",
|
|
71
|
+
gtin: "MH01-XS-Orange",
|
|
72
|
+
name: "Chaz Kangeroo Hoodie-XS-Orange main",
|
|
73
|
+
summary: "<p>test short description</p>",
|
|
74
|
+
categoryRefs: ["STANDARD_CATEGORY"],
|
|
75
|
+
price: [
|
|
76
|
+
{ type: "DEFAULT", currency: "USD", value: "52.000000" },
|
|
77
|
+
{ type: "SPECIAL", currency: "USD", value: "9.000000" }
|
|
78
|
+
],
|
|
79
|
+
taxType: {
|
|
80
|
+
country: "AU",
|
|
81
|
+
group: "Tax Group",
|
|
82
|
+
tariff: "Tax Tariff"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
await client.sendEvent(event);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
const fullError = extractError(error);
|
|
91
|
+
console.error('Error Thrown:', JSON.stringify(fullError, null, 2));
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Resulting Output:**
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"name": "FluentAPIError",
|
|
100
|
+
"message": "Request failed: 404",
|
|
101
|
+
"statusCode": 404,
|
|
102
|
+
"details": "No workflow rule matched this event"
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
[Full error type documentation →](./modules/error-handling-02-error-types.md)
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## ❓ Where is the Status Code?
|
|
111
|
+
|
|
112
|
+
The location of the HTTP status code depends on whether the call **Succeeded** or **Failed**.
|
|
113
|
+
|
|
114
|
+
### Event API (sendEvent)
|
|
115
|
+
|
|
116
|
+
| Outcome | Where is the Status Code? | Example Value |
|
|
117
|
+
| :--- | :--- | :--- |
|
|
118
|
+
| **✅ Success** | `response.statusCode` | `200`, `201` |
|
|
119
|
+
| **❌ Failure** | `error.statusCode` | `400`, `404`, `500` |
|
|
120
|
+
|
|
121
|
+
### GraphQL API (graphql)
|
|
122
|
+
|
|
123
|
+
| Outcome | Where is the Status Code? | Example Value |
|
|
124
|
+
| :--- | :--- | :--- |
|
|
125
|
+
| **✅ Success** | **Not returned** (Implicit 200 OK) | N/A |
|
|
126
|
+
| **⚠️ Execution Error** | **Not returned** (Implicit 200 OK) | N/A |
|
|
127
|
+
| **❌ HTTP Failure** | `error.statusCode` | `500`, `503` |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 🔍 Anatomy of an API Call
|
|
132
|
+
|
|
133
|
+
This section visualizes exactly what goes **IN** and what comes **OUT** of SDK operations.
|
|
134
|
+
|
|
135
|
+
### 1. Event API Anatomy
|
|
136
|
+
|
|
137
|
+
**Scenario:** Sending a Product Upsert event.
|
|
138
|
+
|
|
139
|
+
#### A. The Input (Event Payload)
|
|
140
|
+
```typescript
|
|
141
|
+
const event = {
|
|
142
|
+
name: "UPSERT_PRODUCT",
|
|
143
|
+
retailerId: "2",
|
|
144
|
+
entityRef: "PC:MASTER:2",
|
|
145
|
+
entityType: "PRODUCT_CATALOGUE",
|
|
146
|
+
// ...
|
|
147
|
+
};
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### B. The Call
|
|
151
|
+
```typescript
|
|
152
|
+
try {
|
|
153
|
+
const response = await client.sendEvent(event);
|
|
154
|
+
console.log('Success:', response);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error('Error:', extractError(error));
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
#### C. The Output (Two Possible Outcomes)
|
|
161
|
+
|
|
162
|
+
| Outcome | Structure Returned / Thrown | Status Code Location |
|
|
163
|
+
| :--- | :--- | :--- |
|
|
164
|
+
| **✅ SUCCESS** | **`EventResponse` Object**<br>Returned by `await` | **`response.statusCode`**<br>(e.g. 200) |
|
|
165
|
+
| **❌ FAILURE** | **`FluentAPIError` Object**<br>Caught in `catch` block | **`error.statusCode`**<br>(e.g. 400) |
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
### 2. GraphQL API Anatomy
|
|
170
|
+
|
|
171
|
+
**Scenario:** Querying an Order by Reference.
|
|
172
|
+
|
|
173
|
+
#### A. The Input (Query & Variables)
|
|
174
|
+
```typescript
|
|
175
|
+
const query = `
|
|
176
|
+
query GetOrder($ref: String!) {
|
|
177
|
+
order(ref: $ref) {
|
|
178
|
+
id
|
|
179
|
+
ref
|
|
180
|
+
status
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
`;
|
|
184
|
+
|
|
185
|
+
const variables = {
|
|
186
|
+
ref: "ORD-123"
|
|
187
|
+
};
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
#### B. The Call
|
|
191
|
+
```typescript
|
|
192
|
+
try {
|
|
193
|
+
const response = await client.graphql({ query, variables });
|
|
194
|
+
console.log('Success:', response.data);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
console.error('Error:', extractError(error));
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### C. The Output (Three Possible Outcomes)
|
|
201
|
+
|
|
202
|
+
| Outcome | Structure Returned / Thrown | Status Code Location |
|
|
203
|
+
| :--- | :--- | :--- |
|
|
204
|
+
| **✅ SUCCESS** | **`GraphQLResponse` Object**<br>Returned by `await` | **None**<br>(Implicit 200 OK) |
|
|
205
|
+
| **❌ HTTP FAILURE** | **`FluentAPIError` Object**<br>Caught in `catch` block | **`error.statusCode`**<br>(e.g. 500) |
|
|
206
|
+
| **⚠️ EXECUTION ERROR** | **`GraphQLExecutionError` Object**<br>Caught in `catch` block | **None**<br>(Implicit 200 OK) |
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Event API Error Handling
|
|
211
|
+
|
|
212
|
+
### Success Response Structure
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
interface EventResponse {
|
|
216
|
+
id?: string; // Event ID (if returned)
|
|
217
|
+
success: boolean; // Always true for success
|
|
218
|
+
status?: number; // Event status from Fluent API (CREATED, COMPLETED, etc.)
|
|
219
|
+
statusCode?: number; // HTTP status code (200, 201, 202)
|
|
220
|
+
message?: string; // Optional message
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Basic Try-Catch Pattern
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
import { FluentClient, FluentAPIError, AuthenticationError } from '@fluentcommerce/fc-connect-sdk';
|
|
228
|
+
|
|
229
|
+
const client = new FluentClient({
|
|
230
|
+
baseUrl: 'https://api.fluentcommerce.com',
|
|
231
|
+
clientId: 'YOUR_CLIENT_ID',
|
|
232
|
+
clientSecret: 'YOUR_CLIENT_SECRET',
|
|
233
|
+
username: 'YOUR_USERNAME',
|
|
234
|
+
password: 'YOUR_PASSWORD',
|
|
235
|
+
retailerId: 'YOUR_RETAILER_ID'
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
// SUCCESS CASE
|
|
240
|
+
const result = await client.sendEvent({
|
|
241
|
+
name: 'OrderCancel1',
|
|
242
|
+
entityRef: 'G_TEST_FC_POSTMAN_34',
|
|
243
|
+
entityType: 'ORDER',
|
|
244
|
+
retailerId: '2'
|
|
245
|
+
}, 'async');
|
|
246
|
+
|
|
247
|
+
console.log('✅ Event sent successfully');
|
|
248
|
+
console.log('HTTP Status:', result.statusCode); // 200
|
|
249
|
+
console.log('Success:', result.success); // true
|
|
250
|
+
console.log('Event ID:', result.id); // evt-123 (if available)
|
|
251
|
+
|
|
252
|
+
} catch (error) {
|
|
253
|
+
if (error instanceof FluentAPIError) {
|
|
254
|
+
// API Error (400, 404, 500, etc.)
|
|
255
|
+
console.error('❌ API Error');
|
|
256
|
+
console.error('Status:', error.statusCode); // 400, 404, 500, etc.
|
|
257
|
+
console.error('Message:', error.message); // "Request failed: 400"
|
|
258
|
+
console.error('Details:', error.details); // Response body
|
|
259
|
+
|
|
260
|
+
} else if (error instanceof AuthenticationError) {
|
|
261
|
+
// Auth failed after 3 retries
|
|
262
|
+
console.error('❌ Authentication Error');
|
|
263
|
+
console.error('Message:', error.message);
|
|
264
|
+
console.error('Status:', error.statusCode); // 401
|
|
265
|
+
|
|
266
|
+
} else {
|
|
267
|
+
// Unknown error
|
|
268
|
+
console.error('❌ Unexpected Error:', error);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Async Mode - Success Examples
|
|
274
|
+
|
|
275
|
+
**Input:**
|
|
276
|
+
```json
|
|
277
|
+
{
|
|
278
|
+
"name": "OrderCancel1",
|
|
279
|
+
"entityRef": "G_TEST_FC_POSTMAN_34",
|
|
280
|
+
"entityType": "ORDER",
|
|
281
|
+
"retailerId": "2"
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Output (Success):**
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"success": true,
|
|
289
|
+
"statusCode": 200
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Code:**
|
|
294
|
+
```typescript
|
|
295
|
+
const result = await client.sendEvent({
|
|
296
|
+
name: 'OrderCancel1',
|
|
297
|
+
entityRef: 'G_TEST_FC_POSTMAN_34',
|
|
298
|
+
entityType: 'ORDER',
|
|
299
|
+
retailerId: '2'
|
|
300
|
+
}, 'async');
|
|
301
|
+
|
|
302
|
+
// result.success = true
|
|
303
|
+
// result.statusCode = 200
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
### ⚡ SDK Automatic Retry Behavior
|
|
309
|
+
|
|
310
|
+
> **IMPORTANT:** The SDK automatically retries certain errors WITHOUT your intervention
|
|
311
|
+
|
|
312
|
+
**✅ Automatically Retried (3 attempts with exponential backoff):**
|
|
313
|
+
- `401 Unauthorized` - Token refresh attempt
|
|
314
|
+
- `500 Internal Server Error` - Transient server issue
|
|
315
|
+
- `503 Service Unavailable` - API temporarily overloaded
|
|
316
|
+
- Network errors - Connection failures
|
|
317
|
+
|
|
318
|
+
**❌ NOT Retried (Fail Immediately):**
|
|
319
|
+
- `400 Bad Request` - Invalid event payload
|
|
320
|
+
- `404 Not Found` - No workflow matched (sync mode)
|
|
321
|
+
- `422 Unprocessable Entity` - Validation failed
|
|
322
|
+
- All other 4xx errors
|
|
323
|
+
|
|
324
|
+
**Example:**
|
|
325
|
+
```typescript
|
|
326
|
+
try {
|
|
327
|
+
await client.sendEvent(event);
|
|
328
|
+
} catch (error) {
|
|
329
|
+
if (error instanceof FluentAPIError && error.statusCode === 500) {
|
|
330
|
+
// ⚠️ SDK already retried 3 times with exponential backoff
|
|
331
|
+
// This error means all retries failed
|
|
332
|
+
console.error('Server error after 3 automatic retries');
|
|
333
|
+
|
|
334
|
+
// You may want to implement additional retry logic here
|
|
335
|
+
// or send to a dead letter queue
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
### Async Mode - Error Examples
|
|
343
|
+
|
|
344
|
+
#### Error 1: Invalid Event Name (400)
|
|
345
|
+
|
|
346
|
+
**Input:**
|
|
347
|
+
```json
|
|
348
|
+
{
|
|
349
|
+
"name": "InvalidEventName",
|
|
350
|
+
"entityRef": "TEST-123",
|
|
351
|
+
"entityType": "ORDER",
|
|
352
|
+
"retailerId": "2"
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**Error Thrown:**
|
|
357
|
+
```typescript
|
|
358
|
+
FluentAPIError {
|
|
359
|
+
name: "FluentAPIError",
|
|
360
|
+
message: "Request failed: 400",
|
|
361
|
+
statusCode: 400,
|
|
362
|
+
details: "Bad Request - Invalid event configuration"
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**Handling:**
|
|
367
|
+
```typescript
|
|
368
|
+
try {
|
|
369
|
+
await client.sendEvent({
|
|
370
|
+
name: 'InvalidEventName', // ❌ No workflow configured
|
|
371
|
+
entityRef: 'TEST-123',
|
|
372
|
+
entityType: 'ORDER',
|
|
373
|
+
retailerId: '2'
|
|
374
|
+
}, 'async');
|
|
375
|
+
} catch (error) {
|
|
376
|
+
if (error instanceof FluentAPIError && error.statusCode === 400) {
|
|
377
|
+
console.error('Invalid event configuration');
|
|
378
|
+
// ⚠️ SDK did NOT retry (4xx = client error)
|
|
379
|
+
// Fix: Check event name in Rubix workflow configuration
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
#### Error 2: Missing retailerId (SDK Validation)
|
|
387
|
+
|
|
388
|
+
**Input:**
|
|
389
|
+
```json
|
|
390
|
+
{
|
|
391
|
+
"name": "TEST.EVENT",
|
|
392
|
+
"entityRef": "TEST-123",
|
|
393
|
+
"entityType": "ORDER"
|
|
394
|
+
// ❌ Missing retailerId
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
**Error Thrown:**
|
|
399
|
+
```typescript
|
|
400
|
+
FluentValidationError {
|
|
401
|
+
name: "FluentValidationError",
|
|
402
|
+
message: "retailerId is required for Event API. Provide it in the event payload or client config.",
|
|
403
|
+
code: "VALIDATION_ERROR",
|
|
404
|
+
statusCode: 400
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
**Key Point**: This error is thrown BEFORE the API call (fail-fast).
|
|
409
|
+
|
|
410
|
+
**Handling:**
|
|
411
|
+
```typescript
|
|
412
|
+
try {
|
|
413
|
+
await client.sendEvent({
|
|
414
|
+
name: 'TEST.EVENT',
|
|
415
|
+
entityRef: 'TEST-123',
|
|
416
|
+
entityType: 'ORDER'
|
|
417
|
+
// ❌ Missing retailerId
|
|
418
|
+
}, 'async');
|
|
419
|
+
} catch (error) {
|
|
420
|
+
if (error instanceof Error && error.message.includes('retailerId is required')) {
|
|
421
|
+
console.error('❌ retailerId validation failed (caught BEFORE API call)');
|
|
422
|
+
// Fix: Add retailerId to event or client config
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
### Sync Mode - Success and Error Examples
|
|
430
|
+
|
|
431
|
+
#### Sync Mode Success
|
|
432
|
+
|
|
433
|
+
**Input:**
|
|
434
|
+
```json
|
|
435
|
+
{
|
|
436
|
+
"name": "OrderCancel1",
|
|
437
|
+
"entityRef": "ORDER-SYNC-123",
|
|
438
|
+
"entityType": "ORDER",
|
|
439
|
+
"retailerId": "2"
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
**Output (if workflow configured and succeeds):**
|
|
444
|
+
```json
|
|
445
|
+
{
|
|
446
|
+
"success": true,
|
|
447
|
+
"status": "COMPLETED",
|
|
448
|
+
"statusCode": 201,
|
|
449
|
+
"message": "Workflow completed successfully"
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
**Code:**
|
|
454
|
+
```typescript
|
|
455
|
+
const result = await client.sendEvent({
|
|
456
|
+
name: 'OrderCancel1',
|
|
457
|
+
entityRef: 'ORDER-SYNC-123',
|
|
458
|
+
entityType: 'ORDER',
|
|
459
|
+
retailerId: '2'
|
|
460
|
+
}, 'sync'); // ← Sync mode waits for workflow
|
|
461
|
+
|
|
462
|
+
console.log('Workflow completed:', result.status);
|
|
463
|
+
console.log('HTTP Status:', result.statusCode);
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
#### Sync Mode Error: No Workflow Match (404)
|
|
469
|
+
|
|
470
|
+
**Input:**
|
|
471
|
+
```json
|
|
472
|
+
{
|
|
473
|
+
"name": "OrderCancel1",
|
|
474
|
+
"entityRef": "SYNC-NO-MATCH",
|
|
475
|
+
"entityType": "ORDER",
|
|
476
|
+
"retailerId": "2"
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
**Error Thrown:**
|
|
481
|
+
```typescript
|
|
482
|
+
FluentAPIError {
|
|
483
|
+
name: "FluentAPIError",
|
|
484
|
+
message: "Request failed: 404",
|
|
485
|
+
statusCode: 404,
|
|
486
|
+
details: "No workflow rule matched this event"
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
**Handling:**
|
|
491
|
+
```typescript
|
|
492
|
+
try {
|
|
493
|
+
await client.sendEvent({
|
|
494
|
+
name: 'OrderCancel1',
|
|
495
|
+
entityRef: 'SYNC-NO-MATCH',
|
|
496
|
+
entityType: 'ORDER',
|
|
497
|
+
retailerId: '2'
|
|
498
|
+
}, 'sync');
|
|
499
|
+
} catch (error) {
|
|
500
|
+
if (error instanceof FluentAPIError && error.statusCode === 404) {
|
|
501
|
+
console.error('No workflow rule matched (sync mode detected this)');
|
|
502
|
+
// ⚠️ Async mode would return 200 (doesn't wait for workflow)
|
|
503
|
+
// ✅ Sync mode detected the failure immediately
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
### Batch Processing with Per-Event Error Handling
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
interface EventResult {
|
|
514
|
+
sku: string;
|
|
515
|
+
success: boolean;
|
|
516
|
+
statusCode?: number;
|
|
517
|
+
error?: string;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
async function processProducts(products: Product[]): Promise<EventResult[]> {
|
|
521
|
+
const results: EventResult[] = [];
|
|
522
|
+
|
|
523
|
+
for (const product of products) {
|
|
524
|
+
try {
|
|
525
|
+
const response = await client.sendEvent({
|
|
526
|
+
name: 'PRODUCT.UPSERT',
|
|
527
|
+
entityType: 'PRODUCT',
|
|
528
|
+
entityRef: product.sku,
|
|
529
|
+
retailerId: '2',
|
|
530
|
+
attributes: product
|
|
531
|
+
}, 'async');
|
|
532
|
+
|
|
533
|
+
// SUCCESS
|
|
534
|
+
results.push({
|
|
535
|
+
sku: product.sku,
|
|
536
|
+
success: true,
|
|
537
|
+
statusCode: response.statusCode
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
console.log(`✅ ${product.sku} - HTTP ${response.statusCode}`);
|
|
541
|
+
|
|
542
|
+
} catch (error) {
|
|
543
|
+
if (error instanceof FluentAPIError) {
|
|
544
|
+
// API ERROR (SDK already retried 401/5xx)
|
|
545
|
+
results.push({
|
|
546
|
+
sku: product.sku,
|
|
547
|
+
success: false,
|
|
548
|
+
error: `HTTP ${error.statusCode}: ${error.message}`
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
console.error(`❌ ${product.sku} - ${error.statusCode}: ${error.message}`);
|
|
552
|
+
|
|
553
|
+
// Handle specific error codes
|
|
554
|
+
if (error.statusCode === 400) {
|
|
555
|
+
console.error(' → Invalid product data');
|
|
556
|
+
} else if (error.statusCode === 422) {
|
|
557
|
+
console.error(' → Validation failed');
|
|
558
|
+
} else if (error.statusCode === 500) {
|
|
559
|
+
console.error(' → Server error (SDK already retried 3x)');
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
} else if (error instanceof AuthenticationError) {
|
|
563
|
+
// AUTH FAILED (after 3 retries)
|
|
564
|
+
console.error(`❌ Auth failed: ${error.message}`);
|
|
565
|
+
// Stop processing - credentials are invalid
|
|
566
|
+
break;
|
|
567
|
+
|
|
568
|
+
} else {
|
|
569
|
+
// UNEXPECTED ERROR
|
|
570
|
+
results.push({
|
|
571
|
+
sku: product.sku,
|
|
572
|
+
success: false,
|
|
573
|
+
error: error.message
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
return results;
|
|
580
|
+
}
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## GraphQL Error Handling
|
|
586
|
+
|
|
587
|
+
### Success Response Structure
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
interface GraphQLResponse {
|
|
591
|
+
data: any; // Query/mutation result
|
|
592
|
+
errors?: undefined; // No errors on success
|
|
593
|
+
}
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
### Error Response Structure
|
|
597
|
+
|
|
598
|
+
When GraphQL returns errors, SDK throws `GraphQLExecutionError`:
|
|
599
|
+
|
|
600
|
+
```typescript
|
|
601
|
+
{
|
|
602
|
+
name: 'GraphQLExecutionError',
|
|
603
|
+
message: string, // First error message
|
|
604
|
+
graphqlErrors: Array<{
|
|
605
|
+
message: string,
|
|
606
|
+
locations?: Array<{ line: number, column: number }>,
|
|
607
|
+
path?: Array<string | number>,
|
|
608
|
+
extensions?: { ... }
|
|
609
|
+
}>,
|
|
610
|
+
query: string,
|
|
611
|
+
variables?: { ... }
|
|
612
|
+
}
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
### Basic Try-Catch Pattern
|
|
616
|
+
|
|
617
|
+
```typescript
|
|
618
|
+
try {
|
|
619
|
+
// SUCCESS CASE
|
|
620
|
+
const result = await client.graphql({
|
|
621
|
+
query: `
|
|
622
|
+
query GetOrder($ref: String!) {
|
|
623
|
+
order(ref: $ref) {
|
|
624
|
+
id
|
|
625
|
+
ref
|
|
626
|
+
status
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
`,
|
|
630
|
+
variables: { ref: 'ORDER-123' }
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
console.log('✅ GraphQL query succeeded');
|
|
634
|
+
console.log('Data:', result.data);
|
|
635
|
+
console.log('Order:', result.data.order);
|
|
636
|
+
|
|
637
|
+
} catch (error) {
|
|
638
|
+
if (error instanceof GraphQLExecutionError) {
|
|
639
|
+
// GraphQL returned errors
|
|
640
|
+
console.error('❌ GraphQL Execution Error');
|
|
641
|
+
console.error('Message:', error.message);
|
|
642
|
+
console.error('All errors:', error.getErrorMessages());
|
|
643
|
+
console.error('Query:', error.query);
|
|
644
|
+
|
|
645
|
+
// Check for specific errors
|
|
646
|
+
const errorMessages = error.getErrorMessages();
|
|
647
|
+
if (errorMessages.some(msg => msg.includes('invalidField'))) {
|
|
648
|
+
console.error(' → Invalid field in query');
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
} else if (error instanceof FluentAPIError) {
|
|
652
|
+
// HTTP error (400, 500, etc.)
|
|
653
|
+
console.error('❌ API Error');
|
|
654
|
+
console.error('Status:', error.statusCode);
|
|
655
|
+
console.error('Message:', error.message);
|
|
656
|
+
|
|
657
|
+
} else if (error instanceof AuthenticationError) {
|
|
658
|
+
// Auth failed
|
|
659
|
+
console.error('❌ Authentication failed');
|
|
660
|
+
|
|
661
|
+
} else {
|
|
662
|
+
console.error('❌ Unexpected error:', error);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
### GraphQL Success Example
|
|
668
|
+
|
|
669
|
+
**Input:**
|
|
670
|
+
```graphql
|
|
671
|
+
query GetOrder($ref: String!) {
|
|
672
|
+
order(ref: $ref) {
|
|
673
|
+
id
|
|
674
|
+
ref
|
|
675
|
+
status
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
**Variables:**
|
|
681
|
+
```json
|
|
682
|
+
{
|
|
683
|
+
"ref": "ORDER-123"
|
|
684
|
+
}
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
**Output (Success):**
|
|
688
|
+
```json
|
|
689
|
+
{
|
|
690
|
+
"data": {
|
|
691
|
+
"order": {
|
|
692
|
+
"id": "ORDER-123",
|
|
693
|
+
"ref": "ORD-2024-123",
|
|
694
|
+
"status": "CREATED"
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
**Code:**
|
|
701
|
+
```typescript
|
|
702
|
+
const result = await client.graphql({
|
|
703
|
+
query: `query GetOrder($ref: String!) { order(ref: $ref) { id ref status } }`,
|
|
704
|
+
variables: { ref: 'ORDER-123' }
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
console.log('Order ID:', result.data.order.id);
|
|
708
|
+
console.log('Order Ref:', result.data.order.ref);
|
|
709
|
+
console.log('Order Status:', result.data.order.status);
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
---
|
|
713
|
+
|
|
714
|
+
### GraphQL Error Examples
|
|
715
|
+
|
|
716
|
+
#### Error 1: Invalid Field
|
|
717
|
+
|
|
718
|
+
**Input:**
|
|
719
|
+
```graphql
|
|
720
|
+
query GetOrder {
|
|
721
|
+
order(ref: "123") {
|
|
722
|
+
id
|
|
723
|
+
invalidField # ❌ Field doesn't exist
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
**Error Thrown:**
|
|
729
|
+
```typescript
|
|
730
|
+
GraphQLExecutionError {
|
|
731
|
+
name: "GraphQLExecutionError",
|
|
732
|
+
message: "Field \"invalidField\" doesn't exist on type \"Order\"",
|
|
733
|
+
graphqlErrors: [
|
|
734
|
+
{
|
|
735
|
+
message: "Field \"invalidField\" doesn't exist on type \"Order\"",
|
|
736
|
+
locations: [{ line: 4, column: 5 }],
|
|
737
|
+
extensions: { ... }
|
|
738
|
+
}
|
|
739
|
+
],
|
|
740
|
+
query: "query GetOrder { ... }"
|
|
741
|
+
}
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
**Handling:**
|
|
745
|
+
```typescript
|
|
746
|
+
try {
|
|
747
|
+
await client.graphql({
|
|
748
|
+
query: `query GetOrder { order(ref: "123") { id invalidField } }`
|
|
749
|
+
});
|
|
750
|
+
} catch (error) {
|
|
751
|
+
if (error instanceof GraphQLExecutionError) {
|
|
752
|
+
console.error('GraphQL Error:', error.message);
|
|
753
|
+
|
|
754
|
+
// Get all error messages
|
|
755
|
+
const messages = error.getErrorMessages();
|
|
756
|
+
console.error('All errors:', messages);
|
|
757
|
+
|
|
758
|
+
// Check for specific error
|
|
759
|
+
const errorMessages = error.getErrorMessages();
|
|
760
|
+
if (errorMessages.some(msg => msg.includes('invalidField'))) {
|
|
761
|
+
console.error('Invalid field in query - check schema');
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
---
|
|
768
|
+
|
|
769
|
+
#### Error 2: Syntax Error
|
|
770
|
+
|
|
771
|
+
**Input:**
|
|
772
|
+
```graphql
|
|
773
|
+
query GetOrder {
|
|
774
|
+
order(ref: "123" # ❌ Missing closing parenthesis
|
|
775
|
+
id
|
|
776
|
+
ref
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
**Error Thrown:**
|
|
782
|
+
```typescript
|
|
783
|
+
FluentAPIError {
|
|
784
|
+
name: "FluentAPIError",
|
|
785
|
+
message: "Request failed: 400",
|
|
786
|
+
statusCode: 400,
|
|
787
|
+
details: {
|
|
788
|
+
errors: [
|
|
789
|
+
{
|
|
790
|
+
message: "Syntax Error: Expected ')', found Name 'id'",
|
|
791
|
+
locations: [{ line: 3, column: 5 }]
|
|
792
|
+
}
|
|
793
|
+
]
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
**Note**: Syntax errors throw `FluentAPIError` (400), not `GraphQLExecutionError`.
|
|
799
|
+
|
|
800
|
+
---
|
|
801
|
+
|
|
802
|
+
### GraphQL with Pagination
|
|
803
|
+
|
|
804
|
+
```typescript
|
|
805
|
+
try {
|
|
806
|
+
const result = await client.graphql({
|
|
807
|
+
query: `
|
|
808
|
+
query GetOrders {
|
|
809
|
+
orders(first: 50) {
|
|
810
|
+
edges {
|
|
811
|
+
node {
|
|
812
|
+
id
|
|
813
|
+
ref
|
|
814
|
+
status
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
pageInfo {
|
|
818
|
+
hasNextPage
|
|
819
|
+
endCursor
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
`,
|
|
824
|
+
pagination: {
|
|
825
|
+
enabled: true,
|
|
826
|
+
maxPages: 10,
|
|
827
|
+
maxRecords: 500
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
console.log('✅ Fetched', result.data.orders.edges.length, 'orders');
|
|
832
|
+
console.log('Total pages fetched:', result.extensions?.autoPagination?.totalPages);
|
|
833
|
+
|
|
834
|
+
} catch (error) {
|
|
835
|
+
if (error instanceof GraphQLExecutionError) {
|
|
836
|
+
console.error('GraphQL error during pagination:', error.message);
|
|
837
|
+
} else if (error instanceof FluentAPIError) {
|
|
838
|
+
console.error('API error during pagination:', error.statusCode);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
---
|
|
844
|
+
|
|
845
|
+
## Batch API Error Handling
|
|
846
|
+
|
|
847
|
+
### Success Response Structure
|
|
848
|
+
|
|
849
|
+
```typescript
|
|
850
|
+
interface FluentBatchResponse {
|
|
851
|
+
id: string; // Batch ID
|
|
852
|
+
jobId: string; // Job ID
|
|
853
|
+
status: string; // Batch status (CREATED, SUBMITTED, PROCESSING, COMPLETED, FAILED)
|
|
854
|
+
processedEntities?: number; // Number of entities processed
|
|
855
|
+
totalEntities?: number; // Total entities in batch
|
|
856
|
+
createdAt?: string; // ISO timestamp
|
|
857
|
+
updatedAt?: string; // ISO timestamp
|
|
858
|
+
errors?: Array<{ // Failed entity details (if any)
|
|
859
|
+
code: string;
|
|
860
|
+
message: string;
|
|
861
|
+
entityIndex?: number;
|
|
862
|
+
}>;
|
|
863
|
+
}
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
### Basic Try-Catch Pattern
|
|
867
|
+
|
|
868
|
+
```typescript
|
|
869
|
+
try {
|
|
870
|
+
// SUCCESS CASE
|
|
871
|
+
const batch = await client.sendBatch('job-123', {
|
|
872
|
+
entities: [
|
|
873
|
+
{ id: '1', sku: 'SKU-001', name: 'Product 1' },
|
|
874
|
+
{ id: '2', sku: 'SKU-002', name: 'Product 2' }
|
|
875
|
+
],
|
|
876
|
+
action: 'UPSERT'
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
console.log('✅ Batch sent successfully');
|
|
880
|
+
console.log('Batch ID:', batch.id);
|
|
881
|
+
console.log('Status:', batch.status);
|
|
882
|
+
|
|
883
|
+
} catch (error) {
|
|
884
|
+
if (error instanceof FluentAPIError) {
|
|
885
|
+
console.error('❌ Batch Error');
|
|
886
|
+
console.error('Status:', error.statusCode);
|
|
887
|
+
console.error('Message:', error.message);
|
|
888
|
+
|
|
889
|
+
// Common errors
|
|
890
|
+
if (error.statusCode === 404) {
|
|
891
|
+
console.error(' → Job not found or expired');
|
|
892
|
+
} else if (error.statusCode === 400) {
|
|
893
|
+
console.error(' → Invalid batch payload');
|
|
894
|
+
} else if (error.statusCode === 500) {
|
|
895
|
+
console.error(' → Server error (SDK already retried 3x)');
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
### Batch Success Example
|
|
902
|
+
|
|
903
|
+
**Input:**
|
|
904
|
+
```typescript
|
|
905
|
+
await client.sendBatch('job-123', {
|
|
906
|
+
entities: [
|
|
907
|
+
{ id: '1', sku: 'SKU-001', name: 'Product 1', price: 19.99 },
|
|
908
|
+
{ id: '2', sku: 'SKU-002', name: 'Product 2', price: 29.99 }
|
|
909
|
+
],
|
|
910
|
+
action: 'UPSERT'
|
|
911
|
+
});
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
**Output (Success):**
|
|
915
|
+
```json
|
|
916
|
+
{
|
|
917
|
+
"id": "batch-456",
|
|
918
|
+
"jobId": "job-123",
|
|
919
|
+
"status": "CREATED",
|
|
920
|
+
"processedEntities": 2,
|
|
921
|
+
"totalEntities": 2
|
|
922
|
+
}
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
---
|
|
926
|
+
|
|
927
|
+
### Batch Error Examples
|
|
928
|
+
|
|
929
|
+
#### Error 1: Job Expired (404)
|
|
930
|
+
|
|
931
|
+
**Input:**
|
|
932
|
+
```typescript
|
|
933
|
+
await client.sendBatch('job-expired-123', {
|
|
934
|
+
entities: [...],
|
|
935
|
+
action: 'UPSERT'
|
|
936
|
+
});
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
**Error Thrown:**
|
|
940
|
+
```typescript
|
|
941
|
+
FluentAPIError {
|
|
942
|
+
name: "FluentAPIError",
|
|
943
|
+
message: "Request failed: 404",
|
|
944
|
+
statusCode: 404,
|
|
945
|
+
details: "Job not found or expired"
|
|
946
|
+
}
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
**Handling:**
|
|
950
|
+
```typescript
|
|
951
|
+
try {
|
|
952
|
+
await client.sendBatch(jobId, { entities, action: 'UPSERT' });
|
|
953
|
+
} catch (error) {
|
|
954
|
+
if (error instanceof FluentAPIError && error.statusCode === 404) {
|
|
955
|
+
console.error('Job expired - recreating...');
|
|
956
|
+
|
|
957
|
+
// Recreate job and retry
|
|
958
|
+
const newJob = await client.createJob({
|
|
959
|
+
name: 'inventory-sync',
|
|
960
|
+
retailerId: '2'
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
await client.sendBatch(newJob.id, { entities, action: 'UPSERT' });
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
---
|
|
969
|
+
|
|
970
|
+
#### Error 2: Invalid Batch Payload (400)
|
|
971
|
+
|
|
972
|
+
**Input:**
|
|
973
|
+
```typescript
|
|
974
|
+
await client.sendBatch('job-123', {
|
|
975
|
+
entities: [], // ❌ Empty entities array
|
|
976
|
+
action: 'UPSERT'
|
|
977
|
+
});
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
**Error Thrown:**
|
|
981
|
+
```typescript
|
|
982
|
+
FluentAPIError {
|
|
983
|
+
name: "FluentAPIError",
|
|
984
|
+
message: "Request failed: 400",
|
|
985
|
+
statusCode: 400,
|
|
986
|
+
details: "Batch must contain at least one entity"
|
|
987
|
+
}
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
---
|
|
991
|
+
|
|
992
|
+
### Batch Processing with Retry
|
|
993
|
+
|
|
994
|
+
```typescript
|
|
995
|
+
async function sendBatchWithRetry(
|
|
996
|
+
jobId: string,
|
|
997
|
+
entities: any[],
|
|
998
|
+
maxRetries: number = 3
|
|
999
|
+
): Promise<FluentBatchResponse> {
|
|
1000
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1001
|
+
try {
|
|
1002
|
+
const batch = await client.sendBatch(jobId, {
|
|
1003
|
+
entities,
|
|
1004
|
+
action: 'UPSERT'
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
console.log(`✅ Batch sent (attempt ${attempt + 1})`);
|
|
1008
|
+
return batch;
|
|
1009
|
+
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
if (error instanceof FluentAPIError) {
|
|
1012
|
+
if (error.statusCode === 404 && attempt < maxRetries) {
|
|
1013
|
+
// Job expired - recreate
|
|
1014
|
+
console.log(`⚠️ Job expired, recreating... (attempt ${attempt + 1})`);
|
|
1015
|
+
|
|
1016
|
+
const newJob = await client.createJob({
|
|
1017
|
+
name: 'inventory-sync',
|
|
1018
|
+
retailerId: '2'
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
jobId = newJob.id;
|
|
1022
|
+
// Retry with new job ID
|
|
1023
|
+
continue;
|
|
1024
|
+
|
|
1025
|
+
} else if (error.statusCode === 500 && attempt < maxRetries) {
|
|
1026
|
+
// Server error - SDK already retried 3x
|
|
1027
|
+
console.log(`⚠️ Server error, waiting before retry...`);
|
|
1028
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
1029
|
+
continue;
|
|
1030
|
+
|
|
1031
|
+
} else {
|
|
1032
|
+
// Non-retryable error or max retries reached
|
|
1033
|
+
throw error;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
throw error;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
throw new Error('Max retries exceeded');
|
|
1041
|
+
}
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
---
|
|
1045
|
+
|
|
1046
|
+
## Job API Error Handling
|
|
1047
|
+
|
|
1048
|
+
### Success Response Structure
|
|
1049
|
+
|
|
1050
|
+
```typescript
|
|
1051
|
+
interface FluentJobResponse {
|
|
1052
|
+
id: string; // Job ID
|
|
1053
|
+
name: string; // Job name
|
|
1054
|
+
retailerId: string;
|
|
1055
|
+
status?: string; // Job status (CREATED, ACTIVE, PROCESSING, COMPLETED, FAILED)
|
|
1056
|
+
meta?: FluentJobMetadata; // Job metadata (preprocessing, source, etc.)
|
|
1057
|
+
createdAt?: string; // ISO timestamp
|
|
1058
|
+
updatedAt?: string; // ISO timestamp
|
|
1059
|
+
}
|
|
1060
|
+
```
|
|
1061
|
+
|
|
1062
|
+
### Basic Try-Catch Pattern
|
|
1063
|
+
|
|
1064
|
+
```typescript
|
|
1065
|
+
try {
|
|
1066
|
+
// SUCCESS CASE
|
|
1067
|
+
const job = await client.createJob({
|
|
1068
|
+
name: 'inventory-sync',
|
|
1069
|
+
retailerId: '2'
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
console.log('✅ Job created successfully');
|
|
1073
|
+
console.log('Job ID:', job.id);
|
|
1074
|
+
console.log('Status:', job.status);
|
|
1075
|
+
console.log('Created:', job.createdAt);
|
|
1076
|
+
|
|
1077
|
+
} catch (error) {
|
|
1078
|
+
if (error instanceof FluentAPIError) {
|
|
1079
|
+
console.error('❌ Job Creation Error');
|
|
1080
|
+
console.error('Status:', error.statusCode);
|
|
1081
|
+
console.error('Message:', error.message);
|
|
1082
|
+
|
|
1083
|
+
if (error.statusCode === 400) {
|
|
1084
|
+
console.error(' → Invalid job payload');
|
|
1085
|
+
} else if (error.statusCode === 403) {
|
|
1086
|
+
console.error(' → Permission denied');
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
### Job Success Example
|
|
1093
|
+
|
|
1094
|
+
**Input:**
|
|
1095
|
+
```typescript
|
|
1096
|
+
await client.createJob({
|
|
1097
|
+
name: 'product-import-batch',
|
|
1098
|
+
retailerId: '2'
|
|
1099
|
+
});
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
**Output (Success):**
|
|
1103
|
+
```json
|
|
1104
|
+
{
|
|
1105
|
+
"id": "job-789",
|
|
1106
|
+
"name": "product-import-batch",
|
|
1107
|
+
"status": "CREATED",
|
|
1108
|
+
"retailerId": "2",
|
|
1109
|
+
"createdAt": "2025-01-19T10:30:00Z",
|
|
1110
|
+
"updatedAt": "2025-01-19T10:30:00Z"
|
|
1111
|
+
}
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
---
|
|
1115
|
+
|
|
1116
|
+
### Job Status Checking
|
|
1117
|
+
|
|
1118
|
+
**Response Structure:**
|
|
1119
|
+
```typescript
|
|
1120
|
+
interface FluentJobStatus {
|
|
1121
|
+
id: string;
|
|
1122
|
+
status: string; // Job status (CREATED, ACTIVE, PROCESSING, COMPLETED, FAILED)
|
|
1123
|
+
progress?: number; // Progress percentage (0-100)
|
|
1124
|
+
message?: string; // Status message
|
|
1125
|
+
completedAt?: string; // ISO timestamp when completed
|
|
1126
|
+
failedAt?: string; // ISO timestamp when failed
|
|
1127
|
+
errors?: Array<{ // Error details (if failed)
|
|
1128
|
+
code: string;
|
|
1129
|
+
message: string;
|
|
1130
|
+
}>;
|
|
1131
|
+
}
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
**Example:**
|
|
1135
|
+
```typescript
|
|
1136
|
+
try {
|
|
1137
|
+
const status = await client.getJobStatus('job-789');
|
|
1138
|
+
|
|
1139
|
+
console.log('Job Status:', status.status);
|
|
1140
|
+
console.log('Progress:', status.progress);
|
|
1141
|
+
|
|
1142
|
+
if (status.status === 'FAILED') {
|
|
1143
|
+
console.warn('⚠️ Job failed - check errors');
|
|
1144
|
+
console.error('Errors:', status.errors);
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
} catch (error) {
|
|
1148
|
+
if (error instanceof FluentAPIError && error.statusCode === 404) {
|
|
1149
|
+
console.error('Job not found');
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
```
|
|
1153
|
+
|
|
1154
|
+
---
|
|
1155
|
+
|
|
1156
|
+
## Common Patterns
|
|
1157
|
+
|
|
1158
|
+
### Pattern 1: Centralized Error Handler
|
|
1159
|
+
|
|
1160
|
+
```typescript
|
|
1161
|
+
function handleSDKError(error: unknown, context: string): void {
|
|
1162
|
+
if (error instanceof FluentAPIError) {
|
|
1163
|
+
console.error(`[${context}] API Error:`, {
|
|
1164
|
+
status: error.statusCode,
|
|
1165
|
+
message: error.message,
|
|
1166
|
+
details: error.details
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
// Log to monitoring service
|
|
1170
|
+
logger.error('Fluent API Error', {
|
|
1171
|
+
context,
|
|
1172
|
+
status: error.statusCode,
|
|
1173
|
+
message: error.message
|
|
1174
|
+
});
|
|
1175
|
+
|
|
1176
|
+
} else if (error instanceof GraphQLExecutionError) {
|
|
1177
|
+
console.error(`[${context}] GraphQL Error:`, {
|
|
1178
|
+
message: error.message,
|
|
1179
|
+
errors: error.getErrorMessages(),
|
|
1180
|
+
query: error.query
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
logger.error('GraphQL Execution Error', {
|
|
1184
|
+
context,
|
|
1185
|
+
errors: error.getErrorMessages()
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
} else if (error instanceof AuthenticationError) {
|
|
1189
|
+
console.error(`[${context}] Auth Error:`, {
|
|
1190
|
+
message: error.message,
|
|
1191
|
+
status: error.statusCode
|
|
1192
|
+
});
|
|
1193
|
+
|
|
1194
|
+
// Alert - credentials may be invalid
|
|
1195
|
+
alertService.send('Fluent Auth Failure', error.message);
|
|
1196
|
+
|
|
1197
|
+
} else {
|
|
1198
|
+
console.error(`[${context}] Unexpected Error:`, error);
|
|
1199
|
+
logger.error('Unexpected SDK Error', { context, error });
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
// Usage
|
|
1204
|
+
try {
|
|
1205
|
+
await client.sendEvent(event);
|
|
1206
|
+
} catch (error) {
|
|
1207
|
+
handleSDKError(error, 'Event API');
|
|
1208
|
+
throw error; // Re-throw if needed
|
|
1209
|
+
}
|
|
1210
|
+
```
|
|
1211
|
+
|
|
1212
|
+
---
|
|
1213
|
+
|
|
1214
|
+
### Pattern 2: Retry with Backoff
|
|
1215
|
+
|
|
1216
|
+
```typescript
|
|
1217
|
+
async function retryWithBackoff<T>(
|
|
1218
|
+
operation: () => Promise<T>,
|
|
1219
|
+
maxRetries: number = 3,
|
|
1220
|
+
baseDelay: number = 1000
|
|
1221
|
+
): Promise<T> {
|
|
1222
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1223
|
+
try {
|
|
1224
|
+
return await operation();
|
|
1225
|
+
} catch (error) {
|
|
1226
|
+
if (error instanceof FluentAPIError) {
|
|
1227
|
+
// Only retry 5xx server errors
|
|
1228
|
+
if (error.statusCode >= 500 && attempt < maxRetries) {
|
|
1229
|
+
const delay = baseDelay * Math.pow(2, attempt);
|
|
1230
|
+
console.log(`⚠️ Server error, retrying in ${delay}ms...`);
|
|
1231
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
1232
|
+
continue;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
throw error;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
throw new Error('Max retries exceeded');
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// Usage
|
|
1242
|
+
const result = await retryWithBackoff(
|
|
1243
|
+
() => client.sendEvent(event),
|
|
1244
|
+
3,
|
|
1245
|
+
1000
|
|
1246
|
+
);
|
|
1247
|
+
```
|
|
1248
|
+
|
|
1249
|
+
---
|
|
1250
|
+
|
|
1251
|
+
### Pattern 3: Batch with Dead Letter Queue
|
|
1252
|
+
|
|
1253
|
+
```typescript
|
|
1254
|
+
interface ProcessingResult {
|
|
1255
|
+
successful: any[];
|
|
1256
|
+
failed: Array<{ item: any; error: string }>;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
async function processBatchWithDLQ(
|
|
1260
|
+
items: any[]
|
|
1261
|
+
): Promise<ProcessingResult> {
|
|
1262
|
+
const result: ProcessingResult = {
|
|
1263
|
+
successful: [],
|
|
1264
|
+
failed: []
|
|
1265
|
+
};
|
|
1266
|
+
|
|
1267
|
+
for (const item of items) {
|
|
1268
|
+
try {
|
|
1269
|
+
const response = await client.sendEvent({
|
|
1270
|
+
name: 'PRODUCT.UPSERT',
|
|
1271
|
+
entityType: 'PRODUCT',
|
|
1272
|
+
entityRef: item.sku,
|
|
1273
|
+
retailerId: '2',
|
|
1274
|
+
attributes: item
|
|
1275
|
+
});
|
|
1276
|
+
|
|
1277
|
+
result.successful.push(item);
|
|
1278
|
+
|
|
1279
|
+
} catch (error) {
|
|
1280
|
+
if (error instanceof FluentAPIError) {
|
|
1281
|
+
// Add to dead letter queue
|
|
1282
|
+
result.failed.push({
|
|
1283
|
+
item,
|
|
1284
|
+
error: `HTTP ${error.statusCode}: ${error.message}`
|
|
1285
|
+
});
|
|
1286
|
+
|
|
1287
|
+
// Log to DLQ for manual review
|
|
1288
|
+
await deadLetterQueue.add({
|
|
1289
|
+
item,
|
|
1290
|
+
error: error.message,
|
|
1291
|
+
status: error.statusCode,
|
|
1292
|
+
timestamp: new Date().toISOString()
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
return result;
|
|
1299
|
+
}
|
|
1300
|
+
```
|
|
1301
|
+
|
|
1302
|
+
---
|
|
1303
|
+
|
|
1304
|
+
## Live Examples
|
|
1305
|
+
|
|
1306
|
+
### Test Environment
|
|
1307
|
+
|
|
1308
|
+
```yaml
|
|
1309
|
+
Base URL: https://api.fluentcommerce.com
|
|
1310
|
+
Retailer ID: YOUR_RETAILER_ID
|
|
1311
|
+
Client ID: YOUR_CLIENT_ID
|
|
1312
|
+
```
|
|
1313
|
+
|
|
1314
|
+
### Example 1: Event API Success
|
|
1315
|
+
|
|
1316
|
+
**Code:**
|
|
1317
|
+
```typescript
|
|
1318
|
+
const client = new FluentClient({
|
|
1319
|
+
baseUrl: 'https://api.fluentcommerce.com',
|
|
1320
|
+
clientId: 'YOUR_CLIENT_ID',
|
|
1321
|
+
clientSecret: 'YOUR_CLIENT_SECRET',
|
|
1322
|
+
username: 'YOUR_USERNAME',
|
|
1323
|
+
password: 'YOUR_PASSWORD',
|
|
1324
|
+
retailerId: 'YOUR_RETAILER_ID'
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
const result = await client.sendEvent({
|
|
1328
|
+
name: 'OrderCancel1',
|
|
1329
|
+
entityRef: 'G_TEST_FC_POSTMAN_34',
|
|
1330
|
+
entityType: 'ORDER',
|
|
1331
|
+
retailerId: '2'
|
|
1332
|
+
}, 'async');
|
|
1333
|
+
|
|
1334
|
+
console.log('Success:', result.success); // true
|
|
1335
|
+
console.log('HTTP Status:', result.statusCode); // 200
|
|
1336
|
+
```
|
|
1337
|
+
|
|
1338
|
+
**Console Output:**
|
|
1339
|
+
```
|
|
1340
|
+
Success: true
|
|
1341
|
+
HTTP Status: 200
|
|
1342
|
+
```
|
|
1343
|
+
|
|
1344
|
+
---
|
|
1345
|
+
|
|
1346
|
+
### Example 2: Event API Error (400)
|
|
1347
|
+
|
|
1348
|
+
**Code:**
|
|
1349
|
+
```typescript
|
|
1350
|
+
try {
|
|
1351
|
+
await client.sendEvent({
|
|
1352
|
+
name: 'InvalidEventName',
|
|
1353
|
+
entityRef: 'TEST-123',
|
|
1354
|
+
entityType: 'ORDER',
|
|
1355
|
+
retailerId: '2'
|
|
1356
|
+
}, 'async');
|
|
1357
|
+
} catch (error) {
|
|
1358
|
+
if (error instanceof FluentAPIError) {
|
|
1359
|
+
console.error('Error Status:', error.statusCode);
|
|
1360
|
+
console.error('Error Message:', error.message);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
```
|
|
1364
|
+
|
|
1365
|
+
**Console Output:**
|
|
1366
|
+
```
|
|
1367
|
+
Error Status: 400
|
|
1368
|
+
Error Message: Request failed: 400
|
|
1369
|
+
```
|
|
1370
|
+
|
|
1371
|
+
---
|
|
1372
|
+
|
|
1373
|
+
### Example 3: Sync Mode - No Workflow Match (404)
|
|
1374
|
+
|
|
1375
|
+
**Code:**
|
|
1376
|
+
```typescript
|
|
1377
|
+
try {
|
|
1378
|
+
await client.sendEvent({
|
|
1379
|
+
name: 'OrderCancel1',
|
|
1380
|
+
entityRef: 'SYNC-NO-MATCH',
|
|
1381
|
+
entityType: 'ORDER',
|
|
1382
|
+
retailerId: '2'
|
|
1383
|
+
}, 'sync');
|
|
1384
|
+
} catch (error) {
|
|
1385
|
+
if (error instanceof FluentAPIError) {
|
|
1386
|
+
console.error('Sync mode detected workflow failure');
|
|
1387
|
+
console.error('Status:', error.statusCode); // 404
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
```
|
|
1391
|
+
|
|
1392
|
+
**Console Output:**
|
|
1393
|
+
```
|
|
1394
|
+
Sync mode detected workflow failure
|
|
1395
|
+
Status: 404
|
|
1396
|
+
```
|
|
1397
|
+
|
|
1398
|
+
---
|
|
1399
|
+
|
|
1400
|
+
## Summary Table
|
|
1401
|
+
|
|
1402
|
+
| API | Success Type | Error Type | SDK Retries | Notes |
|
|
1403
|
+
|-----|-------------|------------|-------------|-------|
|
|
1404
|
+
| **Event API** | `EventResponse` | `FluentAPIError`, `AuthenticationError` | ✅ 401 (3x), 5xx (3x) | Async = fast, Sync = detects workflow errors |
|
|
1405
|
+
| **GraphQL** | `GraphQLResponse` | `GraphQLExecutionError`, `FluentAPIError`, `AuthenticationError` | ✅ 401 (3x), 5xx (3x) | GraphQL errors vs HTTP errors |
|
|
1406
|
+
| **Batch API** | `FluentBatchResponse` | `FluentAPIError`, `AuthenticationError` | ✅ 401 (3x), 5xx (3x) | Watch for job expiry (404) |
|
|
1407
|
+
| **Job API** | `FluentJobResponse` | `FluentAPIError`, `AuthenticationError` | ✅ 401 (3x), 5xx (3x) | Jobs expire after 1 hour |
|
|
1408
|
+
|
|
1409
|
+
---
|
|
1410
|
+
|
|
1411
|
+
## Quick Reference
|
|
1412
|
+
|
|
1413
|
+
### When to Use Try-Catch
|
|
1414
|
+
|
|
1415
|
+
✅ **Always use try-catch** for:
|
|
1416
|
+
- `sendEvent()`
|
|
1417
|
+
- `graphql()`
|
|
1418
|
+
- `sendBatch()`
|
|
1419
|
+
- `createJob()`
|
|
1420
|
+
- `getJobStatus()`
|
|
1421
|
+
- Any SDK method that makes API calls
|
|
1422
|
+
|
|
1423
|
+
### Error Handling Checklist
|
|
1424
|
+
|
|
1425
|
+
- [ ] Catch `FluentAPIError` for HTTP errors
|
|
1426
|
+
- [ ] Catch `AuthenticationError` for auth failures
|
|
1427
|
+
- [ ] Catch `GraphQLExecutionError` for GraphQL errors
|
|
1428
|
+
- [ ] Check `error.statusCode` to determine error type
|
|
1429
|
+
- [ ] Log errors with context for debugging
|
|
1430
|
+
- [ ] Implement retry logic for transient errors (5xx)
|
|
1431
|
+
- [ ] Use dead letter queue for persistent failures
|
|
1432
|
+
- [ ] Alert on authentication failures
|
|
1433
|
+
|
|
1434
|
+
---
|
|
1435
|
+
|
|
1436
|
+
**SDK Version**: 0.1.46
|
|
1437
|
+
**Date**: 2025-01-19
|