1
0
mirror of synced 2026-01-09 06:03:17 -05:00
Files
airbyte/airbyte-integrations/connectors/source-quickbooks/source_quickbooks/components.py
Maxime Carbonneau-Leclerc 074cddf690 🐛 Source Quickbooks: update following state management changes in the CDK (#27803)
* Update quickbooks following state management cleanup

* Update changelogs

* Update tests

* Ensure CDK version

* Update update_state to close_slice

* Update test to use partition end datetime

* Update considering fix

* renaming
2023-06-29 11:13:02 -04:00

93 lines
3.3 KiB
Python

#
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
#
import datetime
import typing
from collections import abc
from dataclasses import dataclass
import dpath.util
from airbyte_cdk.sources.declarative.incremental import DatetimeBasedCursor
from airbyte_cdk.sources.declarative.types import Record, StreamSlice
class LastRecordDictProxy(abc.MutableMapping):
"""
Patch a dict object to be able to get/set/delete/etc... values by path.
Example:
>>> record = LastRecordDictProxy({"root": {"nested": "value"}})
>>> record["root/nested"]
<<< "value"
>>> record.get("root/nested")
<<< "value"
"""
def __init__(self, record: Record, field_mapping: typing.Mapping = None):
self._record: Record = record
self._field_mapping = field_mapping if field_mapping is not None else {}
def __setitem__(self, k: str, v: typing.Any) -> None:
dpath.util.set(self._record, k, v)
def __delitem__(self, v: str) -> None:
dpath.util.delete(self._record, v)
def __getitem__(self, k: str) -> typing.Any:
try:
return dpath.util.get(self._record, k)
except KeyError as e:
if k in self._field_mapping:
return dpath.util.get(self._record, self._field_mapping[k])
raise e
def __len__(self) -> int:
return len(self._record)
def __iter__(self) -> typing.Iterator:
return self._record
def __bool__(self):
return bool(self._record)
@dataclass
class CustomDatetimeBasedCursor(DatetimeBasedCursor):
"""
This class is used to override the default DatetimeBasedCursor behavior in the way the cursor values from the `last_record` are
retrieved, specifically the nested values. In case the last_record looks like follows, there is no way we can get the nested cursor
value for now by means of the base class.
{
"id": "id",
"Metadata": {
"LastUpdatedTime": "<DateTime>"
}
}
To adopt this change to the LowCode CDK, this issue was created - https://github.com/airbytehq/airbyte/issues/25008.
"""
def close_slice(self, stream_slice: StreamSlice, most_recent_record: typing.Optional[Record]) -> None:
super(CustomDatetimeBasedCursor, self).close_slice(
stream_slice=stream_slice,
last_record=LastRecordDictProxy(most_recent_record, {self.cursor_field.eval(self.config): "MetaData/LastUpdatedTime"}),
)
def _format_datetime(self, dt: datetime.datetime):
return dt.isoformat("T", "seconds")
def parse_date(self, date: str) -> datetime.datetime:
return datetime.datetime.strptime(date, self.datetime_format).astimezone(self._timezone)
def should_be_synced(self, record: Record) -> bool:
"""
As of 2023-06-28, the expectation is that this method will only be used for semi-incremental and data feed and therefore the
implementation is irrelevant for quickbooks
"""
return True
def is_greater_than_or_equal(self, first: Record, second: Record) -> bool:
return super(CustomDatetimeBasedCursor, self).close_slice(
LastRecordDictProxy(first, {self.cursor_field.eval(self.config): "MetaData/LastUpdatedTime"}),
LastRecordDictProxy(second, {self.cursor_field.eval(self.config): "MetaData/LastUpdatedTime"}),
)