airbyte-cdk 0.72.1__py3-none-any.whl → 6.13.1.dev4106__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (517) hide show
  1. airbyte_cdk/__init__.py +355 -6
  2. airbyte_cdk/cli/__init__.py +1 -0
  3. airbyte_cdk/cli/source_declarative_manifest/__init__.py +5 -0
  4. airbyte_cdk/cli/source_declarative_manifest/_run.py +230 -0
  5. airbyte_cdk/cli/source_declarative_manifest/spec.json +17 -0
  6. airbyte_cdk/config_observation.py +29 -10
  7. airbyte_cdk/connector.py +24 -24
  8. airbyte_cdk/connector_builder/README.md +53 -0
  9. airbyte_cdk/connector_builder/connector_builder_handler.py +37 -11
  10. airbyte_cdk/connector_builder/main.py +45 -13
  11. airbyte_cdk/connector_builder/message_grouper.py +189 -50
  12. airbyte_cdk/connector_builder/models.py +3 -2
  13. airbyte_cdk/destinations/__init__.py +4 -3
  14. airbyte_cdk/destinations/destination.py +54 -20
  15. airbyte_cdk/destinations/vector_db_based/README.md +37 -0
  16. airbyte_cdk/destinations/vector_db_based/config.py +40 -17
  17. airbyte_cdk/destinations/vector_db_based/document_processor.py +56 -17
  18. airbyte_cdk/destinations/vector_db_based/embedder.py +57 -15
  19. airbyte_cdk/destinations/vector_db_based/test_utils.py +14 -4
  20. airbyte_cdk/destinations/vector_db_based/utils.py +8 -2
  21. airbyte_cdk/destinations/vector_db_based/writer.py +24 -5
  22. airbyte_cdk/entrypoint.py +153 -44
  23. airbyte_cdk/exception_handler.py +21 -3
  24. airbyte_cdk/logger.py +30 -44
  25. airbyte_cdk/models/__init__.py +13 -2
  26. airbyte_cdk/models/airbyte_protocol.py +86 -1
  27. airbyte_cdk/models/airbyte_protocol_serializers.py +44 -0
  28. airbyte_cdk/models/file_transfer_record_message.py +13 -0
  29. airbyte_cdk/models/well_known_types.py +1 -1
  30. airbyte_cdk/sources/__init__.py +5 -1
  31. airbyte_cdk/sources/abstract_source.py +125 -79
  32. airbyte_cdk/sources/concurrent_source/__init__.py +7 -2
  33. airbyte_cdk/sources/concurrent_source/concurrent_read_processor.py +102 -36
  34. airbyte_cdk/sources/concurrent_source/concurrent_source.py +29 -36
  35. airbyte_cdk/sources/concurrent_source/concurrent_source_adapter.py +94 -10
  36. airbyte_cdk/sources/concurrent_source/stream_thread_exception.py +25 -0
  37. airbyte_cdk/sources/concurrent_source/thread_pool_manager.py +20 -14
  38. airbyte_cdk/sources/config.py +3 -2
  39. airbyte_cdk/sources/connector_state_manager.py +49 -83
  40. airbyte_cdk/sources/declarative/async_job/job.py +52 -0
  41. airbyte_cdk/sources/declarative/async_job/job_orchestrator.py +497 -0
  42. airbyte_cdk/sources/declarative/async_job/job_tracker.py +75 -0
  43. airbyte_cdk/sources/declarative/async_job/repository.py +35 -0
  44. airbyte_cdk/sources/declarative/async_job/status.py +24 -0
  45. airbyte_cdk/sources/declarative/async_job/timer.py +39 -0
  46. airbyte_cdk/sources/declarative/auth/__init__.py +2 -3
  47. airbyte_cdk/sources/declarative/auth/declarative_authenticator.py +3 -1
  48. airbyte_cdk/sources/declarative/auth/jwt.py +191 -0
  49. airbyte_cdk/sources/declarative/auth/oauth.py +60 -20
  50. airbyte_cdk/sources/declarative/auth/selective_authenticator.py +10 -2
  51. airbyte_cdk/sources/declarative/auth/token.py +28 -10
  52. airbyte_cdk/sources/declarative/auth/token_provider.py +9 -8
  53. airbyte_cdk/sources/declarative/checks/check_stream.py +16 -8
  54. airbyte_cdk/sources/declarative/checks/connection_checker.py +4 -2
  55. airbyte_cdk/sources/declarative/concurrency_level/__init__.py +7 -0
  56. airbyte_cdk/sources/declarative/concurrency_level/concurrency_level.py +50 -0
  57. airbyte_cdk/sources/declarative/concurrent_declarative_source.py +421 -0
  58. airbyte_cdk/sources/declarative/datetime/datetime_parser.py +4 -0
  59. airbyte_cdk/sources/declarative/datetime/min_max_datetime.py +26 -6
  60. airbyte_cdk/sources/declarative/declarative_component_schema.yaml +1185 -85
  61. airbyte_cdk/sources/declarative/declarative_source.py +5 -2
  62. airbyte_cdk/sources/declarative/declarative_stream.py +95 -9
  63. airbyte_cdk/sources/declarative/decoders/__init__.py +23 -2
  64. airbyte_cdk/sources/declarative/decoders/composite_raw_decoder.py +97 -0
  65. airbyte_cdk/sources/declarative/decoders/decoder.py +11 -4
  66. airbyte_cdk/sources/declarative/decoders/json_decoder.py +92 -5
  67. airbyte_cdk/sources/declarative/decoders/noop_decoder.py +21 -0
  68. airbyte_cdk/sources/declarative/decoders/pagination_decoder_decorator.py +39 -0
  69. airbyte_cdk/sources/declarative/decoders/xml_decoder.py +98 -0
  70. airbyte_cdk/sources/declarative/extractors/__init__.py +12 -1
  71. airbyte_cdk/sources/declarative/extractors/dpath_extractor.py +29 -24
  72. airbyte_cdk/sources/declarative/extractors/http_selector.py +4 -5
  73. airbyte_cdk/sources/declarative/extractors/record_extractor.py +2 -3
  74. airbyte_cdk/sources/declarative/extractors/record_filter.py +65 -8
  75. airbyte_cdk/sources/declarative/extractors/record_selector.py +85 -26
  76. airbyte_cdk/sources/declarative/extractors/response_to_file_extractor.py +177 -0
  77. airbyte_cdk/sources/declarative/extractors/type_transformer.py +55 -0
  78. airbyte_cdk/sources/declarative/incremental/__init__.py +25 -3
  79. airbyte_cdk/sources/declarative/incremental/datetime_based_cursor.py +156 -48
  80. airbyte_cdk/sources/declarative/incremental/declarative_cursor.py +13 -0
  81. airbyte_cdk/sources/declarative/incremental/global_substream_cursor.py +350 -0
  82. airbyte_cdk/sources/declarative/incremental/per_partition_cursor.py +159 -74
  83. airbyte_cdk/sources/declarative/incremental/per_partition_with_global.py +200 -0
  84. airbyte_cdk/sources/declarative/incremental/resumable_full_refresh_cursor.py +122 -0
  85. airbyte_cdk/sources/declarative/interpolation/filters.py +27 -1
  86. airbyte_cdk/sources/declarative/interpolation/interpolated_boolean.py +23 -5
  87. airbyte_cdk/sources/declarative/interpolation/interpolated_mapping.py +12 -8
  88. airbyte_cdk/sources/declarative/interpolation/interpolated_nested_mapping.py +13 -6
  89. airbyte_cdk/sources/declarative/interpolation/interpolated_string.py +21 -6
  90. airbyte_cdk/sources/declarative/interpolation/interpolation.py +9 -3
  91. airbyte_cdk/sources/declarative/interpolation/jinja.py +72 -37
  92. airbyte_cdk/sources/declarative/interpolation/macros.py +72 -17
  93. airbyte_cdk/sources/declarative/manifest_declarative_source.py +193 -52
  94. airbyte_cdk/sources/declarative/migrations/legacy_to_per_partition_state_migration.py +98 -0
  95. airbyte_cdk/sources/declarative/migrations/state_migration.py +24 -0
  96. airbyte_cdk/sources/declarative/models/__init__.py +1 -1
  97. airbyte_cdk/sources/declarative/models/declarative_component_schema.py +1319 -603
  98. airbyte_cdk/sources/declarative/parsers/custom_exceptions.py +2 -2
  99. airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py +26 -4
  100. airbyte_cdk/sources/declarative/parsers/manifest_reference_resolver.py +26 -15
  101. airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +1695 -225
  102. airbyte_cdk/sources/declarative/partition_routers/__init__.py +24 -4
  103. airbyte_cdk/sources/declarative/partition_routers/async_job_partition_router.py +65 -0
  104. airbyte_cdk/sources/declarative/partition_routers/cartesian_product_stream_slicer.py +176 -0
  105. airbyte_cdk/sources/declarative/partition_routers/list_partition_router.py +39 -9
  106. airbyte_cdk/sources/declarative/partition_routers/partition_router.py +62 -0
  107. airbyte_cdk/sources/declarative/partition_routers/single_partition_router.py +15 -3
  108. airbyte_cdk/sources/declarative/partition_routers/substream_partition_router.py +222 -39
  109. airbyte_cdk/sources/declarative/requesters/error_handlers/__init__.py +19 -5
  110. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/__init__.py +3 -1
  111. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py +19 -7
  112. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py +19 -7
  113. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/header_helper.py +4 -2
  114. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py +41 -9
  115. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py +29 -14
  116. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategy.py +5 -13
  117. airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +32 -16
  118. airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py +46 -56
  119. airbyte_cdk/sources/declarative/requesters/error_handlers/default_http_response_filter.py +40 -0
  120. airbyte_cdk/sources/declarative/requesters/error_handlers/error_handler.py +6 -32
  121. airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +119 -41
  122. airbyte_cdk/sources/declarative/requesters/http_job_repository.py +228 -0
  123. airbyte_cdk/sources/declarative/requesters/http_requester.py +98 -344
  124. airbyte_cdk/sources/declarative/requesters/paginators/__init__.py +14 -3
  125. airbyte_cdk/sources/declarative/requesters/paginators/default_paginator.py +105 -46
  126. airbyte_cdk/sources/declarative/requesters/paginators/no_pagination.py +14 -8
  127. airbyte_cdk/sources/declarative/requesters/paginators/paginator.py +19 -8
  128. airbyte_cdk/sources/declarative/requesters/paginators/strategies/__init__.py +9 -3
  129. airbyte_cdk/sources/declarative/requesters/paginators/strategies/cursor_pagination_strategy.py +53 -21
  130. airbyte_cdk/sources/declarative/requesters/paginators/strategies/offset_increment.py +42 -19
  131. airbyte_cdk/sources/declarative/requesters/paginators/strategies/page_increment.py +25 -12
  132. airbyte_cdk/sources/declarative/requesters/paginators/strategies/pagination_strategy.py +13 -10
  133. airbyte_cdk/sources/declarative/requesters/paginators/strategies/stop_condition.py +26 -13
  134. airbyte_cdk/sources/declarative/requesters/request_options/__init__.py +15 -2
  135. airbyte_cdk/sources/declarative/requesters/request_options/datetime_based_request_options_provider.py +91 -0
  136. airbyte_cdk/sources/declarative/requesters/request_options/default_request_options_provider.py +60 -0
  137. airbyte_cdk/sources/declarative/requesters/request_options/interpolated_nested_request_input_provider.py +31 -14
  138. airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_input_provider.py +27 -15
  139. airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_options_provider.py +63 -10
  140. airbyte_cdk/sources/declarative/requesters/request_options/request_options_provider.py +1 -1
  141. airbyte_cdk/sources/declarative/requesters/requester.py +9 -17
  142. airbyte_cdk/sources/declarative/resolvers/__init__.py +41 -0
  143. airbyte_cdk/sources/declarative/resolvers/components_resolver.py +55 -0
  144. airbyte_cdk/sources/declarative/resolvers/config_components_resolver.py +136 -0
  145. airbyte_cdk/sources/declarative/resolvers/http_components_resolver.py +112 -0
  146. airbyte_cdk/sources/declarative/retrievers/__init__.py +6 -2
  147. airbyte_cdk/sources/declarative/retrievers/async_retriever.py +100 -0
  148. airbyte_cdk/sources/declarative/retrievers/retriever.py +1 -3
  149. airbyte_cdk/sources/declarative/retrievers/simple_retriever.py +228 -72
  150. airbyte_cdk/sources/declarative/schema/__init__.py +14 -1
  151. airbyte_cdk/sources/declarative/schema/default_schema_loader.py +5 -3
  152. airbyte_cdk/sources/declarative/schema/dynamic_schema_loader.py +236 -0
  153. airbyte_cdk/sources/declarative/schema/json_file_schema_loader.py +8 -8
  154. airbyte_cdk/sources/declarative/spec/spec.py +12 -5
  155. airbyte_cdk/sources/declarative/stream_slicers/__init__.py +1 -2
  156. airbyte_cdk/sources/declarative/stream_slicers/declarative_partition_generator.py +88 -0
  157. airbyte_cdk/sources/declarative/stream_slicers/stream_slicer.py +9 -14
  158. airbyte_cdk/sources/declarative/transformations/add_fields.py +19 -11
  159. airbyte_cdk/sources/declarative/transformations/flatten_fields.py +52 -0
  160. airbyte_cdk/sources/declarative/transformations/keys_replace_transformation.py +61 -0
  161. airbyte_cdk/sources/declarative/transformations/keys_to_lower_transformation.py +22 -0
  162. airbyte_cdk/sources/declarative/transformations/keys_to_snake_transformation.py +68 -0
  163. airbyte_cdk/sources/declarative/transformations/remove_fields.py +13 -10
  164. airbyte_cdk/sources/declarative/transformations/transformation.py +5 -5
  165. airbyte_cdk/sources/declarative/types.py +19 -110
  166. airbyte_cdk/sources/declarative/yaml_declarative_source.py +31 -10
  167. airbyte_cdk/sources/embedded/base_integration.py +16 -5
  168. airbyte_cdk/sources/embedded/catalog.py +16 -4
  169. airbyte_cdk/sources/embedded/runner.py +19 -3
  170. airbyte_cdk/sources/embedded/tools.py +5 -2
  171. airbyte_cdk/sources/file_based/README.md +152 -0
  172. airbyte_cdk/sources/file_based/__init__.py +24 -0
  173. airbyte_cdk/sources/file_based/availability_strategy/__init__.py +9 -2
  174. airbyte_cdk/sources/file_based/availability_strategy/abstract_file_based_availability_strategy.py +22 -6
  175. airbyte_cdk/sources/file_based/availability_strategy/default_file_based_availability_strategy.py +46 -10
  176. airbyte_cdk/sources/file_based/config/abstract_file_based_spec.py +58 -10
  177. airbyte_cdk/sources/file_based/config/avro_format.py +2 -1
  178. airbyte_cdk/sources/file_based/config/csv_format.py +29 -10
  179. airbyte_cdk/sources/file_based/config/excel_format.py +18 -0
  180. airbyte_cdk/sources/file_based/config/file_based_stream_config.py +16 -4
  181. airbyte_cdk/sources/file_based/config/jsonl_format.py +2 -1
  182. airbyte_cdk/sources/file_based/config/parquet_format.py +2 -1
  183. airbyte_cdk/sources/file_based/config/unstructured_format.py +13 -5
  184. airbyte_cdk/sources/file_based/discovery_policy/__init__.py +6 -2
  185. airbyte_cdk/sources/file_based/discovery_policy/abstract_discovery_policy.py +2 -4
  186. airbyte_cdk/sources/file_based/discovery_policy/default_discovery_policy.py +7 -2
  187. airbyte_cdk/sources/file_based/exceptions.py +52 -15
  188. airbyte_cdk/sources/file_based/file_based_source.py +163 -33
  189. airbyte_cdk/sources/file_based/file_based_stream_reader.py +83 -5
  190. airbyte_cdk/sources/file_based/file_types/__init__.py +14 -1
  191. airbyte_cdk/sources/file_based/file_types/avro_parser.py +75 -24
  192. airbyte_cdk/sources/file_based/file_types/csv_parser.py +116 -34
  193. airbyte_cdk/sources/file_based/file_types/excel_parser.py +196 -0
  194. airbyte_cdk/sources/file_based/file_types/file_transfer.py +37 -0
  195. airbyte_cdk/sources/file_based/file_types/file_type_parser.py +4 -1
  196. airbyte_cdk/sources/file_based/file_types/jsonl_parser.py +24 -8
  197. airbyte_cdk/sources/file_based/file_types/parquet_parser.py +60 -18
  198. airbyte_cdk/sources/file_based/file_types/unstructured_parser.py +145 -41
  199. airbyte_cdk/sources/file_based/remote_file.py +1 -1
  200. airbyte_cdk/sources/file_based/schema_helpers.py +38 -10
  201. airbyte_cdk/sources/file_based/schema_validation_policies/__init__.py +3 -1
  202. airbyte_cdk/sources/file_based/schema_validation_policies/abstract_schema_validation_policy.py +3 -1
  203. airbyte_cdk/sources/file_based/schema_validation_policies/default_schema_validation_policies.py +16 -5
  204. airbyte_cdk/sources/file_based/stream/abstract_file_based_stream.py +50 -13
  205. airbyte_cdk/sources/file_based/stream/concurrent/adapters.py +67 -27
  206. airbyte_cdk/sources/file_based/stream/concurrent/cursor/__init__.py +5 -1
  207. airbyte_cdk/sources/file_based/stream/concurrent/cursor/abstract_concurrent_file_based_cursor.py +14 -23
  208. airbyte_cdk/sources/file_based/stream/concurrent/cursor/file_based_concurrent_cursor.py +54 -18
  209. airbyte_cdk/sources/file_based/stream/concurrent/cursor/file_based_final_state_cursor.py +21 -9
  210. airbyte_cdk/sources/file_based/stream/cursor/abstract_file_based_cursor.py +3 -1
  211. airbyte_cdk/sources/file_based/stream/cursor/default_file_based_cursor.py +27 -10
  212. airbyte_cdk/sources/file_based/stream/default_file_based_stream.py +175 -45
  213. airbyte_cdk/sources/http_logger.py +8 -3
  214. airbyte_cdk/sources/message/__init__.py +7 -1
  215. airbyte_cdk/sources/message/repository.py +18 -4
  216. airbyte_cdk/sources/source.py +42 -38
  217. airbyte_cdk/sources/streams/__init__.py +2 -2
  218. airbyte_cdk/sources/streams/availability_strategy.py +54 -3
  219. airbyte_cdk/sources/streams/call_rate.py +64 -21
  220. airbyte_cdk/sources/streams/checkpoint/__init__.py +26 -0
  221. airbyte_cdk/sources/streams/checkpoint/checkpoint_reader.py +335 -0
  222. airbyte_cdk/sources/{declarative/incremental → streams/checkpoint}/cursor.py +17 -14
  223. airbyte_cdk/sources/streams/checkpoint/per_partition_key_serializer.py +22 -0
  224. airbyte_cdk/sources/streams/checkpoint/resumable_full_refresh_cursor.py +51 -0
  225. airbyte_cdk/sources/streams/checkpoint/substream_resumable_full_refresh_cursor.py +110 -0
  226. airbyte_cdk/sources/streams/concurrent/README.md +7 -0
  227. airbyte_cdk/sources/streams/concurrent/abstract_stream.py +7 -2
  228. airbyte_cdk/sources/streams/concurrent/adapters.py +84 -75
  229. airbyte_cdk/sources/streams/concurrent/availability_strategy.py +30 -2
  230. airbyte_cdk/sources/streams/concurrent/cursor.py +298 -42
  231. airbyte_cdk/sources/streams/concurrent/default_stream.py +12 -3
  232. airbyte_cdk/sources/streams/concurrent/exceptions.py +3 -0
  233. airbyte_cdk/sources/streams/concurrent/helpers.py +14 -3
  234. airbyte_cdk/sources/streams/concurrent/partition_enqueuer.py +12 -3
  235. airbyte_cdk/sources/streams/concurrent/partition_reader.py +10 -3
  236. airbyte_cdk/sources/streams/concurrent/partitions/partition.py +1 -16
  237. airbyte_cdk/sources/streams/concurrent/partitions/stream_slicer.py +21 -0
  238. airbyte_cdk/sources/streams/concurrent/partitions/types.py +15 -5
  239. airbyte_cdk/sources/streams/concurrent/state_converters/abstract_stream_state_converter.py +109 -17
  240. airbyte_cdk/sources/streams/concurrent/state_converters/datetime_stream_state_converter.py +90 -72
  241. airbyte_cdk/sources/streams/core.py +412 -87
  242. airbyte_cdk/sources/streams/http/__init__.py +2 -1
  243. airbyte_cdk/sources/streams/http/availability_strategy.py +12 -101
  244. airbyte_cdk/sources/streams/http/error_handlers/__init__.py +22 -0
  245. airbyte_cdk/sources/streams/http/error_handlers/backoff_strategy.py +28 -0
  246. airbyte_cdk/sources/streams/http/error_handlers/default_backoff_strategy.py +17 -0
  247. airbyte_cdk/sources/streams/http/error_handlers/default_error_mapping.py +86 -0
  248. airbyte_cdk/sources/streams/http/error_handlers/error_handler.py +42 -0
  249. airbyte_cdk/sources/streams/http/error_handlers/error_message_parser.py +19 -0
  250. airbyte_cdk/sources/streams/http/error_handlers/http_status_error_handler.py +110 -0
  251. airbyte_cdk/sources/streams/http/error_handlers/json_error_message_parser.py +52 -0
  252. airbyte_cdk/sources/streams/http/error_handlers/response_models.py +65 -0
  253. airbyte_cdk/sources/streams/http/exceptions.py +27 -7
  254. airbyte_cdk/sources/streams/http/http.py +369 -246
  255. airbyte_cdk/sources/streams/http/http_client.py +531 -0
  256. airbyte_cdk/sources/streams/http/rate_limiting.py +76 -12
  257. airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py +28 -9
  258. airbyte_cdk/sources/streams/http/requests_native_auth/abstract_token.py +2 -1
  259. airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py +90 -35
  260. airbyte_cdk/sources/streams/http/requests_native_auth/token.py +13 -3
  261. airbyte_cdk/sources/types.py +154 -0
  262. airbyte_cdk/sources/utils/record_helper.py +36 -21
  263. airbyte_cdk/sources/utils/schema_helpers.py +13 -6
  264. airbyte_cdk/sources/utils/slice_logger.py +4 -1
  265. airbyte_cdk/sources/utils/transform.py +54 -20
  266. airbyte_cdk/sql/_util/hashing.py +34 -0
  267. airbyte_cdk/sql/_util/name_normalizers.py +92 -0
  268. airbyte_cdk/sql/constants.py +32 -0
  269. airbyte_cdk/sql/exceptions.py +235 -0
  270. airbyte_cdk/sql/secrets.py +123 -0
  271. airbyte_cdk/sql/shared/__init__.py +15 -0
  272. airbyte_cdk/sql/shared/catalog_providers.py +145 -0
  273. airbyte_cdk/sql/shared/sql_processor.py +786 -0
  274. airbyte_cdk/sql/types.py +160 -0
  275. airbyte_cdk/test/catalog_builder.py +70 -18
  276. airbyte_cdk/test/entrypoint_wrapper.py +117 -42
  277. airbyte_cdk/test/mock_http/__init__.py +1 -1
  278. airbyte_cdk/test/mock_http/matcher.py +6 -0
  279. airbyte_cdk/test/mock_http/mocker.py +57 -10
  280. airbyte_cdk/test/mock_http/request.py +19 -3
  281. airbyte_cdk/test/mock_http/response.py +3 -1
  282. airbyte_cdk/test/mock_http/response_builder.py +32 -16
  283. airbyte_cdk/test/state_builder.py +18 -10
  284. airbyte_cdk/test/utils/__init__.py +1 -0
  285. airbyte_cdk/test/utils/data.py +24 -0
  286. airbyte_cdk/test/utils/http_mocking.py +16 -0
  287. airbyte_cdk/test/utils/manifest_only_fixtures.py +60 -0
  288. airbyte_cdk/test/utils/reading.py +26 -0
  289. airbyte_cdk/utils/__init__.py +2 -1
  290. airbyte_cdk/utils/airbyte_secrets_utils.py +5 -3
  291. airbyte_cdk/utils/analytics_message.py +10 -2
  292. airbyte_cdk/utils/datetime_format_inferrer.py +4 -1
  293. airbyte_cdk/utils/event_timing.py +10 -10
  294. airbyte_cdk/utils/mapping_helpers.py +3 -1
  295. airbyte_cdk/utils/message_utils.py +20 -11
  296. airbyte_cdk/utils/print_buffer.py +75 -0
  297. airbyte_cdk/utils/schema_inferrer.py +198 -28
  298. airbyte_cdk/utils/slice_hasher.py +30 -0
  299. airbyte_cdk/utils/spec_schema_transformations.py +6 -3
  300. airbyte_cdk/utils/stream_status_utils.py +8 -1
  301. airbyte_cdk/utils/traced_exception.py +61 -21
  302. airbyte_cdk-6.13.1.dev4106.dist-info/METADATA +109 -0
  303. airbyte_cdk-6.13.1.dev4106.dist-info/RECORD +349 -0
  304. {airbyte_cdk-0.72.1.dist-info → airbyte_cdk-6.13.1.dev4106.dist-info}/WHEEL +1 -2
  305. airbyte_cdk-6.13.1.dev4106.dist-info/entry_points.txt +3 -0
  306. airbyte_cdk/sources/declarative/create_partial.py +0 -92
  307. airbyte_cdk/sources/declarative/parsers/class_types_registry.py +0 -102
  308. airbyte_cdk/sources/declarative/parsers/default_implementation_registry.py +0 -64
  309. airbyte_cdk/sources/declarative/requesters/error_handlers/response_action.py +0 -16
  310. airbyte_cdk/sources/declarative/requesters/error_handlers/response_status.py +0 -68
  311. airbyte_cdk/sources/declarative/stream_slicers/cartesian_product_stream_slicer.py +0 -114
  312. airbyte_cdk/sources/deprecated/base_source.py +0 -94
  313. airbyte_cdk/sources/deprecated/client.py +0 -99
  314. airbyte_cdk/sources/singer/__init__.py +0 -8
  315. airbyte_cdk/sources/singer/singer_helpers.py +0 -304
  316. airbyte_cdk/sources/singer/source.py +0 -186
  317. airbyte_cdk/sources/streams/concurrent/partitions/record.py +0 -23
  318. airbyte_cdk/sources/streams/http/auth/__init__.py +0 -17
  319. airbyte_cdk/sources/streams/http/auth/core.py +0 -29
  320. airbyte_cdk/sources/streams/http/auth/oauth.py +0 -113
  321. airbyte_cdk/sources/streams/http/auth/token.py +0 -47
  322. airbyte_cdk/sources/streams/utils/stream_helper.py +0 -40
  323. airbyte_cdk/sources/utils/catalog_helpers.py +0 -22
  324. airbyte_cdk/sources/utils/schema_models.py +0 -84
  325. airbyte_cdk-0.72.1.dist-info/METADATA +0 -243
  326. airbyte_cdk-0.72.1.dist-info/RECORD +0 -466
  327. airbyte_cdk-0.72.1.dist-info/top_level.txt +0 -3
  328. source_declarative_manifest/main.py +0 -29
  329. unit_tests/connector_builder/__init__.py +0 -3
  330. unit_tests/connector_builder/test_connector_builder_handler.py +0 -871
  331. unit_tests/connector_builder/test_message_grouper.py +0 -713
  332. unit_tests/connector_builder/utils.py +0 -27
  333. unit_tests/destinations/test_destination.py +0 -243
  334. unit_tests/singer/test_singer_helpers.py +0 -56
  335. unit_tests/singer/test_singer_source.py +0 -112
  336. unit_tests/sources/__init__.py +0 -0
  337. unit_tests/sources/concurrent_source/__init__.py +0 -3
  338. unit_tests/sources/concurrent_source/test_concurrent_source_adapter.py +0 -106
  339. unit_tests/sources/declarative/__init__.py +0 -3
  340. unit_tests/sources/declarative/auth/__init__.py +0 -3
  341. unit_tests/sources/declarative/auth/test_oauth.py +0 -331
  342. unit_tests/sources/declarative/auth/test_selective_authenticator.py +0 -39
  343. unit_tests/sources/declarative/auth/test_session_token_auth.py +0 -182
  344. unit_tests/sources/declarative/auth/test_token_auth.py +0 -200
  345. unit_tests/sources/declarative/auth/test_token_provider.py +0 -73
  346. unit_tests/sources/declarative/checks/__init__.py +0 -3
  347. unit_tests/sources/declarative/checks/test_check_stream.py +0 -146
  348. unit_tests/sources/declarative/decoders/__init__.py +0 -0
  349. unit_tests/sources/declarative/decoders/test_json_decoder.py +0 -16
  350. unit_tests/sources/declarative/external_component.py +0 -13
  351. unit_tests/sources/declarative/extractors/__init__.py +0 -3
  352. unit_tests/sources/declarative/extractors/test_dpath_extractor.py +0 -55
  353. unit_tests/sources/declarative/extractors/test_record_filter.py +0 -55
  354. unit_tests/sources/declarative/extractors/test_record_selector.py +0 -179
  355. unit_tests/sources/declarative/incremental/__init__.py +0 -0
  356. unit_tests/sources/declarative/incremental/test_datetime_based_cursor.py +0 -860
  357. unit_tests/sources/declarative/incremental/test_per_partition_cursor.py +0 -406
  358. unit_tests/sources/declarative/incremental/test_per_partition_cursor_integration.py +0 -332
  359. unit_tests/sources/declarative/interpolation/__init__.py +0 -3
  360. unit_tests/sources/declarative/interpolation/test_filters.py +0 -80
  361. unit_tests/sources/declarative/interpolation/test_interpolated_boolean.py +0 -40
  362. unit_tests/sources/declarative/interpolation/test_interpolated_mapping.py +0 -35
  363. unit_tests/sources/declarative/interpolation/test_interpolated_nested_mapping.py +0 -45
  364. unit_tests/sources/declarative/interpolation/test_interpolated_string.py +0 -25
  365. unit_tests/sources/declarative/interpolation/test_jinja.py +0 -240
  366. unit_tests/sources/declarative/interpolation/test_macros.py +0 -73
  367. unit_tests/sources/declarative/parsers/__init__.py +0 -3
  368. unit_tests/sources/declarative/parsers/test_manifest_component_transformer.py +0 -406
  369. unit_tests/sources/declarative/parsers/test_manifest_reference_resolver.py +0 -139
  370. unit_tests/sources/declarative/parsers/test_model_to_component_factory.py +0 -1847
  371. unit_tests/sources/declarative/parsers/testing_components.py +0 -36
  372. unit_tests/sources/declarative/partition_routers/__init__.py +0 -3
  373. unit_tests/sources/declarative/partition_routers/test_list_partition_router.py +0 -155
  374. unit_tests/sources/declarative/partition_routers/test_single_partition_router.py +0 -14
  375. unit_tests/sources/declarative/partition_routers/test_substream_partition_router.py +0 -404
  376. unit_tests/sources/declarative/requesters/__init__.py +0 -3
  377. unit_tests/sources/declarative/requesters/error_handlers/__init__.py +0 -3
  378. unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/__init__.py +0 -3
  379. unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_constant_backoff.py +0 -34
  380. unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_exponential_backoff.py +0 -36
  381. unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_header_helper.py +0 -38
  382. unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_time_from_header.py +0 -35
  383. unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_until_time_from_header.py +0 -64
  384. unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py +0 -213
  385. unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py +0 -178
  386. unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py +0 -121
  387. unit_tests/sources/declarative/requesters/error_handlers/test_response_status.py +0 -44
  388. unit_tests/sources/declarative/requesters/paginators/__init__.py +0 -3
  389. unit_tests/sources/declarative/requesters/paginators/test_cursor_pagination_strategy.py +0 -64
  390. unit_tests/sources/declarative/requesters/paginators/test_default_paginator.py +0 -313
  391. unit_tests/sources/declarative/requesters/paginators/test_no_paginator.py +0 -12
  392. unit_tests/sources/declarative/requesters/paginators/test_offset_increment.py +0 -58
  393. unit_tests/sources/declarative/requesters/paginators/test_page_increment.py +0 -70
  394. unit_tests/sources/declarative/requesters/paginators/test_request_option.py +0 -43
  395. unit_tests/sources/declarative/requesters/paginators/test_stop_condition.py +0 -105
  396. unit_tests/sources/declarative/requesters/request_options/__init__.py +0 -3
  397. unit_tests/sources/declarative/requesters/request_options/test_interpolated_request_options_provider.py +0 -101
  398. unit_tests/sources/declarative/requesters/test_http_requester.py +0 -974
  399. unit_tests/sources/declarative/requesters/test_interpolated_request_input_provider.py +0 -32
  400. unit_tests/sources/declarative/retrievers/__init__.py +0 -3
  401. unit_tests/sources/declarative/retrievers/test_simple_retriever.py +0 -542
  402. unit_tests/sources/declarative/schema/__init__.py +0 -6
  403. unit_tests/sources/declarative/schema/source_test/SourceTest.py +0 -8
  404. unit_tests/sources/declarative/schema/source_test/__init__.py +0 -3
  405. unit_tests/sources/declarative/schema/test_default_schema_loader.py +0 -32
  406. unit_tests/sources/declarative/schema/test_inline_schema_loader.py +0 -19
  407. unit_tests/sources/declarative/schema/test_json_file_schema_loader.py +0 -26
  408. unit_tests/sources/declarative/states/__init__.py +0 -3
  409. unit_tests/sources/declarative/stream_slicers/__init__.py +0 -3
  410. unit_tests/sources/declarative/stream_slicers/test_cartesian_product_stream_slicer.py +0 -225
  411. unit_tests/sources/declarative/test_create_partial.py +0 -83
  412. unit_tests/sources/declarative/test_declarative_stream.py +0 -103
  413. unit_tests/sources/declarative/test_manifest_declarative_source.py +0 -1260
  414. unit_tests/sources/declarative/test_types.py +0 -39
  415. unit_tests/sources/declarative/test_yaml_declarative_source.py +0 -148
  416. unit_tests/sources/file_based/__init__.py +0 -0
  417. unit_tests/sources/file_based/availability_strategy/__init__.py +0 -0
  418. unit_tests/sources/file_based/availability_strategy/test_default_file_based_availability_strategy.py +0 -100
  419. unit_tests/sources/file_based/config/__init__.py +0 -0
  420. unit_tests/sources/file_based/config/test_abstract_file_based_spec.py +0 -28
  421. unit_tests/sources/file_based/config/test_csv_format.py +0 -34
  422. unit_tests/sources/file_based/config/test_file_based_stream_config.py +0 -84
  423. unit_tests/sources/file_based/discovery_policy/__init__.py +0 -0
  424. unit_tests/sources/file_based/discovery_policy/test_default_discovery_policy.py +0 -31
  425. unit_tests/sources/file_based/file_types/__init__.py +0 -0
  426. unit_tests/sources/file_based/file_types/test_avro_parser.py +0 -243
  427. unit_tests/sources/file_based/file_types/test_csv_parser.py +0 -546
  428. unit_tests/sources/file_based/file_types/test_jsonl_parser.py +0 -158
  429. unit_tests/sources/file_based/file_types/test_parquet_parser.py +0 -274
  430. unit_tests/sources/file_based/file_types/test_unstructured_parser.py +0 -593
  431. unit_tests/sources/file_based/helpers.py +0 -70
  432. unit_tests/sources/file_based/in_memory_files_source.py +0 -211
  433. unit_tests/sources/file_based/scenarios/__init__.py +0 -0
  434. unit_tests/sources/file_based/scenarios/avro_scenarios.py +0 -744
  435. unit_tests/sources/file_based/scenarios/check_scenarios.py +0 -220
  436. unit_tests/sources/file_based/scenarios/concurrent_incremental_scenarios.py +0 -2844
  437. unit_tests/sources/file_based/scenarios/csv_scenarios.py +0 -3105
  438. unit_tests/sources/file_based/scenarios/file_based_source_builder.py +0 -91
  439. unit_tests/sources/file_based/scenarios/incremental_scenarios.py +0 -1926
  440. unit_tests/sources/file_based/scenarios/jsonl_scenarios.py +0 -930
  441. unit_tests/sources/file_based/scenarios/parquet_scenarios.py +0 -754
  442. unit_tests/sources/file_based/scenarios/scenario_builder.py +0 -234
  443. unit_tests/sources/file_based/scenarios/unstructured_scenarios.py +0 -608
  444. unit_tests/sources/file_based/scenarios/user_input_schema_scenarios.py +0 -746
  445. unit_tests/sources/file_based/scenarios/validation_policy_scenarios.py +0 -726
  446. unit_tests/sources/file_based/stream/__init__.py +0 -0
  447. unit_tests/sources/file_based/stream/concurrent/__init__.py +0 -0
  448. unit_tests/sources/file_based/stream/concurrent/test_adapters.py +0 -362
  449. unit_tests/sources/file_based/stream/concurrent/test_file_based_concurrent_cursor.py +0 -458
  450. unit_tests/sources/file_based/stream/test_default_file_based_cursor.py +0 -310
  451. unit_tests/sources/file_based/stream/test_default_file_based_stream.py +0 -244
  452. unit_tests/sources/file_based/test_file_based_scenarios.py +0 -320
  453. unit_tests/sources/file_based/test_file_based_stream_reader.py +0 -272
  454. unit_tests/sources/file_based/test_scenarios.py +0 -253
  455. unit_tests/sources/file_based/test_schema_helpers.py +0 -346
  456. unit_tests/sources/fixtures/__init__.py +0 -3
  457. unit_tests/sources/fixtures/source_test_fixture.py +0 -153
  458. unit_tests/sources/message/__init__.py +0 -0
  459. unit_tests/sources/message/test_repository.py +0 -153
  460. unit_tests/sources/streams/__init__.py +0 -0
  461. unit_tests/sources/streams/concurrent/__init__.py +0 -3
  462. unit_tests/sources/streams/concurrent/scenarios/__init__.py +0 -3
  463. unit_tests/sources/streams/concurrent/scenarios/incremental_scenarios.py +0 -250
  464. unit_tests/sources/streams/concurrent/scenarios/stream_facade_builder.py +0 -140
  465. unit_tests/sources/streams/concurrent/scenarios/stream_facade_scenarios.py +0 -452
  466. unit_tests/sources/streams/concurrent/scenarios/test_concurrent_scenarios.py +0 -76
  467. unit_tests/sources/streams/concurrent/scenarios/thread_based_concurrent_stream_scenarios.py +0 -418
  468. unit_tests/sources/streams/concurrent/scenarios/thread_based_concurrent_stream_source_builder.py +0 -142
  469. unit_tests/sources/streams/concurrent/scenarios/utils.py +0 -55
  470. unit_tests/sources/streams/concurrent/test_adapters.py +0 -380
  471. unit_tests/sources/streams/concurrent/test_concurrent_read_processor.py +0 -684
  472. unit_tests/sources/streams/concurrent/test_cursor.py +0 -139
  473. unit_tests/sources/streams/concurrent/test_datetime_state_converter.py +0 -369
  474. unit_tests/sources/streams/concurrent/test_default_stream.py +0 -197
  475. unit_tests/sources/streams/concurrent/test_partition_enqueuer.py +0 -90
  476. unit_tests/sources/streams/concurrent/test_partition_reader.py +0 -67
  477. unit_tests/sources/streams/concurrent/test_thread_pool_manager.py +0 -106
  478. unit_tests/sources/streams/http/__init__.py +0 -0
  479. unit_tests/sources/streams/http/auth/__init__.py +0 -0
  480. unit_tests/sources/streams/http/auth/test_auth.py +0 -173
  481. unit_tests/sources/streams/http/requests_native_auth/__init__.py +0 -0
  482. unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py +0 -423
  483. unit_tests/sources/streams/http/test_availability_strategy.py +0 -180
  484. unit_tests/sources/streams/http/test_http.py +0 -635
  485. unit_tests/sources/streams/test_availability_strategy.py +0 -70
  486. unit_tests/sources/streams/test_call_rate.py +0 -300
  487. unit_tests/sources/streams/test_stream_read.py +0 -405
  488. unit_tests/sources/streams/test_streams_core.py +0 -184
  489. unit_tests/sources/test_abstract_source.py +0 -1442
  490. unit_tests/sources/test_concurrent_source.py +0 -112
  491. unit_tests/sources/test_config.py +0 -92
  492. unit_tests/sources/test_connector_state_manager.py +0 -482
  493. unit_tests/sources/test_http_logger.py +0 -252
  494. unit_tests/sources/test_integration_source.py +0 -86
  495. unit_tests/sources/test_source.py +0 -684
  496. unit_tests/sources/test_source_read.py +0 -460
  497. unit_tests/test/__init__.py +0 -0
  498. unit_tests/test/mock_http/__init__.py +0 -0
  499. unit_tests/test/mock_http/test_matcher.py +0 -53
  500. unit_tests/test/mock_http/test_mocker.py +0 -214
  501. unit_tests/test/mock_http/test_request.py +0 -117
  502. unit_tests/test/mock_http/test_response_builder.py +0 -177
  503. unit_tests/test/test_entrypoint_wrapper.py +0 -240
  504. unit_tests/utils/__init__.py +0 -0
  505. unit_tests/utils/test_datetime_format_inferrer.py +0 -60
  506. unit_tests/utils/test_mapping_helpers.py +0 -54
  507. unit_tests/utils/test_message_utils.py +0 -91
  508. unit_tests/utils/test_rate_limiting.py +0 -26
  509. unit_tests/utils/test_schema_inferrer.py +0 -202
  510. unit_tests/utils/test_secret_utils.py +0 -135
  511. unit_tests/utils/test_stream_status_utils.py +0 -61
  512. unit_tests/utils/test_traced_exception.py +0 -107
  513. /airbyte_cdk/sources/{deprecated → declarative/async_job}/__init__.py +0 -0
  514. {source_declarative_manifest → airbyte_cdk/sources/declarative/migrations}/__init__.py +0 -0
  515. {unit_tests/destinations → airbyte_cdk/sql}/__init__.py +0 -0
  516. {unit_tests/singer → airbyte_cdk/sql/_util}/__init__.py +0 -0
  517. {airbyte_cdk-0.72.1.dist-info → airbyte_cdk-6.13.1.dev4106.dist-info}/LICENSE.txt +0 -0
@@ -2,18 +2,27 @@
2
2
  # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
3
  #
4
4
 
5
- from dataclasses import InitVar, dataclass
6
- from typing import Any, List, Mapping, MutableMapping, Optional, Union
5
+ from dataclasses import InitVar, dataclass, field
6
+ from typing import Any, Mapping, MutableMapping, Optional, Union
7
7
 
8
8
  import requests
9
- from airbyte_cdk.sources.declarative.decoders.decoder import Decoder
10
- from airbyte_cdk.sources.declarative.decoders.json_decoder import JsonDecoder
9
+
10
+ from airbyte_cdk.sources.declarative.decoders import (
11
+ Decoder,
12
+ JsonDecoder,
13
+ PaginationDecoderDecorator,
14
+ )
11
15
  from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
12
16
  from airbyte_cdk.sources.declarative.requesters.paginators.paginator import Paginator
13
- from airbyte_cdk.sources.declarative.requesters.paginators.strategies.pagination_strategy import PaginationStrategy
14
- from airbyte_cdk.sources.declarative.requesters.request_option import RequestOption, RequestOptionType
17
+ from airbyte_cdk.sources.declarative.requesters.paginators.strategies.pagination_strategy import (
18
+ PaginationStrategy,
19
+ )
20
+ from airbyte_cdk.sources.declarative.requesters.request_option import (
21
+ RequestOption,
22
+ RequestOptionType,
23
+ )
15
24
  from airbyte_cdk.sources.declarative.requesters.request_path import RequestPath
16
- from airbyte_cdk.sources.declarative.types import Config, Record, StreamSlice, StreamState
25
+ from airbyte_cdk.sources.types import Config, Record, StreamSlice, StreamState
17
26
 
18
27
 
19
28
  @dataclass
@@ -90,28 +99,52 @@ class DefaultPaginator(Paginator):
90
99
  config: Config
91
100
  url_base: Union[InterpolatedString, str]
92
101
  parameters: InitVar[Mapping[str, Any]]
93
- decoder: Decoder = JsonDecoder(parameters={})
102
+ decoder: Decoder = field(
103
+ default_factory=lambda: PaginationDecoderDecorator(decoder=JsonDecoder(parameters={}))
104
+ )
94
105
  page_size_option: Optional[RequestOption] = None
95
106
  page_token_option: Optional[Union[RequestPath, RequestOption]] = None
96
107
 
97
108
  def __post_init__(self, parameters: Mapping[str, Any]) -> None:
98
109
  if self.page_size_option and not self.pagination_strategy.get_page_size():
99
- raise ValueError("page_size_option cannot be set if the pagination strategy does not have a page_size")
110
+ raise ValueError(
111
+ "page_size_option cannot be set if the pagination strategy does not have a page_size"
112
+ )
100
113
  if isinstance(self.url_base, str):
101
114
  self.url_base = InterpolatedString(string=self.url_base, parameters=parameters)
102
- self._token = self.pagination_strategy.initial_token
103
115
 
104
- def next_page_token(self, response: requests.Response, last_records: List[Record]) -> Optional[Mapping[str, Any]]:
105
- self._token = self.pagination_strategy.next_page_token(response, last_records)
106
- if self._token:
107
- return {"next_page_token": self._token}
116
+ def get_initial_token(self) -> Optional[Any]:
117
+ """
118
+ Return the page token that should be used for the first request of a stream
119
+
120
+ WARNING: get_initial_token() should not be used by streams that use RFR that perform checkpointing
121
+ of state using page numbers. Because paginators are stateless
122
+ """
123
+ return self.pagination_strategy.initial_token
124
+
125
+ def next_page_token(
126
+ self,
127
+ response: requests.Response,
128
+ last_page_size: int,
129
+ last_record: Optional[Record],
130
+ last_page_token_value: Optional[Any] = None,
131
+ ) -> Optional[Mapping[str, Any]]:
132
+ next_page_token = self.pagination_strategy.next_page_token(
133
+ response=response,
134
+ last_page_size=last_page_size,
135
+ last_record=last_record,
136
+ last_page_token_value=last_page_token_value,
137
+ )
138
+ if next_page_token:
139
+ return {"next_page_token": next_page_token}
108
140
  else:
109
141
  return None
110
142
 
111
- def path(self) -> Optional[str]:
112
- if self._token and self.page_token_option and isinstance(self.page_token_option, RequestPath):
143
+ def path(self, next_page_token: Optional[Mapping[str, Any]]) -> Optional[str]:
144
+ token = next_page_token.get("next_page_token") if next_page_token else None
145
+ if token and self.page_token_option and isinstance(self.page_token_option, RequestPath):
113
146
  # Replace url base to only return the path
114
- return str(self._token).replace(self.url_base.eval(self.config), "") # type: ignore # url_base is casted to a InterpolatedString in __post_init__
147
+ return str(token).replace(self.url_base.eval(self.config), "") # type: ignore # url_base is casted to a InterpolatedString in __post_init__
115
148
  else:
116
149
  return None
117
150
 
@@ -122,7 +155,7 @@ class DefaultPaginator(Paginator):
122
155
  stream_slice: Optional[StreamSlice] = None,
123
156
  next_page_token: Optional[Mapping[str, Any]] = None,
124
157
  ) -> MutableMapping[str, Any]:
125
- return self._get_request_options(RequestOptionType.request_parameter)
158
+ return self._get_request_options(RequestOptionType.request_parameter, next_page_token)
126
159
 
127
160
  def get_request_headers(
128
161
  self,
@@ -131,7 +164,7 @@ class DefaultPaginator(Paginator):
131
164
  stream_slice: Optional[StreamSlice] = None,
132
165
  next_page_token: Optional[Mapping[str, Any]] = None,
133
166
  ) -> Mapping[str, str]:
134
- return self._get_request_options(RequestOptionType.header)
167
+ return self._get_request_options(RequestOptionType.header, next_page_token)
135
168
 
136
169
  def get_request_body_data(
137
170
  self,
@@ -140,7 +173,7 @@ class DefaultPaginator(Paginator):
140
173
  stream_slice: Optional[StreamSlice] = None,
141
174
  next_page_token: Optional[Mapping[str, Any]] = None,
142
175
  ) -> Mapping[str, Any]:
143
- return self._get_request_options(RequestOptionType.body_data)
176
+ return self._get_request_options(RequestOptionType.body_data, next_page_token)
144
177
 
145
178
  def get_request_body_json(
146
179
  self,
@@ -149,24 +182,29 @@ class DefaultPaginator(Paginator):
149
182
  stream_slice: Optional[StreamSlice] = None,
150
183
  next_page_token: Optional[Mapping[str, Any]] = None,
151
184
  ) -> Mapping[str, Any]:
152
- return self._get_request_options(RequestOptionType.body_json)
185
+ return self._get_request_options(RequestOptionType.body_json, next_page_token)
153
186
 
154
- def reset(self) -> None:
155
- self.pagination_strategy.reset()
156
- self._token = self.pagination_strategy.initial_token
157
-
158
- def _get_request_options(self, option_type: RequestOptionType) -> MutableMapping[str, Any]:
187
+ def _get_request_options(
188
+ self, option_type: RequestOptionType, next_page_token: Optional[Mapping[str, Any]]
189
+ ) -> MutableMapping[str, Any]:
159
190
  options = {}
160
191
 
192
+ token = next_page_token.get("next_page_token") if next_page_token else None
161
193
  if (
162
194
  self.page_token_option
163
- and self._token is not None
195
+ and token is not None
164
196
  and isinstance(self.page_token_option, RequestOption)
165
197
  and self.page_token_option.inject_into == option_type
166
198
  ):
167
- options[self.page_token_option.field_name.eval(config=self.config)] = self._token
168
- if self.page_size_option and self.pagination_strategy.get_page_size() and self.page_size_option.inject_into == option_type:
169
- options[self.page_size_option.field_name.eval(config=self.config)] = self.pagination_strategy.get_page_size()
199
+ options[self.page_token_option.field_name.eval(config=self.config)] = token # type: ignore # field_name is always cast to an interpolated string
200
+ if (
201
+ self.page_size_option
202
+ and self.pagination_strategy.get_page_size()
203
+ and self.page_size_option.inject_into == option_type
204
+ ):
205
+ options[self.page_size_option.field_name.eval(config=self.config)] = ( # type: ignore [union-attr]
206
+ self.pagination_strategy.get_page_size()
207
+ ) # type: ignore # field_name is always cast to an interpolated string
170
208
  return options
171
209
 
172
210
 
@@ -174,26 +212,43 @@ class PaginatorTestReadDecorator(Paginator):
174
212
  """
175
213
  In some cases, we want to limit the number of requests that are made to the backend source. This class allows for limiting the number of
176
214
  pages that are queried throughout a read command.
215
+
216
+ WARNING: This decorator is not currently thread-safe like the rest of the low-code framework because it has
217
+ an internal state to track the current number of pages counted so that it can exit early during a test read
177
218
  """
178
219
 
179
220
  _PAGE_COUNT_BEFORE_FIRST_NEXT_CALL = 1
180
221
 
181
222
  def __init__(self, decorated: Paginator, maximum_number_of_pages: int = 5) -> None:
182
223
  if maximum_number_of_pages and maximum_number_of_pages < 1:
183
- raise ValueError(f"The maximum number of pages on a test read needs to be strictly positive. Got {maximum_number_of_pages}")
224
+ raise ValueError(
225
+ f"The maximum number of pages on a test read needs to be strictly positive. Got {maximum_number_of_pages}"
226
+ )
184
227
  self._maximum_number_of_pages = maximum_number_of_pages
185
228
  self._decorated = decorated
186
229
  self._page_count = self._PAGE_COUNT_BEFORE_FIRST_NEXT_CALL
187
230
 
188
- def next_page_token(self, response: requests.Response, last_records: List[Record]) -> Optional[Mapping[str, Any]]:
231
+ def get_initial_token(self) -> Optional[Any]:
232
+ self._page_count = self._PAGE_COUNT_BEFORE_FIRST_NEXT_CALL
233
+ return self._decorated.get_initial_token()
234
+
235
+ def next_page_token(
236
+ self,
237
+ response: requests.Response,
238
+ last_page_size: int,
239
+ last_record: Optional[Record],
240
+ last_page_token_value: Optional[Any] = None,
241
+ ) -> Optional[Mapping[str, Any]]:
189
242
  if self._page_count >= self._maximum_number_of_pages:
190
243
  return None
191
244
 
192
245
  self._page_count += 1
193
- return self._decorated.next_page_token(response, last_records)
246
+ return self._decorated.next_page_token(
247
+ response, last_page_size, last_record, last_page_token_value
248
+ )
194
249
 
195
- def path(self) -> Optional[str]:
196
- return self._decorated.path()
250
+ def path(self, next_page_token: Optional[Mapping[str, Any]]) -> Optional[str]:
251
+ return self._decorated.path(next_page_token)
197
252
 
198
253
  def get_request_params(
199
254
  self,
@@ -201,8 +256,10 @@ class PaginatorTestReadDecorator(Paginator):
201
256
  stream_state: Optional[StreamState] = None,
202
257
  stream_slice: Optional[StreamSlice] = None,
203
258
  next_page_token: Optional[Mapping[str, Any]] = None,
204
- ) -> MutableMapping[str, Any]:
205
- return self._decorated.get_request_params(stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token)
259
+ ) -> Mapping[str, Any]:
260
+ return self._decorated.get_request_params(
261
+ stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token
262
+ )
206
263
 
207
264
  def get_request_headers(
208
265
  self,
@@ -211,7 +268,9 @@ class PaginatorTestReadDecorator(Paginator):
211
268
  stream_slice: Optional[StreamSlice] = None,
212
269
  next_page_token: Optional[Mapping[str, Any]] = None,
213
270
  ) -> Mapping[str, str]:
214
- return self._decorated.get_request_headers(stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token)
271
+ return self._decorated.get_request_headers(
272
+ stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token
273
+ )
215
274
 
216
275
  def get_request_body_data(
217
276
  self,
@@ -219,8 +278,10 @@ class PaginatorTestReadDecorator(Paginator):
219
278
  stream_state: Optional[StreamState] = None,
220
279
  stream_slice: Optional[StreamSlice] = None,
221
280
  next_page_token: Optional[Mapping[str, Any]] = None,
222
- ) -> Optional[Union[Mapping[str, Any], str]]:
223
- return self._decorated.get_request_body_data(stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token)
281
+ ) -> Union[Mapping[str, Any], str]:
282
+ return self._decorated.get_request_body_data(
283
+ stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token
284
+ )
224
285
 
225
286
  def get_request_body_json(
226
287
  self,
@@ -228,9 +289,7 @@ class PaginatorTestReadDecorator(Paginator):
228
289
  stream_state: Optional[StreamState] = None,
229
290
  stream_slice: Optional[StreamSlice] = None,
230
291
  next_page_token: Optional[Mapping[str, Any]] = None,
231
- ) -> Optional[Mapping[str, Any]]:
232
- return self._decorated.get_request_body_json(stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token)
233
-
234
- def reset(self) -> None:
235
- self._decorated.reset()
236
- self._page_count = self._PAGE_COUNT_BEFORE_FIRST_NEXT_CALL
292
+ ) -> Mapping[str, Any]:
293
+ return self._decorated.get_request_body_json(
294
+ stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token
295
+ )
@@ -3,11 +3,12 @@
3
3
  #
4
4
 
5
5
  from dataclasses import InitVar, dataclass
6
- from typing import Any, List, Mapping, MutableMapping, Optional, Union
6
+ from typing import Any, Mapping, MutableMapping, Optional, Union
7
7
 
8
8
  import requests
9
+
9
10
  from airbyte_cdk.sources.declarative.requesters.paginators.paginator import Paginator
10
- from airbyte_cdk.sources.declarative.types import Record, StreamSlice, StreamState
11
+ from airbyte_cdk.sources.types import Record, StreamSlice, StreamState
11
12
 
12
13
 
13
14
  @dataclass
@@ -18,7 +19,7 @@ class NoPagination(Paginator):
18
19
 
19
20
  parameters: InitVar[Mapping[str, Any]]
20
21
 
21
- def path(self) -> Optional[str]:
22
+ def path(self, next_page_token: Optional[Mapping[str, Any]]) -> Optional[str]:
22
23
  return None
23
24
 
24
25
  def get_request_params(
@@ -57,9 +58,14 @@ class NoPagination(Paginator):
57
58
  ) -> Mapping[str, Any]:
58
59
  return {}
59
60
 
60
- def next_page_token(self, response: requests.Response, last_records: List[Record]) -> Mapping[str, Any]:
61
- return {}
61
+ def get_initial_token(self) -> Optional[Any]:
62
+ return None
62
63
 
63
- def reset(self) -> None:
64
- # No state to reset
65
- pass
64
+ def next_page_token(
65
+ self,
66
+ response: requests.Response,
67
+ last_page_size: int,
68
+ last_record: Optional[Record],
69
+ last_page_token_value: Optional[Any],
70
+ ) -> Optional[Mapping[str, Any]]:
71
+ return {}
@@ -4,11 +4,14 @@
4
4
 
5
5
  from abc import ABC, abstractmethod
6
6
  from dataclasses import dataclass
7
- from typing import Any, List, Mapping, Optional
7
+ from typing import Any, Mapping, Optional
8
8
 
9
9
  import requests
10
- from airbyte_cdk.sources.declarative.requesters.request_options.request_options_provider import RequestOptionsProvider
11
- from airbyte_cdk.sources.declarative.types import Record
10
+
11
+ from airbyte_cdk.sources.declarative.requesters.request_options.request_options_provider import (
12
+ RequestOptionsProvider,
13
+ )
14
+ from airbyte_cdk.sources.types import Record
12
15
 
13
16
 
14
17
  @dataclass
@@ -21,24 +24,32 @@ class Paginator(ABC, RequestOptionsProvider):
21
24
  """
22
25
 
23
26
  @abstractmethod
24
- def reset(self) -> None:
27
+ def get_initial_token(self) -> Optional[Any]:
25
28
  """
26
- Reset the pagination's inner state
29
+ Get the page token that should be included in the request to get the first page of records
27
30
  """
28
31
 
29
32
  @abstractmethod
30
- def next_page_token(self, response: requests.Response, last_records: List[Record]) -> Optional[Mapping[str, Any]]:
33
+ def next_page_token(
34
+ self,
35
+ response: requests.Response,
36
+ last_page_size: int,
37
+ last_record: Optional[Record],
38
+ last_page_token_value: Optional[Any],
39
+ ) -> Optional[Mapping[str, Any]]:
31
40
  """
32
41
  Returns the next_page_token to use to fetch the next page of records.
33
42
 
34
43
  :param response: the response to process
35
- :param last_records: the records extracted from the response
44
+ :param last_page_size: the number of records read from the response
45
+ :param last_record: the last record extracted from the response
46
+ :param last_page_token_value: The current value of the page token made on the last request
36
47
  :return: A mapping {"next_page_token": <token>} for the next page from the input response object. Returning None means there are no more pages to read in this response.
37
48
  """
38
49
  pass
39
50
 
40
51
  @abstractmethod
41
- def path(self) -> Optional[str]:
52
+ def path(self, next_page_token: Optional[Mapping[str, Any]]) -> Optional[str]:
42
53
  """
43
54
  Returns the URL path to hit to fetch the next page of records
44
55
 
@@ -2,9 +2,15 @@
2
2
  # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
3
  #
4
4
 
5
- from airbyte_cdk.sources.declarative.requesters.paginators.strategies.cursor_pagination_strategy import CursorPaginationStrategy
6
- from airbyte_cdk.sources.declarative.requesters.paginators.strategies.offset_increment import OffsetIncrement
7
- from airbyte_cdk.sources.declarative.requesters.paginators.strategies.page_increment import PageIncrement
5
+ from airbyte_cdk.sources.declarative.requesters.paginators.strategies.cursor_pagination_strategy import (
6
+ CursorPaginationStrategy,
7
+ )
8
+ from airbyte_cdk.sources.declarative.requesters.paginators.strategies.offset_increment import (
9
+ OffsetIncrement,
10
+ )
11
+ from airbyte_cdk.sources.declarative.requesters.paginators.strategies.page_increment import (
12
+ PageIncrement,
13
+ )
8
14
  from airbyte_cdk.sources.declarative.requesters.paginators.strategies.stop_condition import (
9
15
  CursorStopCondition,
10
16
  StopConditionPaginationStrategyDecorator,
@@ -2,16 +2,22 @@
2
2
  # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
3
  #
4
4
 
5
- from dataclasses import InitVar, dataclass
6
- from typing import Any, List, Mapping, Optional, Union
5
+ from dataclasses import InitVar, dataclass, field
6
+ from typing import Any, Dict, Mapping, Optional, Union
7
7
 
8
8
  import requests
9
- from airbyte_cdk.sources.declarative.decoders.decoder import Decoder
10
- from airbyte_cdk.sources.declarative.decoders.json_decoder import JsonDecoder
9
+
10
+ from airbyte_cdk.sources.declarative.decoders import (
11
+ Decoder,
12
+ JsonDecoder,
13
+ PaginationDecoderDecorator,
14
+ )
11
15
  from airbyte_cdk.sources.declarative.interpolation.interpolated_boolean import InterpolatedBoolean
12
16
  from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
13
- from airbyte_cdk.sources.declarative.requesters.paginators.strategies.pagination_strategy import PaginationStrategy
14
- from airbyte_cdk.sources.declarative.types import Config
17
+ from airbyte_cdk.sources.declarative.requesters.paginators.strategies.pagination_strategy import (
18
+ PaginationStrategy,
19
+ )
20
+ from airbyte_cdk.sources.types import Config, Record
15
21
 
16
22
 
17
23
  @dataclass
@@ -32,36 +38,62 @@ class CursorPaginationStrategy(PaginationStrategy):
32
38
  parameters: InitVar[Mapping[str, Any]]
33
39
  page_size: Optional[int] = None
34
40
  stop_condition: Optional[Union[InterpolatedBoolean, str]] = None
35
- decoder: Decoder = JsonDecoder(parameters={})
41
+ decoder: Decoder = field(
42
+ default_factory=lambda: PaginationDecoderDecorator(decoder=JsonDecoder(parameters={}))
43
+ )
36
44
 
37
- def __post_init__(self, parameters: Mapping[str, Any]):
45
+ def __post_init__(self, parameters: Mapping[str, Any]) -> None:
38
46
  if isinstance(self.cursor_value, str):
39
- self.cursor_value = InterpolatedString.create(self.cursor_value, parameters=parameters)
47
+ self._cursor_value = InterpolatedString.create(self.cursor_value, parameters=parameters)
48
+ else:
49
+ self._cursor_value = self.cursor_value
40
50
  if isinstance(self.stop_condition, str):
41
- self.stop_condition = InterpolatedBoolean(condition=self.stop_condition, parameters=parameters)
51
+ self._stop_condition: Optional[InterpolatedBoolean] = InterpolatedBoolean(
52
+ condition=self.stop_condition, parameters=parameters
53
+ )
54
+ else:
55
+ self._stop_condition = self.stop_condition
42
56
 
43
57
  @property
44
58
  def initial_token(self) -> Optional[Any]:
59
+ """
60
+ CursorPaginationStrategy does not have an initial value because the next cursor is typically included
61
+ in the response of the first request. For Resumable Full Refresh streams that checkpoint the page
62
+ cursor, the next cursor should be read from the state or stream slice object.
63
+ """
45
64
  return None
46
65
 
47
- def next_page_token(self, response: requests.Response, last_records: List[Mapping[str, Any]]) -> Optional[Any]:
48
- decoded_response = self.decoder.decode(response)
66
+ def next_page_token(
67
+ self,
68
+ response: requests.Response,
69
+ last_page_size: int,
70
+ last_record: Optional[Record],
71
+ last_page_token_value: Optional[Any] = None,
72
+ ) -> Optional[Any]:
73
+ decoded_response = next(self.decoder.decode(response))
49
74
 
50
75
  # The default way that link is presented in requests.Response is a string of various links (last, next, etc). This
51
76
  # is not indexable or useful for parsing the cursor, so we replace it with the link dictionary from response.links
52
- headers = response.headers
77
+ headers: Dict[str, Any] = dict(response.headers)
53
78
  headers["link"] = response.links
54
-
55
- if self.stop_condition:
56
- should_stop = self.stop_condition.eval(self.config, response=decoded_response, headers=headers, last_records=last_records)
79
+ if self._stop_condition:
80
+ should_stop = self._stop_condition.eval(
81
+ self.config,
82
+ response=decoded_response,
83
+ headers=headers,
84
+ last_record=last_record,
85
+ last_page_size=last_page_size,
86
+ )
57
87
  if should_stop:
58
88
  return None
59
- token = self.cursor_value.eval(config=self.config, last_records=last_records, response=decoded_response, headers=headers)
89
+ token = self._cursor_value.eval(
90
+ config=self.config,
91
+ response=decoded_response,
92
+ headers=headers,
93
+ last_record=last_record,
94
+ last_page_size=last_page_size,
95
+ )
60
96
  return token if token else None
61
97
 
62
- def reset(self):
63
- # No state to reset
64
- pass
65
-
66
98
  def get_page_size(self) -> Optional[int]:
67
99
  return self.page_size
@@ -2,14 +2,21 @@
2
2
  # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
3
  #
4
4
 
5
- from dataclasses import InitVar, dataclass
6
- from typing import Any, List, Mapping, Optional, Union
5
+ from dataclasses import InitVar, dataclass, field
6
+ from typing import Any, Mapping, Optional, Union
7
7
 
8
8
  import requests
9
- from airbyte_cdk.sources.declarative.decoders import Decoder, JsonDecoder
9
+
10
+ from airbyte_cdk.sources.declarative.decoders import (
11
+ Decoder,
12
+ JsonDecoder,
13
+ PaginationDecoderDecorator,
14
+ )
10
15
  from airbyte_cdk.sources.declarative.interpolation import InterpolatedString
11
- from airbyte_cdk.sources.declarative.requesters.paginators.strategies.pagination_strategy import PaginationStrategy
12
- from airbyte_cdk.sources.declarative.types import Config
16
+ from airbyte_cdk.sources.declarative.requesters.paginators.strategies.pagination_strategy import (
17
+ PaginationStrategy,
18
+ )
19
+ from airbyte_cdk.sources.types import Config, Record
13
20
 
14
21
 
15
22
  @dataclass
@@ -39,35 +46,51 @@ class OffsetIncrement(PaginationStrategy):
39
46
  config: Config
40
47
  page_size: Optional[Union[str, int]]
41
48
  parameters: InitVar[Mapping[str, Any]]
42
- decoder: Decoder = JsonDecoder(parameters={})
49
+ decoder: Decoder = field(
50
+ default_factory=lambda: PaginationDecoderDecorator(decoder=JsonDecoder(parameters={}))
51
+ )
43
52
  inject_on_first_request: bool = False
44
53
 
45
- def __post_init__(self, parameters: Mapping[str, Any]):
46
- self._offset = 0
54
+ def __post_init__(self, parameters: Mapping[str, Any]) -> None:
47
55
  page_size = str(self.page_size) if isinstance(self.page_size, int) else self.page_size
48
56
  if page_size:
49
- self._page_size = InterpolatedString(page_size, parameters=parameters)
57
+ self._page_size: Optional[InterpolatedString] = InterpolatedString(
58
+ page_size, parameters=parameters
59
+ )
50
60
  else:
51
61
  self._page_size = None
52
62
 
53
63
  @property
54
64
  def initial_token(self) -> Optional[Any]:
55
65
  if self.inject_on_first_request:
56
- return self._offset
66
+ return 0
57
67
  return None
58
68
 
59
- def next_page_token(self, response: requests.Response, last_records: List[Mapping[str, Any]]) -> Optional[Any]:
60
- decoded_response = self.decoder.decode(response)
69
+ def next_page_token(
70
+ self,
71
+ response: requests.Response,
72
+ last_page_size: int,
73
+ last_record: Optional[Record],
74
+ last_page_token_value: Optional[Any] = None,
75
+ ) -> Optional[Any]:
76
+ decoded_response = next(self.decoder.decode(response))
61
77
 
62
78
  # Stop paginating when there are fewer records than the page size or the current page has no records
63
- if (self._page_size and len(last_records) < self._page_size.eval(self.config, response=decoded_response)) or len(last_records) == 0:
79
+ if (
80
+ self._page_size
81
+ and last_page_size < self._page_size.eval(self.config, response=decoded_response)
82
+ ) or last_page_size == 0:
64
83
  return None
84
+ elif last_page_token_value is None:
85
+ # If the OffsetIncrement strategy does not inject on the first request, the incoming last_page_token_value
86
+ # will be None. For this case, we assume that None was the first page and progress to the next offset
87
+ return 0 + last_page_size
88
+ elif not isinstance(last_page_token_value, int):
89
+ raise ValueError(
90
+ f"Last page token value {last_page_token_value} for OffsetIncrement pagination strategy was not an integer"
91
+ )
65
92
  else:
66
- self._offset += len(last_records)
67
- return self._offset
68
-
69
- def reset(self):
70
- self._offset = 0
93
+ return last_page_token_value + last_page_size
71
94
 
72
95
  def get_page_size(self) -> Optional[int]:
73
96
  if self._page_size:
@@ -76,4 +99,4 @@ class OffsetIncrement(PaginationStrategy):
76
99
  raise Exception(f"{page_size} is of type {type(page_size)}. Expected {int}")
77
100
  return page_size
78
101
  else:
79
- return self._page_size
102
+ return None
@@ -3,12 +3,15 @@
3
3
  #
4
4
 
5
5
  from dataclasses import InitVar, dataclass
6
- from typing import Any, List, Mapping, Optional, Union
6
+ from typing import Any, Mapping, Optional, Union
7
7
 
8
8
  import requests
9
+
9
10
  from airbyte_cdk.sources.declarative.interpolation import InterpolatedString
10
- from airbyte_cdk.sources.declarative.requesters.paginators.strategies.pagination_strategy import PaginationStrategy
11
- from airbyte_cdk.sources.declarative.types import Config, Record
11
+ from airbyte_cdk.sources.declarative.requesters.paginators.strategies.pagination_strategy import (
12
+ PaginationStrategy,
13
+ )
14
+ from airbyte_cdk.sources.types import Config, Record
12
15
 
13
16
 
14
17
  @dataclass
@@ -28,7 +31,6 @@ class PageIncrement(PaginationStrategy):
28
31
  inject_on_first_request: bool = False
29
32
 
30
33
  def __post_init__(self, parameters: Mapping[str, Any]) -> None:
31
- self._page = self.start_from_page
32
34
  if isinstance(self.page_size, int) or (self.page_size is None):
33
35
  self._page_size = self.page_size
34
36
  else:
@@ -40,19 +42,30 @@ class PageIncrement(PaginationStrategy):
40
42
  @property
41
43
  def initial_token(self) -> Optional[Any]:
42
44
  if self.inject_on_first_request:
43
- return self._page
45
+ return self.start_from_page
44
46
  return None
45
47
 
46
- def next_page_token(self, response: requests.Response, last_records: List[Record]) -> Optional[Any]:
48
+ def next_page_token(
49
+ self,
50
+ response: requests.Response,
51
+ last_page_size: int,
52
+ last_record: Optional[Record],
53
+ last_page_token_value: Optional[Any],
54
+ ) -> Optional[Any]:
47
55
  # Stop paginating when there are fewer records than the page size or the current page has no records
48
- if (self._page_size and len(last_records) < self._page_size) or len(last_records) == 0:
56
+ if (self._page_size and last_page_size < self._page_size) or last_page_size == 0:
49
57
  return None
58
+ elif last_page_token_value is None:
59
+ # If the PageIncrement strategy does not inject on the first request, the incoming last_page_token_value
60
+ # may be None. When this is the case, we assume we've already requested the first page specified by
61
+ # start_from_page and must now get the next page
62
+ return self.start_from_page + 1
63
+ elif not isinstance(last_page_token_value, int):
64
+ raise ValueError(
65
+ f"Last page token value {last_page_token_value} for PageIncrement pagination strategy was not an integer"
66
+ )
50
67
  else:
51
- self._page += 1
52
- return self._page
53
-
54
- def reset(self) -> None:
55
- self._page = self.start_from_page
68
+ return last_page_token_value + 1
56
69
 
57
70
  def get_page_size(self) -> Optional[int]:
58
71
  return self._page_size