incognia-python 3.0.0__tar.gz → 3.2.0__tar.gz

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.
Files changed (34) hide show
  1. {incognia_python-3.0.0 → incognia_python-3.2.0}/CODEOWNERS +1 -1
  2. {incognia_python-3.0.0 → incognia_python-3.2.0}/PKG-INFO +3 -2
  3. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia/api.py +102 -12
  4. incognia_python-3.2.0/incognia/datetime_util.py +13 -0
  5. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia/models.py +22 -1
  6. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia_python.egg-info/PKG-INFO +3 -2
  7. {incognia_python-3.0.0 → incognia_python-3.2.0}/tests/test_api.py +450 -4
  8. incognia_python-3.0.0/incognia/datetime_util.py +0 -5
  9. {incognia_python-3.0.0 → incognia_python-3.2.0}/.github/dependabot.yml +0 -0
  10. {incognia_python-3.0.0 → incognia_python-3.2.0}/.github/workflows/codeql.yaml +0 -0
  11. {incognia_python-3.0.0 → incognia_python-3.2.0}/.github/workflows/continuous.yaml +0 -0
  12. {incognia_python-3.0.0 → incognia_python-3.2.0}/.gitignore +0 -0
  13. {incognia_python-3.0.0 → incognia_python-3.2.0}/LICENSE.txt +0 -0
  14. {incognia_python-3.0.0 → incognia_python-3.2.0}/MANIFEST.in +0 -0
  15. {incognia_python-3.0.0 → incognia_python-3.2.0}/README.md +0 -0
  16. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia/__init__.py +0 -0
  17. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia/base_request.py +0 -0
  18. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia/endpoints.py +0 -0
  19. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia/exceptions.py +0 -0
  20. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia/feedback_events.py +0 -0
  21. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia/json_util.py +0 -0
  22. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia/singleton.py +0 -0
  23. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia/token_manager.py +0 -0
  24. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia_python.egg-info/SOURCES.txt +0 -0
  25. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia_python.egg-info/dependency_links.txt +0 -0
  26. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia_python.egg-info/not-zip-safe +0 -0
  27. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia_python.egg-info/requires.txt +0 -0
  28. {incognia_python-3.0.0 → incognia_python-3.2.0}/incognia_python.egg-info/top_level.txt +0 -0
  29. {incognia_python-3.0.0 → incognia_python-3.2.0}/pyproject.toml +0 -0
  30. {incognia_python-3.0.0 → incognia_python-3.2.0}/requirements.txt +0 -0
  31. {incognia_python-3.0.0 → incognia_python-3.2.0}/setup.cfg +0 -0
  32. {incognia_python-3.0.0 → incognia_python-3.2.0}/tests/__init__.py +0 -0
  33. {incognia_python-3.0.0 → incognia_python-3.2.0}/tests/test_base_request.py +0 -0
  34. {incognia_python-3.0.0 → incognia_python-3.2.0}/tests/test_token_manager.py +0 -0
@@ -2,4 +2,4 @@
2
2
  # the repo. Unless a later match takes precedence,
3
3
  # @inloco/incognia-owners will be requested for
4
4
  # review when someone opens a pull request.
5
- * @inloco/incognia-owners
5
+ * @inloco/api-codeowners
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: incognia-python
3
- Version: 3.0.0
3
+ Version: 3.2.0
4
4
  Summary: Python lightweight client library for Incognia APIs
5
5
  Home-page: https://github.com/inloco/incognia-python
6
6
  Author: Incognia
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.9
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE.txt
15
15
  Requires-Dist: requests
16
+ Dynamic: license-file
16
17
 
17
18
  # Incognia API Python Client 🐍
18
19
 
@@ -1,11 +1,20 @@
1
1
  import datetime as dt
2
2
  from typing import Optional, List
3
3
 
4
- from .datetime_util import has_timezone
4
+ from .datetime_util import has_timezone, datetime_valid
5
5
  from .endpoints import Endpoints
6
6
  from .exceptions import IncogniaHTTPError, IncogniaError
7
7
  from .json_util import encode
8
- from .models import Coordinates, StructuredAddress, TransactionAddress, PaymentValue, PaymentMethod
8
+ from .models import (
9
+ Coordinates,
10
+ StructuredAddress,
11
+ TransactionAddress,
12
+ PaymentValue,
13
+ PaymentMethod,
14
+ Location,
15
+ Coupon,
16
+ PersonID,
17
+ )
9
18
  from .singleton import Singleton
10
19
  from .token_manager import TokenManager
11
20
  from .base_request import BaseRequest, JSON_CONTENT_HEADER
@@ -27,7 +36,11 @@ class IncogniaAPI(metaclass=Singleton):
27
36
  address_coordinates: Optional[Coordinates] = None,
28
37
  external_id: Optional[str] = None,
29
38
  policy_id: Optional[str] = None,
30
- account_id: Optional[str] = None) -> dict:
39
+ account_id: Optional[str] = None,
40
+ device_os: Optional[str] = None,
41
+ app_version: Optional[str] = None,
42
+ person_id: Optional[PersonID] = None,
43
+ custom_properties: Optional[dict] = None) -> dict:
31
44
  if not request_token:
32
45
  raise IncogniaError('request_token is required.')
33
46
 
@@ -41,7 +54,36 @@ class IncogniaAPI(metaclass=Singleton):
41
54
  'address_coordinates': address_coordinates,
42
55
  'external_id': external_id,
43
56
  'policy_id': policy_id,
44
- 'account_id': account_id
57
+ 'account_id': account_id,
58
+ 'device_os': device_os.lower() if device_os is not None else None,
59
+ 'app_version': app_version,
60
+ 'person_id': person_id,
61
+ 'custom_properties': custom_properties
62
+ }
63
+ data = encode(body)
64
+ return self.__request.post(Endpoints.SIGNUPS, headers=headers, data=data)
65
+
66
+ except IncogniaHTTPError as e:
67
+ raise IncogniaHTTPError(e) from None
68
+
69
+ def register_new_web_signup(self,
70
+ request_token: Optional[str],
71
+ policy_id: Optional[str] = None,
72
+ account_id: Optional[str] = None,
73
+ custom_properties: Optional[dict] = None,
74
+ person_id: Optional[PersonID] = None) -> dict:
75
+ if not request_token:
76
+ raise IncogniaError('request_token is required.')
77
+
78
+ try:
79
+ headers = self.__get_authorization_header()
80
+ headers.update(JSON_CONTENT_HEADER)
81
+ body = {
82
+ 'request_token': request_token,
83
+ 'policy_id': policy_id,
84
+ 'account_id': account_id,
85
+ 'custom_properties': custom_properties,
86
+ 'person_id': person_id,
45
87
  }
46
88
  data = encode(body)
47
89
  return self.__request.post(Endpoints.SIGNUPS, headers=headers, data=data)
@@ -59,7 +101,8 @@ class IncogniaAPI(metaclass=Singleton):
59
101
  installation_id: Optional[str] = None,
60
102
  request_token: Optional[str] = None,
61
103
  occurred_at: dt.datetime = None,
62
- expires_at: dt.datetime = None) -> None:
104
+ expires_at: dt.datetime = None,
105
+ person_id: Optional[PersonID] = None) -> None:
63
106
  if not event:
64
107
  raise IncogniaError('event is required.')
65
108
  if occurred_at is not None and not has_timezone(occurred_at):
@@ -78,7 +121,8 @@ class IncogniaAPI(metaclass=Singleton):
78
121
  'signup_id': signup_id,
79
122
  'account_id': account_id,
80
123
  'installation_id': installation_id,
81
- 'request_token': request_token
124
+ 'request_token': request_token,
125
+ 'person_id': person_id,
82
126
  }
83
127
  if occurred_at is not None:
84
128
  body['occurred_at'] = occurred_at.isoformat()
@@ -94,15 +138,32 @@ class IncogniaAPI(metaclass=Singleton):
94
138
  request_token: str,
95
139
  account_id: str,
96
140
  external_id: Optional[str] = None,
141
+ location: Optional[Location] = None,
97
142
  addresses: Optional[List[TransactionAddress]] = None,
98
143
  payment_value: Optional[PaymentValue] = None,
99
144
  payment_methods: Optional[List[PaymentMethod]] = None,
100
145
  evaluate: Optional[bool] = None,
101
- policy_id: Optional[str] = None) -> dict:
146
+ policy_id: Optional[str] = None,
147
+ custom_properties: Optional[dict] = None,
148
+ coupon: Optional[Coupon] = None,
149
+ device_os: Optional[str] = None,
150
+ app_version: Optional[str] = None,
151
+ store_id: Optional[str] = None,
152
+ person_id: Optional[PersonID] = None) -> dict:
102
153
  if not request_token:
103
154
  raise IncogniaError('request_token is required.')
104
155
  if not account_id:
105
156
  raise IncogniaError('account_id is required.')
157
+ if location is not None:
158
+ if location['latitude'] is None:
159
+ raise IncogniaError('location argument requires "latitude" field')
160
+ if location['longitude'] is None:
161
+ raise IncogniaError('location argument requires "longitude" field')
162
+ if (
163
+ location['collected_at'] is not None
164
+ and not datetime_valid(location['collected_at'])
165
+ ):
166
+ raise IncogniaError('location["collected_at"] must conform to ISO-8601 format')
106
167
 
107
168
  try:
108
169
  headers = self.__get_authorization_header()
@@ -113,10 +174,17 @@ class IncogniaAPI(metaclass=Singleton):
113
174
  'request_token': request_token,
114
175
  'account_id': account_id,
115
176
  'external_id': external_id,
177
+ 'location': location,
116
178
  'addresses': addresses,
117
179
  'payment_value': payment_value,
118
180
  'payment_methods': payment_methods,
119
- 'policy_id': policy_id
181
+ 'policy_id': policy_id,
182
+ 'custom_properties': custom_properties,
183
+ 'coupon': coupon,
184
+ 'device_os': device_os.lower() if device_os is not None else None,
185
+ 'app_version': app_version,
186
+ 'store_id': store_id,
187
+ 'person_id': person_id,
120
188
  }
121
189
  data = encode(body)
122
190
  return self.__request.post(Endpoints.TRANSACTIONS, headers=headers, params=params,
@@ -128,13 +196,27 @@ class IncogniaAPI(metaclass=Singleton):
128
196
  def register_login(self,
129
197
  request_token: str,
130
198
  account_id: str,
199
+ location: Optional[Location] = None,
131
200
  external_id: Optional[str] = None,
132
201
  evaluate: Optional[bool] = None,
133
- policy_id: Optional[str] = None) -> dict:
202
+ policy_id: Optional[str] = None,
203
+ device_os: Optional[str] = None,
204
+ app_version: Optional[str] = None,
205
+ person_id: Optional[PersonID] = None) -> dict:
134
206
  if not request_token:
135
207
  raise IncogniaError('request_token is required.')
136
208
  if not account_id:
137
209
  raise IncogniaError('account_id is required.')
210
+ if location is not None:
211
+ if location['latitude'] is None:
212
+ raise IncogniaError('location argument requires "latitude" field')
213
+ if location['longitude'] is None:
214
+ raise IncogniaError('location argument requires "longitude" field')
215
+ if (
216
+ location['collected_at'] is not None
217
+ and not datetime_valid(location['collected_at'])
218
+ ):
219
+ raise IncogniaError('location["collected_at"] must conform to ISO-8601 format')
138
220
 
139
221
  try:
140
222
  headers = self.__get_authorization_header()
@@ -144,8 +226,12 @@ class IncogniaAPI(metaclass=Singleton):
144
226
  'type': 'login',
145
227
  'request_token': request_token,
146
228
  'account_id': account_id,
229
+ 'location': location,
147
230
  'external_id': external_id,
148
- 'policy_id': policy_id
231
+ 'policy_id': policy_id,
232
+ 'device_os': device_os.lower() if device_os is not None else None,
233
+ 'app_version': app_version,
234
+ 'person_id': person_id,
149
235
  }
150
236
  data = encode(body)
151
237
  return self.__request.post(Endpoints.TRANSACTIONS, headers=headers, params=params,
@@ -159,7 +245,9 @@ class IncogniaAPI(metaclass=Singleton):
159
245
  account_id: str,
160
246
  external_id: Optional[str] = None,
161
247
  evaluate: Optional[bool] = None,
162
- policy_id: Optional[str] = None) -> dict:
248
+ policy_id: Optional[str] = None,
249
+ custom_properties: Optional[dict] = None,
250
+ person_id: Optional[PersonID] = None) -> dict:
163
251
  if not request_token:
164
252
  raise IncogniaError('request_token is required.')
165
253
  if not account_id:
@@ -174,7 +262,9 @@ class IncogniaAPI(metaclass=Singleton):
174
262
  'request_token': request_token,
175
263
  'account_id': account_id,
176
264
  'external_id': external_id,
177
- 'policy_id': policy_id
265
+ 'policy_id': policy_id,
266
+ 'custom_properties': custom_properties,
267
+ 'person_id': person_id,
178
268
  }
179
269
  data = encode(body)
180
270
  return self.__request.post(Endpoints.TRANSACTIONS, headers=headers, params=params,
@@ -0,0 +1,13 @@
1
+ from datetime import datetime
2
+
3
+
4
+ def has_timezone(d: datetime) -> bool:
5
+ return d.tzinfo is not None and d.tzinfo.utcoffset(d) is not None
6
+
7
+
8
+ def datetime_valid(dt_str):
9
+ try:
10
+ datetime.fromisoformat(dt_str.replace('Z', '+00:00'))
11
+ except ValueError:
12
+ return False
13
+ return True
@@ -20,6 +20,14 @@ class StructuredAddress(TypedDict, total=False):
20
20
  postal_code: str
21
21
 
22
22
 
23
+ class Coupon (TypedDict, total=False):
24
+ type: Literal['percent_off', 'fixed_value']
25
+ value: float
26
+ max_discount: float
27
+ id: str
28
+ name: str
29
+
30
+
23
31
  class TransactionAddress(TypedDict, total=False):
24
32
  type: Literal['shipping', 'billing', 'home']
25
33
  structured_address: StructuredAddress
@@ -39,6 +47,19 @@ class CardInfo(TypedDict, total=False):
39
47
 
40
48
 
41
49
  class PaymentMethod(TypedDict, total=False):
42
- type: Literal['credit', 'debit']
50
+ type: Literal['account_balance', 'apple_pay', 'bancolombia',
51
+ 'boleto_bancario', 'cash', 'credit_card', 'debit_card',
52
+ 'google_pay', 'meal_voucher', 'nu_pay', 'paypal', 'pix']
43
53
  credit_card_info: CardInfo
44
54
  debit_card_info: CardInfo
55
+
56
+
57
+ class Location(TypedDict, total=False):
58
+ latitude: float
59
+ longitude: float
60
+ collected_at: str
61
+
62
+
63
+ class PersonID(TypedDict, total=False):
64
+ type: str
65
+ value: str
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: incognia-python
3
- Version: 3.0.0
3
+ Version: 3.2.0
4
4
  Summary: Python lightweight client library for Incognia APIs
5
5
  Home-page: https://github.com/inloco/incognia-python
6
6
  Author: Incognia
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.9
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE.txt
15
15
  Requires-Dist: requests
16
+ Dynamic: license-file
16
17
 
17
18
  # Incognia API Python Client 🐍
18
19
 
@@ -20,6 +20,12 @@ class TestIncogniaAPI(TestCase):
20
20
  ACCOUNT_ID: Final[str] = 'ANY_ACCOUNT_ID'
21
21
  INVALID_ACCOUNT_ID: Final[str] = 'INVALID_ACCOUNT_ID'
22
22
  ADDRESS_LINE: Final[str] = 'ANY_ADDRESS_LINE'
23
+ DEVICE_OS: Final[str] = 'ANY_DEVICE_OS'
24
+ APP_VERSION: Final[str] = 'ANY_APP_VERSION'
25
+ PERSON_ID: Final[dict] = {
26
+ 'type': 'cpf',
27
+ 'value': '12345678901'
28
+ }
23
29
  STRUCTURED_ADDRESS: Final[dict] = {
24
30
  'locale': 'ANY_LOCALE',
25
31
  'country_name': 'ANY_COUNTRY_NAME',
@@ -60,6 +66,10 @@ class TestIncogniaAPI(TestCase):
60
66
  REGISTER_INVALID_SIGNUP_DATA: Final[bytes] = encode({
61
67
  'request_token': f'{INVALID_REQUEST_TOKEN}'
62
68
  })
69
+ CUSTOM_PROPERTIES: Final[dict] = {
70
+ 'float_field': 6.092,
71
+ 'string_field': "the next generation of identity",
72
+ }
63
73
  FULL_REGISTER_SIGNUP_DATA: Final[bytes] = encode({
64
74
  'request_token': f'{REQUEST_TOKEN}',
65
75
  'address_line': f'{ADDRESS_LINE}',
@@ -67,7 +77,24 @@ class TestIncogniaAPI(TestCase):
67
77
  'address_coordinates': ADDRESS_COORDINATES,
68
78
  'external_id': f'{EXTERNAL_ID}',
69
79
  'policy_id': f'{POLICY_ID}',
70
- 'account_id': f'{ACCOUNT_ID}'
80
+ 'account_id': f'{ACCOUNT_ID}',
81
+ 'device_os': f'{DEVICE_OS.lower()}',
82
+ 'app_version': f'{APP_VERSION}',
83
+ 'person_id': PERSON_ID,
84
+ 'custom_properties': CUSTOM_PROPERTIES
85
+ })
86
+ REGISTER_WEB_SIGNUP_DATA: Final[bytes] = encode({
87
+ 'request_token': f'{REQUEST_TOKEN}'
88
+ })
89
+ REGISTER_INVALID_WEB_SIGNUP_DATA: Final[bytes] = encode({
90
+ 'request_token': f'{INVALID_REQUEST_TOKEN}'
91
+ })
92
+ FULL_REGISTER_WEB_SIGNUP_DATA: Final[bytes] = encode({
93
+ 'request_token': f'{REQUEST_TOKEN}',
94
+ 'policy_id': f'{POLICY_ID}',
95
+ 'account_id': f'{ACCOUNT_ID}',
96
+ 'custom_properties': CUSTOM_PROPERTIES,
97
+ 'person_id': PERSON_ID
71
98
  })
72
99
  OK_STATUS_CODE: Final[int] = 200
73
100
  CLIENT_ERROR_CODE: Final[int] = 400
@@ -76,6 +103,53 @@ class TestIncogniaAPI(TestCase):
76
103
  TIMESTAMP_WITHOUT_TIMEZONE: Final[dt.datetime] = dt.datetime.now()
77
104
  LOGIN_ID: Final[str] = 'ANY_LOGIN_ID'
78
105
  PAYMENT_ID: Final[str] = 'ANY_PAYMENT_ID'
106
+ STORE_ID: Final[str] = 'ANY_STORE_ID'
107
+ COUPON: Final[dict] = {
108
+ "type": "percent_off",
109
+ "value": 2.5,
110
+ "max_discount": 50.0,
111
+ "id": "COUPON_ID",
112
+ "name": "COUPON_NAME"
113
+ }
114
+ TRANSACTION_ADDRESS: Final[dict] = {
115
+ "type": "billing",
116
+ "structured_address": STRUCTURED_ADDRESS,
117
+ "address_coordinates": ADDRESS_COORDINATES
118
+ }
119
+ PAYMENT_VALUE: Final[dict] = {
120
+ 'value': 12.34,
121
+ 'currency': 'BRL'
122
+ }
123
+ CARD_INFO: Final[dict] = {
124
+ 'bin': '0000000000',
125
+ 'last_four_digits': '0000',
126
+ 'expiry_year': '2029',
127
+ 'expiry_month': '04'
128
+ }
129
+ PAYMENT_METHOD: Final[dict] = {
130
+ 'type': 'credit_card',
131
+ 'credit_card_info': CARD_INFO
132
+ }
133
+ LOCATION: Final[dict] = {
134
+ 'latitude': 0.000,
135
+ 'longitude': 89.000,
136
+ 'collected_at': TIMESTAMP.isoformat()
137
+ }
138
+ INVALID_LOCATION_EMPTY_LATITUDE: Final[dict] = {
139
+ 'latitude': None,
140
+ 'longitude': 13.123,
141
+ 'collected_at': TIMESTAMP.isoformat()
142
+ }
143
+ INVALID_LOCATION_EMPTY_LONGITUDE: Final[dict] = {
144
+ 'latitude': 0.000,
145
+ 'longitude': None,
146
+ 'collected_at': TIMESTAMP.isoformat()
147
+ }
148
+ INVALID_LOCATION_WRONG_TIMESTAMP: Final[dict] = {
149
+ 'latitude': 0.000,
150
+ 'longitude': 13.123,
151
+ 'collected_at': "12:04 14/10/2024"
152
+ }
79
153
  REGISTER_VALID_FEEDBACK_DATA: Final[bytes] = encode({
80
154
  'event': f'{VALID_EVENT_FEEDBACK_TYPE}'
81
155
  })
@@ -88,8 +162,9 @@ class TestIncogniaAPI(TestCase):
88
162
  'account_id': f'{ACCOUNT_ID}',
89
163
  'installation_id': f'{INSTALLATION_ID}',
90
164
  'request_token': f'{REQUEST_TOKEN}',
165
+ 'person_id': PERSON_ID,
91
166
  'occurred_at': TIMESTAMP.isoformat(),
92
- 'expires_at': TIMESTAMP.isoformat(),
167
+ 'expires_at': TIMESTAMP.isoformat()
93
168
  })
94
169
  REGISTER_VALID_PAYMENT_DATA: Final[bytes] = encode({
95
170
  'type': 'payment',
@@ -97,6 +172,30 @@ class TestIncogniaAPI(TestCase):
97
172
  'account_id': f'{ACCOUNT_ID}',
98
173
  'policy_id': f'{POLICY_ID}',
99
174
  })
175
+ REGISTER_VALID_PAYMENT_DATA_ALL_FIELDS: Final[bytes] = encode({
176
+ 'type': 'payment',
177
+ 'request_token': f'{REQUEST_TOKEN}',
178
+ 'account_id': f'{ACCOUNT_ID}',
179
+ 'external_id': f'{EXTERNAL_ID}',
180
+ 'location': LOCATION,
181
+ 'addresses': [TRANSACTION_ADDRESS],
182
+ 'payment_value': PAYMENT_VALUE,
183
+ 'payment_methods': PAYMENT_METHOD,
184
+ 'policy_id': f'{POLICY_ID}',
185
+ 'custom_properties': CUSTOM_PROPERTIES,
186
+ 'coupon': COUPON,
187
+ 'device_os': f'{DEVICE_OS.lower()}',
188
+ 'app_version': f'{APP_VERSION}',
189
+ 'store_id': f'{STORE_ID}',
190
+ 'person_id': PERSON_ID
191
+ })
192
+ REGISTER_VALID_PAYMENT_DATA_WITH_LOCATION: Final[bytes] = encode({
193
+ 'type': 'payment',
194
+ 'request_token': f'{REQUEST_TOKEN}',
195
+ 'account_id': f'{ACCOUNT_ID}',
196
+ 'location': LOCATION,
197
+ 'policy_id': f'{POLICY_ID}',
198
+ })
100
199
  REGISTER_INVALID_PAYMENT_DATA: Final[bytes] = encode({
101
200
  'type': 'payment',
102
201
  'request_token': f'{INVALID_REQUEST_TOKEN}',
@@ -108,12 +207,39 @@ class TestIncogniaAPI(TestCase):
108
207
  'account_id': f'{ACCOUNT_ID}',
109
208
  'policy_id': f'{POLICY_ID}'
110
209
  })
210
+ REGISTER_VALID_LOGIN_DATA_WITH_LOCATION: Final[bytes] = encode({
211
+ 'type': 'login',
212
+ 'request_token': f'{REQUEST_TOKEN}',
213
+ 'account_id': f'{ACCOUNT_ID}',
214
+ 'location': LOCATION,
215
+ 'policy_id': f'{POLICY_ID}'
216
+ })
217
+ REGISTER_VALID_LOGIN_DATA_ALL_FIELDS: Final[bytes] = encode({
218
+ 'type': 'login',
219
+ 'request_token': f'{REQUEST_TOKEN}',
220
+ 'account_id': f'{ACCOUNT_ID}',
221
+ 'location': LOCATION,
222
+ 'external_id': f'{EXTERNAL_ID}',
223
+ 'policy_id': f'{POLICY_ID}',
224
+ 'device_os': f'{DEVICE_OS.lower()}',
225
+ 'app_version': f'{APP_VERSION}',
226
+ 'person_id': PERSON_ID
227
+ })
111
228
  REGISTER_VALID_WEB_LOGIN_DATA: Final[bytes] = encode({
112
229
  'type': 'login',
113
230
  'request_token': f'{REQUEST_TOKEN}',
114
231
  'account_id': f'{ACCOUNT_ID}',
115
232
  'policy_id': f'{POLICY_ID}'
116
233
  })
234
+ REGISTER_VALID_WEB_LOGIN_ALL_FIELDS: Final[bytes] = encode({
235
+ 'type': 'login',
236
+ 'request_token': f'{REQUEST_TOKEN}',
237
+ 'account_id': f'{ACCOUNT_ID}',
238
+ 'external_id': f'{EXTERNAL_ID}',
239
+ 'policy_id': f'{POLICY_ID}',
240
+ 'custom_properties': CUSTOM_PROPERTIES,
241
+ 'person_id': PERSON_ID
242
+ })
117
243
  REGISTER_INVALID_LOGIN_DATA: Final[bytes] = encode({
118
244
  'type': 'login',
119
245
  'request_token': f'{INVALID_REQUEST_TOKEN}',
@@ -148,6 +274,22 @@ class TestIncogniaAPI(TestCase):
148
274
 
149
275
  self.assertEqual(response, self.JSON_RESPONSE)
150
276
 
277
+ @patch.object(BaseRequest, 'post')
278
+ @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
279
+ def test_register_new_web_signup_when_request_token_is_valid_should_return_a_valid_dict(
280
+ self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
281
+ mock_base_request_post.configure_mock(return_value=self.JSON_RESPONSE)
282
+
283
+ api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)
284
+ response = api.register_new_web_signup(request_token=self.REQUEST_TOKEN)
285
+
286
+ mock_token_manager_get.assert_called()
287
+ mock_base_request_post.assert_called_with(Endpoints.SIGNUPS,
288
+ headers=self.AUTH_AND_JSON_CONTENT_HEADERS,
289
+ data=self.REGISTER_WEB_SIGNUP_DATA)
290
+
291
+ self.assertEqual(response, self.JSON_RESPONSE)
292
+
151
293
  @patch.object(BaseRequest, 'post')
152
294
  @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
153
295
  def test_register_new_signup_when_request_token_is_valid_should_return_full_valid_dict_(
@@ -161,7 +303,11 @@ class TestIncogniaAPI(TestCase):
161
303
  external_id=self.EXTERNAL_ID,
162
304
  policy_id=self.POLICY_ID,
163
305
  account_id=self.ACCOUNT_ID,
164
- request_token=self.REQUEST_TOKEN)
306
+ request_token=self.REQUEST_TOKEN,
307
+ device_os=self.DEVICE_OS,
308
+ app_version=self.APP_VERSION,
309
+ person_id=self.PERSON_ID,
310
+ custom_properties=self.CUSTOM_PROPERTIES)
165
311
 
166
312
  mock_token_manager_get.assert_called()
167
313
  mock_base_request_post.assert_called_with(Endpoints.SIGNUPS,
@@ -170,6 +316,26 @@ class TestIncogniaAPI(TestCase):
170
316
 
171
317
  self.assertEqual(response, self.JSON_RESPONSE)
172
318
 
319
+ @patch.object(BaseRequest, 'post')
320
+ @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
321
+ def test_register_new_web_signup_when_request_token_is_valid_should_return_full_valid_dict_(
322
+ self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
323
+ mock_base_request_post.configure_mock(return_value=self.JSON_RESPONSE)
324
+
325
+ api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)
326
+ response = api.register_new_web_signup(request_token=self.REQUEST_TOKEN,
327
+ policy_id=self.POLICY_ID,
328
+ account_id=self.ACCOUNT_ID,
329
+ custom_properties=self.CUSTOM_PROPERTIES,
330
+ person_id=self.PERSON_ID)
331
+
332
+ mock_token_manager_get.assert_called()
333
+ mock_base_request_post.assert_called_with(Endpoints.SIGNUPS,
334
+ headers=self.AUTH_AND_JSON_CONTENT_HEADERS,
335
+ data=self.FULL_REGISTER_WEB_SIGNUP_DATA)
336
+
337
+ self.assertEqual(response, self.JSON_RESPONSE)
338
+
173
339
  @patch.object(BaseRequest, 'post')
174
340
  @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
175
341
  def test_register_new_signup_when_request_token_is_empty_should_raise_an_IncogniaError(
@@ -181,6 +347,17 @@ class TestIncogniaAPI(TestCase):
181
347
  mock_token_manager_get.assert_not_called()
182
348
  mock_base_request_post.assert_not_called()
183
349
 
350
+ @patch.object(BaseRequest, 'post')
351
+ @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
352
+ def test_register_new_web_signup_when_request_token_is_empty_should_raise_an_IncogniaError(
353
+ self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
354
+ api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)
355
+
356
+ self.assertRaises(IncogniaError, api.register_new_web_signup, request_token='')
357
+
358
+ mock_token_manager_get.assert_not_called()
359
+ mock_base_request_post.assert_not_called()
360
+
184
361
  @patch.object(BaseRequest, 'post')
185
362
  @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
186
363
  def test_register_new_signup_when_request_token_is_invalid_should_raise_an_IncogniaHTTPError(
@@ -196,6 +373,22 @@ class TestIncogniaAPI(TestCase):
196
373
  headers=self.AUTH_AND_JSON_CONTENT_HEADERS,
197
374
  data=self.REGISTER_INVALID_SIGNUP_DATA)
198
375
 
376
+ @patch.object(BaseRequest, 'post')
377
+ @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
378
+ def test_register_new_web_signup_if_request_token_is_invalid_should_raise_an_IncogniaHTTPError(
379
+ self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
380
+ mock_base_request_post.configure_mock(side_effect=IncogniaHTTPError)
381
+
382
+ api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)
383
+
384
+ self.assertRaises(IncogniaHTTPError, api.register_new_web_signup,
385
+ self.INVALID_REQUEST_TOKEN)
386
+
387
+ mock_token_manager_get.assert_called()
388
+ mock_base_request_post.assert_called_with(Endpoints.SIGNUPS,
389
+ headers=self.AUTH_AND_JSON_CONTENT_HEADERS,
390
+ data=self.REGISTER_INVALID_WEB_SIGNUP_DATA)
391
+
199
392
  @patch.object(BaseRequest, 'post')
200
393
  @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
201
394
  def test_register_feedback_when_required_fields_are_valid_should_work(
@@ -224,7 +417,8 @@ class TestIncogniaAPI(TestCase):
224
417
  signup_id=self.SIGNUP_ID,
225
418
  account_id=self.ACCOUNT_ID,
226
419
  installation_id=self.INSTALLATION_ID,
227
- request_token=self.REQUEST_TOKEN)
420
+ request_token=self.REQUEST_TOKEN,
421
+ person_id=self.PERSON_ID)
228
422
 
229
423
  mock_token_manager_get.assert_called()
230
424
  mock_base_request_post.assert_called_with(Endpoints.FEEDBACKS,
@@ -290,6 +484,39 @@ class TestIncogniaAPI(TestCase):
290
484
 
291
485
  self.assertEqual(request_response, self.JSON_RESPONSE)
292
486
 
487
+ @patch.object(BaseRequest, 'post')
488
+ @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
489
+ def test_register_payment_with_all_fields_should_work(
490
+ self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
491
+ mock_base_request_post.configure_mock(return_value=self.JSON_RESPONSE)
492
+
493
+ api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)
494
+
495
+ request_response = api.register_payment(
496
+ request_token=self.REQUEST_TOKEN,
497
+ account_id=self.ACCOUNT_ID,
498
+ external_id=self.EXTERNAL_ID,
499
+ location=self.LOCATION,
500
+ addresses=[self.TRANSACTION_ADDRESS],
501
+ payment_value=self.PAYMENT_VALUE,
502
+ payment_methods=self.PAYMENT_METHOD,
503
+ policy_id=self.POLICY_ID,
504
+ custom_properties=self.CUSTOM_PROPERTIES,
505
+ coupon=self.COUPON,
506
+ device_os=self.DEVICE_OS,
507
+ app_version=self.APP_VERSION,
508
+ store_id=self.STORE_ID,
509
+ person_id=self.PERSON_ID
510
+ )
511
+
512
+ mock_token_manager_get.assert_called()
513
+ mock_base_request_post.assert_called_with(Endpoints.TRANSACTIONS,
514
+ headers=self.AUTH_AND_JSON_CONTENT_HEADERS,
515
+ params=self.DEFAULT_PARAMS,
516
+ data=self.REGISTER_VALID_PAYMENT_DATA_ALL_FIELDS)
517
+
518
+ self.assertEqual(request_response, self.JSON_RESPONSE)
519
+
293
520
  @patch.object(BaseRequest, 'post')
294
521
  @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
295
522
  def test_register_payment_when_request_token_is_empty_should_raise_an_IncogniaError(
@@ -418,6 +645,28 @@ class TestIncogniaAPI(TestCase):
418
645
 
419
646
  self.assertEqual(request_response, self.JSON_RESPONSE)
420
647
 
648
+ @patch.object(BaseRequest, 'post')
649
+ @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
650
+ def test_register_web_login_when_all_fields_are_valid_should_work(
651
+ self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
652
+ mock_base_request_post.configure_mock(return_value=self.JSON_RESPONSE)
653
+
654
+ api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)
655
+
656
+ request_response = api.register_web_login(self.REQUEST_TOKEN,
657
+ self.ACCOUNT_ID,
658
+ external_id=self.EXTERNAL_ID,
659
+ policy_id=self.POLICY_ID,
660
+ custom_properties=self.CUSTOM_PROPERTIES,
661
+ person_id=self.PERSON_ID)
662
+ mock_token_manager_get.assert_called()
663
+ mock_base_request_post.assert_called_with(Endpoints.TRANSACTIONS,
664
+ headers=self.AUTH_AND_JSON_CONTENT_HEADERS,
665
+ params=self.DEFAULT_PARAMS,
666
+ data=self.REGISTER_VALID_WEB_LOGIN_ALL_FIELDS)
667
+
668
+ self.assertEqual(request_response, self.JSON_RESPONSE)
669
+
421
670
  @patch.object(BaseRequest, 'post')
422
671
  @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
423
672
  def test_register_web_login_when_request_token_is_empty_should_raise_an_IncogniaError(
@@ -461,3 +710,200 @@ class TestIncogniaAPI(TestCase):
461
710
  headers=self.AUTH_AND_JSON_CONTENT_HEADERS,
462
711
  params=self.DEFAULT_PARAMS,
463
712
  data=self.REGISTER_INVALID_WEB_LOGIN_DATA)
713
+
714
+ @patch.object(BaseRequest, 'post')
715
+ @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
716
+ def test_register_login_with_valid_location_should_work(
717
+ self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
718
+
719
+ mock_base_request_post.configure_mock(return_value=self.JSON_RESPONSE)
720
+
721
+ api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)
722
+
723
+ request_response = api.register_login(self.REQUEST_TOKEN,
724
+ self.ACCOUNT_ID,
725
+ location=self.LOCATION,
726
+ policy_id=self.POLICY_ID)
727
+
728
+ mock_token_manager_get.assert_called()
729
+ mock_base_request_post.assert_called_with(Endpoints.TRANSACTIONS,
730
+ headers=self.AUTH_AND_JSON_CONTENT_HEADERS,
731
+ params=self.DEFAULT_PARAMS,
732
+ data=self.REGISTER_VALID_LOGIN_DATA_WITH_LOCATION)
733
+
734
+ self.assertEqual(request_response, self.JSON_RESPONSE)
735
+
736
+ @patch.object(BaseRequest, 'post')
737
+ @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
738
+ def test_register_login_with_all_fields_should_work(
739
+ self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
740
+
741
+ mock_base_request_post.configure_mock(return_value=self.JSON_RESPONSE)
742
+
743
+ api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)
744
+
745
+ request_response = api.register_login(self.REQUEST_TOKEN,
746
+ self.ACCOUNT_ID,
747
+ location=self.LOCATION,
748
+ external_id=self.EXTERNAL_ID,
749
+ policy_id=self.POLICY_ID,
750
+ device_os=self.DEVICE_OS,
751
+ app_version=self.APP_VERSION,
752
+ person_id=self.PERSON_ID)
753
+
754
+ mock_token_manager_get.assert_called()
755
+ mock_base_request_post.assert_called_with(Endpoints.TRANSACTIONS,
756
+ headers=self.AUTH_AND_JSON_CONTENT_HEADERS,
757
+ params=self.DEFAULT_PARAMS,
758
+ data=self.REGISTER_VALID_LOGIN_DATA_ALL_FIELDS)
759
+
760
+ self.assertEqual(request_response, self.JSON_RESPONSE)
761
+
762
+ @patch.object(BaseRequest, 'post')
763
+ @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
764
+ def test_register_login_with_location_and_empty_latitude_should_raise_an_IncogniaError(
765
+ self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
766
+
767
+ mock_base_request_post.configure_mock(return_value=self.JSON_RESPONSE)
768
+
769
+ api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)
770
+
771
+ self.assertRaises(
772
+ IncogniaError, api.register_login,
773
+ self.REQUEST_TOKEN,
774
+ self.ACCOUNT_ID,
775
+ location=self.INVALID_LOCATION_EMPTY_LATITUDE,
776
+ policy_id=self.POLICY_ID,
777
+ )
778
+
779
+ mock_token_manager_get.assert_not_called()
780
+ mock_base_request_post.assert_not_called()
781
+
782
+ @patch.object(BaseRequest, 'post')
783
+ @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
784
+ def test_register_login_with_location_and_empty_longitude_should_raise_an_IncogniaError(
785
+ self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
786
+
787
+ mock_base_request_post.configure_mock(return_value=self.JSON_RESPONSE)
788
+
789
+ api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)
790
+
791
+ self.assertRaises(
792
+ IncogniaError,
793
+ api.register_login,
794
+ self.REQUEST_TOKEN,
795
+ self.ACCOUNT_ID,
796
+ location=self.INVALID_LOCATION_EMPTY_LONGITUDE,
797
+ policy_id=self.POLICY_ID,
798
+ )
799
+
800
+ mock_token_manager_get.assert_not_called()
801
+ mock_base_request_post.assert_not_called()
802
+
803
+ @patch.object(BaseRequest, 'post')
804
+ @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
805
+ def test_register_login_with_location_and_wrong_timestamp_should_raise_an_IncogniaError(
806
+ self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
807
+
808
+ mock_base_request_post.configure_mock(return_value=self.JSON_RESPONSE)
809
+
810
+ api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)
811
+
812
+ self.assertRaises(
813
+ IncogniaError,
814
+ api.register_login,
815
+ self.REQUEST_TOKEN,
816
+ self.ACCOUNT_ID,
817
+ location=self.INVALID_LOCATION_WRONG_TIMESTAMP,
818
+ policy_id=self.POLICY_ID,
819
+ )
820
+
821
+ mock_token_manager_get.assert_not_called()
822
+ mock_base_request_post.assert_not_called()
823
+
824
+ @patch.object(BaseRequest, 'post')
825
+ @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
826
+ def test_register_payment_with_valid_location_should_work(
827
+ self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
828
+
829
+ mock_base_request_post.configure_mock(return_value=self.JSON_RESPONSE)
830
+
831
+ api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)
832
+
833
+ request_response = api.register_payment(
834
+ self.REQUEST_TOKEN,
835
+ self.ACCOUNT_ID,
836
+ location=self.LOCATION,
837
+ policy_id=self.POLICY_ID,
838
+ )
839
+
840
+ mock_token_manager_get.assert_called()
841
+ mock_base_request_post.assert_called_with(
842
+ Endpoints.TRANSACTIONS,
843
+ headers=self.AUTH_AND_JSON_CONTENT_HEADERS,
844
+ params=self.DEFAULT_PARAMS,
845
+ data=self.REGISTER_VALID_PAYMENT_DATA_WITH_LOCATION,
846
+ )
847
+
848
+ self.assertEqual(request_response, self.JSON_RESPONSE)
849
+
850
+ @patch.object(BaseRequest, 'post')
851
+ @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
852
+ def test_register_payment_with_location_and_empty_latitude_should_raise_an_IncogniaError(
853
+ self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
854
+
855
+ mock_base_request_post.configure_mock(return_value=self.JSON_RESPONSE)
856
+
857
+ api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)
858
+
859
+ self.assertRaises(
860
+ IncogniaError, api.register_payment,
861
+ self.REQUEST_TOKEN,
862
+ self.ACCOUNT_ID,
863
+ location=self.INVALID_LOCATION_EMPTY_LATITUDE,
864
+ policy_id=self.POLICY_ID,
865
+ )
866
+
867
+ mock_token_manager_get.assert_not_called()
868
+ mock_base_request_post.assert_not_called()
869
+
870
+ @patch.object(BaseRequest, 'post')
871
+ @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
872
+ def test_register_payment_with_location_and_empty_longitude_should_raise_an_IncogniaError(
873
+ self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
874
+
875
+ mock_base_request_post.configure_mock(return_value=self.JSON_RESPONSE)
876
+
877
+ api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)
878
+
879
+ self.assertRaises(
880
+ IncogniaError, api.register_payment,
881
+ self.REQUEST_TOKEN,
882
+ self.ACCOUNT_ID,
883
+ location=self.INVALID_LOCATION_EMPTY_LONGITUDE,
884
+ policy_id=self.POLICY_ID,
885
+ )
886
+
887
+ mock_token_manager_get.assert_not_called()
888
+ mock_base_request_post.assert_not_called()
889
+
890
+ @patch.object(BaseRequest, 'post')
891
+ @patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
892
+ def test_register_payment_with_location_and_wrong_timestamp_should_raise_an_IncogniaError(
893
+ self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
894
+
895
+ mock_base_request_post.configure_mock(return_value=self.JSON_RESPONSE)
896
+
897
+ api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)
898
+
899
+ self.assertRaises(
900
+ IncogniaError,
901
+ api.register_payment,
902
+ self.REQUEST_TOKEN,
903
+ self.ACCOUNT_ID,
904
+ location=self.INVALID_LOCATION_WRONG_TIMESTAMP,
905
+ policy_id=self.POLICY_ID,
906
+ )
907
+
908
+ mock_token_manager_get.assert_not_called()
909
+ mock_base_request_post.assert_not_called()
@@ -1,5 +0,0 @@
1
- import datetime as dt
2
-
3
-
4
- def has_timezone(d: dt.datetime) -> bool:
5
- return d.tzinfo is not None and d.tzinfo.utcoffset(d) is not None