Skip to content

Commit 33eddee

Browse files
🐛 Source stripe: fix case where stream wont have a state attribute and needs to resolve get_updated_state and upgrade CDK 4 (remove availability strategy) (#43302)
1 parent 64c3870 commit 33eddee

28 files changed

+359
-1260
lines changed

airbyte-integrations/connectors/source-stripe/metadata.yaml

+1-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ data:
1010
connectorSubtype: api
1111
connectorType: source
1212
definitionId: e094cb9a-26de-4645-8761-65c0c425d1de
13-
dockerImageTag: 5.4.12
13+
dockerImageTag: 5.5.0
1414
dockerRepository: airbyte/source-stripe
1515
documentationUrl: https://docs.airbyte.com/integrations/sources/stripe
1616
githubIssueLabel: source-stripe
@@ -25,7 +25,6 @@ data:
2525
registries:
2626
cloud:
2727
enabled: true
28-
dockerImageTag: 5.4.5
2928
oss:
3029
enabled: true
3130
releaseStage: generally_available

airbyte-integrations/connectors/source-stripe/poetry.lock

+31-31
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

airbyte-integrations/connectors/source-stripe/pyproject.toml

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ requires = [ "poetry-core>=1.0.0",]
33
build-backend = "poetry.core.masonry.api"
44

55
[tool.poetry]
6-
version = "5.4.12"
6+
version = "5.5.0"
77
name = "source-stripe"
88
description = "Source implementation for Stripe."
99
authors = [ "Airbyte <[email protected]>",]
@@ -16,10 +16,10 @@ repository = "https://github.com/airbytehq/airbyte"
1616
include = "source_stripe"
1717

1818
[tool.poetry.dependencies]
19-
python = "^3.9,<3.12"
19+
python = "^3.10,<3.12"
2020
stripe = "==2.56.0"
2121
pendulum = "==2.1.2"
22-
airbyte-cdk = "^2"
22+
airbyte-cdk = "^4"
2323

2424
[tool.poetry.scripts]
2525
source-stripe = "source_stripe.run:run"

airbyte-integrations/connectors/source-stripe/source_stripe/availability_strategy.py

-114
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .parent_incremental_stripe_sub_stream_error_handler import ParentIncrementalStripeSubStreamErrorHandler
2+
from .stripe_error_handler import StripeErrorHandler
3+
4+
__all__ = ['StripeErrorHandler', 'ParentIncrementalStripeSubStreamErrorHandler']
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#
2+
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
3+
#
4+
5+
from typing import Optional, Union
6+
7+
import requests
8+
from airbyte_cdk.sources.streams.http.error_handlers.response_models import ErrorResolution
9+
from source_stripe.error_handlers.stripe_error_handler import StripeErrorHandler
10+
11+
12+
class ParentIncrementalStripeSubStreamErrorHandler(StripeErrorHandler):
13+
def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]] = None) -> ErrorResolution:
14+
if not isinstance(response_or_exception, Exception) and response_or_exception.status_code == requests.codes.not_found:
15+
# When running incremental sync with state, the returned parent object very likely will not contain sub-items
16+
# as the events API does not support expandable items. Parent class will try getting sub-items from this object,
17+
# then from its own API. In case there are no sub-items at all for this entity, API will raise 404 error.
18+
self._logger.warning(
19+
f"Data was not found for URL: {response_or_exception.request.url}. "
20+
"If this is a path for getting child attributes like /v1/checkout/sessions/<session_id>/line_items when running "
21+
"the incremental sync, you may safely ignore this warning."
22+
)
23+
return super().interpret_response(response_or_exception)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#
2+
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
3+
#
4+
5+
import logging
6+
from typing import Optional, Union
7+
8+
import requests
9+
from airbyte_cdk.models import FailureType
10+
from airbyte_cdk.sources.streams.http import HttpStream
11+
from airbyte_cdk.sources.streams.http.error_handlers import HttpStatusErrorHandler
12+
from airbyte_cdk.sources.streams.http.error_handlers.response_models import ErrorResolution, ResponseAction
13+
14+
STRIPE_ERROR_CODES = {
15+
"more_permissions_required": "This is most likely due to insufficient permissions on the credentials in use. "
16+
"Try to grant required permissions/scopes or re-authenticate",
17+
"account_invalid": "The card, or account the card is connected to, is invalid. You need to contact your card issuer "
18+
"to check that the card is working correctly.",
19+
"oauth_not_supported": "Please use a different authentication method.",
20+
}
21+
22+
DOCS_URL = f"https://docs.airbyte.com/integrations/sources/stripe"
23+
DOCUMENTATION_MESSAGE = f"Please visit {DOCS_URL} to learn more. "
24+
25+
26+
class StripeErrorHandler(HttpStatusErrorHandler):
27+
def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]] = None) -> ErrorResolution:
28+
if not isinstance(response_or_exception, Exception) and response_or_exception.status_code in (
29+
requests.codes.bad_request,
30+
requests.codes.forbidden,
31+
):
32+
parsed_error = response_or_exception.json()
33+
error_code = parsed_error.get("error", {}).get("code")
34+
error_message = STRIPE_ERROR_CODES.get(error_code, parsed_error.get("error", {}).get("message"))
35+
if error_message:
36+
reason = f"The endpoint {response_or_exception.url} returned {response_or_exception.status_code}: {response_or_exception.reason}. {error_message}. {DOCUMENTATION_MESSAGE} "
37+
response_error_message = HttpStream.parse_response_error_message(response_or_exception)
38+
if response_error_message:
39+
reason += response_error_message
40+
41+
return ErrorResolution(response_action=ResponseAction.IGNORE, error_message=reason)
42+
return super().interpret_response(response_or_exception)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .parent_incremental_stripe_sub_stream_error_mapping import PARENT_INCREMENTAL_STRIPE_SUB_STREAM_ERROR_MAPPING
2+
3+
__all__ = ['PARENT_INCREMENTAL_STRIPE_SUB_STREAM_ERROR_MAPPING']
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#
2+
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
3+
#
4+
5+
from airbyte_cdk.sources.streams.http.error_handlers.default_error_mapping import DEFAULT_ERROR_MAPPING
6+
from airbyte_cdk.sources.streams.http.error_handlers.response_models import ErrorResolution, ResponseAction
7+
8+
PARENT_INCREMENTAL_STRIPE_SUB_STREAM_ERROR_MAPPING = DEFAULT_ERROR_MAPPING | {
9+
404: ErrorResolution(
10+
response_action=ResponseAction.IGNORE,
11+
error_message="Data was not found for URL.",
12+
),
13+
}

0 commit comments

Comments
 (0)