Skip to content

Commit 8c459a6

Browse files
authored
fix(source-tiktok-marketing): fix error handling for 40100 error codes (airbytehq#46676)
1 parent f8ac7f9 commit 8c459a6

File tree

10 files changed

+535
-256
lines changed

10 files changed

+535
-256
lines changed

airbyte-integrations/connectors/source-tiktok-marketing/metadata.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ data:
1111
connectorSubtype: api
1212
connectorType: source
1313
definitionId: 4bfac00d-ce15-44ff-95b9-9e3c3e8fbd35
14-
dockerImageTag: 4.3.4
14+
dockerImageTag: 4.3.5
1515
dockerRepository: airbyte/source-tiktok-marketing
1616
documentationUrl: https://docs.airbyte.com/integrations/sources/tiktok-marketing
1717
githubIssueLabel: source-tiktok-marketing

airbyte-integrations/connectors/source-tiktok-marketing/poetry.lock

Lines changed: 421 additions & 170 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

airbyte-integrations/connectors/source-tiktok-marketing/pyproject.toml

Lines changed: 3 additions & 2 deletions
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 = "4.3.4"
6+
version = "4.3.5"
77
name = "source-tiktok-marketing"
88
description = "Source implementation for Tiktok Marketing."
99
authors = [ "Airbyte <[email protected]>",]
@@ -17,7 +17,8 @@ include = "source_tiktok_marketing"
1717

1818
[tool.poetry.dependencies]
1919
python = "^3.10,<3.12"
20-
airbyte-cdk = "^4.5.4"
20+
airbyte-cdk = "^5"
21+
freezegun = "^1.1.0"
2122

2223
[tool.poetry.scripts]
2324
source-tiktok-marketing = "source_tiktok_marketing.run:run"

airbyte-integrations/connectors/source-tiktok-marketing/source_tiktok_marketing/manifest.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ definitions:
2222
error_handler:
2323
type: DefaultErrorHandler
2424
response_filters:
25+
- predicate: "{{ response.get('code') == 40100 }}"
26+
action: RATE_LIMITED
2527
- predicate: "{{ response.get('code') != 0 }}"
2628
action: FAIL
2729
error_message: "{{ response['message'] }}"

airbyte-integrations/connectors/source-tiktok-marketing/unit_tests/conftest.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
22

3-
import os
3+
import datetime
44

5+
import freezegun
56
import pytest
67

78

@@ -20,3 +21,9 @@ def config_fixture():
2021
}
2122
return config
2223

24+
25+
@pytest.fixture()
26+
def mock_sleep(monkeypatch):
27+
with freezegun.freeze_time(datetime.datetime.now(), ignore=["_pytest.runner", "_pytest.terminal"]) as frozen_datetime:
28+
monkeypatch.setattr("time.sleep", lambda x: frozen_datetime.tick(x))
29+
yield

airbyte-integrations/connectors/source-tiktok-marketing/unit_tests/integration/test_creative_assets_music.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
from unittest import TestCase
55

66
from advetiser_slices import mock_advertisers_slices
7+
from airbyte_cdk.models import SyncMode
78
from airbyte_cdk.test.catalog_builder import CatalogBuilder
89
from airbyte_cdk.test.entrypoint_wrapper import read
910
from airbyte_cdk.test.mock_http import HttpMocker, HttpRequest, HttpResponse
1011
from airbyte_cdk.test.mock_http.response_builder import find_template
11-
from airbyte_protocol.models import SyncMode
1212
from config_builder import ConfigBuilder
1313
from source_tiktok_marketing import SourceTiktokMarketing
1414

airbyte-integrations/connectors/source-tiktok-marketing/unit_tests/integration/test_creative_assets_portfolios.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
from unittest import TestCase
55

66
from advetiser_slices import mock_advertisers_slices
7+
from airbyte_cdk.models import SyncMode
78
from airbyte_cdk.test.catalog_builder import CatalogBuilder
89
from airbyte_cdk.test.entrypoint_wrapper import read
910
from airbyte_cdk.test.mock_http import HttpMocker, HttpRequest, HttpResponse
1011
from airbyte_cdk.test.mock_http.response_builder import find_template
11-
from airbyte_protocol.models import SyncMode
1212
from config_builder import ConfigBuilder
1313
from source_tiktok_marketing import SourceTiktokMarketing
1414

airbyte-integrations/connectors/source-tiktok-marketing/unit_tests/integration/test_reports_hourly.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
from unittest import TestCase
55

66
from advetiser_slices import mock_advertisers_slices
7+
from airbyte_cdk.models import SyncMode
78
from airbyte_cdk.test.catalog_builder import CatalogBuilder
89
from airbyte_cdk.test.entrypoint_wrapper import read
910
from airbyte_cdk.test.mock_http import HttpMocker, HttpRequest, HttpResponse
1011
from airbyte_cdk.test.mock_http.response_builder import find_template
1112
from airbyte_cdk.test.state_builder import StateBuilder
12-
from airbyte_protocol.models import SyncMode
1313
from config_builder import ConfigBuilder
1414
from source_tiktok_marketing import SourceTiktokMarketing
1515

@@ -162,7 +162,7 @@ def test_read_with_state(self, http_mocker: HttpMocker):
162162
)
163163

164164
assert len(output.records) == 1
165-
assert output.state_messages[0].state.stream.stream_state.dict()["states"] == [
165+
assert output.state_messages[0].state.stream.stream_state.states == [
166166
{"cursor": {"stat_time_hour": self.cursor}, "partition": {"advertiser_id": self.advertiser_id, "parent_slice": {}}}
167167
]
168168

@@ -339,7 +339,7 @@ def test_read_with_state(self, http_mocker: HttpMocker):
339339
)
340340

341341
assert len(output.records) == 1
342-
assert output.state_messages[0].state.stream.stream_state.dict()["states"] == [
342+
assert output.state_messages[0].state.stream.stream_state.states == [
343343
{"cursor": {"stat_time_hour": self.cursor}, "partition": {"advertiser_id": self.advertiser_id, "parent_slice": {}}}
344344
]
345345

@@ -492,7 +492,7 @@ def test_read_with_state(self, http_mocker: HttpMocker):
492492
)
493493

494494
assert len(output.records) == 1
495-
assert output.state_messages[0].state.stream.stream_state.dict()["states"] == [
495+
assert output.state_messages[0].state.stream.stream_state.states == [
496496
{"cursor": {"stat_time_hour": self.cursor}, "partition": {"advertiser_id": self.advertiser_id, "parent_slice": {}}}
497497
]
498498

@@ -607,7 +607,7 @@ def test_read_with_state(self, http_mocker: HttpMocker):
607607
)
608608

609609
assert len(output.records) == 1
610-
assert output.state_messages[0].state.stream.stream_state.dict()["states"] == [
610+
assert output.state_messages[0].state.stream.stream_state.states == [
611611
{"cursor": {"stat_time_hour": self.cursor}, "partition": {"advertiser_id": self.advertiser_id, "parent_slice": {}}}
612612
]
613613

airbyte-integrations/connectors/source-tiktok-marketing/unit_tests/test_source.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,29 @@ def test_source_check_connection_ok(config, requests_mock):
5353
assert SourceTiktokMarketing().check_connection(logger_mock, config) == (True, None)
5454

5555

56-
def test_source_check_connection_failed(config, requests_mock):
56+
@pytest.mark.parametrize(
57+
"json_response, expected_result, expected_message",
58+
[
59+
({"code": 40105, "message": "Access token is incorrect or has been revoked."},
60+
(False, "Access token is incorrect or has been revoked."),
61+
None),
62+
({"code": 40100, "message": "App reaches the QPS limit."},
63+
None,
64+
38)
65+
]
66+
)
67+
@pytest.mark.usefixtures("mock_sleep")
68+
def test_source_check_connection_failed(config, requests_mock, capsys, json_response, expected_result, expected_message):
5769
requests_mock.get(
5870
"https://business-api.tiktok.com/open_api/v1.3/oauth2/advertiser/get/",
59-
json={"code": 40105, "message": "Access token is incorrect or has been revoked."}
71+
json=json_response
6072
)
73+
6174
logger_mock = MagicMock()
62-
assert SourceTiktokMarketing().check_connection(logger_mock, config) == (
63-
False, "Access token is incorrect or has been revoked."
64-
)
75+
result = SourceTiktokMarketing().check_connection(logger_mock, config)
76+
77+
if expected_result is not None:
78+
assert result == expected_result
79+
if expected_message is not None:
80+
trace_messages = capsys.readouterr().out.split()
81+
assert len(trace_messages) == expected_message

0 commit comments

Comments
 (0)