mailbox-org-api 2.4__tar.gz → 2.5.1__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 (21) hide show
  1. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/PKG-INFO +1 -1
  2. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/mailbox_org_api/APIClient.py +75 -47
  3. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/mailbox_org_api.egg-info/PKG-INFO +1 -1
  4. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/pyproject.toml +1 -1
  5. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/setup.py +1 -1
  6. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/tests/TestAPIClient.py +7 -3
  7. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/LICENSE +0 -0
  8. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/README.md +0 -0
  9. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/mailbox_org_api/APIError.py +0 -0
  10. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/mailbox_org_api/Account.py +0 -0
  11. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/mailbox_org_api/Invoice.py +0 -0
  12. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/mailbox_org_api/Mail.py +0 -0
  13. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/mailbox_org_api/__init__.py +0 -0
  14. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/mailbox_org_api.egg-info/SOURCES.txt +0 -0
  15. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/mailbox_org_api.egg-info/dependency_links.txt +0 -0
  16. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/mailbox_org_api.egg-info/top_level.txt +0 -0
  17. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/setup.cfg +0 -0
  18. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/tests/TestAccount.py +0 -0
  19. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/tests/TestInvoice.py +0 -0
  20. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/tests/TestMail.py +0 -0
  21. {mailbox_org_api-2.4 → mailbox_org_api-2.5.1}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mailbox-org-api
3
- Version: 2.4
3
+ Version: 2.5.1
4
4
  Summary: A library to access the mailbox Business API.
5
5
  Author: Hendrik Schlange
6
6
  Author-email: Hendrik Schlange <mail@heshsum.de>
@@ -24,7 +24,7 @@ class APIClient:
24
24
  Object for API Client
25
25
  """
26
26
 
27
- def __init__(self, debug_output=False, max_retries=5):
27
+ def __init__(self, debug_output=False, max_retries=5, request_timeout: int = 30):
28
28
  # URL of the API
29
29
  self.url = "https://api.mailbox.org/v1/"
30
30
 
@@ -60,6 +60,8 @@ class APIClient:
60
60
  adapter = HTTPAdapter(max_retries=retry_strategy)
61
61
  self.session.mount('https://', adapter)
62
62
 
63
+ self.request_timeout = request_timeout
64
+
63
65
  # Increment the request ID
64
66
  def get_jsonrpc_id(self):
65
67
  """Method to create the JSON RPC request ID. """
@@ -90,20 +92,26 @@ class APIClient:
90
92
  # Check if any parameter is 'password' or 'pass'.
91
93
  # Check explicit for these strings as not to interfere with possible other
92
94
  # parameters containing a substring.
95
+ sensitive_keys = ['password', 'pass', 'password_hash', 'new_account_password', 'token',
96
+ 'HPLS-AUTH', 'auth_id', 'telephone_password']
93
97
  for param in print_request['params']:
94
- if param == 'password' or param == 'pass':
95
- # If True, replace the logged value with 'xxx'
98
+ if param in sensitive_keys:
96
99
  print_request['params'][param] = 'xxx'
97
100
 
98
101
  # Print the clean version of the request
99
102
  print('API full request:\t', print_request)
100
103
 
101
- api_response = self.session.post(self.url, data=json.dumps(request))
102
104
  try:
103
- api_response = api_response.json()
105
+ response = self.session.post(self.url, json=request, timeout=self.request_timeout)
106
+ response.raise_for_status()
104
107
  except RequestException as error:
105
- print('Non-JSON response received.\nFull response:\t', api_response,
106
- '\nError:', {error})
108
+ raise APIError(message=f"HTTP request failed: {error}", code=-32000) from error
109
+
110
+ try:
111
+ api_response = response.json()
112
+ except (ValueError, requests.exceptions.JSONDecodeError) as error:
113
+ print(f'API Full response: {response.content}')
114
+ raise APIError(message="Non-JSON response received from API", code=-32700) from error
107
115
  if self.debug_output:
108
116
  print('API full response:\t', api_response)
109
117
 
@@ -150,8 +158,9 @@ class APIClient:
150
158
  api_response = self.api_request('deauth', {})
151
159
  if api_response:
152
160
  # The auth header is stripped
153
- self.session.headers.pop("HPLS-AUTH")
161
+ self.session.headers.pop("HPLS-AUTH", None)
154
162
  self.auth_id = None
163
+ self.level = None
155
164
  self.session.close()
156
165
  return api_response
157
166
 
@@ -283,22 +292,25 @@ class APIClient:
283
292
  """
284
293
  Function to get a specific invoice object for an account
285
294
  :param account: the account name
286
- :param invoice_id: the id of the invoice to request
295
+ :param invoice_id: the ID of the invoice to request
287
296
  :return: the invoice as an Invoice object
288
297
  """
289
298
  # Get all invoices for the account
290
299
  invoice_list = self.account_invoice_list(account)
291
300
 
292
- # Initialize the Invoice object
293
- invoice = Invoice(account, invoice_id)
294
-
295
- # Retrieve the other data for the invoice from the invoice list
296
- for i in invoice_list:
297
- if i['invoice_id'] == invoice_id:
298
- invoice.date = i['date']
299
- invoice.status = i['status']
300
- invoice.token = i['token']
301
- return invoice
301
+ # Iterate over all invoices and create an object if the ID matches
302
+ for invoice in invoice_list:
303
+ if invoice['invoice_id'] == invoice_id:
304
+ # Initialize the Invoice object
305
+ invoice = Invoice(account, invoice_id)
306
+ # Retrieve the other data for the invoice from the invoice list
307
+ for i in invoice_list:
308
+ if i['invoice_id'] == invoice_id:
309
+ invoice.date = i['date']
310
+ invoice.status = i['status']
311
+ invoice.token = i['token']
312
+ return invoice
313
+ raise ValueError('Invoice not found')
302
314
 
303
315
  def account_invoice_get_list(self, account: str) -> list:
304
316
  """
@@ -584,7 +596,7 @@ class APIClient:
584
596
  if 'password' in kwargs and 'password_hash' in kwargs:
585
597
  raise KeyError('''Simultaneous usage of 'password' and 'password_hash' not allowed.''')
586
598
 
587
- if 'additional_mail_quota' in kwargs or 'additional_cloud_quota' in kwargs and 'plan' not in kwargs:
599
+ if ('additional_mail_quota' in kwargs or 'additional_cloud_quota' in kwargs) and 'plan' not in kwargs:
588
600
  raise KeyError('''If setting additional quota, 'plan' must be given.''')
589
601
 
590
602
  # Check for each argument in kwargs if it is a valid function parameter.
@@ -618,7 +630,8 @@ class APIClient:
618
630
  :param password: the password to set
619
631
  :return: the response for the request
620
632
  """
621
- return self.api_request('mail.set', {'mail': mail, 'password': password, 'require_reset': True})
633
+ return self.api_request('mail.set', {'mail': mail, 'password': password,
634
+ 'require_reset_password': True})
622
635
 
623
636
  def mail_set_plan(self, mail: str, plan: str) -> dict:
624
637
  """
@@ -922,10 +935,16 @@ class APIClient:
922
935
  if mail_addresses_to_add is None and mail_addresses_to_remove is None:
923
936
  raise ValueError('mail_addresses_to_add or mail_addresses_to_remove are required')
924
937
 
925
- params = {'group_id': group_id, 'display_name': display_name, 'mail_addresses_to_add': mail_addresses_to_add,
926
- 'mail_addresses_to_remove': mail_addresses_to_remove}
927
- if account:
928
- params['account'] = account
938
+ raw_params = {
939
+ 'group_id': group_id,
940
+ 'display_name': display_name,
941
+ 'mail_addresses_to_add': mail_addresses_to_add,
942
+ 'mail_addresses_to_remove': mail_addresses_to_remove,
943
+ 'account': account,
944
+ }
945
+ # Filter out any keys whose values are None so they aren't serialized as `null`
946
+ params = {k: v for k, v in raw_params.items() if v is not None}
947
+
929
948
  return self.api_request('group.set', params)
930
949
 
931
950
  def mail_passwordreset_listmethods(self, mail: str) -> dict:
@@ -1014,8 +1033,15 @@ class APIClient:
1014
1033
  :param adminmail: admin email address of the mailing list (optional).
1015
1034
  :return: the mailbox API response for the request - True if the mailing list was changed, error code otherwise.
1016
1035
  """
1017
- return self.api_request('mailinglist.set', {'mailinglist': mailinglist, 'account': account,
1018
- 'password': password, 'adminmail': adminmail})
1036
+ raw_params = {
1037
+ 'mailinglist': mailinglist,
1038
+ 'account': account,
1039
+ 'password': password,
1040
+ 'adminmail': adminmail
1041
+ }
1042
+ # Filter out any keys whose values are None so they aren't serialized as `null`
1043
+ params = {k: v for k, v in raw_params.items() if v is not None}
1044
+ return self.api_request('mailinglist.set', params)
1019
1045
 
1020
1046
  def mailinglist_del(self, mailinglist: str, account: str) -> dict:
1021
1047
  """
@@ -1066,21 +1092,26 @@ class APIClient:
1066
1092
  :param spam_folder: name of the spam folder.
1067
1093
  :return: the response for the request - True if adding was successful, error code otherwise.
1068
1094
  """
1069
- return self.api_request('additionalmailaccount.add', {'new_account_mail': new_account_mail,
1070
- 'new_account_password': new_account_password,
1071
- 'parent_mail': parent_mail,
1072
- 'primary_address': primary_address,
1073
- 'mail_server': mail_server, 'mail_port': str(mail_port),
1074
- 'transport_server': transport_server,
1075
- 'transport_port': str(transport_port),
1076
- 'mail_secure': mail_secure,
1077
- 'mail_starttls': mail_starttls,
1078
- 'transport_secure': transport_secure,
1079
- 'transport_starttls': transport_starttls,
1080
- 'trash_folder': trash_folder,
1081
- 'sent_folder': sent_folder,
1082
- 'drafts_folder': drafts_folder,
1083
- 'spam_folder': spam_folder})
1095
+ raw_params = {
1096
+ 'new_account_mail': new_account_mail,
1097
+ 'new_account_password': new_account_password,
1098
+ 'parent_mail': parent_mail,
1099
+ 'primary_address': primary_address,
1100
+ 'mail_server': mail_server, 'mail_port': str(mail_port),
1101
+ 'transport_server': transport_server,
1102
+ 'transport_port': str(transport_port),
1103
+ 'mail_secure': mail_secure,
1104
+ 'mail_starttls': mail_starttls,
1105
+ 'transport_secure': transport_secure,
1106
+ 'transport_starttls': transport_starttls,
1107
+ 'trash_folder': trash_folder,
1108
+ 'sent_folder': sent_folder,
1109
+ 'drafts_folder': drafts_folder,
1110
+ 'spam_folder': spam_folder
1111
+ }
1112
+ # Filter out any keys whose values are None so they aren't serialized as `null`
1113
+ params = {k: v for k, v in raw_params.items() if v is not None}
1114
+ return self.api_request('additionalmailaccount.add', params)
1084
1115
 
1085
1116
  def additionalmailaccount_delete(self, parent_mail: str, account_mail: str) -> dict:
1086
1117
  """
@@ -1111,16 +1142,13 @@ class APIClient:
1111
1142
  {'delete_mail_accounts_and_domains': delete_mail_accounts_and_domains})
1112
1143
 
1113
1144
 
1114
- def validate_params(allowed: dict, actual: dict) -> bool | None:
1145
+ def validate_params(allowed: dict, actual: dict) -> bool:
1115
1146
  for arg in actual:
1116
1147
  if arg not in allowed:
1117
1148
  raise ValueError(f'Parameter {arg} not a valid parameter.')
1118
-
1119
1149
  if not isinstance(actual[arg], allowed[arg]):
1120
1150
  raise TypeError(f'Attribute {arg} must be of type {str(allowed[arg])}. {str(type(actual[arg]))} given')
1121
- else:
1122
- return True
1123
- return None
1151
+ return True
1124
1152
 
1125
1153
 
1126
1154
  def bool2str(state: bool) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mailbox-org-api
3
- Version: 2.4
3
+ Version: 2.5.1
4
4
  Summary: A library to access the mailbox Business API.
5
5
  Author: Hendrik Schlange
6
6
  Author-email: Hendrik Schlange <mail@heshsum.de>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mailbox-org-api"
3
- version = "2.4"
3
+ version = "2.5.1"
4
4
  authors = [
5
5
  { name="Hendrik Schlange", email="mail@heshsum.de" },
6
6
  ]
@@ -3,7 +3,7 @@ from setuptools import find_packages, setup
3
3
  setup(
4
4
  name='mailbox_org_api',
5
5
  packages=find_packages(),
6
- version='2.4',
6
+ version='2.5.1',
7
7
  description='A library to access the mailbox Business API',
8
8
  author='Hendrik Schlange',
9
9
  install_requires=['requests'],
@@ -379,6 +379,9 @@ class TestAPIClient:
379
379
  api = APIClient.APIClient()
380
380
  api.auth(api_test_user, api_test_pass)
381
381
  mail = test_id + '@' + domain
382
+
383
+ # Ensure that the plan supports capabilities
384
+ api.mail_set_plan(mail, 'standard')
382
385
  for i in capabilities:
383
386
  api.mail_capabilities_set(mail, [i])
384
387
  # The API returns a list of capabilities
@@ -406,7 +409,7 @@ class TestAPIClient:
406
409
  api.auth(api_test_user, api_test_pass)
407
410
  mails = api.mail_list(domain)
408
411
  mail = mails[0]['mail']
409
- plans = ['premium', 'light', 'standard']
412
+ plans = ['premium', 'standard']
410
413
  for plan in plans:
411
414
  api.mail_set_plan(mail, plan)
412
415
  assert str(api.mail_get(mail)['plan']).lower() == plan
@@ -468,16 +471,17 @@ class TestAPIClient:
468
471
  mail = test_id + '@' + domain
469
472
  additional_mail_quota = 23
470
473
  api.mail_set_additional_mail_quota(mail, additional_mail_quota)
471
- assert additional_mail_quota == api.mail_get(mail)['additional_mail_quota']
474
+ assert int(api.mail_get(mail)['additional_mail_quota']) == additional_mail_quota
472
475
  api.deauth()
473
476
 
477
+ @pytest.mark.depends(name="test_mail_add")
474
478
  def test_mail_set_additional_cloud_quota(self):
475
479
  api = APIClient.APIClient()
476
480
  api.auth(api_test_user, api_test_pass)
477
481
  mail = test_id + '@' + domain
478
482
  additional_cloud_quota = 42
479
483
  api.mail_set_additional_cloud_quota(mail, additional_cloud_quota)
480
- assert additional_cloud_quota == api.mail_get(mail)['additional_cloud_quota']
484
+ assert int(api.mail_get(mail)['additional_cloud_quota']) == additional_cloud_quota
481
485
  api.deauth()
482
486
 
483
487
  @pytest.mark.depends(name="test_mail_add")
File without changes
File without changes
File without changes