Skip to content

Commit 3d24434

Browse files
authored
RELEASE: V1.1 (#39)
* DEVEXP-340/DEVEXP-471 - automated CI/CD release to PyPI and async library replacement (#29) * DEVEXP-464 update verification API with backwards compat (#36) * fix: recursion error (#38) * [SMS] service plan id version of the API (#17) * relese: bump the package version
1 parent be6b973 commit 3d24434

File tree

69 files changed

+1148
-344
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+1148
-344
lines changed

.github/workflows/release-sdk.yaml

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Release Python SDK
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
env:
8+
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
9+
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
10+
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v3
16+
- name: Setup Python
17+
uses: actions/setup-python@v2
18+
- name: Install packaging tools
19+
run: |
20+
python -m pip install --upgrade pip
21+
pip install twine
22+
pip install poetry
23+
- name: Build package
24+
run: |
25+
poetry build
26+
- name: Verify package
27+
run: |
28+
twine check dist/*
29+
- name: Release package
30+
run: |
31+
twine upload dist/*

.github/workflows/run-tests.yml

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ env:
77
PROJECT_ID: ${{ secrets.PROJECT_ID }}
88
NUMBERS_ORIGIN: ${{ secrets.NUMBERS_ORIGIN }}
99
SMS_ORIGIN: ${{ secrets.SMS_ORIGIN }}
10+
SERVICE_PLAN_ID: ${{ secrets.SERVICE_PLAN_ID }}
11+
SMS_API_TOKEN: ${{ secrets.SMS_API_TOKEN }}
1012
CONVERSATION_ORIGIN: ${{ secrets.CONVERSATION_ORIGIN }}
1113
AUTH_ORIGIN: ${{ secrets.AUTH_ORIGIN }}
1214
DISABLE_SSL: ${{ secrets.DISABLE_SSL }}

pyproject.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[tool.poetry]
22
name = "sinch"
33
description = "Sinch SDK for Python programming language"
4-
version = "1.0.0"
4+
version = "1.1.0"
55
license = "Apache 2.0"
66
readme = "README.md"
77
authors = [
@@ -27,7 +27,7 @@ keywords = ["sinch", "sdk"]
2727
[tool.poetry.dependencies]
2828
python = ">=3.9"
2929
requests = "*"
30-
aiohttp = "*"
30+
httpx = "*"
3131

3232
[build-system]
3333
requires = ["poetry-core"]

sinch/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
""" Sinch Python SDK
22
To access Sinch resources, use the Sync or Async version of the Sinch Client.
33
"""
4-
__version__ = "1.0.0"
4+
__version__ = "1.1.0"
55

66
from sinch.core.clients.sinch_client_sync import SinchClient
77
from sinch.core.clients.sinch_client_async import SinchClientAsync

sinch/core/adapters/asyncio_http_adapter.py

-53
This file was deleted.

sinch/core/adapters/httpx_adapter.py

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import httpx
2+
from sinch.core.ports.http_transport import AsyncHTTPTransport, HttpRequest
3+
from sinch.core.endpoint import HTTPEndpoint
4+
from sinch.core.models.http_response import HTTPResponse
5+
6+
7+
class HTTPXTransport(AsyncHTTPTransport):
8+
def __init__(self, sinch):
9+
super().__init__(sinch)
10+
self.http_session = None
11+
12+
async def request(self, endpoint: HTTPEndpoint) -> HTTPResponse:
13+
request_data: HttpRequest = self.prepare_request(endpoint)
14+
request_data: HttpRequest = await self.authenticate(endpoint, request_data)
15+
16+
if not self.http_session:
17+
self.http_session = httpx.AsyncClient()
18+
19+
self.sinch.configuration.logger.debug(
20+
f"Async HTTP {request_data.http_method} call with headers:"
21+
f" {request_data.headers} and body: {request_data.request_body} to URL: {request_data.url}"
22+
)
23+
24+
if isinstance(request_data.request_body, str):
25+
response = await self.http_session.request(
26+
method=request_data.http_method,
27+
headers=request_data.headers,
28+
url=request_data.url,
29+
content=request_data.request_body,
30+
auth=request_data.auth,
31+
params=request_data.query_params,
32+
timeout=self.sinch.configuration.connection_timeout
33+
)
34+
else:
35+
response = await self.http_session.request(
36+
method=request_data.http_method,
37+
headers=request_data.headers,
38+
url=request_data.url,
39+
data=request_data.request_body,
40+
auth=request_data.auth,
41+
params=request_data.query_params,
42+
timeout=self.sinch.configuration.connection_timeout,
43+
)
44+
45+
response_body = self.deserialize_json_response(response)
46+
47+
self.sinch.configuration.logger.debug(
48+
f"Async HTTP {response.status_code} response with headers: {response.headers}"
49+
f"and body: {response_body} from URL: {request_data.url}"
50+
)
51+
52+
return await self.handle_response(
53+
endpoint=endpoint,
54+
http_response=HTTPResponse(
55+
status_code=response.status_code,
56+
body=response_body,
57+
headers=response.headers
58+
)
59+
)
60+
61+
async def close_session(self):
62+
await self.http_session.aclose()

sinch/core/adapters/requests_http_transport.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import requests
2-
import json
32
from sinch.core.ports.http_transport import HTTPTransport, HttpRequest
43
from sinch.core.endpoint import HTTPEndpoint
54
from sinch.core.models.http_response import HTTPResponse
@@ -29,9 +28,7 @@ def request(self, endpoint: HTTPEndpoint) -> HTTPResponse:
2928
params=request_data.query_params
3029
)
3130

32-
response_body = response.content
33-
if response_body:
34-
response_body = json.loads(response_body)
31+
response_body = self.deserialize_json_response(response)
3532

3633
self.sinch.configuration.logger.debug(
3734
f"Sync HTTP {response.status_code} response with headers: {response.headers}"

sinch/core/clients/sinch_client_async.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from sinch.core.clients.sinch_client_base import SinchClientBase
33
from sinch.core.clients.sinch_client_configuration import Configuration
44
from sinch.core.token_manager import TokenManagerAsync
5-
from sinch.core.adapters.asyncio_http_adapter import HTTPTransportAioHTTP
5+
from sinch.core.adapters.httpx_adapter import HTTPXTransport
66
from sinch.domains.authentication import AuthenticationAsync
77
from sinch.domains.numbers import NumbersAsync
88
from sinch.domains.conversation import ConversationAsync
@@ -25,18 +25,22 @@ def __init__(
2525
logger_name: str = None,
2626
logger: Logger = None,
2727
application_key: str = None,
28-
application_secret: str = None
28+
application_secret: str = None,
29+
service_plan_id: str = None,
30+
sms_api_token: str = None
2931
):
3032
self.configuration = Configuration(
3133
key_id=key_id,
3234
key_secret=key_secret,
3335
project_id=project_id,
3436
logger_name=logger_name,
3537
logger=logger,
36-
transport=HTTPTransportAioHTTP(self),
38+
transport=HTTPXTransport(self),
3739
token_manager=TokenManagerAsync(self),
3840
application_secret=application_secret,
39-
application_key=application_key
41+
application_key=application_key,
42+
service_plan_id=service_plan_id,
43+
sms_api_token=sms_api_token
4044
)
4145

4246
self.authentication = AuthenticationAsync(self)

sinch/core/clients/sinch_client_base.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ def __init__(
3030
logger_name: str = None,
3131
logger: Logger = None,
3232
application_key: str = None,
33-
application_secret: str = None
33+
application_secret: str = None,
34+
service_plan_id: str = None,
35+
sms_api_token: str = None
3436
):
3537
pass
3638

sinch/core/clients/sinch_client_configuration.py

+56-16
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from sinch.core.ports.http_transport import HTTPTransport
66
from sinch.core.token_manager import TokenManager, TokenManagerAsync
7+
from sinch.core.enums import HTTPAuthentication
78

89

910
class Configuration:
@@ -22,14 +23,18 @@ def __init__(
2223
disable_https=False,
2324
connection_timeout=10,
2425
application_key: str = None,
25-
application_secret: str = None
26+
application_secret: str = None,
27+
service_plan_id: str = None,
28+
sms_api_token: str = None
2629
):
2730
self.key_id = key_id
2831
self.key_secret = key_secret
2932
self.project_id = project_id
3033
self.application_key = application_key
3134
self.application_secret = application_secret
3235
self.connection_timeout = connection_timeout
36+
self.sms_api_token = sms_api_token
37+
self.service_plan_id = service_plan_id
3338
self.auth_origin = "auth.sinch.com"
3439
self.numbers_origin = "numbers.api.sinch.com"
3540
self.verification_origin = "verification.api.sinch.com"
@@ -39,7 +44,10 @@ def __init__(
3944
self._conversation_region = "eu"
4045
self._conversation_domain = ".conversation.api.sinch.com"
4146
self._sms_region = "us"
47+
self._sms_region_with_service_plan_id = "us"
4248
self._sms_domain = "zt.{}.sms.api.sinch.com"
49+
self._sms_domain_with_service_plan_id = "{}.sms.api.sinch.com"
50+
self._sms_authentication = HTTPAuthentication.OAUTH.value
4351
self._templates_region = "eu"
4452
self._templates_domain = ".template.api.sinch.com"
4553
self.token_manager = token_manager
@@ -48,6 +56,7 @@ def __init__(
4856

4957
self._set_conversation_origin()
5058
self._set_sms_origin()
59+
self._set_sms_origin_with_service_plan_id()
5160
self._set_templates_origin()
5261
self._set_voice_origin()
5362

@@ -58,23 +67,35 @@ def __init__(
5867
else:
5968
self.logger = logging.getLogger("Sinch")
6069

61-
def _set_voice_origin(self):
62-
if not self._voice_region:
63-
self.voice_origin = self._voice_domain.format("calling")
64-
else:
65-
self.voice_origin = self._voice_domain.format("calling-" + self._voice_region)
70+
def _set_sms_origin_with_service_plan_id(self):
71+
self.sms_origin_with_service_plan_id = self._sms_domain_with_service_plan_id.format(
72+
self._sms_region_with_service_plan_id
73+
)
6674

67-
def _set_voice_region(self, region):
68-
self._voice_region = region
69-
self._set_voice_origin()
75+
def _set_sms_region_with_service_plan_id(self, region):
76+
self._sms_region_with_service_plan_id = region
77+
self._set_sms_origin_with_service_plan_id()
7078

71-
def _get_voice_region(self):
72-
return self._voice_region
79+
def _get_sms_region_with_service_plan_id(self):
80+
return self._sms_region_with_service_plan_id
7381

74-
voice_region = property(
75-
_get_voice_region,
76-
_set_voice_region,
77-
doc="Voice Region"
82+
sms_region_with_service_plan_id = property(
83+
_get_sms_region_with_service_plan_id,
84+
_set_sms_region_with_service_plan_id,
85+
doc="SMS Region for service plan id version of the SMS API"
86+
)
87+
88+
def _set_sms_domain_with_service_plan_id(self, domain):
89+
self._sms_domain_with_service_plan_id = domain
90+
self._set_sms_origin_with_service_plan_id()
91+
92+
def _get_sms_domain_with_service_plan_id(self):
93+
return self._sms_domain_with_service_plan_id
94+
95+
sms_domain_with_service_plan_id = property(
96+
_get_sms_domain_with_service_plan_id,
97+
_set_sms_domain_with_service_plan_id,
98+
doc="SMS Domain for service plan id version of the SMS API"
7899
)
79100

80101
def _set_sms_origin(self):
@@ -98,7 +119,7 @@ def _set_sms_domain(self, domain):
98119
self._set_sms_origin()
99120

100121
def _get_sms_domain(self):
101-
return self.sms_domain
122+
return self._sms_domain
102123

103124
sms_domain = property(
104125
_get_sms_domain,
@@ -163,3 +184,22 @@ def _get_templates_domain(self):
163184
_set_templates_domain,
164185
doc="Conversation API Templates Domain"
165186
)
187+
188+
def _set_voice_origin(self):
189+
if not self._voice_region:
190+
self.voice_origin = self._voice_domain.format("calling")
191+
else:
192+
self.voice_origin = self._voice_domain.format("calling-" + self._voice_region)
193+
194+
def _set_voice_region(self, region):
195+
self._voice_region = region
196+
self._set_voice_origin()
197+
198+
def _get_voice_region(self):
199+
return self._voice_region
200+
201+
voice_region = property(
202+
_get_voice_region,
203+
_set_voice_region,
204+
doc="Voice Region"
205+
)

sinch/core/clients/sinch_client_sync.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ def __init__(
2525
logger_name: str = None,
2626
logger: Logger = None,
2727
application_key: str = None,
28-
application_secret: str = None
28+
application_secret: str = None,
29+
service_plan_id: str = None,
30+
sms_api_token: str = None
2931
):
3032
self.configuration = Configuration(
3133
key_id=key_id,
@@ -36,7 +38,9 @@ def __init__(
3638
transport=HTTPTransportRequests(self),
3739
token_manager=TokenManager(self),
3840
application_key=application_key,
39-
application_secret=application_secret
41+
application_secret=application_secret,
42+
service_plan_id=service_plan_id,
43+
sms_api_token=sms_api_token
4044
)
4145

4246
self.authentication = Authentication(self)

0 commit comments

Comments
 (0)