@fluentcommerce/fc-connect-sdk 0.1.53 → 0.1.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -2
- package/README.md +39 -0
- package/dist/cjs/auth/index.d.ts +3 -0
- package/dist/cjs/auth/index.js +13 -0
- package/dist/cjs/auth/profile-loader.d.ts +18 -0
- package/dist/cjs/auth/profile-loader.js +208 -0
- package/dist/cjs/client-factory.d.ts +4 -0
- package/dist/cjs/client-factory.js +10 -0
- package/dist/cjs/clients/fluent-client.js +13 -6
- package/dist/cjs/index.d.ts +3 -1
- package/dist/cjs/index.js +8 -2
- package/dist/cjs/utils/pagination-helpers.js +38 -2
- package/dist/cjs/versori/fluent-versori-client.js +11 -5
- package/dist/esm/auth/index.d.ts +3 -0
- package/dist/esm/auth/index.js +2 -0
- package/dist/esm/auth/profile-loader.d.ts +18 -0
- package/dist/esm/auth/profile-loader.js +169 -0
- package/dist/esm/client-factory.d.ts +4 -0
- package/dist/esm/client-factory.js +9 -0
- package/dist/esm/clients/fluent-client.js +13 -6
- package/dist/esm/index.d.ts +3 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/utils/pagination-helpers.js +38 -2
- package/dist/esm/versori/fluent-versori-client.js +11 -5
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/tsconfig.types.tsbuildinfo +1 -1
- package/dist/types/auth/index.d.ts +3 -0
- package/dist/types/auth/profile-loader.d.ts +18 -0
- package/dist/types/client-factory.d.ts +4 -0
- package/dist/types/index.d.ts +3 -1
- package/docs/00-START-HERE/EXPORT-VALIDATION.md +158 -158
- package/docs/00-START-HERE/cli-analyze-source-structure-guide.md +655 -655
- package/docs/00-START-HERE/cli-documentation-index.md +202 -202
- package/docs/00-START-HERE/cli-quick-reference.md +252 -252
- package/docs/00-START-HERE/decision-tree.md +552 -552
- package/docs/00-START-HERE/getting-started.md +1070 -1070
- package/docs/00-START-HERE/mapper-quick-decision-guide.md +235 -235
- package/docs/00-START-HERE/readme.md +237 -237
- package/docs/00-START-HERE/retailerid-configuration.md +404 -404
- package/docs/00-START-HERE/sdk-philosophy.md +794 -794
- package/docs/00-START-HERE/troubleshooting-quick-reference.md +1086 -1086
- package/docs/01-TEMPLATES/faq.md +686 -686
- package/docs/01-TEMPLATES/patterns/pattern-templates-guide.md +68 -68
- package/docs/01-TEMPLATES/patterns/patterns-csv-schema-validation-and-rejection-report.md +233 -233
- package/docs/01-TEMPLATES/patterns/patterns-custom-resolvers.md +407 -407
- package/docs/01-TEMPLATES/patterns/patterns-error-handling-retry.md +511 -511
- package/docs/01-TEMPLATES/patterns/patterns-field-mapping-universal.md +701 -701
- package/docs/01-TEMPLATES/patterns/patterns-large-file-splitting.md +1430 -1430
- package/docs/01-TEMPLATES/patterns/patterns-master-data-etl.md +2399 -2399
- package/docs/01-TEMPLATES/patterns/patterns-pagination-streaming.md +447 -447
- package/docs/01-TEMPLATES/patterns/patterns-state-duplicate-prevention.md +385 -385
- package/docs/01-TEMPLATES/readme.md +957 -957
- package/docs/01-TEMPLATES/standalone/standalone-asn-inbound-processing.md +1209 -1209
- package/docs/01-TEMPLATES/standalone/standalone-graphql-query-export.md +1140 -1140
- package/docs/01-TEMPLATES/standalone/standalone-graphql-to-parquet-partitioned-s3.md +432 -432
- package/docs/01-TEMPLATES/standalone/standalone-multi-channel-inventory-sync.md +1185 -1185
- package/docs/01-TEMPLATES/standalone/standalone-multi-source-aggregation.md +1462 -1462
- package/docs/01-TEMPLATES/standalone/standalone-s3-csv-batch-api.md +1390 -1390
- package/docs/01-TEMPLATES/standalone/standalone-s3-csv-inventory-to-batch.md +330 -330
- package/docs/01-TEMPLATES/standalone/standalone-scripts-guide.md +87 -87
- package/docs/01-TEMPLATES/standalone/standalone-sftp-xml-graphql.md +1444 -1444
- package/docs/01-TEMPLATES/standalone/standalone-webhook-payload-processing.md +688 -688
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-dropship-order-routing.md +193 -193
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-graphql-parquet-extraction.md +518 -518
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-inter-location-transfers.md +2162 -2162
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-pre-order-allocation.md +2226 -2226
- package/docs/01-TEMPLATES/versori/business-examples/business-scenarios-guide.md +87 -87
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-connection-validation-pattern.md +656 -656
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-dual-workflow-connector.md +835 -835
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-guide.md +108 -108
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-kv-state-management.md +1533 -1533
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-xml-response-patterns.md +1160 -1160
- package/docs/01-TEMPLATES/versori/versori-platform-guide.md +201 -201
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-asn-purchase-order.md +1906 -1906
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-dropship-routing.md +1074 -1074
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-flash-sale-reserve.md +1395 -1395
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-generic-xml-order.md +888 -888
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-payment-gateway-integration.md +2478 -2478
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-rma-returns-comprehensive.md +2240 -2240
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-xml-order-ingestion.md +2029 -2029
- package/docs/01-TEMPLATES/versori/webhooks/webhook-templates-guide.md +140 -140
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/inventory-mapping.json +20 -20
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/products_2025-01-22.csv +11 -11
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/sample-data-guide.md +34 -34
- package/docs/01-TEMPLATES/versori/workflows/_examples/workflow-examples-guide.md +36 -36
- package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-modes-guide.md +1038 -1038
- package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-workflows-guide.md +138 -138
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/graphql-extraction-guide.md +63 -63
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-csv.md +2062 -2062
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-xml.md +2294 -2294
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-s3-csv.md +2461 -2461
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-sftp-xml.md +2529 -2529
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-csv.md +2464 -2464
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-json.md +1959 -1959
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-s3-csv.md +1953 -1953
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-sftp-xml.md +2541 -2541
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-s3-json.md +2384 -2384
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-sftp-xml.md +2445 -2445
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-csv.md +2355 -2355
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-json.md +2042 -2042
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-sftp-xml.md +2726 -2726
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/batch-api-guide.md +206 -206
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-cycle-count-reconciliation.md +2030 -2030
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-multi-channel-inventory-sync.md +1882 -1882
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-csv-inventory-batch.md +2827 -2827
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-json-inventory-batch.md +1952 -1952
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-xml-inventory-batch.md +3289 -3289
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-csv-inventory-batch.md +3064 -3064
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-json-inventory-batch.md +3238 -3238
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-xml-inventory-batch.md +2977 -2977
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/event-api-guide.md +321 -321
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-json-order-cancel-event.md +959 -959
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-xml-order-cancel-event.md +1170 -1170
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-csv-product-event.md +2312 -2312
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-json-product-event.md +2999 -2999
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-parquet-product-event.md +2836 -2836
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-xml-product-event.md +2395 -2395
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-csv-product-event.md +2295 -2295
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-json-product-event.md +2602 -2602
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-parquet-product-event.md +2589 -2589
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-xml-product-event.md +3578 -3578
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/graphql-mutations-guide.md +93 -93
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-json-order-update-graphql.md +1260 -1260
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-xml-order-update-graphql.md +1472 -1472
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-control-graphql.md +2417 -2417
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-location-graphql.md +2811 -2811
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-price-graphql.md +2619 -2619
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-json-location-graphql.md +2807 -2807
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-xml-location-graphql.md +2373 -2373
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-control-graphql.md +2740 -2740
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-location-graphql.md +2760 -2760
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-json-location-graphql.md +1710 -1710
- package/docs/01-TEMPLATES/versori/workflows/ingestion/ingestion-workflows-guide.md +136 -136
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/rubix-webhooks-guide.md +520 -520
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-inline.md +1418 -1418
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-universal-mapper.md +1785 -1785
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-order-attribute-update.md +824 -824
- package/docs/01-TEMPLATES/versori/workflows/workflows-overview-guide.md +646 -646
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-batch-archival.md +724 -724
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-job-tracker.md +627 -627
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-partial-batch-recovery.md +561 -561
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-quick-reference.md +367 -367
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-readme.md +407 -407
- package/docs/02-CORE-GUIDES/advanced-services/readme.md +49 -49
- package/docs/02-CORE-GUIDES/api-reference/api-reference-quick-reference.md +548 -548
- package/docs/02-CORE-GUIDES/api-reference/event-api-input-output-reference.md +702 -1171
- package/docs/02-CORE-GUIDES/api-reference/examples/client-initialization.ts +286 -286
- package/docs/02-CORE-GUIDES/api-reference/graphql-error-classification.md +337 -337
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-01-client-api.md +399 -482
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-03-authentication.md +199 -199
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-04-graphql-mapping.md +925 -925
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-05-services.md +1198 -1198
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-06-data-sources.md +1083 -1083
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-07-parsers.md +1097 -1097
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-pagination.md +513 -513
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-types.md +545 -597
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-error-handling.md +527 -527
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-webhook-validation.md +514 -514
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-extraction.md +557 -557
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-utilities.md +412 -412
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-cli-tools.md +423 -423
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-error-handling.md +716 -716
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-analyze-source-structure.md +518 -518
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-partial-responses.md +212 -212
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-testing.md +300 -300
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-13-resolver-builder.md +322 -322
- package/docs/02-CORE-GUIDES/api-reference/readme.md +279 -279
- package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-quick-reference.md +351 -351
- package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-readme.md +277 -277
- package/docs/02-CORE-GUIDES/auto-pagination/examples/auto-pagination-readme.md +178 -178
- package/docs/02-CORE-GUIDES/auto-pagination/examples/common-patterns.ts +351 -351
- package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-products.ts +384 -384
- package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-virtual-positions.ts +308 -308
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-01-foundations.md +470 -470
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-02-quick-start.md +713 -713
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-03-configuration.md +754 -754
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-04-advanced-patterns.md +732 -732
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-05-sdk-integration.md +847 -847
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-06-troubleshooting.md +359 -359
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-07-api-reference.md +462 -462
- package/docs/02-CORE-GUIDES/auto-pagination/readme.md +54 -54
- package/docs/02-CORE-GUIDES/data-sources/data-sources-file-operations-error-handling.md +1487 -1487
- package/docs/02-CORE-GUIDES/data-sources/data-sources-quick-reference.md +836 -836
- package/docs/02-CORE-GUIDES/data-sources/data-sources-readme.md +276 -276
- package/docs/02-CORE-GUIDES/data-sources/data-sources-sftp-credential-access-security.md +553 -553
- package/docs/02-CORE-GUIDES/data-sources/examples/common-patterns.ts +409 -409
- package/docs/02-CORE-GUIDES/data-sources/examples/data-sources-readme.md +178 -178
- package/docs/02-CORE-GUIDES/data-sources/examples/s3-operations.ts +308 -308
- package/docs/02-CORE-GUIDES/data-sources/examples/sftp-operations.ts +371 -371
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-01-foundations.md +735 -735
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-02-s3-operations.md +1302 -1302
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-03-sftp-operations.md +1379 -1379
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-04-file-patterns.md +941 -941
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-05-advanced-topics.md +813 -813
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-06-integration-patterns.md +486 -486
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-07-troubleshooting.md +387 -387
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-08-api-reference.md +417 -417
- package/docs/02-CORE-GUIDES/data-sources/readme.md +77 -77
- package/docs/02-CORE-GUIDES/error-handling-guide.md +936 -936
- package/docs/02-CORE-GUIDES/extraction/examples/02-core-guides-extraction-readme.md +116 -116
- package/docs/02-CORE-GUIDES/extraction/examples/common-patterns.ts +428 -428
- package/docs/02-CORE-GUIDES/extraction/examples/extract-inventory-basic.ts +187 -187
- package/docs/02-CORE-GUIDES/extraction/extraction-quick-reference.md +596 -596
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-01-foundations.md +514 -514
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-02-basic-extraction.md +823 -823
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-03-parquet-processing.md +507 -507
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-04-data-enrichment.md +546 -546
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-05-transformation.md +494 -494
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-export-formats.md +458 -458
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-performance.md +138 -138
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-api-reference.md +148 -148
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-optimization.md +692 -692
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-08-extraction-orchestrator.md +1008 -1008
- package/docs/02-CORE-GUIDES/extraction/readme.md +151 -151
- package/docs/02-CORE-GUIDES/ingestion/examples/_simple-kv-store.ts +40 -40
- package/docs/02-CORE-GUIDES/ingestion/examples/error-recovery.ts +728 -728
- package/docs/02-CORE-GUIDES/ingestion/examples/event-driven.ts +501 -501
- package/docs/02-CORE-GUIDES/ingestion/examples/local-file-ingestion.ts +88 -88
- package/docs/02-CORE-GUIDES/ingestion/examples/parquet-ingestion.ts +117 -117
- package/docs/02-CORE-GUIDES/ingestion/examples/performance-optimized.ts +647 -647
- package/docs/02-CORE-GUIDES/ingestion/examples/s3-csv-ingestion.ts +169 -169
- package/docs/02-CORE-GUIDES/ingestion/examples/sftp-csv-ingestion.ts +134 -134
- package/docs/02-CORE-GUIDES/ingestion/ingestion-quick-reference.md +546 -546
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-01-introduction.md +626 -626
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-02-quick-start.md +658 -658
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-03-data-sources.md +1052 -1052
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-04-field-mapping.md +763 -763
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-05-advanced-parsers.md +676 -676
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-06-batch-api.md +1295 -1295
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-api-reference.md +138 -138
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-state-management.md +1037 -1037
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-08-performance-optimization.md +1349 -1349
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-09-best-practices.md +1893 -1893
- package/docs/02-CORE-GUIDES/ingestion/readme.md +160 -160
- package/docs/02-CORE-GUIDES/logging-guide.md +585 -585
- package/docs/02-CORE-GUIDES/mapping/error-handling-patterns.md +401 -401
- package/docs/02-CORE-GUIDES/mapping/examples/02-core-guides-mapping-readme.md +128 -128
- package/docs/02-CORE-GUIDES/mapping/examples/common-patterns.ts +273 -273
- package/docs/02-CORE-GUIDES/mapping/examples/csv-location-ingestion.json +36 -36
- package/docs/02-CORE-GUIDES/mapping/examples/csv-mapping.ts +242 -242
- package/docs/02-CORE-GUIDES/mapping/examples/graphql-to-parquet-extraction.json +36 -36
- package/docs/02-CORE-GUIDES/mapping/examples/json-mapping.ts +213 -213
- package/docs/02-CORE-GUIDES/mapping/examples/json-product-to-mutation.json +48 -48
- package/docs/02-CORE-GUIDES/mapping/examples/xml-mapping.ts +291 -291
- package/docs/02-CORE-GUIDES/mapping/examples/xml-order-to-mutation.json +45 -45
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-quick-reference.md +463 -463
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-readme.md +227 -227
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-01-introduction.md +222 -222
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-02-quick-start.md +351 -351
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-03-schema-validation.md +569 -569
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-04-mapping-patterns.md +471 -471
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-05-configuration-reference.md +611 -611
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-advanced-xpath.md +148 -148
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-path-syntax.md +464 -464
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-api-reference.md +94 -94
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-array-handling.md +307 -307
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-08-custom-resolvers.md +544 -544
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-09-advanced-patterns.md +427 -427
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-10-hooks-and-variables.md +336 -336
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-11-error-handling.md +488 -488
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-12-arguments-vs-nodes.md +383 -383
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-13-best-practices.md +477 -477
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/readme.md +62 -62
- package/docs/02-CORE-GUIDES/mapping/mapping-format-decision-tree.md +480 -480
- package/docs/02-CORE-GUIDES/mapping/mapping-graphql-alias-batching-guide.md +820 -820
- package/docs/02-CORE-GUIDES/mapping/mapping-javascript-objects.md +2369 -2369
- package/docs/02-CORE-GUIDES/mapping/mapping-mapper-comparison-guide.md +682 -682
- package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-07-api-reference.md +1327 -1327
- package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-08-error-handling.md +1142 -1142
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-04-use-cases.md +891 -891
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-helpers-resolvers.md +1126 -1126
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-sdk-resolvers.md +199 -199
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-07-api-reference.md +1319 -1319
- package/docs/02-CORE-GUIDES/mapping/readme.md +178 -178
- package/docs/02-CORE-GUIDES/mapping/resolver-registration.md +410 -410
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/common-patterns.ts +226 -226
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/custom-resolvers.ts +227 -227
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/sdk-resolvers-usage.ts +203 -203
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-readme.md +274 -274
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-api-reference.md +679 -679
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-cookbook.md +826 -826
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-guide.md +1330 -1330
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-helpers-reference.md +1437 -1437
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-parameters-reference.md +553 -553
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-troubleshooting.md +854 -854
- package/docs/02-CORE-GUIDES/mapping/resolvers/readme.md +75 -75
- package/docs/02-CORE-GUIDES/parsers/examples/02-core-guides-parsers-readme.md +161 -161
- package/docs/02-CORE-GUIDES/parsers/examples/csv-parser-examples.ts +110 -110
- package/docs/02-CORE-GUIDES/parsers/examples/json-parser-examples.ts +33 -33
- package/docs/02-CORE-GUIDES/parsers/examples/parquet-parser-examples.ts +47 -47
- package/docs/02-CORE-GUIDES/parsers/examples/xml-parser-examples.ts +38 -38
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-01-foundations.md +355 -355
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-02-csv-parser.md +772 -772
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-03-json-parser.md +789 -789
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-04-xml-parser.md +857 -857
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-05-parquet-parser.md +603 -603
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-integration-patterns.md +702 -702
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-streaming.md +121 -121
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-api-reference.md +89 -89
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-troubleshooting.md +727 -727
- package/docs/02-CORE-GUIDES/parsers/parsers-quick-reference.md +482 -482
- package/docs/02-CORE-GUIDES/parsers/parsers-readme.md +258 -258
- package/docs/02-CORE-GUIDES/parsers/readme.md +65 -65
- package/docs/02-CORE-GUIDES/readme.md +194 -194
- package/docs/02-CORE-GUIDES/webhook-validation/examples/basic-validation.ts +108 -108
- package/docs/02-CORE-GUIDES/webhook-validation/examples/common-patterns.ts +316 -316
- package/docs/02-CORE-GUIDES/webhook-validation/examples/webhook-validation-readme.md +61 -61
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-01-foundations.md +440 -440
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-02-quick-start.md +525 -525
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-03-versori-integration.md +741 -741
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-04-platform-integration.md +629 -629
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-05-configuration.md +535 -535
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-error-handling.md +611 -611
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-troubleshooting.md +124 -124
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-07-api-reference.md +511 -511
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-08-rubix-webhooks.md +590 -590
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-09-rubix-event-vs-http-call.md +432 -432
- package/docs/02-CORE-GUIDES/webhook-validation/readme.md +239 -239
- package/docs/02-CORE-GUIDES/webhook-validation/webhook-validation-quick-reference.md +392 -392
- package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-quick-reference.md +498 -498
- package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-readme.md +313 -313
- package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/common-patterns.ts +612 -612
- package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/connector-scenarios-readme.md +253 -253
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-01-foundations.md +452 -452
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-02-simple-scenarios.md +681 -681
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-03-intermediate-scenarios.md +637 -637
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-04-advanced-scenarios.md +650 -650
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-05-bidirectional-sync.md +233 -233
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-06-production-patterns.md +442 -442
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-07-reference.md +445 -445
- package/docs/03-PATTERN-GUIDES/connector-scenarios/readme.md +31 -31
- package/docs/03-PATTERN-GUIDES/enterprise-integration-patterns.md +1528 -1528
- package/docs/03-PATTERN-GUIDES/error-handling/comprehensive-error-handling-guide.md +1437 -1437
- package/docs/03-PATTERN-GUIDES/error-handling/error-handling-quick-reference.md +390 -390
- package/docs/03-PATTERN-GUIDES/error-handling/examples/common-patterns.ts +438 -438
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-01-foundations.md +362 -362
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-02-error-types.md +850 -850
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-03-utf8-handling.md +456 -456
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-04-error-scenarios.md +658 -658
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-05-calling-patterns.md +671 -671
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-06-retry-strategies.md +1034 -1034
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-07-monitoring.md +653 -653
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-08-api-reference.md +847 -847
- package/docs/03-PATTERN-GUIDES/error-handling/readme.md +36 -36
- package/docs/03-PATTERN-GUIDES/examples/__tests__/readme.md +40 -40
- package/docs/03-PATTERN-GUIDES/examples/__tests__/resolver-examples.test.js +282 -282
- package/docs/03-PATTERN-GUIDES/examples/test-data/03-pattern-guides-readme.md +110 -110
- package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-inventory.json +123 -123
- package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-order.json +171 -171
- package/docs/03-PATTERN-GUIDES/examples/test-data/readme.md +28 -28
- package/docs/03-PATTERN-GUIDES/extraction/extraction-readme.md +15 -15
- package/docs/03-PATTERN-GUIDES/extraction/readme.md +25 -25
- package/docs/03-PATTERN-GUIDES/file-operations/examples/common-patterns.ts +407 -407
- package/docs/03-PATTERN-GUIDES/file-operations/examples/file-operations-readme.md +142 -142
- package/docs/03-PATTERN-GUIDES/file-operations/file-operations-quick-reference.md +462 -462
- package/docs/03-PATTERN-GUIDES/file-operations/file-operations-readme.md +379 -379
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-01-foundations.md +430 -430
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-02-quick-start.md +484 -484
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-03-s3-operations.md +507 -507
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-04-sftp-operations.md +963 -963
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-05-streaming-performance.md +503 -503
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-archive-patterns.md +386 -386
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-error-handling.md +117 -117
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-api-reference.md +78 -78
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-testing-troubleshooting.md +567 -567
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-08-api-reference.md +1055 -1055
- package/docs/03-PATTERN-GUIDES/file-operations/readme.md +32 -32
- package/docs/03-PATTERN-GUIDES/ingestion/ingestion-readme.md +15 -15
- package/docs/03-PATTERN-GUIDES/ingestion/readme.md +25 -25
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/batch-processing.ts +130 -130
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/common-patterns.ts +360 -360
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/delta-sync.ts +130 -130
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/integration-patterns-readme.md +100 -100
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/real-time-webhook.ts +398 -398
- package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-quick-reference.md +962 -962
- package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-readme.md +134 -134
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-01-real-time-processing.md +991 -991
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-02-batch-processing.md +1547 -1547
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-03-delta-sync.md +1108 -1108
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-04-webhook-patterns.md +1181 -1181
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-05-error-handling.md +1061 -1061
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-advanced-integration-services.md +1547 -1547
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-performance.md +109 -109
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-07-api-reference.md +34 -34
- package/docs/03-PATTERN-GUIDES/integration-patterns/readme.md +30 -30
- package/docs/03-PATTERN-GUIDES/logging-minimal-mode.md +128 -128
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/common-patterns.ts +380 -380
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/multiple-connections-readme.md +139 -139
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/parallel-root-connections.ts +149 -149
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/real-world-scenarios.ts +405 -405
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-01-foundations.md +378 -378
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-02-quick-start.md +566 -566
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-03-targeting-connections.md +659 -659
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-04-parallel-queries.md +656 -656
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-05-best-practices.md +624 -624
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-api-reference.md +824 -824
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-versori.md +119 -119
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-07-api-reference.md +87 -87
- package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-quick-reference.md +353 -353
- package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-readme.md +270 -270
- package/docs/03-PATTERN-GUIDES/multiple-connections/readme.md +30 -30
- package/docs/03-PATTERN-GUIDES/pagination/pagination-readme.md +14 -14
- package/docs/03-PATTERN-GUIDES/pagination/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/parquet/examples/common-patterns.ts +180 -180
- package/docs/03-PATTERN-GUIDES/parquet/examples/read-parquet.ts +48 -48
- package/docs/03-PATTERN-GUIDES/parquet/examples/write-parquet.ts +65 -65
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-01-introduction.md +393 -393
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-02-quick-start.md +572 -572
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-03-reading-parquet.md +525 -525
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-04-writing-parquet.md +554 -554
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-05-graphql-extraction.md +405 -405
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-performance.md +104 -104
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-s3-integration.md +511 -511
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-api-reference.md +90 -90
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-performance-optimization.md +525 -525
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-08-best-practices.md +712 -712
- package/docs/03-PATTERN-GUIDES/parquet/parquet-quick-reference.md +683 -683
- package/docs/03-PATTERN-GUIDES/parquet/parquet-readme.md +248 -248
- package/docs/03-PATTERN-GUIDES/parquet/readme.md +32 -32
- package/docs/03-PATTERN-GUIDES/parsers/parsers-readme.md +12 -12
- package/docs/03-PATTERN-GUIDES/parsers/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/readme.md +159 -159
- package/docs/03-PATTERN-GUIDES/webhooks/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/webhooks/webhooks-readme.md +8 -8
- package/docs/04-REFERENCE/architecture/architecture-01-overview.md +427 -427
- package/docs/04-REFERENCE/architecture/architecture-02-client-architecture.md +424 -424
- package/docs/04-REFERENCE/architecture/architecture-03-data-flow.md +690 -690
- package/docs/04-REFERENCE/architecture/architecture-04-service-layer.md +834 -834
- package/docs/04-REFERENCE/architecture/architecture-05-integration-architecture.md +655 -655
- package/docs/04-REFERENCE/architecture/architecture-06-state-management.md +653 -653
- package/docs/04-REFERENCE/architecture/architecture-adding-new-data-sources.md +686 -686
- package/docs/04-REFERENCE/architecture/readme.md +279 -279
- package/docs/04-REFERENCE/platforms/deno/readme.md +117 -117
- package/docs/04-REFERENCE/platforms/nodejs/readme.md +146 -146
- package/docs/04-REFERENCE/platforms/readme.md +135 -135
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-01-introduction.md +398 -398
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-02-quick-start.md +560 -560
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-03-authentication.md +757 -757
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-04-workflows.md +2476 -2476
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-05-connections.md +1167 -1167
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-kv-storage.md +990 -990
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-state-management.md +121 -121
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-api-reference.md +68 -68
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-deployment.md +731 -731
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-08-best-practices.md +1111 -1111
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-09-signature-reference.md +766 -766
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-readme.md +299 -299
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-s3-sftp-configuration-guide.md +1425 -1425
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-api-key-security.md +816 -816
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-connection-security.md +681 -681
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-workflow-task-types.md +708 -708
- package/docs/04-REFERENCE/platforms/versori/readme.md +108 -108
- package/docs/04-REFERENCE/readme.md +148 -148
- package/docs/04-REFERENCE/resolver-signature/examples/advanced-resolvers.ts +482 -482
- package/docs/04-REFERENCE/resolver-signature/examples/async-resolvers.ts +496 -496
- package/docs/04-REFERENCE/resolver-signature/examples/basic-resolvers.ts +343 -343
- package/docs/04-REFERENCE/resolver-signature/examples/resolver-signature-readme.md +188 -188
- package/docs/04-REFERENCE/resolver-signature/examples/testing-resolvers.ts +463 -463
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-01-foundations.md +286 -286
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-02-parameter-reference.md +643 -643
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-03-basic-examples.md +521 -521
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-04-advanced-patterns.md +739 -739
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-05-sdk-resolvers.md +531 -531
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-migration-guide.md +650 -650
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-testing.md +125 -125
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-07-api-reference.md +794 -794
- package/docs/04-REFERENCE/resolver-signature/readme.md +64 -64
- package/docs/04-REFERENCE/resolver-signature/resolver-signature-quick-reference.md +270 -270
- package/docs/04-REFERENCE/resolver-signature/resolver-signature-readme.md +351 -351
- package/docs/04-REFERENCE/schema/fluent-commerce-schema.json +764 -764
- package/docs/04-REFERENCE/schema/readme.md +141 -141
- package/docs/04-REFERENCE/testing/examples/04-reference-testing-readme.md +158 -158
- package/docs/04-REFERENCE/testing/examples/fluent-testing.ts +62 -62
- package/docs/04-REFERENCE/testing/examples/health-check.ts +155 -155
- package/docs/04-REFERENCE/testing/examples/integration-test.ts +119 -119
- package/docs/04-REFERENCE/testing/examples/performance-test.ts +183 -183
- package/docs/04-REFERENCE/testing/examples/s3-testing.ts +127 -127
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-01-foundations.md +267 -267
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-02-s3-testing.md +599 -599
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-03-fluent-testing.md +589 -589
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-04-integration-testing.md +699 -699
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-05-debugging.md +478 -478
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-cicd-integration.md +463 -463
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-preflight-validation.md +131 -131
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-best-practices.md +499 -499
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-coverage-ci.md +165 -165
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-08-api-reference.md +634 -634
- package/docs/04-REFERENCE/testing/readme.md +86 -86
- package/docs/04-REFERENCE/testing/testing-quick-reference.md +667 -667
- package/docs/04-REFERENCE/testing/testing-readme.md +286 -286
- package/docs/04-REFERENCE/troubleshooting/readme.md +144 -144
- package/docs/04-REFERENCE/troubleshooting/troubleshooting-deno-sftp-compatibility.md +392 -392
- package/docs/template-loading-matrix.md +242 -242
- package/package.json +5 -3
|
@@ -1,813 +1,813 @@
|
|
|
1
|
-
# Module 5: Advanced Topics
|
|
2
|
-
|
|
3
|
-
Master advanced data source patterns - dual AWS credentials, security best practices, monitoring, testing strategies, and troubleshooting.
|
|
4
|
-
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
- [Dual AWS Credentials](#dual-aws-credentials)
|
|
8
|
-
- [Security Best Practices](#security-best-practices)
|
|
9
|
-
- [Monitoring and Observability](#monitoring-and-observability)
|
|
10
|
-
- [Testing Strategies](#testing-strategies)
|
|
11
|
-
- [Troubleshooting](#troubleshooting)
|
|
12
|
-
- [Performance Tuning](#performance-tuning)
|
|
13
|
-
- [Production Checklist](#production-checklist)
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## Dual AWS Credentials
|
|
18
|
-
|
|
19
|
-
### Why Use Separate AWS Accounts?
|
|
20
|
-
|
|
21
|
-
**Separation of Concerns**:
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
┌─────────────────────────────────────────────────────┐
|
|
25
|
-
│ Vendor AWS Account (Source) │
|
|
26
|
-
│ - Read-only access to vendor's S3 bucket │
|
|
27
|
-
│ - Limited IAM permissions (ListBucket, GetObject) │
|
|
28
|
-
│ - Vendor manages security and compliance │
|
|
29
|
-
└────────────────┬────────────────────────────────────┘
|
|
30
|
-
│
|
|
31
|
-
▼ (Read inventory files)
|
|
32
|
-
┌─────────────────────────────────────────────────────┐
|
|
33
|
-
│ Your Application (Processing) │
|
|
34
|
-
│ - Runs with vendor credentials to read │
|
|
35
|
-
│ - Runs with your credentials to write │
|
|
36
|
-
└────────────────┬────────────────────────────────────┘
|
|
37
|
-
│
|
|
38
|
-
▼ (Write processed data)
|
|
39
|
-
┌─────────────────────────────────────────────────────┐
|
|
40
|
-
│ Your AWS Account (Target) │
|
|
41
|
-
│ - Full write access to your data lake │
|
|
42
|
-
│ - Your IAM permissions (PutObject, lifecycle rules) │
|
|
43
|
-
│ - You manage retention and compliance │
|
|
44
|
-
└─────────────────────────────────────────────────────┘
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
**Benefits**:
|
|
48
|
-
|
|
49
|
-
- ✅ **Security**: Vendor credentials can't write to your buckets
|
|
50
|
-
- ✅ **Compliance**: Clear data ownership and responsibility
|
|
51
|
-
- ✅ **Cost control**: Separate billing for source vs target storage
|
|
52
|
-
- ✅ **Flexibility**: Different retention policies per account
|
|
53
|
-
|
|
54
|
-
### Implementation Pattern
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
import {
|
|
58
|
-
S3DataSource,
|
|
59
|
-
createConsoleLogger,
|
|
60
|
-
toStructuredLogger
|
|
61
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
62
|
-
|
|
63
|
-
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
64
|
-
logLevel: 'info'
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// Source S3 (Vendor's AWS account - READ ONLY)
|
|
68
|
-
const sourceS3 = new S3DataSource(
|
|
69
|
-
{
|
|
70
|
-
type: 'S3_CSV',
|
|
71
|
-
connectionId: 's3-source-vendor',
|
|
72
|
-
name: 'S3 Source Vendor',
|
|
73
|
-
s3Config: {
|
|
74
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!, // Vendor credentials
|
|
75
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
76
|
-
region: process.env.AWS_REGION || 'us-east-1',
|
|
77
|
-
bucket: process.env.SOURCE_BUCKET, // Vendor's bucket
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
logger
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
// Target S3 (Your AWS account - WRITE)
|
|
84
|
-
const targetS3 = new S3DataSource(
|
|
85
|
-
{
|
|
86
|
-
type: 'S3_PARQUET',
|
|
87
|
-
connectionId: 's3-target-own',
|
|
88
|
-
name: 'S3 Target Own',
|
|
89
|
-
s3Config: {
|
|
90
|
-
accessKeyId: process.env.TARGET_AWS_ACCESS_KEY_ID!, // Your credentials
|
|
91
|
-
secretAccessKey: process.env.TARGET_AWS_SECRET_ACCESS_KEY!,
|
|
92
|
-
region: process.env.TARGET_AWS_REGION || 'us-west-2',
|
|
93
|
-
bucket: process.env.TARGET_BUCKET, // Your data lake
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
logger
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
// Usage: Read from vendor, write to your data lake
|
|
100
|
-
async function processInventory() {
|
|
101
|
-
// Read from vendor's S3
|
|
102
|
-
const files = await sourceS3.listFiles({ prefix: 'inventory/' });
|
|
103
|
-
|
|
104
|
-
for (const file of files) {
|
|
105
|
-
// Download from vendor
|
|
106
|
-
const content = await sourceS3.downloadFile(file.path);
|
|
107
|
-
|
|
108
|
-
// Process data
|
|
109
|
-
const processed = await processData(content);
|
|
110
|
-
|
|
111
|
-
// Write to your data lake
|
|
112
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
113
|
-
await targetS3.uploadFile(`s3://your-bucket/processed/${timestamp}/${file.name}`, processed);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### Environment Variables for Dual Credentials
|
|
119
|
-
|
|
120
|
-
```bash
|
|
121
|
-
# .env file
|
|
122
|
-
|
|
123
|
-
# Source AWS Account (Vendor)
|
|
124
|
-
AWS_ACCESS_KEY_ID=AKIAVENDORKEY123
|
|
125
|
-
AWS_SECRET_ACCESS_KEY=vendorSecretKey456
|
|
126
|
-
AWS_REGION=us-east-1
|
|
127
|
-
SOURCE_BUCKET=vendor-inventory-bucket
|
|
128
|
-
|
|
129
|
-
# Target AWS Account (Your Data Lake)
|
|
130
|
-
TARGET_AWS_ACCESS_KEY_ID=AKIAYOURKEY789
|
|
131
|
-
TARGET_AWS_SECRET_ACCESS_KEY=yourSecretKey012
|
|
132
|
-
TARGET_AWS_REGION=us-west-2
|
|
133
|
-
TARGET_BUCKET=your-datalake-bucket
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### IAM Policy Examples
|
|
137
|
-
|
|
138
|
-
**Source (Vendor) - Read-Only Policy**:
|
|
139
|
-
|
|
140
|
-
```json
|
|
141
|
-
{
|
|
142
|
-
"Version": "2012-10-17",
|
|
143
|
-
"Statement": [
|
|
144
|
-
{
|
|
145
|
-
"Effect": "Allow",
|
|
146
|
-
"Action": ["s3:ListBucket", "s3:GetObject", "s3:GetObjectVersion"],
|
|
147
|
-
"Resource": ["arn:aws:s3:::vendor-inventory-bucket", "arn:aws:s3:::vendor-inventory-bucket/*"]
|
|
148
|
-
}
|
|
149
|
-
]
|
|
150
|
-
}
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
**Target (Your) - Write + Lifecycle Policy**:
|
|
154
|
-
|
|
155
|
-
```json
|
|
156
|
-
{
|
|
157
|
-
"Version": "2012-10-17",
|
|
158
|
-
"Statement": [
|
|
159
|
-
{
|
|
160
|
-
"Effect": "Allow",
|
|
161
|
-
"Action": ["s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
|
|
162
|
-
"Resource": ["arn:aws:s3:::your-datalake-bucket", "arn:aws:s3:::your-datalake-bucket/*"]
|
|
163
|
-
}
|
|
164
|
-
]
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
---
|
|
169
|
-
|
|
170
|
-
## Security Best Practices
|
|
171
|
-
|
|
172
|
-
### SSH Key Management (SFTP)
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
// 1. Use private key authentication (NOT passwords)
|
|
176
|
-
const sftpSource = new SftpDataSource(
|
|
177
|
-
{
|
|
178
|
-
type: 'SFTP_CSV',
|
|
179
|
-
connectionId: 'vendor-sftp',
|
|
180
|
-
settings: {
|
|
181
|
-
host: 'sftp.vendor.com',
|
|
182
|
-
username: 'integration',
|
|
183
|
-
privateKey: process.env.SFTP_PRIVATE_KEY, // ✅ SSH key (recommended)
|
|
184
|
-
// password: process.env.SFTP_PASSWORD, // ❌ Avoid passwords
|
|
185
|
-
remotePath: '/data/inventory',
|
|
186
|
-
},
|
|
187
|
-
},
|
|
188
|
-
logger
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
// 2. Use encrypted private keys with passphrase
|
|
192
|
-
const secureConfig = {
|
|
193
|
-
privateKey: process.env.SFTP_PRIVATE_KEY, // Encrypted key
|
|
194
|
-
passphrase: process.env.SFTP_PASSPHRASE, // Key passphrase
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
// 3. Rotate keys regularly (every 90 days)
|
|
198
|
-
// 4. Never commit keys to version control
|
|
199
|
-
// 5. Use environment-specific keys (dev, staging, prod)
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### Presigned URL Security (S3)
|
|
203
|
-
|
|
204
|
-
```typescript
|
|
205
|
-
// Presigned URL expiration (1 hour default)
|
|
206
|
-
// URLs auto-expire after this period
|
|
207
|
-
|
|
208
|
-
// Security checklist:
|
|
209
|
-
// ✅ URLs expire after 1 hour (not customizable in current SDK)
|
|
210
|
-
// ✅ Signature validates credentials at generation time
|
|
211
|
-
// ✅ No credentials exposed in URL (only signature)
|
|
212
|
-
// ❌ Anyone with URL can access during expiry window
|
|
213
|
-
// ❌ URLs can be logged in proxies/browsers
|
|
214
|
-
|
|
215
|
-
// For sensitive data, consider:
|
|
216
|
-
// 1. Shorter processing windows (< 1 hour)
|
|
217
|
-
// 2. Additional application-level encryption
|
|
218
|
-
// 3. VPC endpoints (private S3 access)
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
### Credential Storage
|
|
222
|
-
|
|
223
|
-
```bash
|
|
224
|
-
# ✅ GOOD: Environment variables (never in code)
|
|
225
|
-
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
|
|
226
|
-
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
|
|
227
|
-
|
|
228
|
-
# ✅ BETTER: Secret management service
|
|
229
|
-
# - AWS Secrets Manager
|
|
230
|
-
# - HashiCorp Vault
|
|
231
|
-
# - Versori environment variables
|
|
232
|
-
|
|
233
|
-
# ❌ NEVER: Hardcoded in source code
|
|
234
|
-
const accessKey = "AKIAIOSFODNN7EXAMPLE"; // ❌ DON'T DO THIS
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### File Permissions (SFTP)
|
|
238
|
-
|
|
239
|
-
```typescript
|
|
240
|
-
// Set restrictive file permissions
|
|
241
|
-
await sftpSource.uploadFile('/exports/sensitive-data.csv', content, {
|
|
242
|
-
permissions: '0600', // Read/write owner only (rw-------)
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
// Permission codes (octal):
|
|
246
|
-
// 0644 = rw-r--r-- (owner: rw, group: r, other: r) - default
|
|
247
|
-
// 0600 = rw------- (owner: rw, group: -, other: -) - secure
|
|
248
|
-
// 0755 = rwxr-xr-x (owner: rwx, group: rx, other: rx) - directories
|
|
249
|
-
// 0700 = rwx------ (owner: rwx, group: -, other: -) - secure dirs
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
---
|
|
253
|
-
|
|
254
|
-
## Monitoring and Observability
|
|
255
|
-
|
|
256
|
-
### Structured Logging
|
|
257
|
-
|
|
258
|
-
```typescript
|
|
259
|
-
import {
|
|
260
|
-
createConsoleLogger,
|
|
261
|
-
toStructuredLogger
|
|
262
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
263
|
-
|
|
264
|
-
const logger = new LoggingService({
|
|
265
|
-
level: 'info', // debug, info, warn, error
|
|
266
|
-
context: { service: 'inventory-ingestion', version: '1.0' },
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
// File processing with rich context
|
|
270
|
-
async function processFileWithLogging(dataSource: DataSource, file: FileMetadata) {
|
|
271
|
-
const startTime = Date.now();
|
|
272
|
-
|
|
273
|
-
logger.info('Processing file started', {
|
|
274
|
-
fileName: file.name,
|
|
275
|
-
fileSize: file.size,
|
|
276
|
-
lastModified: file.lastModified.toISOString(),
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
try {
|
|
280
|
-
const content = await dataSource.downloadFile(file.path);
|
|
281
|
-
const records = parseContent(content);
|
|
282
|
-
|
|
283
|
-
logger.info('File parsed successfully', {
|
|
284
|
-
fileName: file.name,
|
|
285
|
-
recordCount: records.length,
|
|
286
|
-
parseTime: Date.now() - startTime,
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
await sendToFluent(records);
|
|
290
|
-
|
|
291
|
-
const duration = Date.now() - startTime;
|
|
292
|
-
logger.info('File processed successfully', {
|
|
293
|
-
fileName: file.name,
|
|
294
|
-
recordCount: records.length,
|
|
295
|
-
duration,
|
|
296
|
-
throughput: (records.length / (duration / 1000)).toFixed(2) + ' records/sec',
|
|
297
|
-
});
|
|
298
|
-
} catch (error) {
|
|
299
|
-
logger.error('File processing failed', error as Error, {
|
|
300
|
-
fileName: file.name,
|
|
301
|
-
duration: Date.now() - startTime,
|
|
302
|
-
});
|
|
303
|
-
throw error;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### Metrics Collection
|
|
309
|
-
|
|
310
|
-
```typescript
|
|
311
|
-
interface ProcessingMetrics {
|
|
312
|
-
filesProcessed: number;
|
|
313
|
-
filesFailed: number;
|
|
314
|
-
totalRecords: number;
|
|
315
|
-
totalDuration: number;
|
|
316
|
-
errors: Array<{ file: string; error: string }>;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
async function processWithMetrics(
|
|
320
|
-
dataSource: DataSource,
|
|
321
|
-
files: FileMetadata[]
|
|
322
|
-
): Promise<ProcessingMetrics> {
|
|
323
|
-
const metrics: ProcessingMetrics = {
|
|
324
|
-
filesProcessed: 0,
|
|
325
|
-
filesFailed: 0,
|
|
326
|
-
totalRecords: 0,
|
|
327
|
-
totalDuration: 0,
|
|
328
|
-
errors: [],
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
const startTime = Date.now();
|
|
332
|
-
|
|
333
|
-
for (const file of files) {
|
|
334
|
-
try {
|
|
335
|
-
const records = await processFile(dataSource, file.path);
|
|
336
|
-
metrics.filesProcessed++;
|
|
337
|
-
metrics.totalRecords += records.length;
|
|
338
|
-
} catch (error) {
|
|
339
|
-
metrics.filesFailed++;
|
|
340
|
-
metrics.errors.push({
|
|
341
|
-
file: file.name,
|
|
342
|
-
error: (error as Error).message,
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
metrics.totalDuration = Date.now() - startTime;
|
|
348
|
-
|
|
349
|
-
// Log summary metrics
|
|
350
|
-
logger.info('Processing complete', {
|
|
351
|
-
...metrics,
|
|
352
|
-
averageRecordsPerFile: (metrics.totalRecords / metrics.filesProcessed).toFixed(2),
|
|
353
|
-
throughput: (metrics.totalRecords / (metrics.totalDuration / 1000)).toFixed(2) + ' records/sec',
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
return metrics;
|
|
357
|
-
}
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
### Health Checks
|
|
361
|
-
|
|
362
|
-
```typescript
|
|
363
|
-
async function performHealthCheck(
|
|
364
|
-
s3Source?: S3DataSource,
|
|
365
|
-
sftpSource?: SftpDataSource
|
|
366
|
-
): Promise<{ s3: boolean; sftp: boolean }> {
|
|
367
|
-
const health = { s3: false, sftp: false };
|
|
368
|
-
|
|
369
|
-
// Check S3 connectivity
|
|
370
|
-
if (s3Source) {
|
|
371
|
-
try {
|
|
372
|
-
health.s3 = await s3Source.validateConnection();
|
|
373
|
-
logger.info('S3 health check', { status: health.s3 ? 'OK' : 'FAIL' });
|
|
374
|
-
} catch (error) {
|
|
375
|
-
logger.error('S3 health check failed', error as Error);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Check SFTP connectivity
|
|
380
|
-
if (sftpSource) {
|
|
381
|
-
try {
|
|
382
|
-
health.sftp = await sftpSource.validateConnection();
|
|
383
|
-
logger.info('SFTP health check', { status: health.sftp ? 'OK' : 'FAIL' });
|
|
384
|
-
} catch (error) {
|
|
385
|
-
logger.error('SFTP health check failed', error as Error);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
return health;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// Use in startup or scheduled health checks
|
|
393
|
-
async function startupHealthCheck() {
|
|
394
|
-
const health = await performHealthCheck(s3Source, sftpSource);
|
|
395
|
-
|
|
396
|
-
if (!health.s3 || !health.sftp) {
|
|
397
|
-
logger.error('Health check failed', { s3: health.s3, sftp: health.sftp });
|
|
398
|
-
throw new Error('Data source connectivity check failed');
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
logger.info('All health checks passed');
|
|
402
|
-
}
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
---
|
|
406
|
-
|
|
407
|
-
## Testing Strategies
|
|
408
|
-
|
|
409
|
-
### Unit Testing with Mocks
|
|
410
|
-
|
|
411
|
-
```typescript
|
|
412
|
-
// Mock data source for testing
|
|
413
|
-
class MockS3DataSource {
|
|
414
|
-
private files = new Map<string, string>();
|
|
415
|
-
|
|
416
|
-
async listFiles() {
|
|
417
|
-
return Array.from(this.files.keys()).map(path => ({
|
|
418
|
-
name: path.split('/').pop()!,
|
|
419
|
-
path,
|
|
420
|
-
size: this.files.get(path)!.length,
|
|
421
|
-
lastModified: new Date(),
|
|
422
|
-
type: 'file' as const,
|
|
423
|
-
etag: 'mock-etag',
|
|
424
|
-
}));
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
async downloadFile(path: string) {
|
|
428
|
-
if (!this.files.has(path)) {
|
|
429
|
-
throw new Error(`File not found: ${path}`);
|
|
430
|
-
}
|
|
431
|
-
return this.files.get(path)!;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
async writeFile(path: string, content: string) {
|
|
435
|
-
this.files.set(path, content);
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// Add test files
|
|
439
|
-
addTestFile(path: string, content: string) {
|
|
440
|
-
this.files.set(path, content);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// Use in tests
|
|
445
|
-
describe('Inventory Ingestion', () => {
|
|
446
|
-
let mockS3: MockS3DataSource;
|
|
447
|
-
|
|
448
|
-
beforeEach(() => {
|
|
449
|
-
mockS3 = new MockS3DataSource();
|
|
450
|
-
mockS3.addTestFile('inventory.csv', 'sku,quantity\nPROD-001,100');
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
it('should process CSV file', async () => {
|
|
454
|
-
const files = await mockS3.listFiles();
|
|
455
|
-
expect(files).toHaveLength(1);
|
|
456
|
-
|
|
457
|
-
const content = await mockS3.downloadFile('inventory.csv');
|
|
458
|
-
expect(content).toContain('PROD-001');
|
|
459
|
-
});
|
|
460
|
-
});
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
### Integration Testing
|
|
464
|
-
|
|
465
|
-
```typescript
|
|
466
|
-
// Test with real S3 (LocalStack)
|
|
467
|
-
describe('S3DataSource Integration', () => {
|
|
468
|
-
let s3Source: S3DataSource;
|
|
469
|
-
|
|
470
|
-
beforeAll(() => {
|
|
471
|
-
s3Source = new S3DataSource(
|
|
472
|
-
{
|
|
473
|
-
type: 'S3_CSV',
|
|
474
|
-
s3Config: {
|
|
475
|
-
accessKeyId: 'test',
|
|
476
|
-
secretAccessKey: 'test',
|
|
477
|
-
region: 'us-east-1',
|
|
478
|
-
endpoint: 'http://localhost:4566', // LocalStack
|
|
479
|
-
bucket: 'test-bucket',
|
|
480
|
-
},
|
|
481
|
-
},
|
|
482
|
-
logger
|
|
483
|
-
);
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
it('should upload and download file', async () => {
|
|
487
|
-
const testContent = 'sku,quantity\nTEST-001,50';
|
|
488
|
-
|
|
489
|
-
// Upload
|
|
490
|
-
await s3Source.uploadFile('s3://test-bucket/test.csv', testContent);
|
|
491
|
-
|
|
492
|
-
// Download
|
|
493
|
-
const downloaded = await s3Source.downloadFile('s3://test-bucket/test.csv');
|
|
494
|
-
|
|
495
|
-
expect(downloaded).toBe(testContent);
|
|
496
|
-
});
|
|
497
|
-
});
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
### End-to-End Testing
|
|
501
|
-
|
|
502
|
-
```typescript
|
|
503
|
-
// E2E test: Upload to S3 → Process → Validate
|
|
504
|
-
async function e2eTest() {
|
|
505
|
-
const testData = [
|
|
506
|
-
{ sku: 'E2E-001', quantity: 100, location: 'DC-001' },
|
|
507
|
-
{ sku: 'E2E-002', quantity: 200, location: 'DC-002' },
|
|
508
|
-
];
|
|
509
|
-
|
|
510
|
-
// 1. Upload test file
|
|
511
|
-
const csvContent = testData.map(r => `${r.sku},${r.quantity},${r.location}`).join('\n');
|
|
512
|
-
await s3Source.uploadFile('s3://test-bucket/e2e-test.csv', 'sku,quantity,location\n' + csvContent);
|
|
513
|
-
|
|
514
|
-
// 2. Process file
|
|
515
|
-
const files = await s3Source.listFiles({ prefix: 'e2e-test' });
|
|
516
|
-
expect(files.length).toBe(1);
|
|
517
|
-
|
|
518
|
-
const content = await s3Source.downloadFile(files[0].path);
|
|
519
|
-
const records = await csvParser.parse(content as string);
|
|
520
|
-
|
|
521
|
-
// 3. Validate results
|
|
522
|
-
expect(records.length).toBe(2);
|
|
523
|
-
expect(records[0].sku).toBe('E2E-001');
|
|
524
|
-
|
|
525
|
-
// 4. Cleanup
|
|
526
|
-
await s3Source.deleteFile(files[0].path);
|
|
527
|
-
}
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
---
|
|
531
|
-
|
|
532
|
-
## Troubleshooting
|
|
533
|
-
|
|
534
|
-
### Common Issues and Solutions
|
|
535
|
-
|
|
536
|
-
#### Issue 1: S3 Access Denied
|
|
537
|
-
|
|
538
|
-
**Symptoms**:
|
|
539
|
-
|
|
540
|
-
```
|
|
541
|
-
Error: Access Denied (statusCode: 403)
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
**Diagnosis**:
|
|
545
|
-
|
|
546
|
-
```typescript
|
|
547
|
-
// Test S3 connectivity
|
|
548
|
-
const isValid = await s3Source.validateConnection();
|
|
549
|
-
console.log('S3 connection valid:', isValid);
|
|
550
|
-
|
|
551
|
-
// Check bucket access
|
|
552
|
-
const files = await s3Source.listFiles();
|
|
553
|
-
// If this fails, check IAM permissions
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
**Solutions**:
|
|
557
|
-
|
|
558
|
-
1. Verify IAM policy allows `s3:ListBucket`, `s3:GetObject`, `s3:PutObject`
|
|
559
|
-
2. Check bucket policy allows your IAM user/role
|
|
560
|
-
3. Verify credentials are correct (not expired)
|
|
561
|
-
4. Ensure bucket exists in the specified region
|
|
562
|
-
|
|
563
|
-
#### Issue 2: SFTP Connection Timeout
|
|
564
|
-
|
|
565
|
-
**Symptoms**:
|
|
566
|
-
|
|
567
|
-
```
|
|
568
|
-
Error: Connection timeout after 30000ms
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
**Diagnosis**:
|
|
572
|
-
|
|
573
|
-
```typescript
|
|
574
|
-
// Test SFTP connectivity
|
|
575
|
-
try {
|
|
576
|
-
const isValid = await sftpSource.validateConnection();
|
|
577
|
-
console.log('SFTP connection valid:', isValid);
|
|
578
|
-
} catch (error) {
|
|
579
|
-
console.error('SFTP connection failed:', error.message);
|
|
580
|
-
}
|
|
581
|
-
```
|
|
582
|
-
|
|
583
|
-
**Solutions**:
|
|
584
|
-
|
|
585
|
-
1. Check firewall rules (allow outbound port 22)
|
|
586
|
-
2. Verify SFTP host is reachable: `telnet sftp.vendor.com 22`
|
|
587
|
-
3. Check SSH credentials (username + privateKey/password)
|
|
588
|
-
4. Increase connection timeout in config
|
|
589
|
-
5. Verify network connectivity to SFTP server
|
|
590
|
-
|
|
591
|
-
#### Issue 3: File Already Exists (SFTP)
|
|
592
|
-
|
|
593
|
-
**Symptoms**:
|
|
594
|
-
|
|
595
|
-
```
|
|
596
|
-
Error: File already exists (SFTP error code 11)
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
**Solutions**:
|
|
600
|
-
|
|
601
|
-
```typescript
|
|
602
|
-
// Option 1: Enable overwrite
|
|
603
|
-
await sftpSource.writeFile(path, content, {
|
|
604
|
-
overwrite: true, // ✅ Allow overwriting
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
// Option 2: Check existence first
|
|
608
|
-
if (await sftpSource.fileExists(path)) {
|
|
609
|
-
await sftpSource.deleteFile(path);
|
|
610
|
-
}
|
|
611
|
-
await sftpSource.writeFile(path, content);
|
|
612
|
-
|
|
613
|
-
// Option 3: Use unique filenames
|
|
614
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
615
|
-
const uniquePath = `/exports/inventory-${timestamp}.csv`;
|
|
616
|
-
await sftpSource.writeFile(uniquePath, content);
|
|
617
|
-
```
|
|
618
|
-
|
|
619
|
-
#### Issue 4: Presigned URL Expired
|
|
620
|
-
|
|
621
|
-
**Symptoms**:
|
|
622
|
-
|
|
623
|
-
```
|
|
624
|
-
Error: Request has expired (statusCode: 403)
|
|
625
|
-
```
|
|
626
|
-
|
|
627
|
-
**Cause**: Presigned URLs expire after 1 hour (default)
|
|
628
|
-
|
|
629
|
-
**Solutions**:
|
|
630
|
-
|
|
631
|
-
1. Complete operations within 1 hour
|
|
632
|
-
2. Regenerate URLs for long-running jobs
|
|
633
|
-
3. Process files in smaller batches
|
|
634
|
-
|
|
635
|
-
#### Issue 5: Out of Memory (Large Files)
|
|
636
|
-
|
|
637
|
-
**Symptoms**:
|
|
638
|
-
|
|
639
|
-
```
|
|
640
|
-
Error: JavaScript heap out of memory
|
|
641
|
-
```
|
|
642
|
-
|
|
643
|
-
**Solutions**:
|
|
644
|
-
|
|
645
|
-
```typescript
|
|
646
|
-
// Option 1: Use chunked processing
|
|
647
|
-
async function processLargeFile(dataSource: DataSource, file: string) {
|
|
648
|
-
const content = await dataSource.downloadFile(file, { encoding: 'binary' });
|
|
649
|
-
|
|
650
|
-
const chunkSize = 1024 * 1024; // 1MB chunks
|
|
651
|
-
for (let offset = 0; offset < (content as Buffer).length; offset += chunkSize) {
|
|
652
|
-
const chunk = (content as Buffer).slice(offset, offset + chunkSize);
|
|
653
|
-
await processChunk(chunk);
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
// Option 2: Increase Node.js heap size
|
|
658
|
-
// node --max-old-space-size=4096 your-script.js (4GB heap)
|
|
659
|
-
|
|
660
|
-
// Option 3: Split large files before processing
|
|
661
|
-
```
|
|
662
|
-
|
|
663
|
-
### Debugging Tips
|
|
664
|
-
|
|
665
|
-
```typescript
|
|
666
|
-
// Enable debug logging
|
|
667
|
-
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
668
|
-
logLevel: 'debug'
|
|
669
|
-
});
|
|
670
|
-
|
|
671
|
-
// Log S3 presigned URLs
|
|
672
|
-
logger.debug('Generated presigned URL', {
|
|
673
|
-
bucket: 'my-bucket',
|
|
674
|
-
key: 'file.csv',
|
|
675
|
-
operation: 'GET',
|
|
676
|
-
// URL is logged internally by S3DataSource
|
|
677
|
-
});
|
|
678
|
-
|
|
679
|
-
// Log SFTP connection details
|
|
680
|
-
const connectionInfo = sftpSource.getConnectionInfo();
|
|
681
|
-
logger.debug('SFTP connection info', connectionInfo);
|
|
682
|
-
|
|
683
|
-
// Log file metadata
|
|
684
|
-
const files = await dataSource.listFiles();
|
|
685
|
-
for (const file of files) {
|
|
686
|
-
logger.debug('File metadata', {
|
|
687
|
-
name: file.name,
|
|
688
|
-
size: file.size,
|
|
689
|
-
lastModified: file.lastModified,
|
|
690
|
-
});
|
|
691
|
-
}
|
|
692
|
-
```
|
|
693
|
-
|
|
694
|
-
---
|
|
695
|
-
|
|
696
|
-
## Performance Tuning
|
|
697
|
-
|
|
698
|
-
### Optimization Strategies
|
|
699
|
-
|
|
700
|
-
```typescript
|
|
701
|
-
// 1. Parallel file processing (S3)
|
|
702
|
-
const maxConcurrency = 10; // S3 scales well
|
|
703
|
-
await Promise.all(files.slice(0, maxConcurrency).map(file => processFile(s3Source, file)));
|
|
704
|
-
|
|
705
|
-
// 2. Connection pooling (SFTP)
|
|
706
|
-
const sftpConfig = {
|
|
707
|
-
settings: {
|
|
708
|
-
maxConnections: 3, // Don't overwhelm vendor SFTP server
|
|
709
|
-
keepaliveInterval: 5000,
|
|
710
|
-
},
|
|
711
|
-
};
|
|
712
|
-
|
|
713
|
-
// 3. Batch size optimization
|
|
714
|
-
const optimalBatchSize = Math.min(
|
|
715
|
-
Math.ceil(records.length / 10), // ~10 batches
|
|
716
|
-
500 // Max 500 per batch
|
|
717
|
-
);
|
|
718
|
-
|
|
719
|
-
// 4. Minimize data source round trips
|
|
720
|
-
const files = await s3Source.listFiles({ prefix: 'inventory/' });
|
|
721
|
-
// Get all file metadata in one call
|
|
722
|
-
|
|
723
|
-
// 5. Use appropriate file formats
|
|
724
|
-
// CSV: Human-readable, moderate size
|
|
725
|
-
// Parquet: Columnar, best compression, fastest queries
|
|
726
|
-
// JSON: Flexible schema, larger size
|
|
727
|
-
```
|
|
728
|
-
|
|
729
|
-
### Performance Benchmarks
|
|
730
|
-
|
|
731
|
-
```
|
|
732
|
-
File Size Benchmarks (approximate):
|
|
733
|
-
┌───────────┬──────────────┬───────────────┬─────────────┐
|
|
734
|
-
│ File Size │ Buffering │ Chunked (1MB) │ Best Choice │
|
|
735
|
-
├───────────┼──────────────┼───────────────┼─────────────┤
|
|
736
|
-
│ < 1MB │ 0.1s │ 0.3s │ Buffering │
|
|
737
|
-
│ 10MB │ 0.8s │ 1.2s │ Buffering │
|
|
738
|
-
│ 50MB │ 3.5s │ 4.0s │ Buffering │
|
|
739
|
-
│ 100MB │ 7.0s │ 7.5s │ Either │
|
|
740
|
-
│ 500MB │ OOM risk │ 35s │ Chunked │
|
|
741
|
-
│ 1GB+ │ Not possible │ 70s+ │ Chunked │
|
|
742
|
-
└───────────┴──────────────┴───────────────┴─────────────┘
|
|
743
|
-
```
|
|
744
|
-
|
|
745
|
-
---
|
|
746
|
-
|
|
747
|
-
## Production Checklist
|
|
748
|
-
|
|
749
|
-
### Pre-Deployment
|
|
750
|
-
|
|
751
|
-
- [ ] **Security**
|
|
752
|
-
- [ ] SSH keys stored in secure secret manager
|
|
753
|
-
- [ ] AWS credentials use least-privilege IAM policies
|
|
754
|
-
- [ ] No hardcoded credentials in source code
|
|
755
|
-
- [ ] SFTP file permissions set to 0600 for sensitive data
|
|
756
|
-
|
|
757
|
-
- [ ] **Configuration**
|
|
758
|
-
- [ ] Environment variables documented
|
|
759
|
-
- [ ] Dual AWS credentials configured (source + target)
|
|
760
|
-
- [ ] Connection timeouts tuned for network conditions
|
|
761
|
-
- [ ] Batch sizes optimized for data volume
|
|
762
|
-
|
|
763
|
-
- [ ] **Monitoring**
|
|
764
|
-
- [ ] Structured logging enabled
|
|
765
|
-
- [ ] Health checks implemented
|
|
766
|
-
- [ ] Metrics collection configured
|
|
767
|
-
- [ ] Alerting set up for failures
|
|
768
|
-
|
|
769
|
-
- [ ] **Error Handling**
|
|
770
|
-
- [ ] Retry logic with exponential backoff
|
|
771
|
-
- [ ] Partial failure handling for batches
|
|
772
|
-
- [ ] Failed records logged for manual review
|
|
773
|
-
- [ ] Circuit breaker pattern for repeated failures
|
|
774
|
-
|
|
775
|
-
- [ ] **State Management**
|
|
776
|
-
- [ ] State service configured (prevent duplicates)
|
|
777
|
-
- [ ] State keys use file metadata (etag or lastModified)
|
|
778
|
-
- [ ] State cleanup strategy defined
|
|
779
|
-
|
|
780
|
-
- [ ] **Testing**
|
|
781
|
-
- [ ] Unit tests pass
|
|
782
|
-
- [ ] Integration tests with real services (or LocalStack)
|
|
783
|
-
- [ ] End-to-end test validates full pipeline
|
|
784
|
-
- [ ] Load testing completed for production volume
|
|
785
|
-
|
|
786
|
-
### Post-Deployment
|
|
787
|
-
|
|
788
|
-
- [ ] **Monitoring**
|
|
789
|
-
- [ ] Logs are being collected
|
|
790
|
-
- [ ] Metrics dashboards created
|
|
791
|
-
- [ ] Alerts are triggering correctly
|
|
792
|
-
|
|
793
|
-
- [ ] **Performance**
|
|
794
|
-
- [ ] Processing throughput meets SLA
|
|
795
|
-
- [ ] Memory usage is stable
|
|
796
|
-
- [ ] Connection pool size is optimal
|
|
797
|
-
|
|
798
|
-
- [ ] **Validation**
|
|
799
|
-
- [ ] Data quality checks passing
|
|
800
|
-
- [ ] No duplicate files processed
|
|
801
|
-
- [ ] Record counts match expected
|
|
802
|
-
|
|
803
|
-
---
|
|
804
|
-
|
|
805
|
-
## Next Steps
|
|
806
|
-
|
|
807
|
-
**Quick reference** → [Quick Reference](../../advanced-services/advanced-services-quick-reference.md)
|
|
808
|
-
|
|
809
|
-
**Complete examples** → [Examples Directory](../examples/)
|
|
810
|
-
|
|
811
|
-
---
|
|
812
|
-
|
|
813
|
-
[← Back to File Processing Patterns](./data-sources-04-file-patterns.md) | [Quick Reference →](../data-sources-quick-reference.md) | [↑ Back to Guide](../data-sources-readme.md)
|
|
1
|
+
# Module 5: Advanced Topics
|
|
2
|
+
|
|
3
|
+
Master advanced data source patterns - dual AWS credentials, security best practices, monitoring, testing strategies, and troubleshooting.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Dual AWS Credentials](#dual-aws-credentials)
|
|
8
|
+
- [Security Best Practices](#security-best-practices)
|
|
9
|
+
- [Monitoring and Observability](#monitoring-and-observability)
|
|
10
|
+
- [Testing Strategies](#testing-strategies)
|
|
11
|
+
- [Troubleshooting](#troubleshooting)
|
|
12
|
+
- [Performance Tuning](#performance-tuning)
|
|
13
|
+
- [Production Checklist](#production-checklist)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Dual AWS Credentials
|
|
18
|
+
|
|
19
|
+
### Why Use Separate AWS Accounts?
|
|
20
|
+
|
|
21
|
+
**Separation of Concerns**:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
┌─────────────────────────────────────────────────────┐
|
|
25
|
+
│ Vendor AWS Account (Source) │
|
|
26
|
+
│ - Read-only access to vendor's S3 bucket │
|
|
27
|
+
│ - Limited IAM permissions (ListBucket, GetObject) │
|
|
28
|
+
│ - Vendor manages security and compliance │
|
|
29
|
+
└────────────────┬────────────────────────────────────┘
|
|
30
|
+
│
|
|
31
|
+
▼ (Read inventory files)
|
|
32
|
+
┌─────────────────────────────────────────────────────┐
|
|
33
|
+
│ Your Application (Processing) │
|
|
34
|
+
│ - Runs with vendor credentials to read │
|
|
35
|
+
│ - Runs with your credentials to write │
|
|
36
|
+
└────────────────┬────────────────────────────────────┘
|
|
37
|
+
│
|
|
38
|
+
▼ (Write processed data)
|
|
39
|
+
┌─────────────────────────────────────────────────────┐
|
|
40
|
+
│ Your AWS Account (Target) │
|
|
41
|
+
│ - Full write access to your data lake │
|
|
42
|
+
│ - Your IAM permissions (PutObject, lifecycle rules) │
|
|
43
|
+
│ - You manage retention and compliance │
|
|
44
|
+
└─────────────────────────────────────────────────────┘
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Benefits**:
|
|
48
|
+
|
|
49
|
+
- ✅ **Security**: Vendor credentials can't write to your buckets
|
|
50
|
+
- ✅ **Compliance**: Clear data ownership and responsibility
|
|
51
|
+
- ✅ **Cost control**: Separate billing for source vs target storage
|
|
52
|
+
- ✅ **Flexibility**: Different retention policies per account
|
|
53
|
+
|
|
54
|
+
### Implementation Pattern
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import {
|
|
58
|
+
S3DataSource,
|
|
59
|
+
createConsoleLogger,
|
|
60
|
+
toStructuredLogger
|
|
61
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
62
|
+
|
|
63
|
+
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
64
|
+
logLevel: 'info'
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Source S3 (Vendor's AWS account - READ ONLY)
|
|
68
|
+
const sourceS3 = new S3DataSource(
|
|
69
|
+
{
|
|
70
|
+
type: 'S3_CSV',
|
|
71
|
+
connectionId: 's3-source-vendor',
|
|
72
|
+
name: 'S3 Source Vendor',
|
|
73
|
+
s3Config: {
|
|
74
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!, // Vendor credentials
|
|
75
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
76
|
+
region: process.env.AWS_REGION || 'us-east-1',
|
|
77
|
+
bucket: process.env.SOURCE_BUCKET, // Vendor's bucket
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
logger
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Target S3 (Your AWS account - WRITE)
|
|
84
|
+
const targetS3 = new S3DataSource(
|
|
85
|
+
{
|
|
86
|
+
type: 'S3_PARQUET',
|
|
87
|
+
connectionId: 's3-target-own',
|
|
88
|
+
name: 'S3 Target Own',
|
|
89
|
+
s3Config: {
|
|
90
|
+
accessKeyId: process.env.TARGET_AWS_ACCESS_KEY_ID!, // Your credentials
|
|
91
|
+
secretAccessKey: process.env.TARGET_AWS_SECRET_ACCESS_KEY!,
|
|
92
|
+
region: process.env.TARGET_AWS_REGION || 'us-west-2',
|
|
93
|
+
bucket: process.env.TARGET_BUCKET, // Your data lake
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
logger
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Usage: Read from vendor, write to your data lake
|
|
100
|
+
async function processInventory() {
|
|
101
|
+
// Read from vendor's S3
|
|
102
|
+
const files = await sourceS3.listFiles({ prefix: 'inventory/' });
|
|
103
|
+
|
|
104
|
+
for (const file of files) {
|
|
105
|
+
// Download from vendor
|
|
106
|
+
const content = await sourceS3.downloadFile(file.path);
|
|
107
|
+
|
|
108
|
+
// Process data
|
|
109
|
+
const processed = await processData(content);
|
|
110
|
+
|
|
111
|
+
// Write to your data lake
|
|
112
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
113
|
+
await targetS3.uploadFile(`s3://your-bucket/processed/${timestamp}/${file.name}`, processed);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Environment Variables for Dual Credentials
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# .env file
|
|
122
|
+
|
|
123
|
+
# Source AWS Account (Vendor)
|
|
124
|
+
AWS_ACCESS_KEY_ID=AKIAVENDORKEY123
|
|
125
|
+
AWS_SECRET_ACCESS_KEY=vendorSecretKey456
|
|
126
|
+
AWS_REGION=us-east-1
|
|
127
|
+
SOURCE_BUCKET=vendor-inventory-bucket
|
|
128
|
+
|
|
129
|
+
# Target AWS Account (Your Data Lake)
|
|
130
|
+
TARGET_AWS_ACCESS_KEY_ID=AKIAYOURKEY789
|
|
131
|
+
TARGET_AWS_SECRET_ACCESS_KEY=yourSecretKey012
|
|
132
|
+
TARGET_AWS_REGION=us-west-2
|
|
133
|
+
TARGET_BUCKET=your-datalake-bucket
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### IAM Policy Examples
|
|
137
|
+
|
|
138
|
+
**Source (Vendor) - Read-Only Policy**:
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"Version": "2012-10-17",
|
|
143
|
+
"Statement": [
|
|
144
|
+
{
|
|
145
|
+
"Effect": "Allow",
|
|
146
|
+
"Action": ["s3:ListBucket", "s3:GetObject", "s3:GetObjectVersion"],
|
|
147
|
+
"Resource": ["arn:aws:s3:::vendor-inventory-bucket", "arn:aws:s3:::vendor-inventory-bucket/*"]
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Target (Your) - Write + Lifecycle Policy**:
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"Version": "2012-10-17",
|
|
158
|
+
"Statement": [
|
|
159
|
+
{
|
|
160
|
+
"Effect": "Allow",
|
|
161
|
+
"Action": ["s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
|
|
162
|
+
"Resource": ["arn:aws:s3:::your-datalake-bucket", "arn:aws:s3:::your-datalake-bucket/*"]
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Security Best Practices
|
|
171
|
+
|
|
172
|
+
### SSH Key Management (SFTP)
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// 1. Use private key authentication (NOT passwords)
|
|
176
|
+
const sftpSource = new SftpDataSource(
|
|
177
|
+
{
|
|
178
|
+
type: 'SFTP_CSV',
|
|
179
|
+
connectionId: 'vendor-sftp',
|
|
180
|
+
settings: {
|
|
181
|
+
host: 'sftp.vendor.com',
|
|
182
|
+
username: 'integration',
|
|
183
|
+
privateKey: process.env.SFTP_PRIVATE_KEY, // ✅ SSH key (recommended)
|
|
184
|
+
// password: process.env.SFTP_PASSWORD, // ❌ Avoid passwords
|
|
185
|
+
remotePath: '/data/inventory',
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
logger
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
// 2. Use encrypted private keys with passphrase
|
|
192
|
+
const secureConfig = {
|
|
193
|
+
privateKey: process.env.SFTP_PRIVATE_KEY, // Encrypted key
|
|
194
|
+
passphrase: process.env.SFTP_PASSPHRASE, // Key passphrase
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// 3. Rotate keys regularly (every 90 days)
|
|
198
|
+
// 4. Never commit keys to version control
|
|
199
|
+
// 5. Use environment-specific keys (dev, staging, prod)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Presigned URL Security (S3)
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
// Presigned URL expiration (1 hour default)
|
|
206
|
+
// URLs auto-expire after this period
|
|
207
|
+
|
|
208
|
+
// Security checklist:
|
|
209
|
+
// ✅ URLs expire after 1 hour (not customizable in current SDK)
|
|
210
|
+
// ✅ Signature validates credentials at generation time
|
|
211
|
+
// ✅ No credentials exposed in URL (only signature)
|
|
212
|
+
// ❌ Anyone with URL can access during expiry window
|
|
213
|
+
// ❌ URLs can be logged in proxies/browsers
|
|
214
|
+
|
|
215
|
+
// For sensitive data, consider:
|
|
216
|
+
// 1. Shorter processing windows (< 1 hour)
|
|
217
|
+
// 2. Additional application-level encryption
|
|
218
|
+
// 3. VPC endpoints (private S3 access)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Credential Storage
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
# ✅ GOOD: Environment variables (never in code)
|
|
225
|
+
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
|
|
226
|
+
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
|
|
227
|
+
|
|
228
|
+
# ✅ BETTER: Secret management service
|
|
229
|
+
# - AWS Secrets Manager
|
|
230
|
+
# - HashiCorp Vault
|
|
231
|
+
# - Versori environment variables
|
|
232
|
+
|
|
233
|
+
# ❌ NEVER: Hardcoded in source code
|
|
234
|
+
const accessKey = "AKIAIOSFODNN7EXAMPLE"; // ❌ DON'T DO THIS
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### File Permissions (SFTP)
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
// Set restrictive file permissions
|
|
241
|
+
await sftpSource.uploadFile('/exports/sensitive-data.csv', content, {
|
|
242
|
+
permissions: '0600', // Read/write owner only (rw-------)
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Permission codes (octal):
|
|
246
|
+
// 0644 = rw-r--r-- (owner: rw, group: r, other: r) - default
|
|
247
|
+
// 0600 = rw------- (owner: rw, group: -, other: -) - secure
|
|
248
|
+
// 0755 = rwxr-xr-x (owner: rwx, group: rx, other: rx) - directories
|
|
249
|
+
// 0700 = rwx------ (owner: rwx, group: -, other: -) - secure dirs
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Monitoring and Observability
|
|
255
|
+
|
|
256
|
+
### Structured Logging
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import {
|
|
260
|
+
createConsoleLogger,
|
|
261
|
+
toStructuredLogger
|
|
262
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
263
|
+
|
|
264
|
+
const logger = new LoggingService({
|
|
265
|
+
level: 'info', // debug, info, warn, error
|
|
266
|
+
context: { service: 'inventory-ingestion', version: '1.0' },
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// File processing with rich context
|
|
270
|
+
async function processFileWithLogging(dataSource: DataSource, file: FileMetadata) {
|
|
271
|
+
const startTime = Date.now();
|
|
272
|
+
|
|
273
|
+
logger.info('Processing file started', {
|
|
274
|
+
fileName: file.name,
|
|
275
|
+
fileSize: file.size,
|
|
276
|
+
lastModified: file.lastModified.toISOString(),
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
const content = await dataSource.downloadFile(file.path);
|
|
281
|
+
const records = parseContent(content);
|
|
282
|
+
|
|
283
|
+
logger.info('File parsed successfully', {
|
|
284
|
+
fileName: file.name,
|
|
285
|
+
recordCount: records.length,
|
|
286
|
+
parseTime: Date.now() - startTime,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
await sendToFluent(records);
|
|
290
|
+
|
|
291
|
+
const duration = Date.now() - startTime;
|
|
292
|
+
logger.info('File processed successfully', {
|
|
293
|
+
fileName: file.name,
|
|
294
|
+
recordCount: records.length,
|
|
295
|
+
duration,
|
|
296
|
+
throughput: (records.length / (duration / 1000)).toFixed(2) + ' records/sec',
|
|
297
|
+
});
|
|
298
|
+
} catch (error) {
|
|
299
|
+
logger.error('File processing failed', error as Error, {
|
|
300
|
+
fileName: file.name,
|
|
301
|
+
duration: Date.now() - startTime,
|
|
302
|
+
});
|
|
303
|
+
throw error;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Metrics Collection
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
interface ProcessingMetrics {
|
|
312
|
+
filesProcessed: number;
|
|
313
|
+
filesFailed: number;
|
|
314
|
+
totalRecords: number;
|
|
315
|
+
totalDuration: number;
|
|
316
|
+
errors: Array<{ file: string; error: string }>;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async function processWithMetrics(
|
|
320
|
+
dataSource: DataSource,
|
|
321
|
+
files: FileMetadata[]
|
|
322
|
+
): Promise<ProcessingMetrics> {
|
|
323
|
+
const metrics: ProcessingMetrics = {
|
|
324
|
+
filesProcessed: 0,
|
|
325
|
+
filesFailed: 0,
|
|
326
|
+
totalRecords: 0,
|
|
327
|
+
totalDuration: 0,
|
|
328
|
+
errors: [],
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const startTime = Date.now();
|
|
332
|
+
|
|
333
|
+
for (const file of files) {
|
|
334
|
+
try {
|
|
335
|
+
const records = await processFile(dataSource, file.path);
|
|
336
|
+
metrics.filesProcessed++;
|
|
337
|
+
metrics.totalRecords += records.length;
|
|
338
|
+
} catch (error) {
|
|
339
|
+
metrics.filesFailed++;
|
|
340
|
+
metrics.errors.push({
|
|
341
|
+
file: file.name,
|
|
342
|
+
error: (error as Error).message,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
metrics.totalDuration = Date.now() - startTime;
|
|
348
|
+
|
|
349
|
+
// Log summary metrics
|
|
350
|
+
logger.info('Processing complete', {
|
|
351
|
+
...metrics,
|
|
352
|
+
averageRecordsPerFile: (metrics.totalRecords / metrics.filesProcessed).toFixed(2),
|
|
353
|
+
throughput: (metrics.totalRecords / (metrics.totalDuration / 1000)).toFixed(2) + ' records/sec',
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
return metrics;
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Health Checks
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
async function performHealthCheck(
|
|
364
|
+
s3Source?: S3DataSource,
|
|
365
|
+
sftpSource?: SftpDataSource
|
|
366
|
+
): Promise<{ s3: boolean; sftp: boolean }> {
|
|
367
|
+
const health = { s3: false, sftp: false };
|
|
368
|
+
|
|
369
|
+
// Check S3 connectivity
|
|
370
|
+
if (s3Source) {
|
|
371
|
+
try {
|
|
372
|
+
health.s3 = await s3Source.validateConnection();
|
|
373
|
+
logger.info('S3 health check', { status: health.s3 ? 'OK' : 'FAIL' });
|
|
374
|
+
} catch (error) {
|
|
375
|
+
logger.error('S3 health check failed', error as Error);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Check SFTP connectivity
|
|
380
|
+
if (sftpSource) {
|
|
381
|
+
try {
|
|
382
|
+
health.sftp = await sftpSource.validateConnection();
|
|
383
|
+
logger.info('SFTP health check', { status: health.sftp ? 'OK' : 'FAIL' });
|
|
384
|
+
} catch (error) {
|
|
385
|
+
logger.error('SFTP health check failed', error as Error);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return health;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Use in startup or scheduled health checks
|
|
393
|
+
async function startupHealthCheck() {
|
|
394
|
+
const health = await performHealthCheck(s3Source, sftpSource);
|
|
395
|
+
|
|
396
|
+
if (!health.s3 || !health.sftp) {
|
|
397
|
+
logger.error('Health check failed', { s3: health.s3, sftp: health.sftp });
|
|
398
|
+
throw new Error('Data source connectivity check failed');
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
logger.info('All health checks passed');
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## Testing Strategies
|
|
408
|
+
|
|
409
|
+
### Unit Testing with Mocks
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
// Mock data source for testing
|
|
413
|
+
class MockS3DataSource {
|
|
414
|
+
private files = new Map<string, string>();
|
|
415
|
+
|
|
416
|
+
async listFiles() {
|
|
417
|
+
return Array.from(this.files.keys()).map(path => ({
|
|
418
|
+
name: path.split('/').pop()!,
|
|
419
|
+
path,
|
|
420
|
+
size: this.files.get(path)!.length,
|
|
421
|
+
lastModified: new Date(),
|
|
422
|
+
type: 'file' as const,
|
|
423
|
+
etag: 'mock-etag',
|
|
424
|
+
}));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async downloadFile(path: string) {
|
|
428
|
+
if (!this.files.has(path)) {
|
|
429
|
+
throw new Error(`File not found: ${path}`);
|
|
430
|
+
}
|
|
431
|
+
return this.files.get(path)!;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
async writeFile(path: string, content: string) {
|
|
435
|
+
this.files.set(path, content);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Add test files
|
|
439
|
+
addTestFile(path: string, content: string) {
|
|
440
|
+
this.files.set(path, content);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Use in tests
|
|
445
|
+
describe('Inventory Ingestion', () => {
|
|
446
|
+
let mockS3: MockS3DataSource;
|
|
447
|
+
|
|
448
|
+
beforeEach(() => {
|
|
449
|
+
mockS3 = new MockS3DataSource();
|
|
450
|
+
mockS3.addTestFile('inventory.csv', 'sku,quantity\nPROD-001,100');
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('should process CSV file', async () => {
|
|
454
|
+
const files = await mockS3.listFiles();
|
|
455
|
+
expect(files).toHaveLength(1);
|
|
456
|
+
|
|
457
|
+
const content = await mockS3.downloadFile('inventory.csv');
|
|
458
|
+
expect(content).toContain('PROD-001');
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Integration Testing
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
// Test with real S3 (LocalStack)
|
|
467
|
+
describe('S3DataSource Integration', () => {
|
|
468
|
+
let s3Source: S3DataSource;
|
|
469
|
+
|
|
470
|
+
beforeAll(() => {
|
|
471
|
+
s3Source = new S3DataSource(
|
|
472
|
+
{
|
|
473
|
+
type: 'S3_CSV',
|
|
474
|
+
s3Config: {
|
|
475
|
+
accessKeyId: 'test',
|
|
476
|
+
secretAccessKey: 'test',
|
|
477
|
+
region: 'us-east-1',
|
|
478
|
+
endpoint: 'http://localhost:4566', // LocalStack
|
|
479
|
+
bucket: 'test-bucket',
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
logger
|
|
483
|
+
);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it('should upload and download file', async () => {
|
|
487
|
+
const testContent = 'sku,quantity\nTEST-001,50';
|
|
488
|
+
|
|
489
|
+
// Upload
|
|
490
|
+
await s3Source.uploadFile('s3://test-bucket/test.csv', testContent);
|
|
491
|
+
|
|
492
|
+
// Download
|
|
493
|
+
const downloaded = await s3Source.downloadFile('s3://test-bucket/test.csv');
|
|
494
|
+
|
|
495
|
+
expect(downloaded).toBe(testContent);
|
|
496
|
+
});
|
|
497
|
+
});
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### End-to-End Testing
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
// E2E test: Upload to S3 → Process → Validate
|
|
504
|
+
async function e2eTest() {
|
|
505
|
+
const testData = [
|
|
506
|
+
{ sku: 'E2E-001', quantity: 100, location: 'DC-001' },
|
|
507
|
+
{ sku: 'E2E-002', quantity: 200, location: 'DC-002' },
|
|
508
|
+
];
|
|
509
|
+
|
|
510
|
+
// 1. Upload test file
|
|
511
|
+
const csvContent = testData.map(r => `${r.sku},${r.quantity},${r.location}`).join('\n');
|
|
512
|
+
await s3Source.uploadFile('s3://test-bucket/e2e-test.csv', 'sku,quantity,location\n' + csvContent);
|
|
513
|
+
|
|
514
|
+
// 2. Process file
|
|
515
|
+
const files = await s3Source.listFiles({ prefix: 'e2e-test' });
|
|
516
|
+
expect(files.length).toBe(1);
|
|
517
|
+
|
|
518
|
+
const content = await s3Source.downloadFile(files[0].path);
|
|
519
|
+
const records = await csvParser.parse(content as string);
|
|
520
|
+
|
|
521
|
+
// 3. Validate results
|
|
522
|
+
expect(records.length).toBe(2);
|
|
523
|
+
expect(records[0].sku).toBe('E2E-001');
|
|
524
|
+
|
|
525
|
+
// 4. Cleanup
|
|
526
|
+
await s3Source.deleteFile(files[0].path);
|
|
527
|
+
}
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
---
|
|
531
|
+
|
|
532
|
+
## Troubleshooting
|
|
533
|
+
|
|
534
|
+
### Common Issues and Solutions
|
|
535
|
+
|
|
536
|
+
#### Issue 1: S3 Access Denied
|
|
537
|
+
|
|
538
|
+
**Symptoms**:
|
|
539
|
+
|
|
540
|
+
```
|
|
541
|
+
Error: Access Denied (statusCode: 403)
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
**Diagnosis**:
|
|
545
|
+
|
|
546
|
+
```typescript
|
|
547
|
+
// Test S3 connectivity
|
|
548
|
+
const isValid = await s3Source.validateConnection();
|
|
549
|
+
console.log('S3 connection valid:', isValid);
|
|
550
|
+
|
|
551
|
+
// Check bucket access
|
|
552
|
+
const files = await s3Source.listFiles();
|
|
553
|
+
// If this fails, check IAM permissions
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
**Solutions**:
|
|
557
|
+
|
|
558
|
+
1. Verify IAM policy allows `s3:ListBucket`, `s3:GetObject`, `s3:PutObject`
|
|
559
|
+
2. Check bucket policy allows your IAM user/role
|
|
560
|
+
3. Verify credentials are correct (not expired)
|
|
561
|
+
4. Ensure bucket exists in the specified region
|
|
562
|
+
|
|
563
|
+
#### Issue 2: SFTP Connection Timeout
|
|
564
|
+
|
|
565
|
+
**Symptoms**:
|
|
566
|
+
|
|
567
|
+
```
|
|
568
|
+
Error: Connection timeout after 30000ms
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
**Diagnosis**:
|
|
572
|
+
|
|
573
|
+
```typescript
|
|
574
|
+
// Test SFTP connectivity
|
|
575
|
+
try {
|
|
576
|
+
const isValid = await sftpSource.validateConnection();
|
|
577
|
+
console.log('SFTP connection valid:', isValid);
|
|
578
|
+
} catch (error) {
|
|
579
|
+
console.error('SFTP connection failed:', error.message);
|
|
580
|
+
}
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
**Solutions**:
|
|
584
|
+
|
|
585
|
+
1. Check firewall rules (allow outbound port 22)
|
|
586
|
+
2. Verify SFTP host is reachable: `telnet sftp.vendor.com 22`
|
|
587
|
+
3. Check SSH credentials (username + privateKey/password)
|
|
588
|
+
4. Increase connection timeout in config
|
|
589
|
+
5. Verify network connectivity to SFTP server
|
|
590
|
+
|
|
591
|
+
#### Issue 3: File Already Exists (SFTP)
|
|
592
|
+
|
|
593
|
+
**Symptoms**:
|
|
594
|
+
|
|
595
|
+
```
|
|
596
|
+
Error: File already exists (SFTP error code 11)
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
**Solutions**:
|
|
600
|
+
|
|
601
|
+
```typescript
|
|
602
|
+
// Option 1: Enable overwrite
|
|
603
|
+
await sftpSource.writeFile(path, content, {
|
|
604
|
+
overwrite: true, // ✅ Allow overwriting
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
// Option 2: Check existence first
|
|
608
|
+
if (await sftpSource.fileExists(path)) {
|
|
609
|
+
await sftpSource.deleteFile(path);
|
|
610
|
+
}
|
|
611
|
+
await sftpSource.writeFile(path, content);
|
|
612
|
+
|
|
613
|
+
// Option 3: Use unique filenames
|
|
614
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
615
|
+
const uniquePath = `/exports/inventory-${timestamp}.csv`;
|
|
616
|
+
await sftpSource.writeFile(uniquePath, content);
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
#### Issue 4: Presigned URL Expired
|
|
620
|
+
|
|
621
|
+
**Symptoms**:
|
|
622
|
+
|
|
623
|
+
```
|
|
624
|
+
Error: Request has expired (statusCode: 403)
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
**Cause**: Presigned URLs expire after 1 hour (default)
|
|
628
|
+
|
|
629
|
+
**Solutions**:
|
|
630
|
+
|
|
631
|
+
1. Complete operations within 1 hour
|
|
632
|
+
2. Regenerate URLs for long-running jobs
|
|
633
|
+
3. Process files in smaller batches
|
|
634
|
+
|
|
635
|
+
#### Issue 5: Out of Memory (Large Files)
|
|
636
|
+
|
|
637
|
+
**Symptoms**:
|
|
638
|
+
|
|
639
|
+
```
|
|
640
|
+
Error: JavaScript heap out of memory
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
**Solutions**:
|
|
644
|
+
|
|
645
|
+
```typescript
|
|
646
|
+
// Option 1: Use chunked processing
|
|
647
|
+
async function processLargeFile(dataSource: DataSource, file: string) {
|
|
648
|
+
const content = await dataSource.downloadFile(file, { encoding: 'binary' });
|
|
649
|
+
|
|
650
|
+
const chunkSize = 1024 * 1024; // 1MB chunks
|
|
651
|
+
for (let offset = 0; offset < (content as Buffer).length; offset += chunkSize) {
|
|
652
|
+
const chunk = (content as Buffer).slice(offset, offset + chunkSize);
|
|
653
|
+
await processChunk(chunk);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Option 2: Increase Node.js heap size
|
|
658
|
+
// node --max-old-space-size=4096 your-script.js (4GB heap)
|
|
659
|
+
|
|
660
|
+
// Option 3: Split large files before processing
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
### Debugging Tips
|
|
664
|
+
|
|
665
|
+
```typescript
|
|
666
|
+
// Enable debug logging
|
|
667
|
+
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
668
|
+
logLevel: 'debug'
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
// Log S3 presigned URLs
|
|
672
|
+
logger.debug('Generated presigned URL', {
|
|
673
|
+
bucket: 'my-bucket',
|
|
674
|
+
key: 'file.csv',
|
|
675
|
+
operation: 'GET',
|
|
676
|
+
// URL is logged internally by S3DataSource
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
// Log SFTP connection details
|
|
680
|
+
const connectionInfo = sftpSource.getConnectionInfo();
|
|
681
|
+
logger.debug('SFTP connection info', connectionInfo);
|
|
682
|
+
|
|
683
|
+
// Log file metadata
|
|
684
|
+
const files = await dataSource.listFiles();
|
|
685
|
+
for (const file of files) {
|
|
686
|
+
logger.debug('File metadata', {
|
|
687
|
+
name: file.name,
|
|
688
|
+
size: file.size,
|
|
689
|
+
lastModified: file.lastModified,
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
---
|
|
695
|
+
|
|
696
|
+
## Performance Tuning
|
|
697
|
+
|
|
698
|
+
### Optimization Strategies
|
|
699
|
+
|
|
700
|
+
```typescript
|
|
701
|
+
// 1. Parallel file processing (S3)
|
|
702
|
+
const maxConcurrency = 10; // S3 scales well
|
|
703
|
+
await Promise.all(files.slice(0, maxConcurrency).map(file => processFile(s3Source, file)));
|
|
704
|
+
|
|
705
|
+
// 2. Connection pooling (SFTP)
|
|
706
|
+
const sftpConfig = {
|
|
707
|
+
settings: {
|
|
708
|
+
maxConnections: 3, // Don't overwhelm vendor SFTP server
|
|
709
|
+
keepaliveInterval: 5000,
|
|
710
|
+
},
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
// 3. Batch size optimization
|
|
714
|
+
const optimalBatchSize = Math.min(
|
|
715
|
+
Math.ceil(records.length / 10), // ~10 batches
|
|
716
|
+
500 // Max 500 per batch
|
|
717
|
+
);
|
|
718
|
+
|
|
719
|
+
// 4. Minimize data source round trips
|
|
720
|
+
const files = await s3Source.listFiles({ prefix: 'inventory/' });
|
|
721
|
+
// Get all file metadata in one call
|
|
722
|
+
|
|
723
|
+
// 5. Use appropriate file formats
|
|
724
|
+
// CSV: Human-readable, moderate size
|
|
725
|
+
// Parquet: Columnar, best compression, fastest queries
|
|
726
|
+
// JSON: Flexible schema, larger size
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
### Performance Benchmarks
|
|
730
|
+
|
|
731
|
+
```
|
|
732
|
+
File Size Benchmarks (approximate):
|
|
733
|
+
┌───────────┬──────────────┬───────────────┬─────────────┐
|
|
734
|
+
│ File Size │ Buffering │ Chunked (1MB) │ Best Choice │
|
|
735
|
+
├───────────┼──────────────┼───────────────┼─────────────┤
|
|
736
|
+
│ < 1MB │ 0.1s │ 0.3s │ Buffering │
|
|
737
|
+
│ 10MB │ 0.8s │ 1.2s │ Buffering │
|
|
738
|
+
│ 50MB │ 3.5s │ 4.0s │ Buffering │
|
|
739
|
+
│ 100MB │ 7.0s │ 7.5s │ Either │
|
|
740
|
+
│ 500MB │ OOM risk │ 35s │ Chunked │
|
|
741
|
+
│ 1GB+ │ Not possible │ 70s+ │ Chunked │
|
|
742
|
+
└───────────┴──────────────┴───────────────┴─────────────┘
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
---
|
|
746
|
+
|
|
747
|
+
## Production Checklist
|
|
748
|
+
|
|
749
|
+
### Pre-Deployment
|
|
750
|
+
|
|
751
|
+
- [ ] **Security**
|
|
752
|
+
- [ ] SSH keys stored in secure secret manager
|
|
753
|
+
- [ ] AWS credentials use least-privilege IAM policies
|
|
754
|
+
- [ ] No hardcoded credentials in source code
|
|
755
|
+
- [ ] SFTP file permissions set to 0600 for sensitive data
|
|
756
|
+
|
|
757
|
+
- [ ] **Configuration**
|
|
758
|
+
- [ ] Environment variables documented
|
|
759
|
+
- [ ] Dual AWS credentials configured (source + target)
|
|
760
|
+
- [ ] Connection timeouts tuned for network conditions
|
|
761
|
+
- [ ] Batch sizes optimized for data volume
|
|
762
|
+
|
|
763
|
+
- [ ] **Monitoring**
|
|
764
|
+
- [ ] Structured logging enabled
|
|
765
|
+
- [ ] Health checks implemented
|
|
766
|
+
- [ ] Metrics collection configured
|
|
767
|
+
- [ ] Alerting set up for failures
|
|
768
|
+
|
|
769
|
+
- [ ] **Error Handling**
|
|
770
|
+
- [ ] Retry logic with exponential backoff
|
|
771
|
+
- [ ] Partial failure handling for batches
|
|
772
|
+
- [ ] Failed records logged for manual review
|
|
773
|
+
- [ ] Circuit breaker pattern for repeated failures
|
|
774
|
+
|
|
775
|
+
- [ ] **State Management**
|
|
776
|
+
- [ ] State service configured (prevent duplicates)
|
|
777
|
+
- [ ] State keys use file metadata (etag or lastModified)
|
|
778
|
+
- [ ] State cleanup strategy defined
|
|
779
|
+
|
|
780
|
+
- [ ] **Testing**
|
|
781
|
+
- [ ] Unit tests pass
|
|
782
|
+
- [ ] Integration tests with real services (or LocalStack)
|
|
783
|
+
- [ ] End-to-end test validates full pipeline
|
|
784
|
+
- [ ] Load testing completed for production volume
|
|
785
|
+
|
|
786
|
+
### Post-Deployment
|
|
787
|
+
|
|
788
|
+
- [ ] **Monitoring**
|
|
789
|
+
- [ ] Logs are being collected
|
|
790
|
+
- [ ] Metrics dashboards created
|
|
791
|
+
- [ ] Alerts are triggering correctly
|
|
792
|
+
|
|
793
|
+
- [ ] **Performance**
|
|
794
|
+
- [ ] Processing throughput meets SLA
|
|
795
|
+
- [ ] Memory usage is stable
|
|
796
|
+
- [ ] Connection pool size is optimal
|
|
797
|
+
|
|
798
|
+
- [ ] **Validation**
|
|
799
|
+
- [ ] Data quality checks passing
|
|
800
|
+
- [ ] No duplicate files processed
|
|
801
|
+
- [ ] Record counts match expected
|
|
802
|
+
|
|
803
|
+
---
|
|
804
|
+
|
|
805
|
+
## Next Steps
|
|
806
|
+
|
|
807
|
+
**Quick reference** → [Quick Reference](../../advanced-services/advanced-services-quick-reference.md)
|
|
808
|
+
|
|
809
|
+
**Complete examples** → [Examples Directory](../examples/)
|
|
810
|
+
|
|
811
|
+
---
|
|
812
|
+
|
|
813
|
+
[← Back to File Processing Patterns](./data-sources-04-file-patterns.md) | [Quick Reference →](../data-sources-quick-reference.md) | [↑ Back to Guide](../data-sources-readme.md)
|