From b5afcca6c2c4b29330f81f92b79bf2526645223a Mon Sep 17 00:00:00 2001 From: Manisha Singh Date: Fri, 24 Nov 2023 00:29:36 +0530 Subject: [PATCH] feat: geolocation setter in sendgrid-python for GDPR compliance (#1073) --- .github/workflows/lint-python.yml | 2 +- examples/dataresidency/set_region.py | 37 ++++++++++++++++++++++++++++ sendgrid/base_interface.py | 23 ++++++++++++++++- test/unit/test_sendgrid.py | 27 ++++++++++++++++++++ 4 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 examples/dataresidency/set_region.py create mode 100644 test/unit/test_sendgrid.py diff --git a/.github/workflows/lint-python.yml b/.github/workflows/lint-python.yml index 1c5f53f23..ace47f77d 100644 --- a/.github/workflows/lint-python.yml +++ b/.github/workflows/lint-python.yml @@ -13,4 +13,4 @@ jobs: steps: - uses: actions/checkout@v3 - run: pip install --user ruff - - run: ruff --format=github --ignore="E722,F40,F841" --line-length=681 --target-version=py37 . + - run: ruff --ignore="E722,F40,F841" --line-length=320 --target-version=py37 . diff --git a/examples/dataresidency/set_region.py b/examples/dataresidency/set_region.py new file mode 100644 index 000000000..9aae2611f --- /dev/null +++ b/examples/dataresidency/set_region.py @@ -0,0 +1,37 @@ +import sendgrid +import os + +from sendgrid import Email, To, Content, Mail + +# Example 1 +# setting region to be "global" + +sg = sendgrid.SendGridAPIClient(api_key=os.environ.get('SENDGRID_API_KEY')) +from_email = Email("example@abc.com") +to_email = To("example@abc.com") +subject = "Sending with SendGrid is Fun" +content = Content("text/plain", "and easy to do anywhere, even with Python") +mail = Mail(from_email, to_email, subject, content) +sg.set_sendgrid_data_residency("global") +print(sg.client.host) +response = sg.client.mail.send.post(request_body=mail.get()) +print(response) +print(response.status_code) +print(response.body) +print(response.headers) + +# Example 2 +# setting region to "eu" +sg = sendgrid.SendGridAPIClient(api_key=os.environ.get('SENDGRID_API_KEY_EU')) +sg.set_sendgrid_data_residency("eu") +from_email = Email("example@abc.com") +to_email = To("example@abc.com") +subject = "Sending with SendGrid is Fun" +content = Content("text/plain", "and easy to do anywhere, even with Python") +print(sg.client.host) +mail = Mail(from_email, to_email, subject, content) +response = sg.client.mail.send.post(request_body=mail.get()) +print(response) +print(response.status_code) +print(response.body) +print(response.headers) \ No newline at end of file diff --git a/sendgrid/base_interface.py b/sendgrid/base_interface.py index 92b38247e..f94f09479 100644 --- a/sendgrid/base_interface.py +++ b/sendgrid/base_interface.py @@ -1,5 +1,6 @@ import python_http_client +region_host_dict = {'eu':'https://api.eu.sendgrid.com','global':'https://api.sendgrid.com'} class BaseInterface(object): def __init__(self, auth, host, impersonate_subuser): @@ -22,10 +23,10 @@ def __init__(self, auth, host, impersonate_subuser): """ from . import __version__ self.auth = auth - self.host = host self.impersonate_subuser = impersonate_subuser self.version = __version__ self.useragent = 'sendgrid/{};python'.format(self.version) + self.host = host self.client = python_http_client.Client( host=self.host, @@ -60,3 +61,23 @@ def send(self, message): message = message.get() return self.client.mail.send.post(request_body=message) + + def set_sendgrid_data_residency(self, region): + """ + Client libraries contain setters for specifying region/edge. + This supports global and eu regions only. This set will likely expand in the future. + Global is the default residency (or region) + Global region means the message will be sent through https://api.sendgrid.com + EU region means the message will be sent through https://api.eu.sendgrid.com + :param region: string + :return: + """ + if region in region_host_dict.keys(): + self.host = region_host_dict[region] + if self._default_headers is not None: + self.client = python_http_client.Client( + host=self.host, + request_headers=self._default_headers, + version=3) + else: + raise ValueError("region can only be \"eu\" or \"global\"") diff --git a/test/unit/test_sendgrid.py b/test/unit/test_sendgrid.py new file mode 100644 index 000000000..328d978ab --- /dev/null +++ b/test/unit/test_sendgrid.py @@ -0,0 +1,27 @@ +import unittest +import sendgrid + +class UnitTests(unittest.TestCase): + def test_host_with_no_region(self): + sg = sendgrid.SendGridAPIClient(api_key='MY_API_KEY') + self.assertEqual("https://api.sendgrid.com",sg.client.host) + + def test_host_with_eu_region(self): + sg = sendgrid.SendGridAPIClient(api_key='MY_API_KEY') + sg.set_sendgrid_data_residency("eu") + self.assertEqual("https://api.eu.sendgrid.com",sg.client.host) + + def test_host_with_global_region(self): + sg = sendgrid.SendGridAPIClient(api_key='MY_API_KEY') + sg.set_sendgrid_data_residency("global") + self.assertEqual("https://api.sendgrid.com",sg.client.host) + + def test_with_region_is_none(self): + sg = sendgrid.SendGridAPIClient(api_key='MY_API_KEY') + with self.assertRaises(ValueError): + sg.set_sendgrid_data_residency(None) + + def test_with_region_is_invalid(self): + sg = sendgrid.SendGridAPIClient(api_key='MY_API_KEY') + with self.assertRaises(ValueError): + sg.set_sendgrid_data_residency("abc") \ No newline at end of file