Skip to content

Commit be4416a

Browse files
Support for transactional SMS API (#108)
1 parent bfc6707 commit be4416a

6 files changed

Lines changed: 88 additions & 21 deletions

File tree

.github/workflows/main.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,22 @@ on: [ push ]
44

55
jobs:
66
build:
7-
runs-on: ubuntu-20.04
7+
runs-on: ubuntu-latest
88
strategy:
9+
fail-fast: false
910
matrix:
10-
python: [ '3.6', '3.7', '3.8','3.9' ]
11+
python: [ '3.9', '3.10', '3.11', '3.12', '3.13' ]
1112
steps:
12-
- uses: actions/checkout@v2
13+
- uses: actions/checkout@v4
1314
- name: Set up Python
14-
uses: actions/setup-python@v2
15+
uses: actions/setup-python@v5.6.0
1516
with:
1617
python-version: ${{ matrix.python }}
1718
- name: install dependencies for the minor version
1819
run: |
1920
python -m venv venv
2021
. venv/bin/activate
21-
export version=$(grep -v '^$' ${GITHUB_WORKSPACE}/requirements.txt | grep requests | awk -F'=' '{print $2}' )
22-
pip install requests==$(echo $version)
22+
pip install -r requirements.txt
2323
- name: run tests
2424
run: |
2525
. venv/bin/activate

customerio/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33
from customerio.client_base import CustomerIOException
44
from customerio.track import CustomerIO
5-
from customerio.api import APIClient, SendEmailRequest, SendPushRequest
5+
from customerio.api import APIClient, SendEmailRequest, SendPushRequest, SendSMSRequest
66
from customerio.regions import Regions

customerio/api.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ def send_push(self, request):
2727
request = request._to_dict()
2828
resp = self.send_request('POST', self.url + "/v1/send/push", request)
2929
return json.loads(resp)
30+
31+
def send_sms(self, request):
32+
if isinstance(request, SendSMSRequest):
33+
request = request._to_dict()
34+
resp = self.send_request('POST', self.url + "/v1/send/sms", request)
35+
return json.loads(resp)
3036

3137
# builds the session.
3238
def _build_session(self):
@@ -211,3 +217,50 @@ def _to_dict(self):
211217
data[name] = value
212218

213219
return data
220+
221+
class SendSMSRequest(object):
222+
'''An object with all the options avaiable for triggering a transactional push message'''
223+
def __init__(self,
224+
transactional_message_id=None,
225+
to=None,
226+
identifiers=None,
227+
disable_message_retention=None,
228+
send_to_unsubscribed=None,
229+
queue_draft=None,
230+
message_data=None,
231+
send_at=None,
232+
language=None,
233+
):
234+
235+
self.transactional_message_id = transactional_message_id
236+
self.to = to
237+
self.identifiers = identifiers
238+
self.disable_message_retention = disable_message_retention
239+
self.send_to_unsubscribed = send_to_unsubscribed
240+
self.queue_draft = queue_draft
241+
self.message_data = message_data
242+
self.send_at = send_at
243+
self.language = language
244+
245+
def _to_dict(self):
246+
'''Build a request payload from the object'''
247+
field_map = dict(
248+
# field name is the same as the payload field name
249+
transactional_message_id="transactional_message_id",
250+
to="to",
251+
identifiers="identifiers",
252+
disable_message_retention="disable_message_retention",
253+
send_to_unsubscribed="send_to_unsubscribed",
254+
queue_draft="queue_draft",
255+
message_data="message_data",
256+
send_at="send_at",
257+
language="language",
258+
)
259+
260+
data = {}
261+
for field, name in field_map.items():
262+
value = getattr(self, field, None)
263+
if value is not None:
264+
data[name] = value
265+
266+
return data

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
requests>=2.20.0
1+
requests>=2.31.0
2+
urllib3>=2.0.0

tests/server.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,18 @@
33
except ImportError:
44
from http.server import BaseHTTPRequestHandler, HTTPServer
55

6-
from functools import wraps
76
from random import randint
87
import json
98
import ssl
109
import time
1110
import threading
1211
import unittest
1312

14-
def sslwrap(func):
15-
@wraps(func)
16-
def bar(*args, **kw):
17-
kw['ssl_version'] = ssl.PROTOCOL_SSLv23
18-
return func(*args, **kw)
19-
return bar
13+
def create_ssl_context():
14+
"""Create SSL context for Python 3.12+ compatibility"""
15+
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
16+
context.minimum_version = ssl.TLSVersion.TLSv1_2
17+
return context
2018

2119
request_counts = dict()
2220

@@ -75,12 +73,11 @@ class HTTPSTestCase(unittest.TestCase):
7573
def setUpClass(cls):
7674
# create a server
7775
cls.server = HTTPServer(("localhost", 0), Handler)
78-
# hack needed to setup ssl server
79-
ssl.wrap_socket = sslwrap(ssl.wrap_socket)
76+
# create SSL context for Python 3.12+ compatibility
77+
context = create_ssl_context()
78+
context.load_cert_chain('./tests/server.pem')
8079
# upgrade to https
81-
cls.server.socket = ssl.wrap_socket(cls.server.socket,
82-
certfile='./tests/server.pem',
83-
server_side=True)
80+
cls.server.socket = context.wrap_socket(cls.server.socket, server_side=True)
8481
# start server instance in new thread
8582
cls.server_thread = threading.Thread(target=cls.server.serve_forever)
8683
cls.server_thread.start()

tests/test_api.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import sys
66
import unittest
77

8-
from customerio import APIClient, SendEmailRequest, SendPushRequest, Regions, CustomerIOException
8+
from customerio import APIClient, SendEmailRequest, SendPushRequest, SendSMSRequest, Regions, CustomerIOException
99
from customerio.__version__ import __version__ as ClientVersion
1010
from tests.server import HTTPSTestCase
1111

@@ -95,5 +95,21 @@ def test_send_push(self):
9595

9696
self.client.send_push(push)
9797

98+
def test_send_sms(self):
99+
self.client.http.hooks = dict(response=partial(self._check_request, rq={
100+
'method': 'POST',
101+
'authorization': "Bearer app_api_key",
102+
'content_type': 'application/json',
103+
'url_suffix': '/v1/send/sms',
104+
'body': {"identifiers": {"id":"customer_1"}, "transactional_message_id": 100}
105+
}))
106+
107+
sms = SendSMSRequest(
108+
identifiers={"id":"customer_1"},
109+
transactional_message_id=100,
110+
)
111+
112+
self.client.send_sms(sms)
113+
98114
if __name__ == '__main__':
99115
unittest.main()

0 commit comments

Comments
 (0)