Skip to content

Commit 6990cc7

Browse files
authored
🐛 Source Intercom: Fix handling of scroll param when it expired (#9513)
* Add handling of scroll param when it expired * Updated PR number * Fix typo in docs * Add unittest * Updated scroll or standard switch mechanism * Updated to linters * Updated spec.yaml and defenitions
1 parent 89308f7 commit 6990cc7

File tree

7 files changed

+55
-12
lines changed

7 files changed

+55
-12
lines changed

airbyte-config/init/src/main/resources/config/STANDARD_SOURCE_DEFINITION/d8313939-3782-41b0-be29-b3ca20d8dd3a.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"sourceDefinitionId": "d8313939-3782-41b0-be29-b3ca20d8dd3a",
33
"name": "Intercom",
44
"dockerRepository": "airbyte/source-intercom",
5-
"dockerImageTag": "0.1.11",
5+
"dockerImageTag": "0.1.13",
66
"documentationUrl": "https://docs.airbyte.io/integrations/sources/intercom",
77
"icon": "intercom.svg"
88
}

airbyte-config/init/src/main/resources/seed/source_definitions.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@
321321
- name: Intercom
322322
sourceDefinitionId: d8313939-3782-41b0-be29-b3ca20d8dd3a
323323
dockerRepository: airbyte/source-intercom
324-
dockerImageTag: 0.1.12
324+
dockerImageTag: 0.1.13
325325
documentationUrl: https://docs.airbyte.io/integrations/sources/intercom
326326
icon: intercom.svg
327327
sourceType: api

airbyte-config/init/src/main/resources/seed/source_specs.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3134,7 +3134,7 @@
31343134
oauthFlowInitParameters: []
31353135
oauthFlowOutputParameters:
31363136
- - "access_token"
3137-
- dockerImage: "airbyte/source-intercom:0.1.12"
3137+
- dockerImage: "airbyte/source-intercom:0.1.13"
31383138
spec:
31393139
documentationUrl: "https://docs.airbyte.io/integrations/sources/intercom"
31403140
connectionSpecification:

airbyte-integrations/connectors/source-intercom/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,5 @@ COPY source_intercom ./source_intercom
3535
ENV AIRBYTE_ENTRYPOINT "python /airbyte/integration_code/main.py"
3636
ENTRYPOINT ["python", "/airbyte/integration_code/main.py"]
3737

38-
LABEL io.airbyte.version=0.1.12
38+
LABEL io.airbyte.version=0.1.13
3939
LABEL io.airbyte.name=airbyte/source-intercom

airbyte-integrations/connectors/source-intercom/source_intercom/source.py

+18-6
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ class EndpointType(Enum):
171171
def __init__(self, *args, **kwargs):
172172
super().__init__(*args, **kwargs)
173173
self._backoff_count = 0
174+
self._use_standard = False
174175
self._endpoint_type = self.EndpointType.scroll
175176
self._total_count = None # uses for saving of a total_count value once
176177

@@ -193,6 +194,9 @@ def next_page_token(self, response: requests.Response) -> Optional[Mapping[str,
193194
return super().next_page_token(response)
194195
return None
195196

197+
def need_use_standard(self):
198+
return not self.can_use_scroll() or self._use_standard
199+
196200
def can_use_scroll(self):
197201
"""Check backoff count"""
198202
return self._backoff_count <= 3
@@ -202,38 +206,46 @@ def path(self, **kwargs) -> str:
202206

203207
@classmethod
204208
def check_exists_scroll(cls, response: requests.Response) -> bool:
205-
if response.status_code == 400:
209+
if response.status_code in [400, 404]:
206210
# example response:
207211
# {..., "errors": [{'code': 'scroll_exists', 'message': 'scroll already exists for this workspace'}]}
212+
# {..., "errors": [{'code': 'not_found', 'message':'scroll parameter not found'}]}
208213
err_body = response.json()["errors"][0]
209-
if err_body["code"] == "scroll_exists":
214+
if err_body["code"] in ["scroll_exists", "not_found"]:
210215
return True
211216

212217
return False
213218

214219
@property
215220
def raise_on_http_errors(self) -> bool:
216-
if not self.can_use_scroll() and self._endpoint_type == self.EndpointType.scroll:
221+
if self.need_use_standard() and self._endpoint_type == self.EndpointType.scroll:
217222
return False
218223
return True
219224

220225
def stream_slices(self, sync_mode, **kwargs) -> Iterable[Optional[Mapping[str, any]]]:
221226
yield None
222-
if not self.can_use_scroll():
227+
if self.need_use_standard():
223228
self._endpoint_type = self.EndpointType.standard
224229
yield None
225230

226231
def should_retry(self, response: requests.Response) -> bool:
227232
if self.check_exists_scroll(response):
228233
self._backoff_count += 1
229-
if not self.can_use_scroll():
230-
self.logger.error("Can't create a new scroll request within an minute. " "Let's try to use a standard non-scroll endpoint.")
234+
if self.need_use_standard():
235+
self.logger.error(
236+
"Can't create a new scroll request within an minute or scroll param was expired. "
237+
"Let's try to use a standard non-scroll endpoint."
238+
)
231239
return False
232240

233241
return True
234242
return super().should_retry(response)
235243

236244
def backoff_time(self, response: requests.Response) -> Optional[float]:
245+
if response.status_code == 404:
246+
self._use_standard = True
247+
# Need return value greater than zero to use UserDefinedBackoffException class
248+
return 0.01
237249
if self.check_exists_scroll(response):
238250
self.logger.warning("A previous scroll request is exists. " "It must be deleted within an minute automatically")
239251
# try to check 3 times

airbyte-integrations/connectors/source-intercom/unit_tests/unit_test.py

+30
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import pytest
77
import requests
8+
from airbyte_cdk.models import SyncMode
89
from airbyte_cdk.sources.streams.http.auth import NoAuth
910
from source_intercom.source import Companies, Contacts, IntercomStream
1011

@@ -46,3 +47,32 @@ def test_get_next_page_token(intercom_class, response_json, expected_output_toke
4647
test = intercom_class(authenticator=NoAuth).next_page_token(response)
4748

4849
assert test == expected_output_token
50+
51+
52+
def test_switch_to_standard_endpoint_if_scroll_expired(requests_mock):
53+
"""
54+
Test shows that if scroll param expired we try sync with standard API.
55+
"""
56+
57+
url = "https://api.intercom.io/companies/scroll"
58+
requests_mock.get(
59+
url,
60+
json={"type": "company.list", "data": [{"type": "company", "id": "530370b477ad7120001d"}], "scroll_param": "expired_scroll_param"},
61+
)
62+
63+
url = "https://api.intercom.io/companies/scroll?scroll_param=expired_scroll_param"
64+
requests_mock.get(url, json={"errors": [{"code": "not_found", "message": "scroll parameter not found"}]}, status_code=404)
65+
66+
url = "https://api.intercom.io/companies"
67+
requests_mock.get(url, json={"type": "company.list", "data": [{"type": "company", "id": "530370b477ad7120001d"}]})
68+
69+
stream1 = Companies(authenticator=NoAuth())
70+
71+
records = []
72+
73+
assert stream1._endpoint_type == Companies.EndpointType.scroll
74+
75+
for slice in stream1.stream_slices(sync_mode=SyncMode.full_refresh):
76+
records += list(stream1.read_records(sync_mode=SyncMode, stream_slice=slice))
77+
78+
assert stream1._endpoint_type == Companies.EndpointType.standard

docs/integrations/sources/intercom.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@ Please read [How to get your Access Token](https://developers.intercom.com/build
5555

5656
| Version | Date | Pull Request | Subject |
5757
| :--- | :--- | :--- | :--- |
58+
| 0.1.13 | 2022-01-14 | [9513](https://github.com/airbytehq/airbyte/pull/9513) | Added handling of scroll param when it expired |
5859
| 0.1.12 | 2021-12-14 | [8429](https://github.com/airbytehq/airbyte/pull/8429) | Updated fields and descriptions |
5960
| 0.1.11 | 2021-12-13 | [8685](https://github.com/airbytehq/airbyte/pull/8685) | Remove time.sleep for rate limit |
60-
| 0.1.10 | 2021-12-10 | [8637](https://github.com/airbytehq/airbyte/pull/8637) | Fix 'conversations' order and sorting. Correction of the companies stream|
61+
| 0.1.10 | 2021-12-10 | [8637](https://github.com/airbytehq/airbyte/pull/8637) | Fix 'conversations' order and sorting. Correction of the companies stream |
6162
| 0.1.9 | 2021-12-03 | [8395](https://github.com/airbytehq/airbyte/pull/8395) | Fix backoff of 'companies' stream |
6263
| 0.1.8 | 2021-11-09 | [7060](https://github.com/airbytehq/airbyte/pull/7060) | Added oauth support |
6364
| 0.1.7 | 2021-11-08 | [7499](https://github.com/airbytehq/airbyte/pull/7499) | Remove base-python dependencies |
@@ -67,4 +68,4 @@ Please read [How to get your Access Token](https://developers.intercom.com/build
6768
| 0.1.3 | 2021-09-08 | [5908](https://github.com/airbytehq/airbyte/pull/5908) | Corrected timestamp and arrays in schemas |
6869
| 0.1.2 | 2021-08-19 | [5531](https://github.com/airbytehq/airbyte/pull/5531) | Corrected pagination |
6970
| 0.1.1 | 2021-07-31 | [5123](https://github.com/airbytehq/airbyte/pull/5123) | Corrected rate limit |
70-
| 0.1.0 | 2021-07-19 | [4676](https://github.com/airbytehq/airbyte/pull/4676) | Release Slack CDK Connector |
71+
| 0.1.0 | 2021-07-19 | [4676](https://github.com/airbytehq/airbyte/pull/4676) | Release Intercom CDK Connector |

0 commit comments

Comments
 (0)