python-terminusgps 33.3.1__py3-none-any.whl → 34.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of python-terminusgps might be problematic. Click here for more details.
- {python_terminusgps-33.3.1.dist-info → python_terminusgps-34.0.0.dist-info}/METADATA +1 -1
- {python_terminusgps-33.3.1.dist-info → python_terminusgps-34.0.0.dist-info}/RECORD +12 -12
- terminusgps/authorizenet/auth.py +3 -3
- terminusgps/authorizenet/profiles/base.py +11 -4
- terminusgps/authorizenet/profiles/payments.py +7 -2
- terminusgps/aws/notifications.py +193 -0
- terminusgps/aws/tests/test_secrets.py +0 -0
- terminusgps/aws/tests/test_utils.py +0 -0
- terminusgps/aws/notifier.py +0 -49
- terminusgps/twilio/tests/test_validators.py +0 -40
- terminusgps/twilio/validators.py +0 -31
- {python_terminusgps-33.3.1.dist-info → python_terminusgps-34.0.0.dist-info}/WHEEL +0 -0
- {python_terminusgps-33.3.1.dist-info → python_terminusgps-34.0.0.dist-info}/licenses/COPYING +0 -0
- /terminusgps/{twilio → aws}/tests/__init__.py +0 -0
- /terminusgps/{twilio/tests/test_caller.py → aws/tests/test_notifications.py} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-terminusgps
|
|
3
|
-
Version:
|
|
3
|
+
Version: 34.0.0
|
|
4
4
|
Summary: Provides abstractions/utilities for working with Wialon API, Authorize.NET API, AWS API, and more.
|
|
5
5
|
Project-URL: Documentation, https://docs.terminusgps.com
|
|
6
6
|
Project-URL: Repository, https://github.com/terminusgps/python-terminusgps
|
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
terminusgps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
terminusgps/authorizenet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
terminusgps/authorizenet/auth.py,sha256=
|
|
3
|
+
terminusgps/authorizenet/auth.py,sha256=g2tJPxP9V1nXnUcKzhHSRSs6ruSIHej5_hcf8P7bod8,1262
|
|
4
4
|
terminusgps/authorizenet/constants.py,sha256=xmulPmiBLhFfZoc7Il3l-DZA9Nq5HSaJmJKU-v-5X-Y,420
|
|
5
5
|
terminusgps/authorizenet/controllers.py,sha256=9t62qdGpmAkP4tNyPefd7vBRutXvMWsxql6z_pg52Bg,2146
|
|
6
6
|
terminusgps/authorizenet/utils.py,sha256=FrKqXWrplcgiET1rDTmUaqmN_ze7j93jWgfFeJR8rlg,7073
|
|
7
7
|
terminusgps/authorizenet/profiles/__init__.py,sha256=IG4XCEEYtHKqXOatlZlob6J3ukAgF7cFR-RXxOSMS1c,161
|
|
8
8
|
terminusgps/authorizenet/profiles/addresses.py,sha256=xDlnFlIjuR7NVXwfj5aJT9E_GolnYFuYASkfenvWfm8,6292
|
|
9
|
-
terminusgps/authorizenet/profiles/base.py,sha256=
|
|
9
|
+
terminusgps/authorizenet/profiles/base.py,sha256=Pu1NGrvC5Ox2W1oZuVcPqUw3aUoSK191Evexw1U6_3k,3940
|
|
10
10
|
terminusgps/authorizenet/profiles/customers.py,sha256=ArXOHPgyBu01tgBPHHYyezb4PIeXh70IWjYCy-lHpWg,11734
|
|
11
|
-
terminusgps/authorizenet/profiles/payments.py,sha256=
|
|
11
|
+
terminusgps/authorizenet/profiles/payments.py,sha256=nhudGuJ6RzmK0MrhDVuYL2BxvyzKNpliBIfIpFU5Y-4,11038
|
|
12
12
|
terminusgps/authorizenet/profiles/subscriptions.py,sha256=m_jb9GqeD1tLmeqSnaGwJrzPcQOulEkF8K1wCaqKR24,7924
|
|
13
13
|
terminusgps/authorizenet/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
14
|
terminusgps/authorizenet/tests/test_auth.py,sha256=sdByYrcv8RKjcJckOHbyHH1z_M4qrATFI3qj_XDQXbk,1624
|
|
15
15
|
terminusgps/authorizenet/tests/test_profiles.py,sha256=tvLBY0iSAC33RlWiBP_NSnPskUQBjfrf0YHMpR-m22k,7566
|
|
16
16
|
terminusgps/authorizenet/tests/test_utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
terminusgps/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
terminusgps/aws/
|
|
18
|
+
terminusgps/aws/notifications.py,sha256=N5p9EqN4wqBRJAMEFOjyseVatpwsxlB0QrHyqFDK5SI,5698
|
|
19
19
|
terminusgps/aws/secrets.py,sha256=MxQEmmBLpMUQ2tYAsHdCYf-7RZ84LiogdKcTsACX5dw,361
|
|
20
20
|
terminusgps/aws/utils.py,sha256=68GWcllu4h41qzFiKdUkWOAA4LDsTPqRY446VRMxYsY,116
|
|
21
|
+
terminusgps/aws/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
+
terminusgps/aws/tests/test_notifications.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
+
terminusgps/aws/tests/test_secrets.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
+
terminusgps/aws/tests/test_utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
25
|
terminusgps/django/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
26
|
terminusgps/django/mixins.py,sha256=Q9ZJuzpk3d9lDnlVA8ZTVvnZWxB13p08EQ8yVJcztn4,1034
|
|
23
27
|
terminusgps/django/settings.py,sha256=yJIcBE0xjY60AXF_gonY5hS3ofseqPW2k9uSoELgoho,639
|
|
@@ -32,10 +36,6 @@ terminusgps/django/tests/test_mixins.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
|
|
|
32
36
|
terminusgps/django/tests/test_utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
37
|
terminusgps/twilio/__init__.py,sha256=dYo41F2jft_eHDWSUtSpKGSRG1bewq_qClqilUJZkYM,33
|
|
34
38
|
terminusgps/twilio/caller.py,sha256=l53-IxXn0wXc_oDcDxfhlrKPz-F4_KfMPV1dAzZjNo4,5060
|
|
35
|
-
terminusgps/twilio/validators.py,sha256=HWCpzgDXTedofty4Zhrm2OenbgtkzrDUC08B1l15gqc,1112
|
|
36
|
-
terminusgps/twilio/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
|
-
terminusgps/twilio/tests/test_caller.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
terminusgps/twilio/tests/test_validators.py,sha256=CETv7Mcr19UMMfh1UaWSV26FiOeh2WCK_VutfKXnf7g,1428
|
|
39
39
|
terminusgps/wialon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
40
|
terminusgps/wialon/constants.py,sha256=Ju2XL2A4DPFD-w8NjYqEfnnqregl1luPT0XGhGSUnO8,6316
|
|
41
41
|
terminusgps/wialon/flags.py,sha256=M50EdhxQ8IMnJnU0mrHK7-h8Asc6tvNiTOOfd1dBW6A,12815
|
|
@@ -54,7 +54,7 @@ terminusgps/wialon/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
54
54
|
terminusgps/wialon/tests/test_items.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
55
|
terminusgps/wialon/tests/test_session.py,sha256=9mBlYchMo9NeqRIZsmxYzrM1zBHorqu4ooxqSNppLpI,1336
|
|
56
56
|
terminusgps/wialon/tests/test_utils.py,sha256=SK4PxJQGECFnzx_EQeRAQfsQ5_3FLaVcis2W9u_ibuI,1730
|
|
57
|
-
python_terminusgps-
|
|
58
|
-
python_terminusgps-
|
|
59
|
-
python_terminusgps-
|
|
60
|
-
python_terminusgps-
|
|
57
|
+
python_terminusgps-34.0.0.dist-info/METADATA,sha256=OLumu5xwbGDw-7yR3niZdmytYWiKwS80xQMazYTcy5g,1616
|
|
58
|
+
python_terminusgps-34.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
59
|
+
python_terminusgps-34.0.0.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
60
|
+
python_terminusgps-34.0.0.dist-info/RECORD,,
|
terminusgps/authorizenet/auth.py
CHANGED
|
@@ -10,7 +10,7 @@ if not settings.configured:
|
|
|
10
10
|
|
|
11
11
|
def get_merchant_auth() -> merchantAuthenticationType:
|
|
12
12
|
"""
|
|
13
|
-
Returns the merchant authentication information for Authorizenet API requests.
|
|
13
|
+
Returns the current merchant authentication information for Authorizenet API requests.
|
|
14
14
|
|
|
15
15
|
:returns: A merchant authentication object.
|
|
16
16
|
:rtype: :py:obj:`~authorizenet.apicontractsv1.merchantAuthenticationType`
|
|
@@ -24,7 +24,7 @@ def get_merchant_auth() -> merchantAuthenticationType:
|
|
|
24
24
|
|
|
25
25
|
def get_environment() -> str:
|
|
26
26
|
"""
|
|
27
|
-
Returns the environment for Authorizenet API requests.
|
|
27
|
+
Returns the current environment for Authorizenet API requests.
|
|
28
28
|
|
|
29
29
|
:returns: An Authorizenet API environment string.
|
|
30
30
|
:rtype: :py:obj:`str`
|
|
@@ -35,7 +35,7 @@ def get_environment() -> str:
|
|
|
35
35
|
|
|
36
36
|
def get_validation_mode() -> str:
|
|
37
37
|
"""
|
|
38
|
-
Returns the validation mode for Authorizenet API requests.
|
|
38
|
+
Returns the current validation mode for Authorizenet API requests.
|
|
39
39
|
|
|
40
40
|
:returns: An Authorizenet API validation string.
|
|
41
41
|
:rtype: :py:obj:`str`
|
|
@@ -62,6 +62,17 @@ class AuthorizenetProfileBase(AuthorizenetControllerExecutor):
|
|
|
62
62
|
"""
|
|
63
63
|
return get_merchant_auth()
|
|
64
64
|
|
|
65
|
+
@property
|
|
66
|
+
def validationMode(self) -> str:
|
|
67
|
+
"""
|
|
68
|
+
The current Authorizenet API validation mode.
|
|
69
|
+
|
|
70
|
+
:returns: An Authorizenet API validation mode string.
|
|
71
|
+
:rtype: :py:obj:`str`
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
return get_validation_mode()
|
|
75
|
+
|
|
65
76
|
|
|
66
77
|
class AuthorizenetSubProfileBase(AuthorizenetProfileBase):
|
|
67
78
|
"""Base class for Authorizenet 'sub-profiles', profiles that have an associated customer profile."""
|
|
@@ -83,10 +94,6 @@ class AuthorizenetSubProfileBase(AuthorizenetProfileBase):
|
|
|
83
94
|
self.customerProfileId = customer_profile_id
|
|
84
95
|
self.default = default
|
|
85
96
|
|
|
86
|
-
@property
|
|
87
|
-
def validationMode(self) -> str:
|
|
88
|
-
return get_validation_mode()
|
|
89
|
-
|
|
90
97
|
@property
|
|
91
98
|
def customerProfileId(self) -> str:
|
|
92
99
|
"""
|
|
@@ -8,7 +8,12 @@ class PaymentProfile(AuthorizenetSubProfileBase):
|
|
|
8
8
|
|
|
9
9
|
@property
|
|
10
10
|
def last_4(self) -> str | None:
|
|
11
|
-
"""
|
|
11
|
+
"""
|
|
12
|
+
Last 4 digits of the payment profile credit card.
|
|
13
|
+
|
|
14
|
+
:type: :py:obj:`str` | :py:obj:`None`
|
|
15
|
+
|
|
16
|
+
"""
|
|
12
17
|
if self.id:
|
|
13
18
|
response = self._authorizenet_get_payment_profile()
|
|
14
19
|
return str(response.paymentProfile.payment.creditCard.cardNumber)[-4:]
|
|
@@ -31,7 +36,7 @@ class PaymentProfile(AuthorizenetSubProfileBase):
|
|
|
31
36
|
"""
|
|
32
37
|
return int(
|
|
33
38
|
self._authorizenet_create_payment_profile(
|
|
34
|
-
address, payment
|
|
39
|
+
address=address, payment=payment
|
|
35
40
|
).customerPaymentProfileId
|
|
36
41
|
)
|
|
37
42
|
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import uuid
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from contextlib import AsyncExitStack
|
|
5
|
+
|
|
6
|
+
from aiobotocore.session import AioSession
|
|
7
|
+
|
|
8
|
+
from terminusgps.django.validators import validate_e164_phone_number
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AsyncNotificationManager:
|
|
12
|
+
def __init__(
|
|
13
|
+
self, group_id: str | None = None, region_name: str = "us-east-1"
|
|
14
|
+
) -> None:
|
|
15
|
+
"""
|
|
16
|
+
Sets :py:attr:`group_id` and :py:attr:`region_name` if provided.
|
|
17
|
+
|
|
18
|
+
:returns: Nothing.
|
|
19
|
+
:rtype: :py:obj:`None`
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
self._exit_stack = AsyncExitStack()
|
|
23
|
+
self._sns_client = None
|
|
24
|
+
self._region_name = region_name
|
|
25
|
+
self.group_id = group_id
|
|
26
|
+
|
|
27
|
+
async def __aenter__(self) -> "AsyncNotificationManager":
|
|
28
|
+
"""
|
|
29
|
+
Creates an asyncronous session and client.
|
|
30
|
+
|
|
31
|
+
:returns: The notification manager.
|
|
32
|
+
:rtype: :py:obj:`~terminusgps.aws.notifications.AsyncNotificationManager`
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
session = AioSession()
|
|
36
|
+
self._sns_client = await self._exit_stack.enter_async_context(
|
|
37
|
+
session.create_client("sns", region_name=self.region_name)
|
|
38
|
+
)
|
|
39
|
+
return self
|
|
40
|
+
|
|
41
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
42
|
+
"""
|
|
43
|
+
Destroys the asyncronous session.
|
|
44
|
+
|
|
45
|
+
:param exc_type: Exception type.
|
|
46
|
+
:param exc_val: Exception value.
|
|
47
|
+
:param exc_tb: Exception traceback.
|
|
48
|
+
:returns: Nothing.
|
|
49
|
+
:rtype: :py:obj:`None`
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
await self._exit_stack.__aexit__(exc_type, exc_val, exc_tb)
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def region_name(self) -> str:
|
|
56
|
+
"""
|
|
57
|
+
An AWS region name.
|
|
58
|
+
|
|
59
|
+
:type: :py:obj:`str`
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
return self._region_name
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def group_id(self) -> str:
|
|
66
|
+
"""
|
|
67
|
+
A message group id.
|
|
68
|
+
|
|
69
|
+
:type: :py:obj:`str`
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
return str(self._group_id)
|
|
73
|
+
|
|
74
|
+
@group_id.setter
|
|
75
|
+
def group_id(self, other: str | None) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Sets :py:attr:`group_id` to ``other``.
|
|
78
|
+
|
|
79
|
+
:param other: A message group id.
|
|
80
|
+
:type other: :py:obj:`str` | :py:obj:`None`
|
|
81
|
+
:returns: Nothing.
|
|
82
|
+
:rtype: :py:obj:`None`
|
|
83
|
+
|
|
84
|
+
"""
|
|
85
|
+
if not other:
|
|
86
|
+
other = str(uuid.uuid4())
|
|
87
|
+
self._group_id = other
|
|
88
|
+
|
|
89
|
+
async def send_push_batch(self, target_arns: Sequence[str], message: str) -> None:
|
|
90
|
+
"""
|
|
91
|
+
Sends a push notification to each AWS resource by ARN in ``target_arns``.
|
|
92
|
+
|
|
93
|
+
:param target_arns: A sequence of AWS resource ARNs.
|
|
94
|
+
:type target_arns: :py:obj:`~collections.abc.Sequence`
|
|
95
|
+
:param message: A message body.
|
|
96
|
+
:type message: :py:obj:`str`
|
|
97
|
+
:returns: Nothing.
|
|
98
|
+
:rtype: :py:obj:`None`
|
|
99
|
+
|
|
100
|
+
"""
|
|
101
|
+
await asyncio.gather(
|
|
102
|
+
*[
|
|
103
|
+
self.send_push(
|
|
104
|
+
target_arn=arn, message=message, message_id=str(uuid.uuid4())
|
|
105
|
+
)
|
|
106
|
+
for arn in target_arns
|
|
107
|
+
]
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
async def send_sms_batch(self, to_numbers: Sequence[str], message: str) -> None:
|
|
111
|
+
"""
|
|
112
|
+
Sends an sms message to each phone number in ``to_numbers``.
|
|
113
|
+
|
|
114
|
+
:param to_numbers: A sequence of phone numbers.
|
|
115
|
+
:type to_numbers: :py:obj:`~collections.abc.Sequence`
|
|
116
|
+
:param message: A message body.
|
|
117
|
+
:type message: :py:obj:`str`
|
|
118
|
+
:raises ValidationError: If a ``to_number`` wasn't a valid E.164 formatted phone number.
|
|
119
|
+
:returns: Nothing.
|
|
120
|
+
:rtype: :py:obj:`None`
|
|
121
|
+
|
|
122
|
+
"""
|
|
123
|
+
for to_number in to_numbers:
|
|
124
|
+
validate_e164_phone_number(to_number)
|
|
125
|
+
|
|
126
|
+
await asyncio.gather(
|
|
127
|
+
*[
|
|
128
|
+
self.send_sms(
|
|
129
|
+
to_number=num, message=message, message_id=str(uuid.uuid4())
|
|
130
|
+
)
|
|
131
|
+
for num in to_numbers
|
|
132
|
+
]
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
async def send_sms(self, to_number: str, message: str, message_id: str) -> None:
|
|
136
|
+
"""
|
|
137
|
+
Sends an sms to ``to_number``.
|
|
138
|
+
|
|
139
|
+
:param to_number: A destination phone number.
|
|
140
|
+
:type to_number: :py:obj:`str`
|
|
141
|
+
:param message: A message body.
|
|
142
|
+
:type message: :py:obj:`str`
|
|
143
|
+
:param message_id: A message deduplication id.
|
|
144
|
+
:type message_id: :py:obj:`str`
|
|
145
|
+
:raises ValidationError: If ``to_number`` wasn't a valid E.164 formatted phone number.
|
|
146
|
+
:returns: Nothing.
|
|
147
|
+
:rtype: :py:obj:`None`
|
|
148
|
+
|
|
149
|
+
"""
|
|
150
|
+
validate_e164_phone_number(to_number)
|
|
151
|
+
|
|
152
|
+
await self._sns_client.publish(
|
|
153
|
+
**{
|
|
154
|
+
"PhoneNumber": to_number,
|
|
155
|
+
"Message": message,
|
|
156
|
+
"MessageDeduplicationId": message_id,
|
|
157
|
+
"MessageGroupId": self.group_id,
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
async def send_push(self, target_arn: str, message: str, message_id: str) -> None:
|
|
162
|
+
"""
|
|
163
|
+
Sends a push notification to ``target_arn``.
|
|
164
|
+
|
|
165
|
+
:param target_arn: An AWS resource ARN.
|
|
166
|
+
:type target_arn: :py:obj:`str`
|
|
167
|
+
:param message: A message body.
|
|
168
|
+
:type message: :py:obj:`str`
|
|
169
|
+
:param message_id: A message deduplication id.
|
|
170
|
+
:type message_id: :py:obj:`str`
|
|
171
|
+
:returns: Nothing.
|
|
172
|
+
:rtype: :py:obj:`None`
|
|
173
|
+
|
|
174
|
+
"""
|
|
175
|
+
await self._sns_client.publish(
|
|
176
|
+
**{
|
|
177
|
+
"TargetArn": target_arn,
|
|
178
|
+
"Message": message,
|
|
179
|
+
"MessageDeduplicationId": message_id,
|
|
180
|
+
"MessageGroupId": self.group_id,
|
|
181
|
+
}
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
async def main() -> None:
|
|
186
|
+
async with AsyncNotificationManager(str(uuid.uuid4())) as manager:
|
|
187
|
+
await manager.send_sms_batch(
|
|
188
|
+
["+17133049421", "+18324667085", "+18322835634"], "This is a test message."
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
if __name__ == "__main__":
|
|
193
|
+
asyncio.run(main())
|
|
File without changes
|
|
File without changes
|
terminusgps/aws/notifier.py
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import uuid
|
|
3
|
-
from contextlib import AsyncExitStack
|
|
4
|
-
|
|
5
|
-
from aiobotocore.session import AioSession
|
|
6
|
-
|
|
7
|
-
from terminusgps.django.validators import validate_e164_phone_number
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class AWSNotifier:
|
|
11
|
-
def __init__(self, region: str = "us-east-1") -> None:
|
|
12
|
-
self._client = None
|
|
13
|
-
self._region = region
|
|
14
|
-
self._exit_stack = AsyncExitStack()
|
|
15
|
-
|
|
16
|
-
async def __aenter__(self):
|
|
17
|
-
session = AioSession()
|
|
18
|
-
self._client = await self._exit_stack.enter_async_context(
|
|
19
|
-
session.create_client("sns")
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
23
|
-
await self._exit_stack.__aexit__(exc_type, exc_val, exc_tb)
|
|
24
|
-
|
|
25
|
-
async def send_sms(
|
|
26
|
-
self, phone_number: str, message: str, message_id: str, group_id: str
|
|
27
|
-
) -> dict:
|
|
28
|
-
validate_e164_phone_number(phone_number)
|
|
29
|
-
await self._client.publish(
|
|
30
|
-
**{
|
|
31
|
-
"PhoneNumber": phone_number,
|
|
32
|
-
"Message": message,
|
|
33
|
-
"MessageDeduplicationId": message_id,
|
|
34
|
-
"MessageGroupId": group_id,
|
|
35
|
-
}
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
async def main() -> None:
|
|
40
|
-
notifier = AWSNotifier()
|
|
41
|
-
message_id = str(uuid.uuid4())
|
|
42
|
-
await notifier.send_sms(
|
|
43
|
-
"+17133049421", "This is a test message", message_id, "message-group"
|
|
44
|
-
)
|
|
45
|
-
return
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if __name__ == "__main__":
|
|
49
|
-
asyncio.run(main())
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
from unittest import TestCase
|
|
2
|
-
|
|
3
|
-
import django
|
|
4
|
-
from django.core.exceptions import ValidationError
|
|
5
|
-
|
|
6
|
-
from terminusgps.twilio import validators
|
|
7
|
-
|
|
8
|
-
django.setup()
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class PhoneNumberValidatorTestCase(TestCase):
|
|
12
|
-
def setUp(self) -> None:
|
|
13
|
-
self.validator = validators.validate_phone_number
|
|
14
|
-
|
|
15
|
-
def test_phone_without_plus(self) -> None:
|
|
16
|
-
"""Fails if a phone number without a '+' leading character passes validation."""
|
|
17
|
-
with self.assertRaises(ValidationError) as context:
|
|
18
|
-
self.validator("15555555555")
|
|
19
|
-
self.assertTrue(
|
|
20
|
-
"Phone number must begin with a '+', got '15555555555'."
|
|
21
|
-
in str(context.exception)
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
def test_phone_min_length(self) -> None:
|
|
25
|
-
"""Fails if a phone number of 8 characters in length passes validation."""
|
|
26
|
-
with self.assertRaises(ValidationError) as context:
|
|
27
|
-
self.validator("+1555555")
|
|
28
|
-
self.assertTrue(
|
|
29
|
-
"Phone number cannot be less than 12 characters, got 8."
|
|
30
|
-
in str(context.exception)
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
def test_phone_max_length(self) -> None:
|
|
34
|
-
"""Fails if a phone number of 20 characters in length passes validation."""
|
|
35
|
-
with self.assertRaises(ValidationError) as context:
|
|
36
|
-
self.validator("+1555555555555555555")
|
|
37
|
-
self.assertTrue(
|
|
38
|
-
"Phone number cannot be greater than 19 characters, got 20."
|
|
39
|
-
in str(context.exception)
|
|
40
|
-
)
|
terminusgps/twilio/validators.py
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
from django.core.exceptions import ValidationError
|
|
2
|
-
from django.utils.translation import gettext_lazy as _
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def validate_phone_number(value: str) -> None:
|
|
6
|
-
min_length, max_length = 12, 19
|
|
7
|
-
|
|
8
|
-
if not value.startswith("+"):
|
|
9
|
-
raise ValidationError(
|
|
10
|
-
_("Phone number must begin with a '+', got '%(value)s'."),
|
|
11
|
-
code="invalid",
|
|
12
|
-
params={"value": value},
|
|
13
|
-
)
|
|
14
|
-
if not value[1:].isdigit():
|
|
15
|
-
raise ValidationError(
|
|
16
|
-
_("Phone number must be '+' followed by a digit, got '%(value)s'"),
|
|
17
|
-
code="invalid",
|
|
18
|
-
params={"value": value},
|
|
19
|
-
)
|
|
20
|
-
if len(value) < min_length:
|
|
21
|
-
raise ValidationError(
|
|
22
|
-
_("Phone number cannot be less than %(min)s characters, got %(len)s."),
|
|
23
|
-
code="invalid",
|
|
24
|
-
params={"min": min_length, "len": len(value)},
|
|
25
|
-
)
|
|
26
|
-
if len(value) > max_length:
|
|
27
|
-
raise ValidationError(
|
|
28
|
-
_("Phone number cannot be greater than %(max)s characters, got %(len)s."),
|
|
29
|
-
code="invalid",
|
|
30
|
-
params={"max": max_length, "len": len(value)},
|
|
31
|
-
)
|
|
File without changes
|
{python_terminusgps-33.3.1.dist-info → python_terminusgps-34.0.0.dist-info}/licenses/COPYING
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|