Skip to content

Commit 0cdb485

Browse files
sherifnadagl-pix
authored andcommitted
🐛 Sendgrid source: Gracefully handle malformed responses from sendgrid API (#4839)
1 parent 8ada997 commit 0cdb485

File tree

9 files changed

+74
-7
lines changed

9 files changed

+74
-7
lines changed

airbyte-config/init/src/main/resources/config/STANDARD_SOURCE_DEFINITION/fbb5fbe2-16ad-4cf4-af7d-ff9d9c316c87.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"sourceDefinitionId": "fbb5fbe2-16ad-4cf4-af7d-ff9d9c316c87",
33
"name": "Sendgrid",
44
"dockerRepository": "airbyte/source-sendgrid",
5-
"dockerImageTag": "0.2.5",
5+
"dockerImageTag": "0.2.6",
66
"documentationUrl": "https://hub.docker.com/r/airbyte/source-sendgrid",
77
"icon": "sendgrid.svg"
88
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
- sourceDefinitionId: fbb5fbe2-16ad-4cf4-af7d-ff9d9c316c87
7474
name: Sendgrid
7575
dockerRepository: airbyte/source-sendgrid
76-
dockerImageTag: 0.2.5
76+
dockerImageTag: 0.2.6
7777
documentationUrl: https://hub.docker.com/r/airbyte/source-sendgrid
7878
icon: sendgrid.svg
7979
- sourceDefinitionId: 9e0556f4-69df-4522-a3fb-03264d36b348

airbyte-integrations/connectors/source-sendgrid/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ RUN pip install .
1212
ENV AIRBYTE_ENTRYPOINT "python /airbyte/integration_code/main.py"
1313
ENTRYPOINT ["python", "/airbyte/integration_code/main.py"]
1414

15-
LABEL io.airbyte.version=0.2.5
15+
LABEL io.airbyte.version=0.2.6
1616
LABEL io.airbyte.name=airbyte/source-sendgrid

airbyte-integrations/connectors/source-sendgrid/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,6 @@ You've checked out the repo, implemented a million dollar feature, and you're re
9898
1. Create a Pull Request
9999
1. Pat yourself on the back for being an awesome contributor
100100
1. Someone from Airbyte will take a look at your PR and iterate with you to merge it into master
101+
102+
### Changelog
103+
See the [docs](https://docs.airbyte.io/integrations/sources/sendgrid#changelog) for the changelog.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#
2+
# MIT License
3+
#
4+
# Copyright (c) 2020 Airbyte
5+
#
6+
# Permission is hereby granted, free of charge, to any person obtaining a copy
7+
# of this software and associated documentation files (the "Software"), to deal
8+
# in the Software without restriction, including without limitation the rights
9+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
# copies of the Software, and to permit persons to whom the Software is
11+
# furnished to do so, subject to the following conditions:
12+
#
13+
# The above copyright notice and this permission notice shall be included in all
14+
# copies or substantial portions of the Software.
15+
#
16+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
# SOFTWARE.
23+
#
24+
25+
26+
def test_example():
27+
assert True

airbyte-integrations/connectors/source-sendgrid/setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,6 @@
3131
author="Airbyte",
3232
author_email="[email protected]",
3333
packages=find_packages(),
34-
install_requires=["airbyte-cdk~=0.1", "backoff", "requests", "pytest==6.1.2"],
34+
install_requires=["airbyte-cdk~=0.1", "backoff", "requests", "pytest==6.1.2", "pytest-mock"],
3535
package_data={"": ["*.json", "schemas/*.json"]},
3636
)

airbyte-integrations/connectors/source-sendgrid/source_sendgrid/streams.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,23 @@ def parse_response(
4848
) -> Iterable[Mapping]:
4949
json_response = response.json()
5050
records = json_response.get(self.data_field, []) if self.data_field is not None else json_response
51-
for record in records:
52-
yield record
51+
52+
if records is not None:
53+
for record in records:
54+
yield record
55+
else:
56+
# TODO sendgrid's API is sending null responses at times. This seems like a bug on the API side, so we're adding
57+
# log statements to help reproduce and prevent the connector from failing.
58+
err_msg = (
59+
f"Response contained no valid JSON data. Response body: {response.text}\n"
60+
f"Response status: {response.status_code}\n"
61+
f"Response body: {response.text}\n"
62+
f"Response headers: {response.headers}\n"
63+
f"Request URL: {response.request.url}\n"
64+
f"Request body: {response.request.body}\n"
65+
)
66+
# do NOT print request headers as it contains auth token
67+
self.logger.info(err_msg)
5368

5469

5570
class SendgridStreamOffsetPagination(SendgridStream):
@@ -72,7 +87,7 @@ def next_page_token(self, response: requests.Response) -> Optional[Mapping[str,
7287
return {"offset": self.offset}
7388

7489

75-
class SendgridStreamIncrementalMixin(HttpStream):
90+
class SendgridStreamIncrementalMixin(HttpStream, ABC):
7691
cursor_field = "created"
7792

7893
def __init__(self, start_time: int, **kwargs):

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,28 @@
2222
# SOFTWARE.
2323
#
2424

25+
from unittest.mock import MagicMock
2526

27+
import pytest
28+
import requests
2629
from airbyte_cdk.logger import AirbyteLogger
2730
from source_sendgrid.source import SourceSendgrid
31+
from source_sendgrid.streams import SendgridStream
32+
33+
34+
@pytest.fixture(name="sendgrid_stream")
35+
def sendgrid_stream_fixture(mocker) -> SendgridStream:
36+
# Wipe the internal list of abstract methods to allow instantiating the abstract class without implementing its abstract methods
37+
mocker.patch("source_sendgrid.streams.SendgridStream.__abstractmethods__", set())
38+
# Mypy yells at us because we're init'ing an abstract class
39+
return SendgridStream() # type: ignore
40+
41+
42+
def test_parse_response_gracefully_handles_nulls(mocker, sendgrid_stream: SendgridStream):
43+
response = requests.Response()
44+
mocker.patch.object(response, "json", return_value=None)
45+
mocker.patch.object(response, "request", return_value=MagicMock())
46+
assert [] == list(sendgrid_stream.parse_response(response))
2847

2948

3049
def test_source_wrong_credentials():

docs/integrations/sources/sendgrid.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,6 @@ Generate a API key using the [Sendgrid documentation](https://sendgrid.com/docs/
4141

4242
We recommend creating a key specifically for Airbyte access. This will allow you to control which resources Airbyte should be able to access. The API key should be read-only on all resources except Marketing, where it needs Full Access.
4343

44+
| Version | Date | Pull Request | Subject |
45+
| :------ | :-------- | :----- | :------ |
46+
| 0.2.6 | 2021-07-19 | [4839](https://github.com/airbytehq/airbyte/pull/4839) | Gracefully handle malformed responses from the API |

0 commit comments

Comments
 (0)