airbyte-cdk 0.72.1__py3-none-any.whl → 6.17.1.dev1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (518) 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 +490 -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 +63 -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 +31 -3
  79. airbyte_cdk/sources/declarative/incremental/concurrent_partition_cursor.py +340 -0
  80. airbyte_cdk/sources/declarative/incremental/datetime_based_cursor.py +156 -48
  81. airbyte_cdk/sources/declarative/incremental/declarative_cursor.py +13 -0
  82. airbyte_cdk/sources/declarative/incremental/global_substream_cursor.py +350 -0
  83. airbyte_cdk/sources/declarative/incremental/per_partition_cursor.py +174 -74
  84. airbyte_cdk/sources/declarative/incremental/per_partition_with_global.py +200 -0
  85. airbyte_cdk/sources/declarative/incremental/resumable_full_refresh_cursor.py +122 -0
  86. airbyte_cdk/sources/declarative/interpolation/filters.py +27 -1
  87. airbyte_cdk/sources/declarative/interpolation/interpolated_boolean.py +23 -5
  88. airbyte_cdk/sources/declarative/interpolation/interpolated_mapping.py +12 -8
  89. airbyte_cdk/sources/declarative/interpolation/interpolated_nested_mapping.py +13 -6
  90. airbyte_cdk/sources/declarative/interpolation/interpolated_string.py +21 -6
  91. airbyte_cdk/sources/declarative/interpolation/interpolation.py +9 -3
  92. airbyte_cdk/sources/declarative/interpolation/jinja.py +72 -37
  93. airbyte_cdk/sources/declarative/interpolation/macros.py +72 -17
  94. airbyte_cdk/sources/declarative/manifest_declarative_source.py +193 -52
  95. airbyte_cdk/sources/declarative/migrations/legacy_to_per_partition_state_migration.py +98 -0
  96. airbyte_cdk/sources/declarative/migrations/state_migration.py +24 -0
  97. airbyte_cdk/sources/declarative/models/__init__.py +1 -1
  98. airbyte_cdk/sources/declarative/models/declarative_component_schema.py +1319 -603
  99. airbyte_cdk/sources/declarative/parsers/custom_exceptions.py +2 -2
  100. airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py +26 -4
  101. airbyte_cdk/sources/declarative/parsers/manifest_reference_resolver.py +26 -15
  102. airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +1759 -225
  103. airbyte_cdk/sources/declarative/partition_routers/__init__.py +24 -4
  104. airbyte_cdk/sources/declarative/partition_routers/async_job_partition_router.py +65 -0
  105. airbyte_cdk/sources/declarative/partition_routers/cartesian_product_stream_slicer.py +176 -0
  106. airbyte_cdk/sources/declarative/partition_routers/list_partition_router.py +39 -9
  107. airbyte_cdk/sources/declarative/partition_routers/partition_router.py +62 -0
  108. airbyte_cdk/sources/declarative/partition_routers/single_partition_router.py +15 -3
  109. airbyte_cdk/sources/declarative/partition_routers/substream_partition_router.py +222 -39
  110. airbyte_cdk/sources/declarative/requesters/error_handlers/__init__.py +19 -5
  111. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/__init__.py +3 -1
  112. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py +19 -7
  113. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py +19 -7
  114. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/header_helper.py +4 -2
  115. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py +41 -9
  116. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py +29 -14
  117. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategy.py +5 -13
  118. airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +32 -16
  119. airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py +46 -56
  120. airbyte_cdk/sources/declarative/requesters/error_handlers/default_http_response_filter.py +40 -0
  121. airbyte_cdk/sources/declarative/requesters/error_handlers/error_handler.py +6 -32
  122. airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +119 -41
  123. airbyte_cdk/sources/declarative/requesters/http_job_repository.py +228 -0
  124. airbyte_cdk/sources/declarative/requesters/http_requester.py +98 -344
  125. airbyte_cdk/sources/declarative/requesters/paginators/__init__.py +14 -3
  126. airbyte_cdk/sources/declarative/requesters/paginators/default_paginator.py +105 -46
  127. airbyte_cdk/sources/declarative/requesters/paginators/no_pagination.py +14 -8
  128. airbyte_cdk/sources/declarative/requesters/paginators/paginator.py +19 -8
  129. airbyte_cdk/sources/declarative/requesters/paginators/strategies/__init__.py +9 -3
  130. airbyte_cdk/sources/declarative/requesters/paginators/strategies/cursor_pagination_strategy.py +53 -21
  131. airbyte_cdk/sources/declarative/requesters/paginators/strategies/offset_increment.py +42 -19
  132. airbyte_cdk/sources/declarative/requesters/paginators/strategies/page_increment.py +25 -12
  133. airbyte_cdk/sources/declarative/requesters/paginators/strategies/pagination_strategy.py +13 -10
  134. airbyte_cdk/sources/declarative/requesters/paginators/strategies/stop_condition.py +26 -13
  135. airbyte_cdk/sources/declarative/requesters/request_options/__init__.py +15 -2
  136. airbyte_cdk/sources/declarative/requesters/request_options/datetime_based_request_options_provider.py +91 -0
  137. airbyte_cdk/sources/declarative/requesters/request_options/default_request_options_provider.py +60 -0
  138. airbyte_cdk/sources/declarative/requesters/request_options/interpolated_nested_request_input_provider.py +31 -14
  139. airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_input_provider.py +27 -15
  140. airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_options_provider.py +63 -10
  141. airbyte_cdk/sources/declarative/requesters/request_options/request_options_provider.py +1 -1
  142. airbyte_cdk/sources/declarative/requesters/requester.py +9 -17
  143. airbyte_cdk/sources/declarative/resolvers/__init__.py +41 -0
  144. airbyte_cdk/sources/declarative/resolvers/components_resolver.py +55 -0
  145. airbyte_cdk/sources/declarative/resolvers/config_components_resolver.py +136 -0
  146. airbyte_cdk/sources/declarative/resolvers/http_components_resolver.py +112 -0
  147. airbyte_cdk/sources/declarative/retrievers/__init__.py +6 -2
  148. airbyte_cdk/sources/declarative/retrievers/async_retriever.py +100 -0
  149. airbyte_cdk/sources/declarative/retrievers/retriever.py +1 -3
  150. airbyte_cdk/sources/declarative/retrievers/simple_retriever.py +229 -73
  151. airbyte_cdk/sources/declarative/schema/__init__.py +14 -1
  152. airbyte_cdk/sources/declarative/schema/default_schema_loader.py +5 -3
  153. airbyte_cdk/sources/declarative/schema/dynamic_schema_loader.py +236 -0
  154. airbyte_cdk/sources/declarative/schema/json_file_schema_loader.py +8 -8
  155. airbyte_cdk/sources/declarative/spec/spec.py +12 -5
  156. airbyte_cdk/sources/declarative/stream_slicers/__init__.py +1 -2
  157. airbyte_cdk/sources/declarative/stream_slicers/declarative_partition_generator.py +88 -0
  158. airbyte_cdk/sources/declarative/stream_slicers/stream_slicer.py +9 -14
  159. airbyte_cdk/sources/declarative/transformations/add_fields.py +19 -11
  160. airbyte_cdk/sources/declarative/transformations/flatten_fields.py +52 -0
  161. airbyte_cdk/sources/declarative/transformations/keys_replace_transformation.py +61 -0
  162. airbyte_cdk/sources/declarative/transformations/keys_to_lower_transformation.py +22 -0
  163. airbyte_cdk/sources/declarative/transformations/keys_to_snake_transformation.py +68 -0
  164. airbyte_cdk/sources/declarative/transformations/remove_fields.py +13 -10
  165. airbyte_cdk/sources/declarative/transformations/transformation.py +5 -5
  166. airbyte_cdk/sources/declarative/types.py +19 -110
  167. airbyte_cdk/sources/declarative/yaml_declarative_source.py +31 -10
  168. airbyte_cdk/sources/embedded/base_integration.py +16 -5
  169. airbyte_cdk/sources/embedded/catalog.py +16 -4
  170. airbyte_cdk/sources/embedded/runner.py +19 -3
  171. airbyte_cdk/sources/embedded/tools.py +5 -2
  172. airbyte_cdk/sources/file_based/README.md +152 -0
  173. airbyte_cdk/sources/file_based/__init__.py +24 -0
  174. airbyte_cdk/sources/file_based/availability_strategy/__init__.py +9 -2
  175. airbyte_cdk/sources/file_based/availability_strategy/abstract_file_based_availability_strategy.py +22 -6
  176. airbyte_cdk/sources/file_based/availability_strategy/default_file_based_availability_strategy.py +46 -10
  177. airbyte_cdk/sources/file_based/config/abstract_file_based_spec.py +47 -10
  178. airbyte_cdk/sources/file_based/config/avro_format.py +2 -1
  179. airbyte_cdk/sources/file_based/config/csv_format.py +29 -10
  180. airbyte_cdk/sources/file_based/config/excel_format.py +18 -0
  181. airbyte_cdk/sources/file_based/config/file_based_stream_config.py +16 -4
  182. airbyte_cdk/sources/file_based/config/jsonl_format.py +2 -1
  183. airbyte_cdk/sources/file_based/config/parquet_format.py +2 -1
  184. airbyte_cdk/sources/file_based/config/unstructured_format.py +13 -5
  185. airbyte_cdk/sources/file_based/discovery_policy/__init__.py +6 -2
  186. airbyte_cdk/sources/file_based/discovery_policy/abstract_discovery_policy.py +2 -4
  187. airbyte_cdk/sources/file_based/discovery_policy/default_discovery_policy.py +7 -2
  188. airbyte_cdk/sources/file_based/exceptions.py +18 -15
  189. airbyte_cdk/sources/file_based/file_based_source.py +140 -33
  190. airbyte_cdk/sources/file_based/file_based_stream_reader.py +69 -5
  191. airbyte_cdk/sources/file_based/file_types/__init__.py +14 -1
  192. airbyte_cdk/sources/file_based/file_types/avro_parser.py +75 -24
  193. airbyte_cdk/sources/file_based/file_types/csv_parser.py +116 -34
  194. airbyte_cdk/sources/file_based/file_types/excel_parser.py +196 -0
  195. airbyte_cdk/sources/file_based/file_types/file_transfer.py +37 -0
  196. airbyte_cdk/sources/file_based/file_types/file_type_parser.py +4 -1
  197. airbyte_cdk/sources/file_based/file_types/jsonl_parser.py +24 -8
  198. airbyte_cdk/sources/file_based/file_types/parquet_parser.py +60 -18
  199. airbyte_cdk/sources/file_based/file_types/unstructured_parser.py +141 -41
  200. airbyte_cdk/sources/file_based/remote_file.py +1 -1
  201. airbyte_cdk/sources/file_based/schema_helpers.py +38 -10
  202. airbyte_cdk/sources/file_based/schema_validation_policies/__init__.py +3 -1
  203. airbyte_cdk/sources/file_based/schema_validation_policies/abstract_schema_validation_policy.py +3 -1
  204. airbyte_cdk/sources/file_based/schema_validation_policies/default_schema_validation_policies.py +16 -5
  205. airbyte_cdk/sources/file_based/stream/abstract_file_based_stream.py +50 -13
  206. airbyte_cdk/sources/file_based/stream/concurrent/adapters.py +67 -27
  207. airbyte_cdk/sources/file_based/stream/concurrent/cursor/__init__.py +5 -1
  208. airbyte_cdk/sources/file_based/stream/concurrent/cursor/abstract_concurrent_file_based_cursor.py +14 -23
  209. airbyte_cdk/sources/file_based/stream/concurrent/cursor/file_based_concurrent_cursor.py +54 -18
  210. airbyte_cdk/sources/file_based/stream/concurrent/cursor/file_based_final_state_cursor.py +21 -9
  211. airbyte_cdk/sources/file_based/stream/cursor/abstract_file_based_cursor.py +3 -1
  212. airbyte_cdk/sources/file_based/stream/cursor/default_file_based_cursor.py +27 -10
  213. airbyte_cdk/sources/file_based/stream/default_file_based_stream.py +147 -45
  214. airbyte_cdk/sources/http_logger.py +8 -3
  215. airbyte_cdk/sources/message/__init__.py +7 -1
  216. airbyte_cdk/sources/message/repository.py +18 -4
  217. airbyte_cdk/sources/source.py +42 -38
  218. airbyte_cdk/sources/streams/__init__.py +2 -2
  219. airbyte_cdk/sources/streams/availability_strategy.py +54 -3
  220. airbyte_cdk/sources/streams/call_rate.py +64 -21
  221. airbyte_cdk/sources/streams/checkpoint/__init__.py +26 -0
  222. airbyte_cdk/sources/streams/checkpoint/checkpoint_reader.py +335 -0
  223. airbyte_cdk/sources/{declarative/incremental → streams/checkpoint}/cursor.py +17 -14
  224. airbyte_cdk/sources/streams/checkpoint/per_partition_key_serializer.py +22 -0
  225. airbyte_cdk/sources/streams/checkpoint/resumable_full_refresh_cursor.py +51 -0
  226. airbyte_cdk/sources/streams/checkpoint/substream_resumable_full_refresh_cursor.py +110 -0
  227. airbyte_cdk/sources/streams/concurrent/README.md +7 -0
  228. airbyte_cdk/sources/streams/concurrent/abstract_stream.py +7 -2
  229. airbyte_cdk/sources/streams/concurrent/adapters.py +84 -75
  230. airbyte_cdk/sources/streams/concurrent/availability_strategy.py +30 -2
  231. airbyte_cdk/sources/streams/concurrent/cursor.py +313 -48
  232. airbyte_cdk/sources/streams/concurrent/default_stream.py +12 -3
  233. airbyte_cdk/sources/streams/concurrent/exceptions.py +3 -0
  234. airbyte_cdk/sources/streams/concurrent/helpers.py +14 -3
  235. airbyte_cdk/sources/streams/concurrent/partition_enqueuer.py +12 -3
  236. airbyte_cdk/sources/streams/concurrent/partition_reader.py +10 -3
  237. airbyte_cdk/sources/streams/concurrent/partitions/partition.py +1 -16
  238. airbyte_cdk/sources/streams/concurrent/partitions/stream_slicer.py +21 -0
  239. airbyte_cdk/sources/streams/concurrent/partitions/types.py +15 -5
  240. airbyte_cdk/sources/streams/concurrent/state_converters/abstract_stream_state_converter.py +109 -17
  241. airbyte_cdk/sources/streams/concurrent/state_converters/datetime_stream_state_converter.py +90 -72
  242. airbyte_cdk/sources/streams/core.py +412 -87
  243. airbyte_cdk/sources/streams/http/__init__.py +2 -1
  244. airbyte_cdk/sources/streams/http/availability_strategy.py +12 -101
  245. airbyte_cdk/sources/streams/http/error_handlers/__init__.py +22 -0
  246. airbyte_cdk/sources/streams/http/error_handlers/backoff_strategy.py +28 -0
  247. airbyte_cdk/sources/streams/http/error_handlers/default_backoff_strategy.py +17 -0
  248. airbyte_cdk/sources/streams/http/error_handlers/default_error_mapping.py +86 -0
  249. airbyte_cdk/sources/streams/http/error_handlers/error_handler.py +42 -0
  250. airbyte_cdk/sources/streams/http/error_handlers/error_message_parser.py +19 -0
  251. airbyte_cdk/sources/streams/http/error_handlers/http_status_error_handler.py +110 -0
  252. airbyte_cdk/sources/streams/http/error_handlers/json_error_message_parser.py +52 -0
  253. airbyte_cdk/sources/streams/http/error_handlers/response_models.py +65 -0
  254. airbyte_cdk/sources/streams/http/exceptions.py +27 -7
  255. airbyte_cdk/sources/streams/http/http.py +369 -246
  256. airbyte_cdk/sources/streams/http/http_client.py +531 -0
  257. airbyte_cdk/sources/streams/http/rate_limiting.py +76 -12
  258. airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py +28 -9
  259. airbyte_cdk/sources/streams/http/requests_native_auth/abstract_token.py +2 -1
  260. airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py +90 -35
  261. airbyte_cdk/sources/streams/http/requests_native_auth/token.py +13 -3
  262. airbyte_cdk/sources/types.py +154 -0
  263. airbyte_cdk/sources/utils/record_helper.py +36 -21
  264. airbyte_cdk/sources/utils/schema_helpers.py +13 -6
  265. airbyte_cdk/sources/utils/slice_logger.py +4 -1
  266. airbyte_cdk/sources/utils/transform.py +54 -20
  267. airbyte_cdk/sql/_util/hashing.py +34 -0
  268. airbyte_cdk/sql/_util/name_normalizers.py +92 -0
  269. airbyte_cdk/sql/constants.py +32 -0
  270. airbyte_cdk/sql/exceptions.py +235 -0
  271. airbyte_cdk/sql/secrets.py +123 -0
  272. airbyte_cdk/sql/shared/__init__.py +15 -0
  273. airbyte_cdk/sql/shared/catalog_providers.py +145 -0
  274. airbyte_cdk/sql/shared/sql_processor.py +786 -0
  275. airbyte_cdk/sql/types.py +160 -0
  276. airbyte_cdk/test/catalog_builder.py +70 -18
  277. airbyte_cdk/test/entrypoint_wrapper.py +117 -42
  278. airbyte_cdk/test/mock_http/__init__.py +1 -1
  279. airbyte_cdk/test/mock_http/matcher.py +6 -0
  280. airbyte_cdk/test/mock_http/mocker.py +57 -10
  281. airbyte_cdk/test/mock_http/request.py +19 -3
  282. airbyte_cdk/test/mock_http/response.py +3 -1
  283. airbyte_cdk/test/mock_http/response_builder.py +32 -16
  284. airbyte_cdk/test/state_builder.py +18 -10
  285. airbyte_cdk/test/utils/__init__.py +1 -0
  286. airbyte_cdk/test/utils/data.py +24 -0
  287. airbyte_cdk/test/utils/http_mocking.py +16 -0
  288. airbyte_cdk/test/utils/manifest_only_fixtures.py +60 -0
  289. airbyte_cdk/test/utils/reading.py +26 -0
  290. airbyte_cdk/utils/__init__.py +2 -1
  291. airbyte_cdk/utils/airbyte_secrets_utils.py +5 -3
  292. airbyte_cdk/utils/analytics_message.py +10 -2
  293. airbyte_cdk/utils/datetime_format_inferrer.py +4 -1
  294. airbyte_cdk/utils/event_timing.py +10 -10
  295. airbyte_cdk/utils/mapping_helpers.py +3 -1
  296. airbyte_cdk/utils/message_utils.py +20 -11
  297. airbyte_cdk/utils/print_buffer.py +75 -0
  298. airbyte_cdk/utils/schema_inferrer.py +198 -28
  299. airbyte_cdk/utils/slice_hasher.py +30 -0
  300. airbyte_cdk/utils/spec_schema_transformations.py +6 -3
  301. airbyte_cdk/utils/stream_status_utils.py +8 -1
  302. airbyte_cdk/utils/traced_exception.py +61 -21
  303. airbyte_cdk-6.17.1.dev1.dist-info/METADATA +109 -0
  304. airbyte_cdk-6.17.1.dev1.dist-info/RECORD +350 -0
  305. {airbyte_cdk-0.72.1.dist-info → airbyte_cdk-6.17.1.dev1.dist-info}/WHEEL +1 -2
  306. airbyte_cdk-6.17.1.dev1.dist-info/entry_points.txt +3 -0
  307. airbyte_cdk/sources/declarative/create_partial.py +0 -92
  308. airbyte_cdk/sources/declarative/parsers/class_types_registry.py +0 -102
  309. airbyte_cdk/sources/declarative/parsers/default_implementation_registry.py +0 -64
  310. airbyte_cdk/sources/declarative/requesters/error_handlers/response_action.py +0 -16
  311. airbyte_cdk/sources/declarative/requesters/error_handlers/response_status.py +0 -68
  312. airbyte_cdk/sources/declarative/stream_slicers/cartesian_product_stream_slicer.py +0 -114
  313. airbyte_cdk/sources/deprecated/base_source.py +0 -94
  314. airbyte_cdk/sources/deprecated/client.py +0 -99
  315. airbyte_cdk/sources/singer/__init__.py +0 -8
  316. airbyte_cdk/sources/singer/singer_helpers.py +0 -304
  317. airbyte_cdk/sources/singer/source.py +0 -186
  318. airbyte_cdk/sources/streams/concurrent/partitions/record.py +0 -23
  319. airbyte_cdk/sources/streams/http/auth/__init__.py +0 -17
  320. airbyte_cdk/sources/streams/http/auth/core.py +0 -29
  321. airbyte_cdk/sources/streams/http/auth/oauth.py +0 -113
  322. airbyte_cdk/sources/streams/http/auth/token.py +0 -47
  323. airbyte_cdk/sources/streams/utils/stream_helper.py +0 -40
  324. airbyte_cdk/sources/utils/catalog_helpers.py +0 -22
  325. airbyte_cdk/sources/utils/schema_models.py +0 -84
  326. airbyte_cdk-0.72.1.dist-info/METADATA +0 -243
  327. airbyte_cdk-0.72.1.dist-info/RECORD +0 -466
  328. airbyte_cdk-0.72.1.dist-info/top_level.txt +0 -3
  329. source_declarative_manifest/main.py +0 -29
  330. unit_tests/connector_builder/__init__.py +0 -3
  331. unit_tests/connector_builder/test_connector_builder_handler.py +0 -871
  332. unit_tests/connector_builder/test_message_grouper.py +0 -713
  333. unit_tests/connector_builder/utils.py +0 -27
  334. unit_tests/destinations/test_destination.py +0 -243
  335. unit_tests/singer/test_singer_helpers.py +0 -56
  336. unit_tests/singer/test_singer_source.py +0 -112
  337. unit_tests/sources/__init__.py +0 -0
  338. unit_tests/sources/concurrent_source/__init__.py +0 -3
  339. unit_tests/sources/concurrent_source/test_concurrent_source_adapter.py +0 -106
  340. unit_tests/sources/declarative/__init__.py +0 -3
  341. unit_tests/sources/declarative/auth/__init__.py +0 -3
  342. unit_tests/sources/declarative/auth/test_oauth.py +0 -331
  343. unit_tests/sources/declarative/auth/test_selective_authenticator.py +0 -39
  344. unit_tests/sources/declarative/auth/test_session_token_auth.py +0 -182
  345. unit_tests/sources/declarative/auth/test_token_auth.py +0 -200
  346. unit_tests/sources/declarative/auth/test_token_provider.py +0 -73
  347. unit_tests/sources/declarative/checks/__init__.py +0 -3
  348. unit_tests/sources/declarative/checks/test_check_stream.py +0 -146
  349. unit_tests/sources/declarative/decoders/__init__.py +0 -0
  350. unit_tests/sources/declarative/decoders/test_json_decoder.py +0 -16
  351. unit_tests/sources/declarative/external_component.py +0 -13
  352. unit_tests/sources/declarative/extractors/__init__.py +0 -3
  353. unit_tests/sources/declarative/extractors/test_dpath_extractor.py +0 -55
  354. unit_tests/sources/declarative/extractors/test_record_filter.py +0 -55
  355. unit_tests/sources/declarative/extractors/test_record_selector.py +0 -179
  356. unit_tests/sources/declarative/incremental/__init__.py +0 -0
  357. unit_tests/sources/declarative/incremental/test_datetime_based_cursor.py +0 -860
  358. unit_tests/sources/declarative/incremental/test_per_partition_cursor.py +0 -406
  359. unit_tests/sources/declarative/incremental/test_per_partition_cursor_integration.py +0 -332
  360. unit_tests/sources/declarative/interpolation/__init__.py +0 -3
  361. unit_tests/sources/declarative/interpolation/test_filters.py +0 -80
  362. unit_tests/sources/declarative/interpolation/test_interpolated_boolean.py +0 -40
  363. unit_tests/sources/declarative/interpolation/test_interpolated_mapping.py +0 -35
  364. unit_tests/sources/declarative/interpolation/test_interpolated_nested_mapping.py +0 -45
  365. unit_tests/sources/declarative/interpolation/test_interpolated_string.py +0 -25
  366. unit_tests/sources/declarative/interpolation/test_jinja.py +0 -240
  367. unit_tests/sources/declarative/interpolation/test_macros.py +0 -73
  368. unit_tests/sources/declarative/parsers/__init__.py +0 -3
  369. unit_tests/sources/declarative/parsers/test_manifest_component_transformer.py +0 -406
  370. unit_tests/sources/declarative/parsers/test_manifest_reference_resolver.py +0 -139
  371. unit_tests/sources/declarative/parsers/test_model_to_component_factory.py +0 -1847
  372. unit_tests/sources/declarative/parsers/testing_components.py +0 -36
  373. unit_tests/sources/declarative/partition_routers/__init__.py +0 -3
  374. unit_tests/sources/declarative/partition_routers/test_list_partition_router.py +0 -155
  375. unit_tests/sources/declarative/partition_routers/test_single_partition_router.py +0 -14
  376. unit_tests/sources/declarative/partition_routers/test_substream_partition_router.py +0 -404
  377. unit_tests/sources/declarative/requesters/__init__.py +0 -3
  378. unit_tests/sources/declarative/requesters/error_handlers/__init__.py +0 -3
  379. unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/__init__.py +0 -3
  380. unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_constant_backoff.py +0 -34
  381. unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_exponential_backoff.py +0 -36
  382. unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_header_helper.py +0 -38
  383. unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_time_from_header.py +0 -35
  384. unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_until_time_from_header.py +0 -64
  385. unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py +0 -213
  386. unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py +0 -178
  387. unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py +0 -121
  388. unit_tests/sources/declarative/requesters/error_handlers/test_response_status.py +0 -44
  389. unit_tests/sources/declarative/requesters/paginators/__init__.py +0 -3
  390. unit_tests/sources/declarative/requesters/paginators/test_cursor_pagination_strategy.py +0 -64
  391. unit_tests/sources/declarative/requesters/paginators/test_default_paginator.py +0 -313
  392. unit_tests/sources/declarative/requesters/paginators/test_no_paginator.py +0 -12
  393. unit_tests/sources/declarative/requesters/paginators/test_offset_increment.py +0 -58
  394. unit_tests/sources/declarative/requesters/paginators/test_page_increment.py +0 -70
  395. unit_tests/sources/declarative/requesters/paginators/test_request_option.py +0 -43
  396. unit_tests/sources/declarative/requesters/paginators/test_stop_condition.py +0 -105
  397. unit_tests/sources/declarative/requesters/request_options/__init__.py +0 -3
  398. unit_tests/sources/declarative/requesters/request_options/test_interpolated_request_options_provider.py +0 -101
  399. unit_tests/sources/declarative/requesters/test_http_requester.py +0 -974
  400. unit_tests/sources/declarative/requesters/test_interpolated_request_input_provider.py +0 -32
  401. unit_tests/sources/declarative/retrievers/__init__.py +0 -3
  402. unit_tests/sources/declarative/retrievers/test_simple_retriever.py +0 -542
  403. unit_tests/sources/declarative/schema/__init__.py +0 -6
  404. unit_tests/sources/declarative/schema/source_test/SourceTest.py +0 -8
  405. unit_tests/sources/declarative/schema/source_test/__init__.py +0 -3
  406. unit_tests/sources/declarative/schema/test_default_schema_loader.py +0 -32
  407. unit_tests/sources/declarative/schema/test_inline_schema_loader.py +0 -19
  408. unit_tests/sources/declarative/schema/test_json_file_schema_loader.py +0 -26
  409. unit_tests/sources/declarative/states/__init__.py +0 -3
  410. unit_tests/sources/declarative/stream_slicers/__init__.py +0 -3
  411. unit_tests/sources/declarative/stream_slicers/test_cartesian_product_stream_slicer.py +0 -225
  412. unit_tests/sources/declarative/test_create_partial.py +0 -83
  413. unit_tests/sources/declarative/test_declarative_stream.py +0 -103
  414. unit_tests/sources/declarative/test_manifest_declarative_source.py +0 -1260
  415. unit_tests/sources/declarative/test_types.py +0 -39
  416. unit_tests/sources/declarative/test_yaml_declarative_source.py +0 -148
  417. unit_tests/sources/file_based/__init__.py +0 -0
  418. unit_tests/sources/file_based/availability_strategy/__init__.py +0 -0
  419. unit_tests/sources/file_based/availability_strategy/test_default_file_based_availability_strategy.py +0 -100
  420. unit_tests/sources/file_based/config/__init__.py +0 -0
  421. unit_tests/sources/file_based/config/test_abstract_file_based_spec.py +0 -28
  422. unit_tests/sources/file_based/config/test_csv_format.py +0 -34
  423. unit_tests/sources/file_based/config/test_file_based_stream_config.py +0 -84
  424. unit_tests/sources/file_based/discovery_policy/__init__.py +0 -0
  425. unit_tests/sources/file_based/discovery_policy/test_default_discovery_policy.py +0 -31
  426. unit_tests/sources/file_based/file_types/__init__.py +0 -0
  427. unit_tests/sources/file_based/file_types/test_avro_parser.py +0 -243
  428. unit_tests/sources/file_based/file_types/test_csv_parser.py +0 -546
  429. unit_tests/sources/file_based/file_types/test_jsonl_parser.py +0 -158
  430. unit_tests/sources/file_based/file_types/test_parquet_parser.py +0 -274
  431. unit_tests/sources/file_based/file_types/test_unstructured_parser.py +0 -593
  432. unit_tests/sources/file_based/helpers.py +0 -70
  433. unit_tests/sources/file_based/in_memory_files_source.py +0 -211
  434. unit_tests/sources/file_based/scenarios/__init__.py +0 -0
  435. unit_tests/sources/file_based/scenarios/avro_scenarios.py +0 -744
  436. unit_tests/sources/file_based/scenarios/check_scenarios.py +0 -220
  437. unit_tests/sources/file_based/scenarios/concurrent_incremental_scenarios.py +0 -2844
  438. unit_tests/sources/file_based/scenarios/csv_scenarios.py +0 -3105
  439. unit_tests/sources/file_based/scenarios/file_based_source_builder.py +0 -91
  440. unit_tests/sources/file_based/scenarios/incremental_scenarios.py +0 -1926
  441. unit_tests/sources/file_based/scenarios/jsonl_scenarios.py +0 -930
  442. unit_tests/sources/file_based/scenarios/parquet_scenarios.py +0 -754
  443. unit_tests/sources/file_based/scenarios/scenario_builder.py +0 -234
  444. unit_tests/sources/file_based/scenarios/unstructured_scenarios.py +0 -608
  445. unit_tests/sources/file_based/scenarios/user_input_schema_scenarios.py +0 -746
  446. unit_tests/sources/file_based/scenarios/validation_policy_scenarios.py +0 -726
  447. unit_tests/sources/file_based/stream/__init__.py +0 -0
  448. unit_tests/sources/file_based/stream/concurrent/__init__.py +0 -0
  449. unit_tests/sources/file_based/stream/concurrent/test_adapters.py +0 -362
  450. unit_tests/sources/file_based/stream/concurrent/test_file_based_concurrent_cursor.py +0 -458
  451. unit_tests/sources/file_based/stream/test_default_file_based_cursor.py +0 -310
  452. unit_tests/sources/file_based/stream/test_default_file_based_stream.py +0 -244
  453. unit_tests/sources/file_based/test_file_based_scenarios.py +0 -320
  454. unit_tests/sources/file_based/test_file_based_stream_reader.py +0 -272
  455. unit_tests/sources/file_based/test_scenarios.py +0 -253
  456. unit_tests/sources/file_based/test_schema_helpers.py +0 -346
  457. unit_tests/sources/fixtures/__init__.py +0 -3
  458. unit_tests/sources/fixtures/source_test_fixture.py +0 -153
  459. unit_tests/sources/message/__init__.py +0 -0
  460. unit_tests/sources/message/test_repository.py +0 -153
  461. unit_tests/sources/streams/__init__.py +0 -0
  462. unit_tests/sources/streams/concurrent/__init__.py +0 -3
  463. unit_tests/sources/streams/concurrent/scenarios/__init__.py +0 -3
  464. unit_tests/sources/streams/concurrent/scenarios/incremental_scenarios.py +0 -250
  465. unit_tests/sources/streams/concurrent/scenarios/stream_facade_builder.py +0 -140
  466. unit_tests/sources/streams/concurrent/scenarios/stream_facade_scenarios.py +0 -452
  467. unit_tests/sources/streams/concurrent/scenarios/test_concurrent_scenarios.py +0 -76
  468. unit_tests/sources/streams/concurrent/scenarios/thread_based_concurrent_stream_scenarios.py +0 -418
  469. unit_tests/sources/streams/concurrent/scenarios/thread_based_concurrent_stream_source_builder.py +0 -142
  470. unit_tests/sources/streams/concurrent/scenarios/utils.py +0 -55
  471. unit_tests/sources/streams/concurrent/test_adapters.py +0 -380
  472. unit_tests/sources/streams/concurrent/test_concurrent_read_processor.py +0 -684
  473. unit_tests/sources/streams/concurrent/test_cursor.py +0 -139
  474. unit_tests/sources/streams/concurrent/test_datetime_state_converter.py +0 -369
  475. unit_tests/sources/streams/concurrent/test_default_stream.py +0 -197
  476. unit_tests/sources/streams/concurrent/test_partition_enqueuer.py +0 -90
  477. unit_tests/sources/streams/concurrent/test_partition_reader.py +0 -67
  478. unit_tests/sources/streams/concurrent/test_thread_pool_manager.py +0 -106
  479. unit_tests/sources/streams/http/__init__.py +0 -0
  480. unit_tests/sources/streams/http/auth/__init__.py +0 -0
  481. unit_tests/sources/streams/http/auth/test_auth.py +0 -173
  482. unit_tests/sources/streams/http/requests_native_auth/__init__.py +0 -0
  483. unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py +0 -423
  484. unit_tests/sources/streams/http/test_availability_strategy.py +0 -180
  485. unit_tests/sources/streams/http/test_http.py +0 -635
  486. unit_tests/sources/streams/test_availability_strategy.py +0 -70
  487. unit_tests/sources/streams/test_call_rate.py +0 -300
  488. unit_tests/sources/streams/test_stream_read.py +0 -405
  489. unit_tests/sources/streams/test_streams_core.py +0 -184
  490. unit_tests/sources/test_abstract_source.py +0 -1442
  491. unit_tests/sources/test_concurrent_source.py +0 -112
  492. unit_tests/sources/test_config.py +0 -92
  493. unit_tests/sources/test_connector_state_manager.py +0 -482
  494. unit_tests/sources/test_http_logger.py +0 -252
  495. unit_tests/sources/test_integration_source.py +0 -86
  496. unit_tests/sources/test_source.py +0 -684
  497. unit_tests/sources/test_source_read.py +0 -460
  498. unit_tests/test/__init__.py +0 -0
  499. unit_tests/test/mock_http/__init__.py +0 -0
  500. unit_tests/test/mock_http/test_matcher.py +0 -53
  501. unit_tests/test/mock_http/test_mocker.py +0 -214
  502. unit_tests/test/mock_http/test_request.py +0 -117
  503. unit_tests/test/mock_http/test_response_builder.py +0 -177
  504. unit_tests/test/test_entrypoint_wrapper.py +0 -240
  505. unit_tests/utils/__init__.py +0 -0
  506. unit_tests/utils/test_datetime_format_inferrer.py +0 -60
  507. unit_tests/utils/test_mapping_helpers.py +0 -54
  508. unit_tests/utils/test_message_utils.py +0 -91
  509. unit_tests/utils/test_rate_limiting.py +0 -26
  510. unit_tests/utils/test_schema_inferrer.py +0 -202
  511. unit_tests/utils/test_secret_utils.py +0 -135
  512. unit_tests/utils/test_stream_status_utils.py +0 -61
  513. unit_tests/utils/test_traced_exception.py +0 -107
  514. /airbyte_cdk/sources/{deprecated → declarative/async_job}/__init__.py +0 -0
  515. {source_declarative_manifest → airbyte_cdk/sources/declarative/migrations}/__init__.py +0 -0
  516. {unit_tests/destinations → airbyte_cdk/sql}/__init__.py +0 -0
  517. {unit_tests/singer → airbyte_cdk/sql/_util}/__init__.py +0 -0
  518. {airbyte_cdk-0.72.1.dist-info → airbyte_cdk-6.17.1.dev1.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