forked from airbytehq/airbyte
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Source Stripe: add availability strategy (airbytehq#22659)
* Source Stripe: add availability strategy * Source Stripe: bump version, update changelog * Source Stripe: optimize substream availability strategy, add tests * Source Stripe: better names * Source Stripe: small review fixes * Update docs/integrations/sources/stripe.md fix changelog message Co-authored-by: Ella Rohm-Ensing <erohmensing@gmail.com> * Update airbyte-integrations/connectors/source-stripe/unit_tests/test_availability_strategy.py assert availability result Co-authored-by: Ella Rohm-Ensing <erohmensing@gmail.com> --------- Co-authored-by: Ella Rohm-Ensing <erohmensing@gmail.com>
- Loading branch information
1 parent
de51dff
commit 1d406ef
Showing
6 changed files
with
157 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
airbyte-integrations/connectors/source-stripe/source_stripe/availability_strategy.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import logging | ||
from typing import Optional, Tuple | ||
|
||
from airbyte_cdk.sources import Source | ||
from airbyte_cdk.sources.streams import Stream | ||
from airbyte_cdk.sources.streams.http.availability_strategy import HttpAvailabilityStrategy | ||
|
||
|
||
class StripeSubStreamAvailabilityStrategy(HttpAvailabilityStrategy): | ||
def check_availability(self, stream: Stream, logger: logging.Logger, source: Optional[Source]) -> Tuple[bool, Optional[str]]: | ||
"""Traverse through all the parents of a given stream and run availability strategy on each of them""" | ||
try: | ||
current_stream, parent_stream = stream, getattr(stream, "parent") | ||
except AttributeError: | ||
return super().check_availability(stream, logger, source) | ||
if parent_stream: | ||
parent_stream_instance = getattr(current_stream, "get_parent_stream_instance")() | ||
# Accessing the `availability_strategy` property will instantiate AvailabilityStrategy under the hood | ||
availability_strategy = parent_stream_instance.availability_strategy | ||
if availability_strategy: | ||
is_available, reason = availability_strategy.check_availability(parent_stream_instance, logger, source) | ||
if not is_available: | ||
return is_available, reason | ||
return super().check_availability(stream, logger, source) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
119 changes: 119 additions & 0 deletions
119
airbyte-integrations/connectors/source-stripe/unit_tests/test_availability_strategy.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import pendulum | ||
from airbyte_cdk.sources.streams.http.availability_strategy import HttpAvailabilityStrategy | ||
|
||
from source_stripe.availability_strategy import StripeSubStreamAvailabilityStrategy | ||
from source_stripe.streams import InvoiceLineItems, Invoices | ||
|
||
|
||
def test_traverse_over_substreams(mocker): | ||
# Mock base HttpAvailabilityStrategy to capture all the check_availability method calls | ||
check_availability_mock = mocker.MagicMock() | ||
check_availability_mock.return_value = (True, None) | ||
mocker.patch( | ||
"airbyte_cdk.sources.streams.http.availability_strategy.HttpAvailabilityStrategy.check_availability", | ||
check_availability_mock | ||
) | ||
|
||
# Prepare tree of nested objects | ||
root = mocker.Mock() | ||
root.availability_strategy = HttpAvailabilityStrategy() | ||
root.parent = None | ||
|
||
child_1 = mocker.Mock() | ||
child_1.availability_strategy = StripeSubStreamAvailabilityStrategy() | ||
child_1.get_parent_stream_instance.return_value = root | ||
|
||
child_1_1 = mocker.Mock() | ||
child_1_1.availability_strategy = StripeSubStreamAvailabilityStrategy() | ||
child_1_1.get_parent_stream_instance.return_value = child_1 | ||
|
||
child_1_1_1 = mocker.Mock() | ||
child_1_1_1.availability_strategy = StripeSubStreamAvailabilityStrategy() | ||
child_1_1_1.get_parent_stream_instance.return_value = child_1_1 | ||
|
||
# Start traverse | ||
is_available, reason = child_1_1_1.availability_strategy.check_availability(child_1_1_1, mocker.Mock(), mocker.Mock()) | ||
|
||
assert is_available and reason is None | ||
|
||
# Check availability strategy was called once for every nested object | ||
assert check_availability_mock.call_count == 4 | ||
|
||
# Check each availability strategy was called with proper instance argument | ||
assert id(check_availability_mock.call_args_list[0].args[0]) == id(root) | ||
assert id(check_availability_mock.call_args_list[1].args[0]) == id(child_1) | ||
assert id(check_availability_mock.call_args_list[2].args[0]) == id(child_1_1) | ||
assert id(check_availability_mock.call_args_list[3].args[0]) == id(child_1_1_1) | ||
|
||
|
||
def test_traverse_over_substreams_failure(mocker): | ||
# Mock base HttpAvailabilityStrategy to capture all the check_availability method calls | ||
check_availability_mock = mocker.MagicMock() | ||
check_availability_mock.side_effect = [(True, None), (False, "child_1")] | ||
mocker.patch( | ||
"airbyte_cdk.sources.streams.http.availability_strategy.HttpAvailabilityStrategy.check_availability", | ||
check_availability_mock | ||
) | ||
|
||
# Prepare tree of nested objects | ||
root = mocker.Mock() | ||
root.availability_strategy = HttpAvailabilityStrategy() | ||
root.parent = None | ||
|
||
child_1 = mocker.Mock() | ||
child_1.availability_strategy = StripeSubStreamAvailabilityStrategy() | ||
child_1.get_parent_stream_instance.return_value = root | ||
|
||
child_1_1 = mocker.Mock() | ||
child_1_1.availability_strategy = StripeSubStreamAvailabilityStrategy() | ||
child_1_1.get_parent_stream_instance.return_value = child_1 | ||
|
||
child_1_1_1 = mocker.Mock() | ||
child_1_1_1.availability_strategy = StripeSubStreamAvailabilityStrategy() | ||
child_1_1_1.get_parent_stream_instance.return_value = child_1_1 | ||
|
||
# Start traverse | ||
is_available, reason = child_1_1_1.availability_strategy.check_availability(child_1_1_1, mocker.Mock(), mocker.Mock()) | ||
|
||
assert not is_available and reason == "child_1" | ||
|
||
# Check availability strategy was called once for every nested object | ||
assert check_availability_mock.call_count == 2 | ||
|
||
# Check each availability strategy was called with proper instance argument | ||
assert id(check_availability_mock.call_args_list[0].args[0]) == id(root) | ||
assert id(check_availability_mock.call_args_list[1].args[0]) == id(child_1) | ||
|
||
|
||
def test_substream_availability(mocker): | ||
check_availability_mock = mocker.MagicMock() | ||
check_availability_mock.return_value = (True, None) | ||
mocker.patch( | ||
"airbyte_cdk.sources.streams.http.availability_strategy.HttpAvailabilityStrategy.check_availability", | ||
check_availability_mock | ||
) | ||
|
||
stream = InvoiceLineItems(start_date=pendulum.today().subtract(days=3).int_timestamp, account_id="None") | ||
is_available, reason = stream.availability_strategy.check_availability(stream, mocker.Mock(), mocker.Mock()) | ||
assert is_available and reason is None | ||
|
||
assert check_availability_mock.call_count == 2 | ||
assert isinstance(check_availability_mock.call_args_list[0].args[0], Invoices) | ||
assert isinstance(check_availability_mock.call_args_list[1].args[0], InvoiceLineItems) | ||
|
||
|
||
def test_substream_availability_no_parent(mocker): | ||
check_availability_mock = mocker.MagicMock() | ||
check_availability_mock.return_value = (True, None) | ||
mocker.patch( | ||
"airbyte_cdk.sources.streams.http.availability_strategy.HttpAvailabilityStrategy.check_availability", | ||
check_availability_mock | ||
) | ||
|
||
stream = InvoiceLineItems(start_date=pendulum.today().subtract(days=3).int_timestamp, account_id="None") | ||
stream.parent = None | ||
|
||
stream.availability_strategy.check_availability(stream, mocker.Mock(), mocker.Mock()) | ||
|
||
assert check_availability_mock.call_count == 1 | ||
assert isinstance(check_availability_mock.call_args_list[0].args[0], InvoiceLineItems) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters