@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,1008 +1,1008 @@
|
|
|
1
|
-
# Module 8: ExtractionOrchestrator - Simplified Data Extraction
|
|
2
|
-
|
|
3
|
-
> **Reusable orchestrator for extracting and transforming GraphQL data with auto-pagination**
|
|
4
|
-
|
|
5
|
-
## 📚 What You'll Learn
|
|
6
|
-
|
|
7
|
-
- ✅ How to use ExtractionOrchestrator for simple data extraction workflows
|
|
8
|
-
- ✅ Path-based result extraction from nested GraphQL responses
|
|
9
|
-
- ✅ Automatic handling of GraphQL connection patterns (edges.node)
|
|
10
|
-
- ✅ Extraction limits and validation
|
|
11
|
-
- ✅ Statistics tracking and error handling
|
|
12
|
-
- ✅ When to use ExtractionOrchestrator vs manual extraction
|
|
13
|
-
|
|
14
|
-
## 🎯 Overview
|
|
15
|
-
|
|
16
|
-
`ExtractionOrchestrator` is a high-level service that simplifies the common pattern of:
|
|
17
|
-
|
|
18
|
-
1. **Execute** GraphQL query with auto-pagination
|
|
19
|
-
2. **Extract** data from nested response paths
|
|
20
|
-
3. **Validate** extracted records (optional)
|
|
21
|
-
4. **Track** statistics and errors
|
|
22
|
-
|
|
23
|
-
### When to Use
|
|
24
|
-
|
|
25
|
-
✅ **Use ExtractionOrchestrator when:**
|
|
26
|
-
|
|
27
|
-
- Extracting data from a single GraphQL query
|
|
28
|
-
- Need auto-pagination handling
|
|
29
|
-
- Want path-based extraction from nested responses
|
|
30
|
-
- Need extraction limits (maxRecords, maxPages, timeout)
|
|
31
|
-
- Want built-in statistics and error tracking
|
|
32
|
-
|
|
33
|
-
❌ **Don't use when:**
|
|
34
|
-
|
|
35
|
-
- Need to combine multiple queries (do separate calls)
|
|
36
|
-
- Complex business logic transformations (use UniversalMapper)
|
|
37
|
-
- Real-time streaming (use direct GraphQL calls)
|
|
38
|
-
|
|
39
|
-
## 🚀 Quick Start
|
|
40
|
-
|
|
41
|
-
### Basic Extraction
|
|
42
|
-
|
|
43
|
-
```typescript
|
|
44
|
-
import { ExtractionOrchestrator, createClient } from '@fluentcommerce/fc-connect-sdk';
|
|
45
|
-
|
|
46
|
-
const client = await createClient({
|
|
47
|
-
config: {
|
|
48
|
-
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
49
|
-
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
50
|
-
clientSecret: process.env.FLUENT_CLIENT_SECRET!
|
|
51
|
-
// retailerId is OPTIONAL for GraphQL queries (only required for Job/Event API)
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
const orchestrator = new ExtractionOrchestrator(client, console);
|
|
56
|
-
|
|
57
|
-
// Extract virtual positions
|
|
58
|
-
const result = await orchestrator.extract({
|
|
59
|
-
query: `
|
|
60
|
-
query GetVirtualPositions($first: Int!, $after: String) {
|
|
61
|
-
virtualPositions(first: $first, after: $after) {
|
|
62
|
-
edges {
|
|
63
|
-
node {
|
|
64
|
-
id
|
|
65
|
-
ref
|
|
66
|
-
productRef
|
|
67
|
-
qty
|
|
68
|
-
groupRef
|
|
69
|
-
}
|
|
70
|
-
cursor
|
|
71
|
-
}
|
|
72
|
-
pageInfo {
|
|
73
|
-
hasNextPage
|
|
74
|
-
# Note: Fluent doesn't return endCursor - use edge cursors instead
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
`,
|
|
79
|
-
resultPath: 'virtualPositions.edges.node',
|
|
80
|
-
pageSize: 200,
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
console.log(`Extracted ${result.data.length} records`);
|
|
84
|
-
console.log(`Pages fetched: ${result.stats.totalPages}`);
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## 📖 Core Concepts
|
|
88
|
-
|
|
89
|
-
### 1. Result Path Extraction
|
|
90
|
-
|
|
91
|
-
The `resultPath` parameter uses dot notation to navigate nested GraphQL responses:
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
// Simple path using dot notation
|
|
95
|
-
resultPath: 'virtualPositions.edges.node';
|
|
96
|
-
|
|
97
|
-
// The orchestrator automatically:
|
|
98
|
-
// 1. Navigates to virtualPositions
|
|
99
|
-
// 2. Extracts edges array
|
|
100
|
-
// 3. Extracts node from each edge
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### 2. Auto-Detection of Edges.Node Pattern
|
|
104
|
-
|
|
105
|
-
The orchestrator automatically detects and extracts from GraphQL connection patterns:
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
// If response has structure:
|
|
109
|
-
{
|
|
110
|
-
virtualPositions: {
|
|
111
|
-
edges: [{ node: { id: '1', ref: 'VP1' } }, { node: { id: '2', ref: 'VP2' } }];
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Path: 'virtualPositions.edges'
|
|
116
|
-
// Result: [{ id: '1', ref: 'VP1' }, { id: '2', ref: 'VP2' }]
|
|
117
|
-
// Automatically extracts 'node' from each edge!
|
|
118
|
-
|
|
119
|
-
// Path: 'virtualPositions.edges.node'
|
|
120
|
-
// Result: Same as above (explicitly extracting node)
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### 3. Extraction Limits and Pagination Notes
|
|
124
|
-
|
|
125
|
-
Control extraction scope with multiple limit options:
|
|
126
|
-
|
|
127
|
-
```typescript
|
|
128
|
-
const result = await orchestrator.extract({
|
|
129
|
-
query: myQuery,
|
|
130
|
-
resultPath: 'data.edges.node',
|
|
131
|
-
|
|
132
|
-
// Limit options (all optional)
|
|
133
|
-
pageSize: 200, // Records per page (default: 200)
|
|
134
|
-
maxRecords: 10000, // Total records to extract
|
|
135
|
-
maxPages: 50, // Maximum pages to fetch
|
|
136
|
-
timeout: 120000, // Query timeout in ms (default: 60000)
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// Note:
|
|
140
|
-
// - 'first' and 'after' are managed by the orchestrator; do not include them in variables
|
|
141
|
-
// - Pagination is handled automatically using the SDK's built-in auto-pagination
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
**Priority when multiple limits are set:**
|
|
145
|
-
|
|
146
|
-
1. `maxRecords` - Stops when total records reach limit
|
|
147
|
-
2. `maxPages` - Stops after fetching specified pages
|
|
148
|
-
3. API returns `hasNextPage: false`
|
|
149
|
-
|
|
150
|
-
### 4. Pagination Direction (Forward vs Backward)
|
|
151
|
-
|
|
152
|
-
**NEW:** ExtractionOrchestrator now supports both forward and backward pagination.
|
|
153
|
-
|
|
154
|
-
#### Forward Pagination (Default)
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
// Forward pagination uses first/after (default behavior)
|
|
158
|
-
const result = await orchestrator.extract({
|
|
159
|
-
query: `
|
|
160
|
-
query GetItems($first: Int!, $after: String) {
|
|
161
|
-
items(first: $first, after: $after) {
|
|
162
|
-
edges {
|
|
163
|
-
node { id name }
|
|
164
|
-
cursor
|
|
165
|
-
}
|
|
166
|
-
pageInfo { hasNextPage }
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
`,
|
|
170
|
-
resultPath: 'items.edges.node',
|
|
171
|
-
direction: 'forward', // Optional - this is the default
|
|
172
|
-
});
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
#### Backward Pagination (Reverse)
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
// Backward pagination uses last/before
|
|
179
|
-
const result = await orchestrator.extract({
|
|
180
|
-
query: `
|
|
181
|
-
query GetItems($last: Int!, $before: String) {
|
|
182
|
-
items(last: $last, before: $before) {
|
|
183
|
-
edges {
|
|
184
|
-
node { id name }
|
|
185
|
-
cursor
|
|
186
|
-
}
|
|
187
|
-
pageInfo { hasPreviousPage }
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
`,
|
|
191
|
-
resultPath: 'items.edges.node',
|
|
192
|
-
direction: 'backward', // ✅ Enable reverse pagination
|
|
193
|
-
});
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
#### Query Requirements by Direction
|
|
197
|
-
|
|
198
|
-
| Direction | Parameters Required | PageInfo Required | Cursor Selection |
|
|
199
|
-
|-----------|-------------------|-------------------|------------------|
|
|
200
|
-
| `forward` (default) | `$first`, `$after` | `hasNextPage` | Last cursor in edges |
|
|
201
|
-
| `backward` | `$last`, `$before` | `hasPreviousPage` | First cursor in edges |
|
|
202
|
-
|
|
203
|
-
#### Important Rules
|
|
204
|
-
|
|
205
|
-
**✅ DO:**
|
|
206
|
-
- Declare `$first/$after` (forward) OR `$last/$before` (backward) in your query signature
|
|
207
|
-
- Include the corresponding parameters in your field arguments: `items(first: $first, after: $after)`
|
|
208
|
-
- Always include `cursor` in edges
|
|
209
|
-
- Include correct `pageInfo` field: `hasNextPage` for forward, `hasPreviousPage` for backward
|
|
210
|
-
|
|
211
|
-
**❌ DON'T:**
|
|
212
|
-
- Mix `first/after` with `direction: 'backward'` - query must match direction
|
|
213
|
-
- Include pagination parameters in `variables` - orchestrator injects them from `pageSize`/cursor
|
|
214
|
-
- Omit `cursor` from edges - orchestrator needs cursors for pagination
|
|
215
|
-
- Forget `hasPreviousPage` when using `direction: 'backward'` - validation will fail
|
|
216
|
-
|
|
217
|
-
#### Use Cases for Backward Pagination
|
|
218
|
-
|
|
219
|
-
**When to use backward:**
|
|
220
|
-
- 📅 Fetching most recent records first (e.g., latest orders, recent activity)
|
|
221
|
-
- 🔄 Reverse-chronological feeds
|
|
222
|
-
- 🎯 "Load more above" UI patterns
|
|
223
|
-
- 📊 Time-series data from newest to oldest
|
|
224
|
-
|
|
225
|
-
```typescript
|
|
226
|
-
// Example: Get latest 1000 orders (most recent first)
|
|
227
|
-
const recentOrders = await orchestrator.extract({
|
|
228
|
-
query: `
|
|
229
|
-
query GetRecentOrders($last: Int!, $before: String) {
|
|
230
|
-
orders(last: $last, before: $before, orderBy: { field: CREATED_ON, direction: DESC }) {
|
|
231
|
-
edges {
|
|
232
|
-
node {
|
|
233
|
-
id
|
|
234
|
-
ref
|
|
235
|
-
createdOn
|
|
236
|
-
status
|
|
237
|
-
}
|
|
238
|
-
cursor
|
|
239
|
-
}
|
|
240
|
-
pageInfo { hasPreviousPage }
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
`,
|
|
244
|
-
resultPath: 'orders.edges.node',
|
|
245
|
-
direction: 'backward',
|
|
246
|
-
maxRecords: 1000,
|
|
247
|
-
pageSize: 200,
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
console.log(`Fetched ${recentOrders.data.length} orders (newest first)`);
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
## 🛠️ Complete API Reference
|
|
254
|
-
|
|
255
|
-
### Constructor
|
|
256
|
-
|
|
257
|
-
```typescript
|
|
258
|
-
constructor(
|
|
259
|
-
client: FluentClient | FluentVersoriClient,
|
|
260
|
-
logger: StructuredLogger
|
|
261
|
-
)
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
**Parameters:**
|
|
265
|
-
|
|
266
|
-
- `client` - Fluent API client instance
|
|
267
|
-
- `logger` - Structured logger (can use `console`)
|
|
268
|
-
|
|
269
|
-
### extract() Method
|
|
270
|
-
|
|
271
|
-
```typescript
|
|
272
|
-
async extract<T = any>(
|
|
273
|
-
options: ExtractionOptions<T>
|
|
274
|
-
): Promise<ExtractionResult<T>>
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
### ExtractionOptions
|
|
278
|
-
|
|
279
|
-
```typescript
|
|
280
|
-
interface ExtractionOptions<T = any> {
|
|
281
|
-
// Required
|
|
282
|
-
query: string; // GraphQL query
|
|
283
|
-
resultPath: string; // Path to extract (e.g., 'virtualPositions.edges.node')
|
|
284
|
-
|
|
285
|
-
// Optional
|
|
286
|
-
variables?: Record<string, any>; // Query variables (default: {})
|
|
287
|
-
pageSize?: number; // Records per page (default: 200)
|
|
288
|
-
maxRecords?: number; // Max total records (default: unlimited)
|
|
289
|
-
maxPages?: number; // Max pages to fetch (default: unlimited)
|
|
290
|
-
timeout?: number; // Query timeout ms (default: 60000)
|
|
291
|
-
direction?: 'forward' | 'backward'; // Pagination direction (default: 'forward')
|
|
292
|
-
validateItem?: (item: T) => boolean; // Optional per-item validation
|
|
293
|
-
errorHandling?: 'throw' | 'partial'; // How to handle GraphQL errors (default: 'throw')
|
|
294
|
-
operationName?: string; // Optional operation name for logging
|
|
295
|
-
}
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
**Notes:**
|
|
299
|
-
- `direction`: `'forward'` (default) uses `first/after` params, requires `pageInfo.hasNextPage`. `'backward'` uses `last/before` params, requires `pageInfo.hasPreviousPage`. Your GraphQL query must match the direction (don't mix `first/after` with `direction: 'backward'`)
|
|
300
|
-
- `errorHandling`: `'throw'` (default) throws `GraphQLExecutionError` on any errors. `'partial'` continues extraction and accumulates errors in `stats.partialErrors`. See [Partial Response Support](../../api-reference/modules/api-reference-12-partial-responses.md) for details.
|
|
301
|
-
|
|
302
|
-
### ExtractionResult
|
|
303
|
-
|
|
304
|
-
```typescript
|
|
305
|
-
interface ExtractionResult<T = any> {
|
|
306
|
-
data: T[]; // Extracted records
|
|
307
|
-
stats: ExtractionStats; // Statistics
|
|
308
|
-
errors?: ExtractionError[]; // Non-fatal errors (validation failures)
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
interface ExtractionStats {
|
|
312
|
-
totalRecords: number; // Total records extracted
|
|
313
|
-
totalPages: number; // Number of pages fetched
|
|
314
|
-
duration: number; // Extraction duration in milliseconds
|
|
315
|
-
truncated: boolean; // Whether extraction was limited
|
|
316
|
-
truncationReason?: 'maxPages' | 'maxRecords' | 'timeout'; // Reason for truncation
|
|
317
|
-
validRecords?: number; // Valid records (if validation used)
|
|
318
|
-
invalidRecords?: number; // Invalid records (if validation used)
|
|
319
|
-
direction?: 'forward' | 'backward'; // Pagination direction used
|
|
320
|
-
partialErrors?: GraphQLError[]; // GraphQL errors (only when errorHandling: 'partial')
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
interface ExtractionError {
|
|
324
|
-
type: 'graphql' | 'network' | 'validation' | 'parsing'; // Error type
|
|
325
|
-
message: string; // Error message
|
|
326
|
-
recordIndex?: number; // Record index (for validation errors)
|
|
327
|
-
details?: any; // Additional error details
|
|
328
|
-
}
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
## 📋 Usage Patterns
|
|
332
|
-
|
|
333
|
-
### Pattern 1: Simple Extraction
|
|
334
|
-
|
|
335
|
-
```typescript
|
|
336
|
-
const orchestrator = new ExtractionOrchestrator(client, console);
|
|
337
|
-
|
|
338
|
-
const result = await orchestrator.extract({
|
|
339
|
-
query: `
|
|
340
|
-
query GetProducts($first: Int!, $after: String) {
|
|
341
|
-
products(first: $first, after: $after) {
|
|
342
|
-
edges {
|
|
343
|
-
node {
|
|
344
|
-
id
|
|
345
|
-
ref
|
|
346
|
-
name
|
|
347
|
-
type
|
|
348
|
-
status
|
|
349
|
-
}
|
|
350
|
-
cursor
|
|
351
|
-
}
|
|
352
|
-
pageInfo { hasNextPage }
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
`,
|
|
356
|
-
resultPath: 'products.edges.node',
|
|
357
|
-
pageSize: 100,
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
console.log(`Extracted ${result.data.length} products`);
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
### Pattern 2: Extraction with Limits
|
|
364
|
-
|
|
365
|
-
```typescript
|
|
366
|
-
// Extract first 5000 records only
|
|
367
|
-
const result = await orchestrator.extract({
|
|
368
|
-
query: inventoryQuery,
|
|
369
|
-
resultPath: 'inventoryPositions.edges.node',
|
|
370
|
-
maxRecords: 5000,
|
|
371
|
-
pageSize: 500, // Use larger pages for efficiency
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
if (result.stats.truncated) {
|
|
375
|
-
console.log('Extraction was limited to 5000 records');
|
|
376
|
-
}
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
### Pattern 3: Extraction with Validation
|
|
380
|
-
|
|
381
|
-
```typescript
|
|
382
|
-
const result = await orchestrator.extract({
|
|
383
|
-
query: virtualPositionsQuery,
|
|
384
|
-
resultPath: 'virtualPositions.edges.node',
|
|
385
|
-
|
|
386
|
-
// Validate each item during extraction
|
|
387
|
-
validateItem: item => {
|
|
388
|
-
return (
|
|
389
|
-
item.ref && // ref is required
|
|
390
|
-
item.productRef && // productRef is required
|
|
391
|
-
typeof item.qty === 'number' && // qty must be a number
|
|
392
|
-
item.qty >= 0 // qty must be non-negative
|
|
393
|
-
);
|
|
394
|
-
},
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
console.log(`Valid records: ${result.stats.validRecords}`);
|
|
398
|
-
console.log(`Invalid records: ${result.stats.invalidRecords}`);
|
|
399
|
-
|
|
400
|
-
// Review validation errors (recordIndex, type, details)
|
|
401
|
-
if (result.errors && result.errors.length > 0) {
|
|
402
|
-
console.log(
|
|
403
|
-
'Validation errors:',
|
|
404
|
-
result.errors.map(e => ({
|
|
405
|
-
type: e.type,
|
|
406
|
-
recordIndex: e.recordIndex,
|
|
407
|
-
details: e.details,
|
|
408
|
-
}))
|
|
409
|
-
);
|
|
410
|
-
}
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
### Pattern 4: Extraction with Variables
|
|
414
|
-
|
|
415
|
-
```typescript
|
|
416
|
-
// Extract orders for specific retailer and date range
|
|
417
|
-
const result = await orchestrator.extract({
|
|
418
|
-
query: `
|
|
419
|
-
query GetOrders(
|
|
420
|
-
$first: Int!,
|
|
421
|
-
$after: String,
|
|
422
|
-
$retailerId: ID!,
|
|
423
|
-
$startDate: DateTime!
|
|
424
|
-
) {
|
|
425
|
-
orders(
|
|
426
|
-
first: $first,
|
|
427
|
-
after: $after,
|
|
428
|
-
retailerId: $retailerId,
|
|
429
|
-
createdOn: { from: $startDate }
|
|
430
|
-
) {
|
|
431
|
-
edges {
|
|
432
|
-
node {
|
|
433
|
-
id
|
|
434
|
-
ref
|
|
435
|
-
status
|
|
436
|
-
createdOn
|
|
437
|
-
totalPrice
|
|
438
|
-
}
|
|
439
|
-
cursor
|
|
440
|
-
}
|
|
441
|
-
pageInfo { hasNextPage }
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
`,
|
|
445
|
-
resultPath: 'orders.edges.node',
|
|
446
|
-
variables: {
|
|
447
|
-
startDate: '2025-01-01T00:00:00Z',
|
|
448
|
-
},
|
|
449
|
-
pageSize: 100,
|
|
450
|
-
});
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
### Pattern 5: Nested Path Extraction
|
|
454
|
-
|
|
455
|
-
```typescript
|
|
456
|
-
// Extract from deeply nested response
|
|
457
|
-
const result = await orchestrator.extract({
|
|
458
|
-
query: `
|
|
459
|
-
query GetCatalogues($first: Int!, $after: String) {
|
|
460
|
-
catalogues(first: $first, after: $after) {
|
|
461
|
-
edges {
|
|
462
|
-
node {
|
|
463
|
-
id
|
|
464
|
-
ref
|
|
465
|
-
name
|
|
466
|
-
products {
|
|
467
|
-
edges {
|
|
468
|
-
node {
|
|
469
|
-
id
|
|
470
|
-
ref
|
|
471
|
-
name
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
cursor
|
|
477
|
-
}
|
|
478
|
-
pageInfo { hasNextPage }
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
`,
|
|
482
|
-
// Extract products from catalogues
|
|
483
|
-
resultPath: 'catalogues.edges.node.products.edges.node',
|
|
484
|
-
pageSize: 50,
|
|
485
|
-
});
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
## 🎯 Real-World Examples
|
|
489
|
-
|
|
490
|
-
### Example 1: Virtual Positions Extraction for SFTP Export
|
|
491
|
-
|
|
492
|
-
```typescript
|
|
493
|
-
import {
|
|
494
|
-
ExtractionOrchestrator,
|
|
495
|
-
createClient,
|
|
496
|
-
SftpDataSource,
|
|
497
|
-
createConsoleLogger,
|
|
498
|
-
toStructuredLogger
|
|
499
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
500
|
-
|
|
501
|
-
async function exportVirtualPositionsToSftp() {
|
|
502
|
-
// Initialize services
|
|
503
|
-
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
504
|
-
logLevel: 'info'
|
|
505
|
-
});
|
|
506
|
-
const client = await createClient({
|
|
507
|
-
config: {
|
|
508
|
-
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
509
|
-
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
510
|
-
clientSecret: process.env.FLUENT_CLIENT_SECRET!
|
|
511
|
-
// retailerId is OPTIONAL for GraphQL queries (only required for Job/Event API)
|
|
512
|
-
}
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
const sftp = new SftpDataSource(
|
|
516
|
-
{
|
|
517
|
-
type: 'SFTP_CSV',
|
|
518
|
-
sftpConfig: {
|
|
519
|
-
settings: {
|
|
520
|
-
host: process.env.SFTP_HOST!,
|
|
521
|
-
port: 22,
|
|
522
|
-
username: process.env.SFTP_USERNAME!,
|
|
523
|
-
password: process.env.SFTP_PASSWORD!,
|
|
524
|
-
},
|
|
525
|
-
},
|
|
526
|
-
connectionId: 'sftp-export',
|
|
527
|
-
},
|
|
528
|
-
logger
|
|
529
|
-
);
|
|
530
|
-
|
|
531
|
-
// Extract virtual positions
|
|
532
|
-
const orchestrator = new ExtractionOrchestrator(client, logger);
|
|
533
|
-
|
|
534
|
-
const result = await orchestrator.extract({
|
|
535
|
-
query: `
|
|
536
|
-
query GetVirtualPositions($first: Int!, $after: String) {
|
|
537
|
-
virtualPositions(first: $first, after: $after) {
|
|
538
|
-
edges {
|
|
539
|
-
node {
|
|
540
|
-
id
|
|
541
|
-
ref
|
|
542
|
-
productRef
|
|
543
|
-
qty
|
|
544
|
-
groupRef
|
|
545
|
-
createdOn
|
|
546
|
-
updatedOn
|
|
547
|
-
}
|
|
548
|
-
cursor
|
|
549
|
-
}
|
|
550
|
-
pageInfo {
|
|
551
|
-
hasNextPage
|
|
552
|
-
# Note: Fluent doesn't return endCursor - use edge cursors instead
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
`,
|
|
557
|
-
resultPath: 'virtualPositions.edges.node',
|
|
558
|
-
pageSize: 500,
|
|
559
|
-
maxRecords: 50000,
|
|
560
|
-
|
|
561
|
-
// Validate data quality
|
|
562
|
-
validateItem: item => {
|
|
563
|
-
return item.ref && item.productRef && typeof item.qty === 'number';
|
|
564
|
-
},
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
logger.info('Extraction complete', {
|
|
568
|
-
totalRecords: result.stats.totalRecords,
|
|
569
|
-
totalPages: result.stats.totalPages,
|
|
570
|
-
duration: result.stats.duration,
|
|
571
|
-
validRecords: result.stats.validRecords,
|
|
572
|
-
invalidRecords: result.stats.invalidRecords,
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
// Convert to CSV format
|
|
576
|
-
const csv = [
|
|
577
|
-
// Header
|
|
578
|
-
'ref,productRef,qty,groupRef,createdOn,updatedOn',
|
|
579
|
-
// Data rows
|
|
580
|
-
...result.data.map(
|
|
581
|
-
item =>
|
|
582
|
-
`${item.ref},${item.productRef},${item.qty},${item.groupRef},${item.createdOn},${item.updatedOn}`
|
|
583
|
-
),
|
|
584
|
-
].join('\n');
|
|
585
|
-
|
|
586
|
-
// Upload to SFTP
|
|
587
|
-
try {
|
|
588
|
-
const filename = `/exports/virtual-positions-${new Date().toISOString()}.csv`;
|
|
589
|
-
await sftp.uploadFile(filename, csv);
|
|
590
|
-
|
|
591
|
-
logger.info('Export complete', { filename, recordCount: result.data.length });
|
|
592
|
-
} finally {
|
|
593
|
-
// Always dispose SFTP connection to prevent connection pool leaks
|
|
594
|
-
await sftp.dispose();
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
### Example 2: Multi-Query Extraction Pattern
|
|
600
|
-
|
|
601
|
-
```typescript
|
|
602
|
-
// For multiple queries, call ExtractionOrchestrator separately for each
|
|
603
|
-
async function extractMultipleEntities() {
|
|
604
|
-
const orchestrator = new ExtractionOrchestrator(client, logger);
|
|
605
|
-
|
|
606
|
-
// Execute extractions in parallel
|
|
607
|
-
const [products, locations, inventories] = await Promise.all([
|
|
608
|
-
// Extract products
|
|
609
|
-
orchestrator.extract({
|
|
610
|
-
query: productsQuery,
|
|
611
|
-
resultPath: 'products.edges.node',
|
|
612
|
-
pageSize: 200,
|
|
613
|
-
}),
|
|
614
|
-
|
|
615
|
-
// Extract locations
|
|
616
|
-
orchestrator.extract({
|
|
617
|
-
query: locationsQuery,
|
|
618
|
-
resultPath: 'locations.edges.node',
|
|
619
|
-
pageSize: 100,
|
|
620
|
-
}),
|
|
621
|
-
|
|
622
|
-
// Extract inventory positions
|
|
623
|
-
orchestrator.extract({
|
|
624
|
-
query: inventoryQuery,
|
|
625
|
-
resultPath: 'inventoryPositions.edges.node',
|
|
626
|
-
maxRecords: 10000,
|
|
627
|
-
pageSize: 500,
|
|
628
|
-
}),
|
|
629
|
-
]);
|
|
630
|
-
|
|
631
|
-
// Combine or process separately in your business logic
|
|
632
|
-
return {
|
|
633
|
-
products: products.data,
|
|
634
|
-
locations: locations.data,
|
|
635
|
-
inventories: inventories.data,
|
|
636
|
-
stats: {
|
|
637
|
-
totalProducts: products.stats.totalRecords,
|
|
638
|
-
totalLocations: locations.stats.totalRecords,
|
|
639
|
-
totalInventories: inventories.stats.totalRecords,
|
|
640
|
-
totalDuration:
|
|
641
|
-
products.stats.duration + locations.stats.duration + inventories.stats.duration,
|
|
642
|
-
},
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
```
|
|
646
|
-
|
|
647
|
-
### Example 3: Extraction with Transformation
|
|
648
|
-
|
|
649
|
-
```typescript
|
|
650
|
-
import { ExtractionOrchestrator, UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
651
|
-
|
|
652
|
-
async function extractAndTransform() {
|
|
653
|
-
const orchestrator = new ExtractionOrchestrator(client, logger);
|
|
654
|
-
|
|
655
|
-
// Step 1: Extract
|
|
656
|
-
const extraction = await orchestrator.extract({
|
|
657
|
-
query: virtualPositionsQuery,
|
|
658
|
-
resultPath: 'virtualPositions.edges.node',
|
|
659
|
-
pageSize: 500,
|
|
660
|
-
});
|
|
661
|
-
|
|
662
|
-
// Step 2: Transform with UniversalMapper
|
|
663
|
-
const mapper = new UniversalMapper({
|
|
664
|
-
fields: {
|
|
665
|
-
reference: { source: 'ref', required: true },
|
|
666
|
-
product: { source: 'productRef', required: true },
|
|
667
|
-
quantity: { source: 'qty', resolver: 'sdk.parseInt' },
|
|
668
|
-
group: { source: 'groupRef', resolver: 'sdk.trim' },
|
|
669
|
-
lastUpdated: { source: 'updatedOn', resolver: 'sdk.formatDate' },
|
|
670
|
-
},
|
|
671
|
-
});
|
|
672
|
-
|
|
673
|
-
const transformation = await mapper.map(extraction.data);
|
|
674
|
-
|
|
675
|
-
if (!transformation.success) {
|
|
676
|
-
logger.error('Transformation failed', { errors: transformation.errors });
|
|
677
|
-
throw new Error('Data transformation failed');
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
return transformation.data;
|
|
681
|
-
}
|
|
682
|
-
```
|
|
683
|
-
|
|
684
|
-
## ⚠️ Error Handling
|
|
685
|
-
|
|
686
|
-
### Handling Extraction Errors
|
|
687
|
-
|
|
688
|
-
```typescript
|
|
689
|
-
try {
|
|
690
|
-
const result = await orchestrator.extract(options);
|
|
691
|
-
|
|
692
|
-
// Check for validation errors
|
|
693
|
-
if (result.errors && result.errors.length > 0) {
|
|
694
|
-
logger.warn(`${result.errors.length} records failed validation`);
|
|
695
|
-
|
|
696
|
-
// Log first few errors for debugging
|
|
697
|
-
result.errors.slice(0, 5).forEach(err => {
|
|
698
|
-
logger.debug('Validation error', {
|
|
699
|
-
type: err.type,
|
|
700
|
-
message: err.message,
|
|
701
|
-
recordIndex: err.recordIndex,
|
|
702
|
-
details: err.details,
|
|
703
|
-
});
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
// Check if extraction was truncated
|
|
708
|
-
if (result.stats.truncated) {
|
|
709
|
-
logger.info('Extraction was limited by maxRecords or maxPages');
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
return result.data;
|
|
713
|
-
} catch (error) {
|
|
714
|
-
// Fatal errors (GraphQL errors, network issues, invalid config)
|
|
715
|
-
logger.error('Extraction failed', error as Error);
|
|
716
|
-
throw error;
|
|
717
|
-
}
|
|
718
|
-
```
|
|
719
|
-
|
|
720
|
-
### Partial Response Mode (errorHandling: 'partial')
|
|
721
|
-
|
|
722
|
-
When GraphQL returns errors but some data is available, you can use partial mode to continue extraction:
|
|
723
|
-
|
|
724
|
-
```typescript
|
|
725
|
-
const result = await orchestrator.extract({
|
|
726
|
-
query: ORDERS_QUERY,
|
|
727
|
-
resultPath: 'orders.edges.node',
|
|
728
|
-
errorHandling: 'partial', // Continue extraction even with errors
|
|
729
|
-
});
|
|
730
|
-
|
|
731
|
-
// Check for partial errors in stats
|
|
732
|
-
if (result.stats.partialErrors) {
|
|
733
|
-
logger.warn('Extraction completed with partial errors', {
|
|
734
|
-
recordsExtracted: result.data.length,
|
|
735
|
-
errorCount: result.stats.partialErrors.length,
|
|
736
|
-
});
|
|
737
|
-
|
|
738
|
-
// Process available data
|
|
739
|
-
console.log(`Extracted ${result.data.length} records despite errors`);
|
|
740
|
-
|
|
741
|
-
// Log errors for investigation
|
|
742
|
-
result.stats.partialErrors.forEach(err => {
|
|
743
|
-
logger.warn('GraphQL error during extraction', {
|
|
744
|
-
message: err.message,
|
|
745
|
-
path: err.path,
|
|
746
|
-
});
|
|
747
|
-
});
|
|
748
|
-
}
|
|
749
|
-
```
|
|
750
|
-
|
|
751
|
-
**Edge Case: Null Data with Errors**
|
|
752
|
-
|
|
753
|
-
When GraphQL returns errors with `data: null` or `data: undefined`:
|
|
754
|
-
|
|
755
|
-
```typescript
|
|
756
|
-
// GraphQL response: { data: null, errors: [{ message: 'Order item not found' }] }
|
|
757
|
-
const result = await orchestrator.extract({
|
|
758
|
-
query: ORDERS_QUERY,
|
|
759
|
-
resultPath: 'orders.edges.node',
|
|
760
|
-
errorHandling: 'partial',
|
|
761
|
-
});
|
|
762
|
-
|
|
763
|
-
// Returns empty array with errors in stats
|
|
764
|
-
expect(result.data).toEqual([]);
|
|
765
|
-
expect(result.stats.partialErrors).toHaveLength(1);
|
|
766
|
-
```
|
|
767
|
-
|
|
768
|
-
**Behavior:**
|
|
769
|
-
- **Partial mode**: Returns empty array `[]`, accumulates errors in `stats.partialErrors`, stops pagination if no data
|
|
770
|
-
- **Throw mode** (default): Throws `GraphQLExecutionError` immediately (backward compatible)
|
|
771
|
-
|
|
772
|
-
**When to Use Partial Mode:**
|
|
773
|
-
- ✅ Batch queries where some items may fail independently
|
|
774
|
-
- ✅ Reporting/analytics where partial data is better than none
|
|
775
|
-
- ✅ Non-critical extractions where you want to process available data
|
|
776
|
-
|
|
777
|
-
**When to Use Throw Mode (default):**
|
|
778
|
-
- ✅ Critical data where consistency matters
|
|
779
|
-
- ✅ Single record operations
|
|
780
|
-
- ✅ When you need to fail fast on any errors
|
|
781
|
-
|
|
782
|
-
### Common Error Scenarios
|
|
783
|
-
|
|
784
|
-
| Error | Cause | Solution |
|
|
785
|
-
| ------------------------------ | ---------------------------- | ------------------------------------------------- |
|
|
786
|
-
| `Query is required` | Empty query string | Provide valid GraphQL query |
|
|
787
|
-
| `Result path is required` | Empty resultPath | Specify path to extract (e.g., 'data.edges.node') |
|
|
788
|
-
| `Page size must be positive` | pageSize ≤ 0 | Use pageSize ≥ 1 |
|
|
789
|
-
| `Max records must be positive` | maxRecords ≤ 0 | Use maxRecords ≥ 1 |
|
|
790
|
-
| `Max pages must be positive` | maxPages ≤ 0 | Use maxPages ≥ 1 |
|
|
791
|
-
| `Timeout must be positive` | timeout ≤ 0 | Use timeout ≥ 1000 (1 second minimum) |
|
|
792
|
-
| `Path extraction failed` | Invalid resultPath | Check path matches response structure |
|
|
793
|
-
| `GraphQL Error: ...` | Invalid query or permissions | Validate query with schema introspection |
|
|
794
|
-
|
|
795
|
-
## 🎓 Best Practices
|
|
796
|
-
|
|
797
|
-
### 1. Choose Appropriate Page Size
|
|
798
|
-
|
|
799
|
-
```typescript
|
|
800
|
-
// ❌ Too small - many API calls
|
|
801
|
-
pageSize: 10;
|
|
802
|
-
|
|
803
|
-
// ✅ Balanced - good for most use cases
|
|
804
|
-
pageSize: 200;
|
|
805
|
-
|
|
806
|
-
// ✅ Large datasets - fewer API calls
|
|
807
|
-
pageSize: 500;
|
|
808
|
-
|
|
809
|
-
// ⚠️ Very large - may cause memory issues
|
|
810
|
-
pageSize: 1000;
|
|
811
|
-
```
|
|
812
|
-
|
|
813
|
-
### 2. Use Extraction Limits for Large Datasets
|
|
814
|
-
|
|
815
|
-
```typescript
|
|
816
|
-
// ✅ Prevent unbounded extraction
|
|
817
|
-
const result = await orchestrator.extract({
|
|
818
|
-
query: largeDatasetQuery,
|
|
819
|
-
resultPath: 'data.edges.node',
|
|
820
|
-
maxRecords: 100000, // Hard limit
|
|
821
|
-
maxPages: 200, // Backup limit
|
|
822
|
-
timeout: 300000, // 5 minute timeout
|
|
823
|
-
});
|
|
824
|
-
```
|
|
825
|
-
|
|
826
|
-
### 3. Implement Validation for Data Quality
|
|
827
|
-
|
|
828
|
-
```typescript
|
|
829
|
-
// ✅ Validate critical fields during extraction
|
|
830
|
-
validateItem: item => {
|
|
831
|
-
// Required fields present
|
|
832
|
-
if (!item.ref || !item.productRef) return false;
|
|
833
|
-
|
|
834
|
-
// Valid data types
|
|
835
|
-
if (typeof item.qty !== 'number') return false;
|
|
836
|
-
|
|
837
|
-
// Business logic validation
|
|
838
|
-
if (item.qty < 0) return false;
|
|
839
|
-
|
|
840
|
-
return true;
|
|
841
|
-
};
|
|
842
|
-
```
|
|
843
|
-
|
|
844
|
-
### 4. Monitor Statistics
|
|
845
|
-
|
|
846
|
-
```typescript
|
|
847
|
-
const result = await orchestrator.extract(options);
|
|
848
|
-
|
|
849
|
-
// Log comprehensive statistics
|
|
850
|
-
logger.info('Extraction complete', {
|
|
851
|
-
totalRecords: result.stats.totalRecords,
|
|
852
|
-
totalPages: result.stats.totalPages,
|
|
853
|
-
duration: result.stats.duration,
|
|
854
|
-
validRecords: result.stats.validRecords,
|
|
855
|
-
invalidRecords: result.stats.invalidRecords,
|
|
856
|
-
truncated: result.stats.truncated,
|
|
857
|
-
truncationReason: result.stats.truncationReason,
|
|
858
|
-
|
|
859
|
-
// Calculate average records per page
|
|
860
|
-
avgRecordsPerPage: Math.round(result.stats.totalRecords / result.stats.totalPages),
|
|
861
|
-
});
|
|
862
|
-
```
|
|
863
|
-
|
|
864
|
-
### 5. Use with JobTracker for Async Workflows
|
|
865
|
-
|
|
866
|
-
```typescript
|
|
867
|
-
import { ExtractionOrchestrator, JobTracker } from '@fluentcommerce/fc-connect-sdk';
|
|
868
|
-
|
|
869
|
-
async function scheduledExtraction() {
|
|
870
|
-
const tracker = new JobTracker(kvAdapter, logger);
|
|
871
|
-
|
|
872
|
-
try {
|
|
873
|
-
// Start job
|
|
874
|
-
const job = await tracker.startJob({
|
|
875
|
-
triggeredBy: 'schedule',
|
|
876
|
-
details: { type: 'virtual-positions-extract' },
|
|
877
|
-
});
|
|
878
|
-
|
|
879
|
-
// Start extraction
|
|
880
|
-
await tracker.update(job.id, {
|
|
881
|
-
status: 'PROCESSING',
|
|
882
|
-
stage: 'extracting',
|
|
883
|
-
});
|
|
884
|
-
|
|
885
|
-
const orchestrator = new ExtractionOrchestrator(client, logger);
|
|
886
|
-
const result = await orchestrator.extract({
|
|
887
|
-
query: virtualPositionsQuery,
|
|
888
|
-
resultPath: 'virtualPositions.edges.node',
|
|
889
|
-
maxRecords: 50000,
|
|
890
|
-
});
|
|
891
|
-
|
|
892
|
-
// Complete
|
|
893
|
-
await tracker.complete(job.id, {
|
|
894
|
-
recordsExtracted: result.stats.totalRecords,
|
|
895
|
-
totalPages: result.stats.totalPages,
|
|
896
|
-
duration: result.stats.duration,
|
|
897
|
-
});
|
|
898
|
-
|
|
899
|
-
return result;
|
|
900
|
-
} catch (error) {
|
|
901
|
-
// Mark failed via update to avoid relying on specific error helpers
|
|
902
|
-
await tracker.update(job.id, { status: 'FAILED', error: (error as Error).message });
|
|
903
|
-
throw error;
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
```
|
|
907
|
-
|
|
908
|
-
## 🔗 Related Documentation
|
|
909
|
-
|
|
910
|
-
- [Module 2: Basic Extraction](./02-core-guides-extraction-02-basic-extraction.md) - Manual GraphQL extraction
|
|
911
|
-
- [Module 4: Data Enrichment](./02-core-guides-extraction-04-data-enrichment.md) - Multi-query patterns
|
|
912
|
-
- [Module 5: Transformation](./02-core-guides-extraction-05-transformation.md) - UniversalMapper guide
|
|
913
|
-
- [Job Tracker](../../advanced-services/advanced-services-job-tracker.md) - Async job management
|
|
914
|
-
- [Auto-Pagination Guide](../../auto-pagination/) - Pagination internals
|
|
915
|
-
|
|
916
|
-
## 📊 Performance Considerations
|
|
917
|
-
|
|
918
|
-
### Memory Usage
|
|
919
|
-
|
|
920
|
-
```typescript
|
|
921
|
-
// ⚠️ High memory - stores all records in memory
|
|
922
|
-
const result = await orchestrator.extract({
|
|
923
|
-
query: myQuery,
|
|
924
|
-
resultPath: 'data.edges.node',
|
|
925
|
-
maxRecords: 1000000, // 1 million records!
|
|
926
|
-
});
|
|
927
|
-
|
|
928
|
-
// ✅ Better - use maxRecords to limit extraction
|
|
929
|
-
// Note: ExtractionOrchestrator handles pagination internally
|
|
930
|
-
// For processing in chunks, use multiple extraction calls with date ranges
|
|
931
|
-
async function extractByDateRange() {
|
|
932
|
-
const dateRanges = [
|
|
933
|
-
{ from: '2025-01-01', to: '2025-01-31' },
|
|
934
|
-
{ from: '2025-02-01', to: '2025-02-28' },
|
|
935
|
-
// ... more ranges
|
|
936
|
-
];
|
|
937
|
-
|
|
938
|
-
for (const range of dateRanges) {
|
|
939
|
-
const result = await orchestrator.extract({
|
|
940
|
-
query: myQuery,
|
|
941
|
-
resultPath: 'data.edges.node',
|
|
942
|
-
variables: {
|
|
943
|
-
dateRangeFilter: range,
|
|
944
|
-
},
|
|
945
|
-
maxRecords: 100000, // Limit per date range
|
|
946
|
-
});
|
|
947
|
-
|
|
948
|
-
// Process chunk
|
|
949
|
-
await processChunk(result.data);
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
```
|
|
953
|
-
|
|
954
|
-
### API Rate Limiting
|
|
955
|
-
|
|
956
|
-
```typescript
|
|
957
|
-
// Add delay between large extractions
|
|
958
|
-
async function extractMultipleWithDelay() {
|
|
959
|
-
const queries = [query1, query2, query3];
|
|
960
|
-
const results = [];
|
|
961
|
-
|
|
962
|
-
for (const query of queries) {
|
|
963
|
-
const result = await orchestrator.extract({
|
|
964
|
-
query,
|
|
965
|
-
resultPath: 'data.edges.node',
|
|
966
|
-
maxRecords: 50000,
|
|
967
|
-
});
|
|
968
|
-
|
|
969
|
-
results.push(result);
|
|
970
|
-
|
|
971
|
-
// Delay 1 second between extractions
|
|
972
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
return results;
|
|
976
|
-
}
|
|
977
|
-
```
|
|
978
|
-
|
|
979
|
-
## ✅ Summary
|
|
980
|
-
|
|
981
|
-
**ExtractionOrchestrator simplifies:**
|
|
982
|
-
|
|
983
|
-
- ✅ GraphQL query execution with auto-pagination
|
|
984
|
-
- ✅ Path-based extraction from nested responses
|
|
985
|
-
- ✅ Automatic edges.node pattern handling
|
|
986
|
-
- ✅ Extraction limits and validation
|
|
987
|
-
- ✅ Statistics tracking and error handling
|
|
988
|
-
|
|
989
|
-
**Use it when you need:**
|
|
990
|
-
|
|
991
|
-
- Single-query data extraction
|
|
992
|
-
- Automatic pagination handling
|
|
993
|
-
- Path-based result extraction
|
|
994
|
-
- Built-in validation and statistics
|
|
995
|
-
|
|
996
|
-
**For complex scenarios:**
|
|
997
|
-
|
|
998
|
-
- Multiple queries → Make separate calls, use `Promise.all()`
|
|
999
|
-
- Business logic → Use `UniversalMapper` for transformation
|
|
1000
|
-
- Real-time streaming → Use direct `client.graphql()` calls
|
|
1001
|
-
|
|
1002
|
-
---
|
|
1003
|
-
|
|
1004
|
-
**Next Steps:**
|
|
1005
|
-
|
|
1006
|
-
- [Job Tracker](../../advanced-services/advanced-services-job-tracker.md) - Async job status tracking
|
|
1007
|
-
- [Module 5: Transformation](./02-core-guides-extraction-05-transformation.md) - Transform extracted data
|
|
1008
|
-
- [Examples Directory](../examples/) - Working code examples
|
|
1
|
+
# Module 8: ExtractionOrchestrator - Simplified Data Extraction
|
|
2
|
+
|
|
3
|
+
> **Reusable orchestrator for extracting and transforming GraphQL data with auto-pagination**
|
|
4
|
+
|
|
5
|
+
## 📚 What You'll Learn
|
|
6
|
+
|
|
7
|
+
- ✅ How to use ExtractionOrchestrator for simple data extraction workflows
|
|
8
|
+
- ✅ Path-based result extraction from nested GraphQL responses
|
|
9
|
+
- ✅ Automatic handling of GraphQL connection patterns (edges.node)
|
|
10
|
+
- ✅ Extraction limits and validation
|
|
11
|
+
- ✅ Statistics tracking and error handling
|
|
12
|
+
- ✅ When to use ExtractionOrchestrator vs manual extraction
|
|
13
|
+
|
|
14
|
+
## 🎯 Overview
|
|
15
|
+
|
|
16
|
+
`ExtractionOrchestrator` is a high-level service that simplifies the common pattern of:
|
|
17
|
+
|
|
18
|
+
1. **Execute** GraphQL query with auto-pagination
|
|
19
|
+
2. **Extract** data from nested response paths
|
|
20
|
+
3. **Validate** extracted records (optional)
|
|
21
|
+
4. **Track** statistics and errors
|
|
22
|
+
|
|
23
|
+
### When to Use
|
|
24
|
+
|
|
25
|
+
✅ **Use ExtractionOrchestrator when:**
|
|
26
|
+
|
|
27
|
+
- Extracting data from a single GraphQL query
|
|
28
|
+
- Need auto-pagination handling
|
|
29
|
+
- Want path-based extraction from nested responses
|
|
30
|
+
- Need extraction limits (maxRecords, maxPages, timeout)
|
|
31
|
+
- Want built-in statistics and error tracking
|
|
32
|
+
|
|
33
|
+
❌ **Don't use when:**
|
|
34
|
+
|
|
35
|
+
- Need to combine multiple queries (do separate calls)
|
|
36
|
+
- Complex business logic transformations (use UniversalMapper)
|
|
37
|
+
- Real-time streaming (use direct GraphQL calls)
|
|
38
|
+
|
|
39
|
+
## 🚀 Quick Start
|
|
40
|
+
|
|
41
|
+
### Basic Extraction
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { ExtractionOrchestrator, createClient } from '@fluentcommerce/fc-connect-sdk';
|
|
45
|
+
|
|
46
|
+
const client = await createClient({
|
|
47
|
+
config: {
|
|
48
|
+
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
49
|
+
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
50
|
+
clientSecret: process.env.FLUENT_CLIENT_SECRET!
|
|
51
|
+
// retailerId is OPTIONAL for GraphQL queries (only required for Job/Event API)
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const orchestrator = new ExtractionOrchestrator(client, console);
|
|
56
|
+
|
|
57
|
+
// Extract virtual positions
|
|
58
|
+
const result = await orchestrator.extract({
|
|
59
|
+
query: `
|
|
60
|
+
query GetVirtualPositions($first: Int!, $after: String) {
|
|
61
|
+
virtualPositions(first: $first, after: $after) {
|
|
62
|
+
edges {
|
|
63
|
+
node {
|
|
64
|
+
id
|
|
65
|
+
ref
|
|
66
|
+
productRef
|
|
67
|
+
qty
|
|
68
|
+
groupRef
|
|
69
|
+
}
|
|
70
|
+
cursor
|
|
71
|
+
}
|
|
72
|
+
pageInfo {
|
|
73
|
+
hasNextPage
|
|
74
|
+
# Note: Fluent doesn't return endCursor - use edge cursors instead
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
`,
|
|
79
|
+
resultPath: 'virtualPositions.edges.node',
|
|
80
|
+
pageSize: 200,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
console.log(`Extracted ${result.data.length} records`);
|
|
84
|
+
console.log(`Pages fetched: ${result.stats.totalPages}`);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 📖 Core Concepts
|
|
88
|
+
|
|
89
|
+
### 1. Result Path Extraction
|
|
90
|
+
|
|
91
|
+
The `resultPath` parameter uses dot notation to navigate nested GraphQL responses:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// Simple path using dot notation
|
|
95
|
+
resultPath: 'virtualPositions.edges.node';
|
|
96
|
+
|
|
97
|
+
// The orchestrator automatically:
|
|
98
|
+
// 1. Navigates to virtualPositions
|
|
99
|
+
// 2. Extracts edges array
|
|
100
|
+
// 3. Extracts node from each edge
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 2. Auto-Detection of Edges.Node Pattern
|
|
104
|
+
|
|
105
|
+
The orchestrator automatically detects and extracts from GraphQL connection patterns:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// If response has structure:
|
|
109
|
+
{
|
|
110
|
+
virtualPositions: {
|
|
111
|
+
edges: [{ node: { id: '1', ref: 'VP1' } }, { node: { id: '2', ref: 'VP2' } }];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Path: 'virtualPositions.edges'
|
|
116
|
+
// Result: [{ id: '1', ref: 'VP1' }, { id: '2', ref: 'VP2' }]
|
|
117
|
+
// Automatically extracts 'node' from each edge!
|
|
118
|
+
|
|
119
|
+
// Path: 'virtualPositions.edges.node'
|
|
120
|
+
// Result: Same as above (explicitly extracting node)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 3. Extraction Limits and Pagination Notes
|
|
124
|
+
|
|
125
|
+
Control extraction scope with multiple limit options:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
const result = await orchestrator.extract({
|
|
129
|
+
query: myQuery,
|
|
130
|
+
resultPath: 'data.edges.node',
|
|
131
|
+
|
|
132
|
+
// Limit options (all optional)
|
|
133
|
+
pageSize: 200, // Records per page (default: 200)
|
|
134
|
+
maxRecords: 10000, // Total records to extract
|
|
135
|
+
maxPages: 50, // Maximum pages to fetch
|
|
136
|
+
timeout: 120000, // Query timeout in ms (default: 60000)
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Note:
|
|
140
|
+
// - 'first' and 'after' are managed by the orchestrator; do not include them in variables
|
|
141
|
+
// - Pagination is handled automatically using the SDK's built-in auto-pagination
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Priority when multiple limits are set:**
|
|
145
|
+
|
|
146
|
+
1. `maxRecords` - Stops when total records reach limit
|
|
147
|
+
2. `maxPages` - Stops after fetching specified pages
|
|
148
|
+
3. API returns `hasNextPage: false`
|
|
149
|
+
|
|
150
|
+
### 4. Pagination Direction (Forward vs Backward)
|
|
151
|
+
|
|
152
|
+
**NEW:** ExtractionOrchestrator now supports both forward and backward pagination.
|
|
153
|
+
|
|
154
|
+
#### Forward Pagination (Default)
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// Forward pagination uses first/after (default behavior)
|
|
158
|
+
const result = await orchestrator.extract({
|
|
159
|
+
query: `
|
|
160
|
+
query GetItems($first: Int!, $after: String) {
|
|
161
|
+
items(first: $first, after: $after) {
|
|
162
|
+
edges {
|
|
163
|
+
node { id name }
|
|
164
|
+
cursor
|
|
165
|
+
}
|
|
166
|
+
pageInfo { hasNextPage }
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
`,
|
|
170
|
+
resultPath: 'items.edges.node',
|
|
171
|
+
direction: 'forward', // Optional - this is the default
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
#### Backward Pagination (Reverse)
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
// Backward pagination uses last/before
|
|
179
|
+
const result = await orchestrator.extract({
|
|
180
|
+
query: `
|
|
181
|
+
query GetItems($last: Int!, $before: String) {
|
|
182
|
+
items(last: $last, before: $before) {
|
|
183
|
+
edges {
|
|
184
|
+
node { id name }
|
|
185
|
+
cursor
|
|
186
|
+
}
|
|
187
|
+
pageInfo { hasPreviousPage }
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
`,
|
|
191
|
+
resultPath: 'items.edges.node',
|
|
192
|
+
direction: 'backward', // ✅ Enable reverse pagination
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### Query Requirements by Direction
|
|
197
|
+
|
|
198
|
+
| Direction | Parameters Required | PageInfo Required | Cursor Selection |
|
|
199
|
+
|-----------|-------------------|-------------------|------------------|
|
|
200
|
+
| `forward` (default) | `$first`, `$after` | `hasNextPage` | Last cursor in edges |
|
|
201
|
+
| `backward` | `$last`, `$before` | `hasPreviousPage` | First cursor in edges |
|
|
202
|
+
|
|
203
|
+
#### Important Rules
|
|
204
|
+
|
|
205
|
+
**✅ DO:**
|
|
206
|
+
- Declare `$first/$after` (forward) OR `$last/$before` (backward) in your query signature
|
|
207
|
+
- Include the corresponding parameters in your field arguments: `items(first: $first, after: $after)`
|
|
208
|
+
- Always include `cursor` in edges
|
|
209
|
+
- Include correct `pageInfo` field: `hasNextPage` for forward, `hasPreviousPage` for backward
|
|
210
|
+
|
|
211
|
+
**❌ DON'T:**
|
|
212
|
+
- Mix `first/after` with `direction: 'backward'` - query must match direction
|
|
213
|
+
- Include pagination parameters in `variables` - orchestrator injects them from `pageSize`/cursor
|
|
214
|
+
- Omit `cursor` from edges - orchestrator needs cursors for pagination
|
|
215
|
+
- Forget `hasPreviousPage` when using `direction: 'backward'` - validation will fail
|
|
216
|
+
|
|
217
|
+
#### Use Cases for Backward Pagination
|
|
218
|
+
|
|
219
|
+
**When to use backward:**
|
|
220
|
+
- 📅 Fetching most recent records first (e.g., latest orders, recent activity)
|
|
221
|
+
- 🔄 Reverse-chronological feeds
|
|
222
|
+
- 🎯 "Load more above" UI patterns
|
|
223
|
+
- 📊 Time-series data from newest to oldest
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
// Example: Get latest 1000 orders (most recent first)
|
|
227
|
+
const recentOrders = await orchestrator.extract({
|
|
228
|
+
query: `
|
|
229
|
+
query GetRecentOrders($last: Int!, $before: String) {
|
|
230
|
+
orders(last: $last, before: $before, orderBy: { field: CREATED_ON, direction: DESC }) {
|
|
231
|
+
edges {
|
|
232
|
+
node {
|
|
233
|
+
id
|
|
234
|
+
ref
|
|
235
|
+
createdOn
|
|
236
|
+
status
|
|
237
|
+
}
|
|
238
|
+
cursor
|
|
239
|
+
}
|
|
240
|
+
pageInfo { hasPreviousPage }
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
`,
|
|
244
|
+
resultPath: 'orders.edges.node',
|
|
245
|
+
direction: 'backward',
|
|
246
|
+
maxRecords: 1000,
|
|
247
|
+
pageSize: 200,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
console.log(`Fetched ${recentOrders.data.length} orders (newest first)`);
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## 🛠️ Complete API Reference
|
|
254
|
+
|
|
255
|
+
### Constructor
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
constructor(
|
|
259
|
+
client: FluentClient | FluentVersoriClient,
|
|
260
|
+
logger: StructuredLogger
|
|
261
|
+
)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Parameters:**
|
|
265
|
+
|
|
266
|
+
- `client` - Fluent API client instance
|
|
267
|
+
- `logger` - Structured logger (can use `console`)
|
|
268
|
+
|
|
269
|
+
### extract() Method
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
async extract<T = any>(
|
|
273
|
+
options: ExtractionOptions<T>
|
|
274
|
+
): Promise<ExtractionResult<T>>
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### ExtractionOptions
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
interface ExtractionOptions<T = any> {
|
|
281
|
+
// Required
|
|
282
|
+
query: string; // GraphQL query
|
|
283
|
+
resultPath: string; // Path to extract (e.g., 'virtualPositions.edges.node')
|
|
284
|
+
|
|
285
|
+
// Optional
|
|
286
|
+
variables?: Record<string, any>; // Query variables (default: {})
|
|
287
|
+
pageSize?: number; // Records per page (default: 200)
|
|
288
|
+
maxRecords?: number; // Max total records (default: unlimited)
|
|
289
|
+
maxPages?: number; // Max pages to fetch (default: unlimited)
|
|
290
|
+
timeout?: number; // Query timeout ms (default: 60000)
|
|
291
|
+
direction?: 'forward' | 'backward'; // Pagination direction (default: 'forward')
|
|
292
|
+
validateItem?: (item: T) => boolean; // Optional per-item validation
|
|
293
|
+
errorHandling?: 'throw' | 'partial'; // How to handle GraphQL errors (default: 'throw')
|
|
294
|
+
operationName?: string; // Optional operation name for logging
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**Notes:**
|
|
299
|
+
- `direction`: `'forward'` (default) uses `first/after` params, requires `pageInfo.hasNextPage`. `'backward'` uses `last/before` params, requires `pageInfo.hasPreviousPage`. Your GraphQL query must match the direction (don't mix `first/after` with `direction: 'backward'`)
|
|
300
|
+
- `errorHandling`: `'throw'` (default) throws `GraphQLExecutionError` on any errors. `'partial'` continues extraction and accumulates errors in `stats.partialErrors`. See [Partial Response Support](../../api-reference/modules/api-reference-12-partial-responses.md) for details.
|
|
301
|
+
|
|
302
|
+
### ExtractionResult
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
interface ExtractionResult<T = any> {
|
|
306
|
+
data: T[]; // Extracted records
|
|
307
|
+
stats: ExtractionStats; // Statistics
|
|
308
|
+
errors?: ExtractionError[]; // Non-fatal errors (validation failures)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
interface ExtractionStats {
|
|
312
|
+
totalRecords: number; // Total records extracted
|
|
313
|
+
totalPages: number; // Number of pages fetched
|
|
314
|
+
duration: number; // Extraction duration in milliseconds
|
|
315
|
+
truncated: boolean; // Whether extraction was limited
|
|
316
|
+
truncationReason?: 'maxPages' | 'maxRecords' | 'timeout'; // Reason for truncation
|
|
317
|
+
validRecords?: number; // Valid records (if validation used)
|
|
318
|
+
invalidRecords?: number; // Invalid records (if validation used)
|
|
319
|
+
direction?: 'forward' | 'backward'; // Pagination direction used
|
|
320
|
+
partialErrors?: GraphQLError[]; // GraphQL errors (only when errorHandling: 'partial')
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
interface ExtractionError {
|
|
324
|
+
type: 'graphql' | 'network' | 'validation' | 'parsing'; // Error type
|
|
325
|
+
message: string; // Error message
|
|
326
|
+
recordIndex?: number; // Record index (for validation errors)
|
|
327
|
+
details?: any; // Additional error details
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## 📋 Usage Patterns
|
|
332
|
+
|
|
333
|
+
### Pattern 1: Simple Extraction
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
const orchestrator = new ExtractionOrchestrator(client, console);
|
|
337
|
+
|
|
338
|
+
const result = await orchestrator.extract({
|
|
339
|
+
query: `
|
|
340
|
+
query GetProducts($first: Int!, $after: String) {
|
|
341
|
+
products(first: $first, after: $after) {
|
|
342
|
+
edges {
|
|
343
|
+
node {
|
|
344
|
+
id
|
|
345
|
+
ref
|
|
346
|
+
name
|
|
347
|
+
type
|
|
348
|
+
status
|
|
349
|
+
}
|
|
350
|
+
cursor
|
|
351
|
+
}
|
|
352
|
+
pageInfo { hasNextPage }
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
`,
|
|
356
|
+
resultPath: 'products.edges.node',
|
|
357
|
+
pageSize: 100,
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
console.log(`Extracted ${result.data.length} products`);
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Pattern 2: Extraction with Limits
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// Extract first 5000 records only
|
|
367
|
+
const result = await orchestrator.extract({
|
|
368
|
+
query: inventoryQuery,
|
|
369
|
+
resultPath: 'inventoryPositions.edges.node',
|
|
370
|
+
maxRecords: 5000,
|
|
371
|
+
pageSize: 500, // Use larger pages for efficiency
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
if (result.stats.truncated) {
|
|
375
|
+
console.log('Extraction was limited to 5000 records');
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Pattern 3: Extraction with Validation
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
const result = await orchestrator.extract({
|
|
383
|
+
query: virtualPositionsQuery,
|
|
384
|
+
resultPath: 'virtualPositions.edges.node',
|
|
385
|
+
|
|
386
|
+
// Validate each item during extraction
|
|
387
|
+
validateItem: item => {
|
|
388
|
+
return (
|
|
389
|
+
item.ref && // ref is required
|
|
390
|
+
item.productRef && // productRef is required
|
|
391
|
+
typeof item.qty === 'number' && // qty must be a number
|
|
392
|
+
item.qty >= 0 // qty must be non-negative
|
|
393
|
+
);
|
|
394
|
+
},
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
console.log(`Valid records: ${result.stats.validRecords}`);
|
|
398
|
+
console.log(`Invalid records: ${result.stats.invalidRecords}`);
|
|
399
|
+
|
|
400
|
+
// Review validation errors (recordIndex, type, details)
|
|
401
|
+
if (result.errors && result.errors.length > 0) {
|
|
402
|
+
console.log(
|
|
403
|
+
'Validation errors:',
|
|
404
|
+
result.errors.map(e => ({
|
|
405
|
+
type: e.type,
|
|
406
|
+
recordIndex: e.recordIndex,
|
|
407
|
+
details: e.details,
|
|
408
|
+
}))
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Pattern 4: Extraction with Variables
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
// Extract orders for specific retailer and date range
|
|
417
|
+
const result = await orchestrator.extract({
|
|
418
|
+
query: `
|
|
419
|
+
query GetOrders(
|
|
420
|
+
$first: Int!,
|
|
421
|
+
$after: String,
|
|
422
|
+
$retailerId: ID!,
|
|
423
|
+
$startDate: DateTime!
|
|
424
|
+
) {
|
|
425
|
+
orders(
|
|
426
|
+
first: $first,
|
|
427
|
+
after: $after,
|
|
428
|
+
retailerId: $retailerId,
|
|
429
|
+
createdOn: { from: $startDate }
|
|
430
|
+
) {
|
|
431
|
+
edges {
|
|
432
|
+
node {
|
|
433
|
+
id
|
|
434
|
+
ref
|
|
435
|
+
status
|
|
436
|
+
createdOn
|
|
437
|
+
totalPrice
|
|
438
|
+
}
|
|
439
|
+
cursor
|
|
440
|
+
}
|
|
441
|
+
pageInfo { hasNextPage }
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
`,
|
|
445
|
+
resultPath: 'orders.edges.node',
|
|
446
|
+
variables: {
|
|
447
|
+
startDate: '2025-01-01T00:00:00Z',
|
|
448
|
+
},
|
|
449
|
+
pageSize: 100,
|
|
450
|
+
});
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Pattern 5: Nested Path Extraction
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
// Extract from deeply nested response
|
|
457
|
+
const result = await orchestrator.extract({
|
|
458
|
+
query: `
|
|
459
|
+
query GetCatalogues($first: Int!, $after: String) {
|
|
460
|
+
catalogues(first: $first, after: $after) {
|
|
461
|
+
edges {
|
|
462
|
+
node {
|
|
463
|
+
id
|
|
464
|
+
ref
|
|
465
|
+
name
|
|
466
|
+
products {
|
|
467
|
+
edges {
|
|
468
|
+
node {
|
|
469
|
+
id
|
|
470
|
+
ref
|
|
471
|
+
name
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
cursor
|
|
477
|
+
}
|
|
478
|
+
pageInfo { hasNextPage }
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
`,
|
|
482
|
+
// Extract products from catalogues
|
|
483
|
+
resultPath: 'catalogues.edges.node.products.edges.node',
|
|
484
|
+
pageSize: 50,
|
|
485
|
+
});
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
## 🎯 Real-World Examples
|
|
489
|
+
|
|
490
|
+
### Example 1: Virtual Positions Extraction for SFTP Export
|
|
491
|
+
|
|
492
|
+
```typescript
|
|
493
|
+
import {
|
|
494
|
+
ExtractionOrchestrator,
|
|
495
|
+
createClient,
|
|
496
|
+
SftpDataSource,
|
|
497
|
+
createConsoleLogger,
|
|
498
|
+
toStructuredLogger
|
|
499
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
500
|
+
|
|
501
|
+
async function exportVirtualPositionsToSftp() {
|
|
502
|
+
// Initialize services
|
|
503
|
+
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
504
|
+
logLevel: 'info'
|
|
505
|
+
});
|
|
506
|
+
const client = await createClient({
|
|
507
|
+
config: {
|
|
508
|
+
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
509
|
+
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
510
|
+
clientSecret: process.env.FLUENT_CLIENT_SECRET!
|
|
511
|
+
// retailerId is OPTIONAL for GraphQL queries (only required for Job/Event API)
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
const sftp = new SftpDataSource(
|
|
516
|
+
{
|
|
517
|
+
type: 'SFTP_CSV',
|
|
518
|
+
sftpConfig: {
|
|
519
|
+
settings: {
|
|
520
|
+
host: process.env.SFTP_HOST!,
|
|
521
|
+
port: 22,
|
|
522
|
+
username: process.env.SFTP_USERNAME!,
|
|
523
|
+
password: process.env.SFTP_PASSWORD!,
|
|
524
|
+
},
|
|
525
|
+
},
|
|
526
|
+
connectionId: 'sftp-export',
|
|
527
|
+
},
|
|
528
|
+
logger
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
// Extract virtual positions
|
|
532
|
+
const orchestrator = new ExtractionOrchestrator(client, logger);
|
|
533
|
+
|
|
534
|
+
const result = await orchestrator.extract({
|
|
535
|
+
query: `
|
|
536
|
+
query GetVirtualPositions($first: Int!, $after: String) {
|
|
537
|
+
virtualPositions(first: $first, after: $after) {
|
|
538
|
+
edges {
|
|
539
|
+
node {
|
|
540
|
+
id
|
|
541
|
+
ref
|
|
542
|
+
productRef
|
|
543
|
+
qty
|
|
544
|
+
groupRef
|
|
545
|
+
createdOn
|
|
546
|
+
updatedOn
|
|
547
|
+
}
|
|
548
|
+
cursor
|
|
549
|
+
}
|
|
550
|
+
pageInfo {
|
|
551
|
+
hasNextPage
|
|
552
|
+
# Note: Fluent doesn't return endCursor - use edge cursors instead
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
`,
|
|
557
|
+
resultPath: 'virtualPositions.edges.node',
|
|
558
|
+
pageSize: 500,
|
|
559
|
+
maxRecords: 50000,
|
|
560
|
+
|
|
561
|
+
// Validate data quality
|
|
562
|
+
validateItem: item => {
|
|
563
|
+
return item.ref && item.productRef && typeof item.qty === 'number';
|
|
564
|
+
},
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
logger.info('Extraction complete', {
|
|
568
|
+
totalRecords: result.stats.totalRecords,
|
|
569
|
+
totalPages: result.stats.totalPages,
|
|
570
|
+
duration: result.stats.duration,
|
|
571
|
+
validRecords: result.stats.validRecords,
|
|
572
|
+
invalidRecords: result.stats.invalidRecords,
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
// Convert to CSV format
|
|
576
|
+
const csv = [
|
|
577
|
+
// Header
|
|
578
|
+
'ref,productRef,qty,groupRef,createdOn,updatedOn',
|
|
579
|
+
// Data rows
|
|
580
|
+
...result.data.map(
|
|
581
|
+
item =>
|
|
582
|
+
`${item.ref},${item.productRef},${item.qty},${item.groupRef},${item.createdOn},${item.updatedOn}`
|
|
583
|
+
),
|
|
584
|
+
].join('\n');
|
|
585
|
+
|
|
586
|
+
// Upload to SFTP
|
|
587
|
+
try {
|
|
588
|
+
const filename = `/exports/virtual-positions-${new Date().toISOString()}.csv`;
|
|
589
|
+
await sftp.uploadFile(filename, csv);
|
|
590
|
+
|
|
591
|
+
logger.info('Export complete', { filename, recordCount: result.data.length });
|
|
592
|
+
} finally {
|
|
593
|
+
// Always dispose SFTP connection to prevent connection pool leaks
|
|
594
|
+
await sftp.dispose();
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
### Example 2: Multi-Query Extraction Pattern
|
|
600
|
+
|
|
601
|
+
```typescript
|
|
602
|
+
// For multiple queries, call ExtractionOrchestrator separately for each
|
|
603
|
+
async function extractMultipleEntities() {
|
|
604
|
+
const orchestrator = new ExtractionOrchestrator(client, logger);
|
|
605
|
+
|
|
606
|
+
// Execute extractions in parallel
|
|
607
|
+
const [products, locations, inventories] = await Promise.all([
|
|
608
|
+
// Extract products
|
|
609
|
+
orchestrator.extract({
|
|
610
|
+
query: productsQuery,
|
|
611
|
+
resultPath: 'products.edges.node',
|
|
612
|
+
pageSize: 200,
|
|
613
|
+
}),
|
|
614
|
+
|
|
615
|
+
// Extract locations
|
|
616
|
+
orchestrator.extract({
|
|
617
|
+
query: locationsQuery,
|
|
618
|
+
resultPath: 'locations.edges.node',
|
|
619
|
+
pageSize: 100,
|
|
620
|
+
}),
|
|
621
|
+
|
|
622
|
+
// Extract inventory positions
|
|
623
|
+
orchestrator.extract({
|
|
624
|
+
query: inventoryQuery,
|
|
625
|
+
resultPath: 'inventoryPositions.edges.node',
|
|
626
|
+
maxRecords: 10000,
|
|
627
|
+
pageSize: 500,
|
|
628
|
+
}),
|
|
629
|
+
]);
|
|
630
|
+
|
|
631
|
+
// Combine or process separately in your business logic
|
|
632
|
+
return {
|
|
633
|
+
products: products.data,
|
|
634
|
+
locations: locations.data,
|
|
635
|
+
inventories: inventories.data,
|
|
636
|
+
stats: {
|
|
637
|
+
totalProducts: products.stats.totalRecords,
|
|
638
|
+
totalLocations: locations.stats.totalRecords,
|
|
639
|
+
totalInventories: inventories.stats.totalRecords,
|
|
640
|
+
totalDuration:
|
|
641
|
+
products.stats.duration + locations.stats.duration + inventories.stats.duration,
|
|
642
|
+
},
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
### Example 3: Extraction with Transformation
|
|
648
|
+
|
|
649
|
+
```typescript
|
|
650
|
+
import { ExtractionOrchestrator, UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
651
|
+
|
|
652
|
+
async function extractAndTransform() {
|
|
653
|
+
const orchestrator = new ExtractionOrchestrator(client, logger);
|
|
654
|
+
|
|
655
|
+
// Step 1: Extract
|
|
656
|
+
const extraction = await orchestrator.extract({
|
|
657
|
+
query: virtualPositionsQuery,
|
|
658
|
+
resultPath: 'virtualPositions.edges.node',
|
|
659
|
+
pageSize: 500,
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
// Step 2: Transform with UniversalMapper
|
|
663
|
+
const mapper = new UniversalMapper({
|
|
664
|
+
fields: {
|
|
665
|
+
reference: { source: 'ref', required: true },
|
|
666
|
+
product: { source: 'productRef', required: true },
|
|
667
|
+
quantity: { source: 'qty', resolver: 'sdk.parseInt' },
|
|
668
|
+
group: { source: 'groupRef', resolver: 'sdk.trim' },
|
|
669
|
+
lastUpdated: { source: 'updatedOn', resolver: 'sdk.formatDate' },
|
|
670
|
+
},
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
const transformation = await mapper.map(extraction.data);
|
|
674
|
+
|
|
675
|
+
if (!transformation.success) {
|
|
676
|
+
logger.error('Transformation failed', { errors: transformation.errors });
|
|
677
|
+
throw new Error('Data transformation failed');
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return transformation.data;
|
|
681
|
+
}
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
## ⚠️ Error Handling
|
|
685
|
+
|
|
686
|
+
### Handling Extraction Errors
|
|
687
|
+
|
|
688
|
+
```typescript
|
|
689
|
+
try {
|
|
690
|
+
const result = await orchestrator.extract(options);
|
|
691
|
+
|
|
692
|
+
// Check for validation errors
|
|
693
|
+
if (result.errors && result.errors.length > 0) {
|
|
694
|
+
logger.warn(`${result.errors.length} records failed validation`);
|
|
695
|
+
|
|
696
|
+
// Log first few errors for debugging
|
|
697
|
+
result.errors.slice(0, 5).forEach(err => {
|
|
698
|
+
logger.debug('Validation error', {
|
|
699
|
+
type: err.type,
|
|
700
|
+
message: err.message,
|
|
701
|
+
recordIndex: err.recordIndex,
|
|
702
|
+
details: err.details,
|
|
703
|
+
});
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// Check if extraction was truncated
|
|
708
|
+
if (result.stats.truncated) {
|
|
709
|
+
logger.info('Extraction was limited by maxRecords or maxPages');
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
return result.data;
|
|
713
|
+
} catch (error) {
|
|
714
|
+
// Fatal errors (GraphQL errors, network issues, invalid config)
|
|
715
|
+
logger.error('Extraction failed', error as Error);
|
|
716
|
+
throw error;
|
|
717
|
+
}
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
### Partial Response Mode (errorHandling: 'partial')
|
|
721
|
+
|
|
722
|
+
When GraphQL returns errors but some data is available, you can use partial mode to continue extraction:
|
|
723
|
+
|
|
724
|
+
```typescript
|
|
725
|
+
const result = await orchestrator.extract({
|
|
726
|
+
query: ORDERS_QUERY,
|
|
727
|
+
resultPath: 'orders.edges.node',
|
|
728
|
+
errorHandling: 'partial', // Continue extraction even with errors
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
// Check for partial errors in stats
|
|
732
|
+
if (result.stats.partialErrors) {
|
|
733
|
+
logger.warn('Extraction completed with partial errors', {
|
|
734
|
+
recordsExtracted: result.data.length,
|
|
735
|
+
errorCount: result.stats.partialErrors.length,
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
// Process available data
|
|
739
|
+
console.log(`Extracted ${result.data.length} records despite errors`);
|
|
740
|
+
|
|
741
|
+
// Log errors for investigation
|
|
742
|
+
result.stats.partialErrors.forEach(err => {
|
|
743
|
+
logger.warn('GraphQL error during extraction', {
|
|
744
|
+
message: err.message,
|
|
745
|
+
path: err.path,
|
|
746
|
+
});
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
**Edge Case: Null Data with Errors**
|
|
752
|
+
|
|
753
|
+
When GraphQL returns errors with `data: null` or `data: undefined`:
|
|
754
|
+
|
|
755
|
+
```typescript
|
|
756
|
+
// GraphQL response: { data: null, errors: [{ message: 'Order item not found' }] }
|
|
757
|
+
const result = await orchestrator.extract({
|
|
758
|
+
query: ORDERS_QUERY,
|
|
759
|
+
resultPath: 'orders.edges.node',
|
|
760
|
+
errorHandling: 'partial',
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
// Returns empty array with errors in stats
|
|
764
|
+
expect(result.data).toEqual([]);
|
|
765
|
+
expect(result.stats.partialErrors).toHaveLength(1);
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
**Behavior:**
|
|
769
|
+
- **Partial mode**: Returns empty array `[]`, accumulates errors in `stats.partialErrors`, stops pagination if no data
|
|
770
|
+
- **Throw mode** (default): Throws `GraphQLExecutionError` immediately (backward compatible)
|
|
771
|
+
|
|
772
|
+
**When to Use Partial Mode:**
|
|
773
|
+
- ✅ Batch queries where some items may fail independently
|
|
774
|
+
- ✅ Reporting/analytics where partial data is better than none
|
|
775
|
+
- ✅ Non-critical extractions where you want to process available data
|
|
776
|
+
|
|
777
|
+
**When to Use Throw Mode (default):**
|
|
778
|
+
- ✅ Critical data where consistency matters
|
|
779
|
+
- ✅ Single record operations
|
|
780
|
+
- ✅ When you need to fail fast on any errors
|
|
781
|
+
|
|
782
|
+
### Common Error Scenarios
|
|
783
|
+
|
|
784
|
+
| Error | Cause | Solution |
|
|
785
|
+
| ------------------------------ | ---------------------------- | ------------------------------------------------- |
|
|
786
|
+
| `Query is required` | Empty query string | Provide valid GraphQL query |
|
|
787
|
+
| `Result path is required` | Empty resultPath | Specify path to extract (e.g., 'data.edges.node') |
|
|
788
|
+
| `Page size must be positive` | pageSize ≤ 0 | Use pageSize ≥ 1 |
|
|
789
|
+
| `Max records must be positive` | maxRecords ≤ 0 | Use maxRecords ≥ 1 |
|
|
790
|
+
| `Max pages must be positive` | maxPages ≤ 0 | Use maxPages ≥ 1 |
|
|
791
|
+
| `Timeout must be positive` | timeout ≤ 0 | Use timeout ≥ 1000 (1 second minimum) |
|
|
792
|
+
| `Path extraction failed` | Invalid resultPath | Check path matches response structure |
|
|
793
|
+
| `GraphQL Error: ...` | Invalid query or permissions | Validate query with schema introspection |
|
|
794
|
+
|
|
795
|
+
## 🎓 Best Practices
|
|
796
|
+
|
|
797
|
+
### 1. Choose Appropriate Page Size
|
|
798
|
+
|
|
799
|
+
```typescript
|
|
800
|
+
// ❌ Too small - many API calls
|
|
801
|
+
pageSize: 10;
|
|
802
|
+
|
|
803
|
+
// ✅ Balanced - good for most use cases
|
|
804
|
+
pageSize: 200;
|
|
805
|
+
|
|
806
|
+
// ✅ Large datasets - fewer API calls
|
|
807
|
+
pageSize: 500;
|
|
808
|
+
|
|
809
|
+
// ⚠️ Very large - may cause memory issues
|
|
810
|
+
pageSize: 1000;
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
### 2. Use Extraction Limits for Large Datasets
|
|
814
|
+
|
|
815
|
+
```typescript
|
|
816
|
+
// ✅ Prevent unbounded extraction
|
|
817
|
+
const result = await orchestrator.extract({
|
|
818
|
+
query: largeDatasetQuery,
|
|
819
|
+
resultPath: 'data.edges.node',
|
|
820
|
+
maxRecords: 100000, // Hard limit
|
|
821
|
+
maxPages: 200, // Backup limit
|
|
822
|
+
timeout: 300000, // 5 minute timeout
|
|
823
|
+
});
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
### 3. Implement Validation for Data Quality
|
|
827
|
+
|
|
828
|
+
```typescript
|
|
829
|
+
// ✅ Validate critical fields during extraction
|
|
830
|
+
validateItem: item => {
|
|
831
|
+
// Required fields present
|
|
832
|
+
if (!item.ref || !item.productRef) return false;
|
|
833
|
+
|
|
834
|
+
// Valid data types
|
|
835
|
+
if (typeof item.qty !== 'number') return false;
|
|
836
|
+
|
|
837
|
+
// Business logic validation
|
|
838
|
+
if (item.qty < 0) return false;
|
|
839
|
+
|
|
840
|
+
return true;
|
|
841
|
+
};
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
### 4. Monitor Statistics
|
|
845
|
+
|
|
846
|
+
```typescript
|
|
847
|
+
const result = await orchestrator.extract(options);
|
|
848
|
+
|
|
849
|
+
// Log comprehensive statistics
|
|
850
|
+
logger.info('Extraction complete', {
|
|
851
|
+
totalRecords: result.stats.totalRecords,
|
|
852
|
+
totalPages: result.stats.totalPages,
|
|
853
|
+
duration: result.stats.duration,
|
|
854
|
+
validRecords: result.stats.validRecords,
|
|
855
|
+
invalidRecords: result.stats.invalidRecords,
|
|
856
|
+
truncated: result.stats.truncated,
|
|
857
|
+
truncationReason: result.stats.truncationReason,
|
|
858
|
+
|
|
859
|
+
// Calculate average records per page
|
|
860
|
+
avgRecordsPerPage: Math.round(result.stats.totalRecords / result.stats.totalPages),
|
|
861
|
+
});
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
### 5. Use with JobTracker for Async Workflows
|
|
865
|
+
|
|
866
|
+
```typescript
|
|
867
|
+
import { ExtractionOrchestrator, JobTracker } from '@fluentcommerce/fc-connect-sdk';
|
|
868
|
+
|
|
869
|
+
async function scheduledExtraction() {
|
|
870
|
+
const tracker = new JobTracker(kvAdapter, logger);
|
|
871
|
+
|
|
872
|
+
try {
|
|
873
|
+
// Start job
|
|
874
|
+
const job = await tracker.startJob({
|
|
875
|
+
triggeredBy: 'schedule',
|
|
876
|
+
details: { type: 'virtual-positions-extract' },
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
// Start extraction
|
|
880
|
+
await tracker.update(job.id, {
|
|
881
|
+
status: 'PROCESSING',
|
|
882
|
+
stage: 'extracting',
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
const orchestrator = new ExtractionOrchestrator(client, logger);
|
|
886
|
+
const result = await orchestrator.extract({
|
|
887
|
+
query: virtualPositionsQuery,
|
|
888
|
+
resultPath: 'virtualPositions.edges.node',
|
|
889
|
+
maxRecords: 50000,
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
// Complete
|
|
893
|
+
await tracker.complete(job.id, {
|
|
894
|
+
recordsExtracted: result.stats.totalRecords,
|
|
895
|
+
totalPages: result.stats.totalPages,
|
|
896
|
+
duration: result.stats.duration,
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
return result;
|
|
900
|
+
} catch (error) {
|
|
901
|
+
// Mark failed via update to avoid relying on specific error helpers
|
|
902
|
+
await tracker.update(job.id, { status: 'FAILED', error: (error as Error).message });
|
|
903
|
+
throw error;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
## 🔗 Related Documentation
|
|
909
|
+
|
|
910
|
+
- [Module 2: Basic Extraction](./02-core-guides-extraction-02-basic-extraction.md) - Manual GraphQL extraction
|
|
911
|
+
- [Module 4: Data Enrichment](./02-core-guides-extraction-04-data-enrichment.md) - Multi-query patterns
|
|
912
|
+
- [Module 5: Transformation](./02-core-guides-extraction-05-transformation.md) - UniversalMapper guide
|
|
913
|
+
- [Job Tracker](../../advanced-services/advanced-services-job-tracker.md) - Async job management
|
|
914
|
+
- [Auto-Pagination Guide](../../auto-pagination/) - Pagination internals
|
|
915
|
+
|
|
916
|
+
## 📊 Performance Considerations
|
|
917
|
+
|
|
918
|
+
### Memory Usage
|
|
919
|
+
|
|
920
|
+
```typescript
|
|
921
|
+
// ⚠️ High memory - stores all records in memory
|
|
922
|
+
const result = await orchestrator.extract({
|
|
923
|
+
query: myQuery,
|
|
924
|
+
resultPath: 'data.edges.node',
|
|
925
|
+
maxRecords: 1000000, // 1 million records!
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
// ✅ Better - use maxRecords to limit extraction
|
|
929
|
+
// Note: ExtractionOrchestrator handles pagination internally
|
|
930
|
+
// For processing in chunks, use multiple extraction calls with date ranges
|
|
931
|
+
async function extractByDateRange() {
|
|
932
|
+
const dateRanges = [
|
|
933
|
+
{ from: '2025-01-01', to: '2025-01-31' },
|
|
934
|
+
{ from: '2025-02-01', to: '2025-02-28' },
|
|
935
|
+
// ... more ranges
|
|
936
|
+
];
|
|
937
|
+
|
|
938
|
+
for (const range of dateRanges) {
|
|
939
|
+
const result = await orchestrator.extract({
|
|
940
|
+
query: myQuery,
|
|
941
|
+
resultPath: 'data.edges.node',
|
|
942
|
+
variables: {
|
|
943
|
+
dateRangeFilter: range,
|
|
944
|
+
},
|
|
945
|
+
maxRecords: 100000, // Limit per date range
|
|
946
|
+
});
|
|
947
|
+
|
|
948
|
+
// Process chunk
|
|
949
|
+
await processChunk(result.data);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
### API Rate Limiting
|
|
955
|
+
|
|
956
|
+
```typescript
|
|
957
|
+
// Add delay between large extractions
|
|
958
|
+
async function extractMultipleWithDelay() {
|
|
959
|
+
const queries = [query1, query2, query3];
|
|
960
|
+
const results = [];
|
|
961
|
+
|
|
962
|
+
for (const query of queries) {
|
|
963
|
+
const result = await orchestrator.extract({
|
|
964
|
+
query,
|
|
965
|
+
resultPath: 'data.edges.node',
|
|
966
|
+
maxRecords: 50000,
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
results.push(result);
|
|
970
|
+
|
|
971
|
+
// Delay 1 second between extractions
|
|
972
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
return results;
|
|
976
|
+
}
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
## ✅ Summary
|
|
980
|
+
|
|
981
|
+
**ExtractionOrchestrator simplifies:**
|
|
982
|
+
|
|
983
|
+
- ✅ GraphQL query execution with auto-pagination
|
|
984
|
+
- ✅ Path-based extraction from nested responses
|
|
985
|
+
- ✅ Automatic edges.node pattern handling
|
|
986
|
+
- ✅ Extraction limits and validation
|
|
987
|
+
- ✅ Statistics tracking and error handling
|
|
988
|
+
|
|
989
|
+
**Use it when you need:**
|
|
990
|
+
|
|
991
|
+
- Single-query data extraction
|
|
992
|
+
- Automatic pagination handling
|
|
993
|
+
- Path-based result extraction
|
|
994
|
+
- Built-in validation and statistics
|
|
995
|
+
|
|
996
|
+
**For complex scenarios:**
|
|
997
|
+
|
|
998
|
+
- Multiple queries → Make separate calls, use `Promise.all()`
|
|
999
|
+
- Business logic → Use `UniversalMapper` for transformation
|
|
1000
|
+
- Real-time streaming → Use direct `client.graphql()` calls
|
|
1001
|
+
|
|
1002
|
+
---
|
|
1003
|
+
|
|
1004
|
+
**Next Steps:**
|
|
1005
|
+
|
|
1006
|
+
- [Job Tracker](../../advanced-services/advanced-services-job-tracker.md) - Async job status tracking
|
|
1007
|
+
- [Module 5: Transformation](./02-core-guides-extraction-05-transformation.md) - Transform extracted data
|
|
1008
|
+
- [Examples Directory](../examples/) - Working code examples
|