airbyte-cdk 6.26.0.dev4106__py3-none-any.whl → 6.26.0.dev4108__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- airbyte_cdk/cli/source_declarative_manifest/_run.py +3 -3
- airbyte_cdk/connector_builder/connector_builder_handler.py +2 -2
- airbyte_cdk/sources/declarative/async_job/job_orchestrator.py +7 -7
- airbyte_cdk/sources/declarative/auth/jwt.py +17 -11
- airbyte_cdk/sources/declarative/auth/oauth.py +22 -13
- airbyte_cdk/sources/declarative/auth/token.py +3 -8
- airbyte_cdk/sources/declarative/auth/token_provider.py +4 -5
- airbyte_cdk/sources/declarative/checks/check_dynamic_stream.py +19 -9
- airbyte_cdk/sources/declarative/concurrent_declarative_source.py +71 -34
- airbyte_cdk/sources/declarative/declarative_component_schema.yaml +33 -4
- airbyte_cdk/sources/declarative/declarative_stream.py +3 -1
- airbyte_cdk/sources/declarative/incremental/concurrent_partition_cursor.py +93 -27
- airbyte_cdk/sources/declarative/incremental/datetime_based_cursor.py +7 -6
- airbyte_cdk/sources/declarative/manifest_declarative_source.py +5 -3
- airbyte_cdk/sources/declarative/models/declarative_component_schema.py +22 -5
- airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +138 -38
- airbyte_cdk/sources/declarative/partition_routers/async_job_partition_router.py +5 -5
- airbyte_cdk/sources/declarative/partition_routers/list_partition_router.py +4 -2
- airbyte_cdk/sources/declarative/partition_routers/substream_partition_router.py +49 -25
- airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +4 -4
- airbyte_cdk/sources/declarative/requesters/http_requester.py +5 -1
- airbyte_cdk/sources/declarative/requesters/paginators/default_paginator.py +6 -5
- airbyte_cdk/sources/declarative/requesters/request_option.py +83 -4
- airbyte_cdk/sources/declarative/requesters/request_options/datetime_based_request_options_provider.py +7 -6
- airbyte_cdk/sources/declarative/retrievers/async_retriever.py +6 -12
- airbyte_cdk/sources/declarative/retrievers/simple_retriever.py +4 -1
- airbyte_cdk/sources/declarative/schema/__init__.py +2 -0
- airbyte_cdk/sources/declarative/schema/dynamic_schema_loader.py +44 -5
- airbyte_cdk/sources/file_based/config/abstract_file_based_spec.py +18 -11
- airbyte_cdk/sources/file_based/config/validate_config_transfer_modes.py +51 -0
- airbyte_cdk/sources/file_based/file_based_source.py +16 -55
- airbyte_cdk/sources/file_based/file_based_stream_reader.py +19 -31
- airbyte_cdk/sources/file_based/stream/default_file_based_stream.py +7 -7
- airbyte_cdk/sources/file_based/stream/identities_stream.py +5 -2
- airbyte_cdk/sources/streams/concurrent/state_converters/datetime_stream_state_converter.py +22 -13
- airbyte_cdk/sources/streams/core.py +6 -6
- airbyte_cdk/sources/streams/http/http.py +1 -2
- airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py +231 -62
- airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py +166 -83
- airbyte_cdk/sources/types.py +4 -2
- airbyte_cdk/sources/utils/transform.py +23 -2
- airbyte_cdk/utils/datetime_helpers.py +499 -0
- airbyte_cdk/utils/mapping_helpers.py +86 -27
- airbyte_cdk/utils/slice_hasher.py +8 -1
- airbyte_cdk-6.26.0.dev4108.dist-info/LICENSE_SHORT +1 -0
- {airbyte_cdk-6.26.0.dev4106.dist-info → airbyte_cdk-6.26.0.dev4108.dist-info}/METADATA +5 -5
- {airbyte_cdk-6.26.0.dev4106.dist-info → airbyte_cdk-6.26.0.dev4108.dist-info}/RECORD +50 -48
- {airbyte_cdk-6.26.0.dev4106.dist-info → airbyte_cdk-6.26.0.dev4108.dist-info}/WHEEL +1 -1
- airbyte_cdk/sources/file_based/config/permissions.py +0 -34
- {airbyte_cdk-6.26.0.dev4106.dist-info → airbyte_cdk-6.26.0.dev4108.dist-info}/LICENSE.txt +0 -0
- {airbyte_cdk-6.26.0.dev4106.dist-info → airbyte_cdk-6.26.0.dev4108.dist-info}/entry_points.txt +0 -0
@@ -33,6 +33,12 @@ from airbyte_cdk.sources.file_based.config.file_based_stream_config import (
|
|
33
33
|
FileBasedStreamConfig,
|
34
34
|
ValidationPolicy,
|
35
35
|
)
|
36
|
+
from airbyte_cdk.sources.file_based.config.validate_config_transfer_modes import (
|
37
|
+
include_identities_stream,
|
38
|
+
preserve_directory_structure,
|
39
|
+
use_file_transfer,
|
40
|
+
use_permissions_transfer,
|
41
|
+
)
|
36
42
|
from airbyte_cdk.sources.file_based.discovery_policy import (
|
37
43
|
AbstractDiscoveryPolicy,
|
38
44
|
DefaultDiscoveryPolicy,
|
@@ -164,7 +170,11 @@ class FileBasedSource(ConcurrentSourceAdapter, ABC):
|
|
164
170
|
tracebacks = []
|
165
171
|
for stream in streams:
|
166
172
|
if isinstance(stream, IdentitiesStream):
|
167
|
-
|
173
|
+
identity = next(iter(stream.load_identity_groups()))
|
174
|
+
if not identity:
|
175
|
+
errors.append(
|
176
|
+
"Unable to get identities for current configuration, please check your credentials"
|
177
|
+
)
|
168
178
|
continue
|
169
179
|
if not isinstance(stream, AbstractFileBasedStream):
|
170
180
|
raise ValueError(f"Stream {stream} is not a file-based stream.")
|
@@ -172,8 +182,7 @@ class FileBasedSource(ConcurrentSourceAdapter, ABC):
|
|
172
182
|
parsed_config = self._get_parsed_config(config)
|
173
183
|
availability_method = (
|
174
184
|
stream.availability_strategy.check_availability
|
175
|
-
if
|
176
|
-
or self._sync_acl_permissions(parsed_config)
|
185
|
+
if use_file_transfer(parsed_config) or use_permissions_transfer(parsed_config)
|
177
186
|
else stream.availability_strategy.check_availability_and_parsability
|
178
187
|
)
|
179
188
|
(
|
@@ -300,7 +309,7 @@ class FileBasedSource(ConcurrentSourceAdapter, ABC):
|
|
300
309
|
|
301
310
|
streams.append(stream)
|
302
311
|
|
303
|
-
if
|
312
|
+
if include_identities_stream(parsed_config):
|
304
313
|
identities_stream = self._make_identities_stream()
|
305
314
|
streams.append(identities_stream)
|
306
315
|
return streams
|
@@ -324,9 +333,9 @@ class FileBasedSource(ConcurrentSourceAdapter, ABC):
|
|
324
333
|
validation_policy=self._validate_and_get_validation_policy(stream_config),
|
325
334
|
errors_collector=self.errors_collector,
|
326
335
|
cursor=cursor,
|
327
|
-
use_file_transfer=
|
328
|
-
preserve_directory_structure=
|
329
|
-
|
336
|
+
use_file_transfer=use_file_transfer(parsed_config),
|
337
|
+
preserve_directory_structure=preserve_directory_structure(parsed_config),
|
338
|
+
use_permissions_transfer=use_permissions_transfer(parsed_config),
|
330
339
|
)
|
331
340
|
|
332
341
|
def _make_identities_stream(
|
@@ -403,51 +412,3 @@ class FileBasedSource(ConcurrentSourceAdapter, ABC):
|
|
403
412
|
"`input_schema` and `schemaless` options cannot both be set",
|
404
413
|
model=FileBasedStreamConfig,
|
405
414
|
)
|
406
|
-
|
407
|
-
@staticmethod
|
408
|
-
def _use_file_transfer(parsed_config: AbstractFileBasedSpec) -> bool:
|
409
|
-
use_file_transfer = (
|
410
|
-
hasattr(parsed_config.delivery_method, "delivery_type")
|
411
|
-
and parsed_config.delivery_method.delivery_type == "use_file_transfer"
|
412
|
-
)
|
413
|
-
return use_file_transfer
|
414
|
-
|
415
|
-
@staticmethod
|
416
|
-
def _use_records_transfer(parsed_config: AbstractFileBasedSpec) -> bool:
|
417
|
-
use_records_transfer = (
|
418
|
-
hasattr(parsed_config.delivery_method, "delivery_type")
|
419
|
-
and parsed_config.delivery_method.delivery_type == "use_records_transfer"
|
420
|
-
)
|
421
|
-
return use_records_transfer
|
422
|
-
|
423
|
-
@staticmethod
|
424
|
-
def _preserve_directory_structure(parsed_config: AbstractFileBasedSpec) -> bool:
|
425
|
-
"""
|
426
|
-
Determines whether to preserve directory structure during file transfer.
|
427
|
-
|
428
|
-
When enabled, files maintain their subdirectory paths in the destination.
|
429
|
-
When disabled, files are flattened to the root of the destination.
|
430
|
-
|
431
|
-
Args:
|
432
|
-
parsed_config: The parsed configuration containing delivery method settings
|
433
|
-
|
434
|
-
Returns:
|
435
|
-
True if directory structure should be preserved (default), False otherwise
|
436
|
-
"""
|
437
|
-
if (
|
438
|
-
FileBasedSource._use_file_transfer(parsed_config)
|
439
|
-
and hasattr(parsed_config.delivery_method, "preserve_directory_structure")
|
440
|
-
and parsed_config.delivery_method.preserve_directory_structure is not None
|
441
|
-
):
|
442
|
-
return parsed_config.delivery_method.preserve_directory_structure
|
443
|
-
return True
|
444
|
-
|
445
|
-
@staticmethod
|
446
|
-
def _sync_acl_permissions(parsed_config: AbstractFileBasedSpec) -> bool:
|
447
|
-
if (
|
448
|
-
FileBasedSource._use_records_transfer(parsed_config)
|
449
|
-
and hasattr(parsed_config.delivery_method, "sync_acl_permissions")
|
450
|
-
and parsed_config.delivery_method.sync_acl_permissions is not None
|
451
|
-
):
|
452
|
-
return parsed_config.delivery_method.sync_acl_permissions
|
453
|
-
return False
|
@@ -13,6 +13,11 @@ from typing import Any, Dict, Iterable, List, Optional, Set
|
|
13
13
|
from wcmatch.glob import GLOBSTAR, globmatch
|
14
14
|
|
15
15
|
from airbyte_cdk.sources.file_based.config.abstract_file_based_spec import AbstractFileBasedSpec
|
16
|
+
from airbyte_cdk.sources.file_based.config.validate_config_transfer_modes import (
|
17
|
+
include_identities_stream,
|
18
|
+
preserve_directory_structure,
|
19
|
+
use_file_transfer,
|
20
|
+
)
|
16
21
|
from airbyte_cdk.sources.file_based.remote_file import RemoteFile
|
17
22
|
|
18
23
|
|
@@ -128,41 +133,18 @@ class AbstractFileBasedStreamReader(ABC):
|
|
128
133
|
|
129
134
|
def use_file_transfer(self) -> bool:
|
130
135
|
if self.config:
|
131
|
-
use_file_transfer
|
132
|
-
hasattr(self.config.delivery_method, "delivery_type")
|
133
|
-
and self.config.delivery_method.delivery_type == "use_file_transfer"
|
134
|
-
)
|
135
|
-
return use_file_transfer
|
136
|
-
return False
|
137
|
-
|
138
|
-
def use_records_transfer(self) -> bool:
|
139
|
-
if self.config:
|
140
|
-
use_records_transfer = (
|
141
|
-
hasattr(self.config.delivery_method, "delivery_type")
|
142
|
-
and self.config.delivery_method.delivery_type == "use_records_transfer"
|
143
|
-
)
|
144
|
-
return use_records_transfer
|
136
|
+
return use_file_transfer(self.config)
|
145
137
|
return False
|
146
138
|
|
147
139
|
def preserve_directory_structure(self) -> bool:
|
148
140
|
# fall back to preserve subdirectories if config is not present or incomplete
|
149
|
-
if
|
150
|
-
self.
|
151
|
-
and self.config
|
152
|
-
and hasattr(self.config.delivery_method, "preserve_directory_structure")
|
153
|
-
and self.config.delivery_method.preserve_directory_structure is not None
|
154
|
-
):
|
155
|
-
return self.config.delivery_method.preserve_directory_structure
|
141
|
+
if self.config:
|
142
|
+
return preserve_directory_structure(self.config)
|
156
143
|
return True
|
157
144
|
|
158
|
-
def
|
159
|
-
if
|
160
|
-
self.config
|
161
|
-
and self.use_records_transfer()
|
162
|
-
and hasattr(self.config.delivery_method, "sync_acl_permissions")
|
163
|
-
and self.config.delivery_method.sync_acl_permissions is not None
|
164
|
-
):
|
165
|
-
return self.config.delivery_method.sync_acl_permissions
|
145
|
+
def include_identities_stream(self) -> bool:
|
146
|
+
if self.config:
|
147
|
+
return include_identities_stream(self.config)
|
166
148
|
return False
|
167
149
|
|
168
150
|
@abstractmethod
|
@@ -203,16 +185,22 @@ class AbstractFileBasedStreamReader(ABC):
|
|
203
185
|
absolute_file_path = path.abspath(local_file_path)
|
204
186
|
return [file_relative_path, local_file_path, absolute_file_path]
|
205
187
|
|
188
|
+
@abstractmethod
|
206
189
|
def get_file_acl_permissions(self, file: RemoteFile, logger: logging.Logger) -> Dict[str, Any]:
|
207
190
|
"""
|
208
191
|
This is required for connectors that will support syncing
|
209
192
|
ACL Permissions from files.
|
210
193
|
"""
|
211
|
-
|
194
|
+
raise NotImplementedError(
|
195
|
+
f"{self.__class__.__name__} must implement get_file_acl_permissions()"
|
196
|
+
)
|
212
197
|
|
198
|
+
@abstractmethod
|
213
199
|
def load_identity_groups(self, logger: logging.Logger) -> Iterable[Dict[str, Any]]:
|
214
200
|
"""
|
215
201
|
This is required for connectors that will support syncing
|
216
202
|
identities.
|
217
203
|
"""
|
218
|
-
|
204
|
+
raise NotImplementedError(
|
205
|
+
f"{self.__class__.__name__} must implement load_identity_groups()"
|
206
|
+
)
|
@@ -48,7 +48,7 @@ class DefaultFileBasedStream(AbstractFileBasedStream, IncrementalMixin):
|
|
48
48
|
|
49
49
|
FILE_TRANSFER_KW = "use_file_transfer"
|
50
50
|
PRESERVE_DIRECTORY_STRUCTURE_KW = "preserve_directory_structure"
|
51
|
-
|
51
|
+
PERMISSIONS_TRANSFER_KW = "use_permissions_transfer"
|
52
52
|
FILES_KEY = "files"
|
53
53
|
DATE_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
|
54
54
|
ab_last_mod_col = "_ab_source_file_last_modified"
|
@@ -58,7 +58,7 @@ class DefaultFileBasedStream(AbstractFileBasedStream, IncrementalMixin):
|
|
58
58
|
airbyte_columns = [ab_last_mod_col, ab_file_name_col]
|
59
59
|
use_file_transfer = False
|
60
60
|
preserve_directory_structure = True
|
61
|
-
|
61
|
+
use_permissions_transfer = False
|
62
62
|
|
63
63
|
def __init__(self, **kwargs: Any):
|
64
64
|
if self.FILE_TRANSFER_KW in kwargs:
|
@@ -67,8 +67,8 @@ class DefaultFileBasedStream(AbstractFileBasedStream, IncrementalMixin):
|
|
67
67
|
self.preserve_directory_structure = kwargs.pop(
|
68
68
|
self.PRESERVE_DIRECTORY_STRUCTURE_KW, True
|
69
69
|
)
|
70
|
-
if self.
|
71
|
-
self.
|
70
|
+
if self.PERMISSIONS_TRANSFER_KW in kwargs:
|
71
|
+
self.use_permissions_transfer = kwargs.pop(self.PERMISSIONS_TRANSFER_KW, False)
|
72
72
|
super().__init__(**kwargs)
|
73
73
|
|
74
74
|
@property
|
@@ -110,7 +110,7 @@ class DefaultFileBasedStream(AbstractFileBasedStream, IncrementalMixin):
|
|
110
110
|
self.ab_file_name_col: {"type": "string"},
|
111
111
|
},
|
112
112
|
}
|
113
|
-
elif self.
|
113
|
+
elif self.use_permissions_transfer:
|
114
114
|
return remote_file_permissions_schema
|
115
115
|
else:
|
116
116
|
return super()._filter_schema_invalid_properties(configured_catalog_json_schema)
|
@@ -194,7 +194,7 @@ class DefaultFileBasedStream(AbstractFileBasedStream, IncrementalMixin):
|
|
194
194
|
yield stream_data_to_airbyte_message(
|
195
195
|
self.name, record, is_file_transfer_message=True
|
196
196
|
)
|
197
|
-
elif self.
|
197
|
+
elif self.use_permissions_transfer:
|
198
198
|
try:
|
199
199
|
permissions_record = self.stream_reader.get_file_acl_permissions(
|
200
200
|
file, logger=self.logger
|
@@ -314,7 +314,7 @@ class DefaultFileBasedStream(AbstractFileBasedStream, IncrementalMixin):
|
|
314
314
|
def _get_raw_json_schema(self) -> JsonSchema:
|
315
315
|
if self.use_file_transfer:
|
316
316
|
return file_transfer_schema
|
317
|
-
elif self.
|
317
|
+
elif self.use_permissions_transfer:
|
318
318
|
return remote_file_permissions_schema
|
319
319
|
elif self.config.input_schema:
|
320
320
|
return self.config.get_input_schema() # type: ignore
|
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
import traceback
|
6
6
|
from functools import cache
|
7
|
-
from typing import Any, Iterable, List, Mapping, MutableMapping, Optional
|
7
|
+
from typing import Any, Dict, Iterable, List, Mapping, MutableMapping, Optional
|
8
8
|
|
9
9
|
from airbyte_protocol_dataclasses.models import SyncMode
|
10
10
|
|
@@ -68,7 +68,7 @@ class IdentitiesStream(Stream):
|
|
68
68
|
stream_state: Optional[Mapping[str, Any]] = None,
|
69
69
|
) -> Iterable[Mapping[str, Any] | AirbyteMessage]:
|
70
70
|
try:
|
71
|
-
identity_groups = self.
|
71
|
+
identity_groups = self.load_identity_groups()
|
72
72
|
for record in identity_groups:
|
73
73
|
yield stream_data_to_airbyte_message(self.name, record)
|
74
74
|
except AirbyteTracedException as exc:
|
@@ -84,6 +84,9 @@ class IdentitiesStream(Stream):
|
|
84
84
|
),
|
85
85
|
)
|
86
86
|
|
87
|
+
def load_identity_groups(self) -> Iterable[Dict[str, Any]]:
|
88
|
+
return self.stream_reader.load_identity_groups(logger=self.logger)
|
89
|
+
|
87
90
|
@cache
|
88
91
|
def get_json_schema(self) -> JsonSchema:
|
89
92
|
return remote_file_identity_schema
|
@@ -6,9 +6,6 @@ from abc import abstractmethod
|
|
6
6
|
from datetime import datetime, timedelta, timezone
|
7
7
|
from typing import Any, Callable, List, MutableMapping, Optional, Tuple
|
8
8
|
|
9
|
-
import pendulum
|
10
|
-
from pendulum.datetime import DateTime
|
11
|
-
|
12
9
|
# FIXME We would eventually like the Concurrent package do be agnostic of the declarative package. However, this is a breaking change and
|
13
10
|
# the goal in the short term is only to fix the issue we are seeing for source-declarative-manifest.
|
14
11
|
from airbyte_cdk.sources.declarative.datetime.datetime_parser import DatetimeParser
|
@@ -17,6 +14,7 @@ from airbyte_cdk.sources.streams.concurrent.state_converters.abstract_stream_sta
|
|
17
14
|
AbstractStreamStateConverter,
|
18
15
|
ConcurrencyCompatibleStateType,
|
19
16
|
)
|
17
|
+
from airbyte_cdk.utils.datetime_helpers import AirbyteDateTime, ab_datetime_now, ab_datetime_parse
|
20
18
|
|
21
19
|
|
22
20
|
class DateTimeStreamStateConverter(AbstractStreamStateConverter):
|
@@ -36,7 +34,7 @@ class DateTimeStreamStateConverter(AbstractStreamStateConverter):
|
|
36
34
|
|
37
35
|
@classmethod
|
38
36
|
def get_end_provider(cls) -> Callable[[], datetime]:
|
39
|
-
return
|
37
|
+
return ab_datetime_now
|
40
38
|
|
41
39
|
@abstractmethod
|
42
40
|
def increment(self, timestamp: datetime) -> datetime: ...
|
@@ -136,10 +134,10 @@ class EpochValueConcurrentStreamStateConverter(DateTimeStreamStateConverter):
|
|
136
134
|
return int(timestamp.timestamp())
|
137
135
|
|
138
136
|
def parse_timestamp(self, timestamp: int) -> datetime:
|
139
|
-
dt_object =
|
140
|
-
if not isinstance(dt_object,
|
137
|
+
dt_object = AirbyteDateTime.fromtimestamp(timestamp, timezone.utc)
|
138
|
+
if not isinstance(dt_object, AirbyteDateTime):
|
141
139
|
raise ValueError(
|
142
|
-
f"
|
140
|
+
f"AirbyteDateTime object was expected but got {type(dt_object)} from AirbyteDateTime.fromtimestamp({timestamp})"
|
143
141
|
)
|
144
142
|
return dt_object
|
145
143
|
|
@@ -169,14 +167,25 @@ class IsoMillisConcurrentStreamStateConverter(DateTimeStreamStateConverter):
|
|
169
167
|
def increment(self, timestamp: datetime) -> datetime:
|
170
168
|
return timestamp + self._cursor_granularity
|
171
169
|
|
172
|
-
def output_format(self, timestamp: datetime) ->
|
173
|
-
|
170
|
+
def output_format(self, timestamp: datetime) -> str:
|
171
|
+
"""Format datetime with milliseconds always included.
|
172
|
+
|
173
|
+
Args:
|
174
|
+
timestamp: The datetime to format.
|
175
|
+
|
176
|
+
Returns:
|
177
|
+
str: ISO8601/RFC3339 formatted string with milliseconds.
|
178
|
+
"""
|
179
|
+
dt = AirbyteDateTime.from_datetime(timestamp)
|
180
|
+
# Always include milliseconds, even if zero
|
181
|
+
millis = dt.microsecond // 1000 if dt.microsecond else 0
|
182
|
+
return f"{dt.year:04d}-{dt.month:02d}-{dt.day:02d}T{dt.hour:02d}:{dt.minute:02d}:{dt.second:02d}.{millis:03d}Z"
|
174
183
|
|
175
184
|
def parse_timestamp(self, timestamp: str) -> datetime:
|
176
|
-
dt_object =
|
177
|
-
if not isinstance(dt_object,
|
185
|
+
dt_object = ab_datetime_parse(timestamp)
|
186
|
+
if not isinstance(dt_object, AirbyteDateTime):
|
178
187
|
raise ValueError(
|
179
|
-
f"
|
188
|
+
f"AirbyteDateTime object was expected but got {type(dt_object)} from parse({timestamp})"
|
180
189
|
)
|
181
190
|
return dt_object
|
182
191
|
|
@@ -184,7 +193,7 @@ class IsoMillisConcurrentStreamStateConverter(DateTimeStreamStateConverter):
|
|
184
193
|
class CustomFormatConcurrentStreamStateConverter(IsoMillisConcurrentStreamStateConverter):
|
185
194
|
"""
|
186
195
|
Datetime State converter that emits state according to the supplied datetime format. The converter supports reading
|
187
|
-
incoming state in any valid datetime format
|
196
|
+
incoming state in any valid datetime format using AirbyteDateTime parsing utilities.
|
188
197
|
"""
|
189
198
|
|
190
199
|
def __init__(
|
@@ -223,17 +223,17 @@ class Stream(ABC):
|
|
223
223
|
record_counter += 1
|
224
224
|
|
225
225
|
checkpoint_interval = self.state_checkpoint_interval
|
226
|
-
checkpoint = checkpoint_reader.get_checkpoint()
|
227
226
|
if (
|
228
227
|
should_checkpoint
|
229
228
|
and checkpoint_interval
|
230
229
|
and record_counter % checkpoint_interval == 0
|
231
|
-
and checkpoint is not None
|
232
230
|
):
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
231
|
+
checkpoint = checkpoint_reader.get_checkpoint()
|
232
|
+
if checkpoint:
|
233
|
+
airbyte_state_message = self._checkpoint_state(
|
234
|
+
checkpoint, state_manager=state_manager
|
235
|
+
)
|
236
|
+
yield airbyte_state_message
|
237
237
|
|
238
238
|
if internal_config.is_limit_reached(record_counter):
|
239
239
|
break
|
@@ -423,8 +423,6 @@ class HttpStream(Stream, CheckpointMixin, ABC):
|
|
423
423
|
stream_slice: Optional[Mapping[str, Any]] = None,
|
424
424
|
stream_state: Optional[Mapping[str, Any]] = None,
|
425
425
|
) -> Iterable[StreamData]:
|
426
|
-
partition, _, _ = self._extract_slice_fields(stream_slice=stream_slice)
|
427
|
-
|
428
426
|
stream_state = stream_state or {}
|
429
427
|
pagination_complete = False
|
430
428
|
next_page_token = None
|
@@ -438,6 +436,7 @@ class HttpStream(Stream, CheckpointMixin, ABC):
|
|
438
436
|
|
439
437
|
cursor = self.get_cursor()
|
440
438
|
if cursor and isinstance(cursor, SubstreamResumableFullRefreshCursor):
|
439
|
+
partition, _, _ = self._extract_slice_fields(stream_slice=stream_slice)
|
441
440
|
# Substreams checkpoint state by marking an entire parent partition as completed so that on the subsequent attempt
|
442
441
|
# after a failure, completed parents are skipped and the sync can make progress
|
443
442
|
cursor.close_slice(StreamSlice(cursor_slice={}, partition=partition))
|