pyxecm 1.5__py3-none-any.whl → 2.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 pyxecm might be problematic. Click here for more details.

Files changed (56) hide show
  1. pyxecm/__init__.py +6 -2
  2. pyxecm/avts.py +1492 -0
  3. pyxecm/coreshare.py +1075 -960
  4. pyxecm/customizer/__init__.py +16 -4
  5. pyxecm/customizer/__main__.py +58 -0
  6. pyxecm/customizer/api/__init__.py +5 -0
  7. pyxecm/customizer/api/__main__.py +6 -0
  8. pyxecm/customizer/api/app.py +914 -0
  9. pyxecm/customizer/api/auth.py +154 -0
  10. pyxecm/customizer/api/metrics.py +92 -0
  11. pyxecm/customizer/api/models.py +13 -0
  12. pyxecm/customizer/api/payload_list.py +865 -0
  13. pyxecm/customizer/api/settings.py +103 -0
  14. pyxecm/customizer/browser_automation.py +332 -139
  15. pyxecm/customizer/customizer.py +1075 -1057
  16. pyxecm/customizer/exceptions.py +35 -0
  17. pyxecm/customizer/guidewire.py +322 -0
  18. pyxecm/customizer/k8s.py +787 -338
  19. pyxecm/customizer/log.py +107 -0
  20. pyxecm/customizer/m365.py +3424 -2270
  21. pyxecm/customizer/nhc.py +1169 -0
  22. pyxecm/customizer/openapi.py +258 -0
  23. pyxecm/customizer/payload.py +18201 -7030
  24. pyxecm/customizer/pht.py +1047 -210
  25. pyxecm/customizer/salesforce.py +836 -727
  26. pyxecm/customizer/sap.py +58 -41
  27. pyxecm/customizer/servicenow.py +851 -383
  28. pyxecm/customizer/settings.py +442 -0
  29. pyxecm/customizer/successfactors.py +408 -346
  30. pyxecm/customizer/translate.py +83 -48
  31. pyxecm/helper/__init__.py +5 -2
  32. pyxecm/helper/assoc.py +98 -38
  33. pyxecm/helper/data.py +2482 -742
  34. pyxecm/helper/logadapter.py +27 -0
  35. pyxecm/helper/web.py +229 -101
  36. pyxecm/helper/xml.py +528 -172
  37. pyxecm/maintenance_page/__init__.py +5 -0
  38. pyxecm/maintenance_page/__main__.py +6 -0
  39. pyxecm/maintenance_page/app.py +51 -0
  40. pyxecm/maintenance_page/settings.py +28 -0
  41. pyxecm/maintenance_page/static/favicon.avif +0 -0
  42. pyxecm/maintenance_page/templates/maintenance.html +165 -0
  43. pyxecm/otac.py +234 -140
  44. pyxecm/otawp.py +2689 -0
  45. pyxecm/otcs.py +12344 -7547
  46. pyxecm/otds.py +3166 -2219
  47. pyxecm/otiv.py +36 -21
  48. pyxecm/otmm.py +1363 -296
  49. pyxecm/otpd.py +231 -127
  50. pyxecm-2.0.0.dist-info/METADATA +145 -0
  51. pyxecm-2.0.0.dist-info/RECORD +54 -0
  52. {pyxecm-1.5.dist-info → pyxecm-2.0.0.dist-info}/WHEEL +1 -1
  53. pyxecm-1.5.dist-info/METADATA +0 -51
  54. pyxecm-1.5.dist-info/RECORD +0 -30
  55. {pyxecm-1.5.dist-info → pyxecm-2.0.0.dist-info/licenses}/LICENSE +0 -0
  56. {pyxecm-1.5.dist-info → pyxecm-2.0.0.dist-info}/top_level.txt +0 -0
pyxecm/coreshare.py CHANGED
@@ -1,92 +1,69 @@
1
- """
2
- CoreShare Module to interact with the Core Share API
3
- See: https://confluence.opentext.com/pages/viewpage.action?spaceKey=OTC&title=APIs+Consumption+based+on+roles
1
+ """CoreShare Module to interact with the Core Share API.
2
+
3
+ See:
4
+ - [Confluence](https://confluence.opentext.com/pages/viewpage.action?spaceKey=OTC&title=APIs+Consumption+based+on+roles)
5
+ - [swagger.otxlab.net](https://swagger.otxlab.net/ui/?branch=master&yaml=application-specific/core/core-api.yaml)
4
6
 
5
7
  Authentication - get Client Secrets:
6
- 1. Login to Core Share as a Tenant Admin User .
7
- 2. Navigate to Security P age.
8
- 3. On OAuth Confidential Clients section provide Description and Redirect URLs. It will populate a
9
- dialog with Client Secret.
10
- 4. Copy Client Secret as it will not be available anywhere once the dialog is closed.
11
-
12
- Class: CoreShare
13
- Methods:
14
-
15
- __init__ : class initializer
16
- config : Returns config data set
17
- credentials: Get credentials (username + password)
18
- set_credentials: Set the credentials for Core Share based on username and password.
19
- request_header: Returns the request header for Core Share API calls
20
- parse_request_response: Parse the REST API responses and convert
21
- them to Python dict in a safe way
22
- lookup_result_value: Lookup a property value based on a provided key / value pair in the response
23
- properties of a Core Share REST API call
24
- exist_result_item: Check if an dict item is in the response
25
- of the Core Share API call
26
- get_result_value: Check if a defined value (based on a key) is in the Core Share API response
27
-
28
- authenticate_admin : Authenticates as Admin at Core Share API
29
- authenticate_user : Authenticates as Service user at Core Share API
30
-
31
- get_groups: Get Core Share groups.
32
- add_group: Add a new Core Share group.
33
- get_group_members: Get Core Share group members.
34
- add_group_member: Add a Core Share user to a Cire Share group.
35
- remove_group_member: Remove a Core Share user from a Core Share group.
36
- get_group_by_id: Get a Core Share group by its ID.
37
- get_group_by_name: Get Core Share group by its name.
38
- search_groups: Search Core Share group(s) by name.
39
-
40
- get_users: Get Core Share users.
41
- get_user_by_id: Get a Core Share user by its ID.
42
- get_user_by_name: Get Core Share user by its first and last name.
43
- search_users: Search Core Share user(s) by name / property.
44
- add_user: Add a new Core Share user. This requires a Tenent Admin authorization.
45
- resend_user_invite: Resend the invite for a Core Share user.
46
- update_user: Update a Core Share user.
47
- add_user_access_role: Add an access role to a Core Share user.
48
- remove_user_access_role: Remove an access role from a Core Share user.
49
- update_user_access_roles: Define the access roles of a Core Share user.
50
- update_user_password: Update the password of a Core Share user.
51
- update_user_photo: Update the Core Share user photo.
52
-
53
- get_folders: Get Core Share folders under a given parent ID.
54
- unshare_folder: Unshare Core Share folder with a given resource ID.
55
- delete_folder: Delete Core Share folder with a given resource ID.
56
- delete_document: Delete Core Share document with a given resource ID.
57
- leave_share: Remove a Core Share user from a share (i.e. the user leaves the share)
58
- stop_share: Stop of share of a user.
59
- cleanup_user_files: Cleanup all files of a user. This handles different types of resources.
60
- get_group_shares: Get (incoming) shares of a Core Share group.
61
- revoke_group_share: Revoke sharing of a folder with a group.
62
- cleanup_group_shares: Cleanup all incoming shares of a group.
8
+ 1. Login to Core Share as a Tenant Admin User.
9
+ 2. Navigate to Security Page.
10
+ 3. On OAuth Confidential Clients section provide Description and Redirect URLs. It will populate a
11
+ dialog with Client Secret.
12
+ 4. Copy Client Secret as it will not be available anywhere once the dialog is closed.
63
13
  """
64
14
 
65
15
  __author__ = "Dr. Marc Diefenbruch"
66
- __copyright__ = "Copyright 2024, OpenText"
16
+ __copyright__ = "Copyright (C) 2024-2025, OpenText"
67
17
  __credits__ = ["Kai-Philip Gatzweiler"]
68
18
  __maintainer__ = "Dr. Marc Diefenbruch"
69
19
  __email__ = "mdiefenb@opentext.com"
70
20
 
71
- import os
72
21
  import json
73
22
  import logging
23
+ import os
24
+ import platform
25
+ import sys
26
+ import time
74
27
  import urllib.parse
28
+ from http import HTTPStatus
29
+ from importlib.metadata import version
75
30
 
76
31
  import requests
77
32
 
78
- logger = logging.getLogger("pyxecm.customizer.coreshare")
33
+ APP_NAME = "pyxecm"
34
+ APP_VERSION = version("pyxecm")
35
+ MODULE_NAME = APP_NAME + ".coreshare"
36
+
37
+ PYTHON_VERSION = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
38
+ OS_INFO = f"{platform.system()} {platform.release()}"
39
+ ARCH_INFO = platform.machine()
40
+ REQUESTS_VERSION = requests.__version__
41
+
42
+ USER_AGENT = (
43
+ f"{APP_NAME}/{APP_VERSION} ({MODULE_NAME}/{APP_VERSION}; "
44
+ f"Python/{PYTHON_VERSION}; {OS_INFO}; {ARCH_INFO}; Requests/{REQUESTS_VERSION})"
45
+ )
46
+
47
+ default_logger = logging.getLogger(MODULE_NAME)
79
48
 
80
49
  REQUEST_LOGIN_HEADERS = {
50
+ "User-Agent": USER_AGENT,
81
51
  "Content-Type": "application/x-www-form-urlencoded",
82
52
  "Accept": "application/json",
83
53
  }
84
54
 
85
55
  REQUEST_TIMEOUT = 60
56
+ REQUEST_RETRY_DELAY = 20
57
+ REQUEST_MAX_RETRIES = 2
86
58
 
59
+ CONTENT_MANAGER_ROLE_ID = 5
60
+ GROUP_ADMIN_ROLE_ID = 3
87
61
 
88
- class CoreShare(object):
89
- """Used to retrieve and automate stettings in Core Share."""
62
+
63
+ class CoreShare:
64
+ """Class CoreShare is used to retrieve and automate settings and objects (users, groups) in Core Share."""
65
+
66
+ logger: logging.Logger = default_logger
90
67
 
91
68
  _config: dict
92
69
  _access_token_user = None
@@ -100,17 +77,31 @@ class CoreShare(object):
100
77
  client_secret: str,
101
78
  username: str,
102
79
  password: str,
103
- ):
104
- """Initialize the CoreShare object
80
+ logger: logging.Logger = default_logger,
81
+ ) -> None:
82
+ """Initialize the CoreShare object.
105
83
 
106
84
  Args:
107
- base_url (str): base URL of the Core Share tenant
108
- sso_url (str): Single Sign On URL of the Core Share tenant
109
- client_id (str): Core Share Client ID
110
- client_secret (str): Core Share Client Secret
111
- username (str): admin user name in Core Share
112
- password (str): admin password in Core Share
85
+ base_url (str):
86
+ The base URL of the Core Share tenant.
87
+ sso_url (str):
88
+ The single sign on URL of the Core Share tenant.
89
+ client_id (str):
90
+ The Core Share Client ID.
91
+ client_secret (str):
92
+ The Core Share client secret.
93
+ username (str):
94
+ The admin user name in Core Share.
95
+ password (str):
96
+ The admin password in Core Share.
97
+ logger (logging.Logger, optional):
98
+ The logging object to use for all log messages. Defaults to default_logger.
99
+
113
100
  """
101
+ if logger != default_logger:
102
+ self.logger = logger.getChild("coreshare")
103
+ for logfilter in logger.filters:
104
+ self.logger.addFilter(logfilter)
114
105
 
115
106
  core_share_config = {}
116
107
 
@@ -131,22 +122,14 @@ class CoreShare(object):
131
122
  core_share_config["invitesUrl"] = core_share_config["restUrlv1"] + "/invites"
132
123
  core_share_config["foldersUrlv1"] = core_share_config["restUrlv1"] + "/folders"
133
124
  core_share_config["foldersUrlv3"] = core_share_config["restUrlv3"] + "/folders"
134
- core_share_config["documentsUrlv1"] = (
135
- core_share_config["restUrlv1"] + "/documents"
136
- )
137
- core_share_config["documentsUrlv3"] = (
138
- core_share_config["restUrlv3"] + "/documents"
139
- )
125
+ core_share_config["documentsUrlv1"] = core_share_config["restUrlv1"] + "/documents"
126
+ core_share_config["documentsUrlv3"] = core_share_config["restUrlv3"] + "/documents"
140
127
  core_share_config["searchUrl"] = core_share_config["baseUrl"] + "/search/v1"
141
128
  core_share_config["searchUserUrl"] = core_share_config["searchUrl"] + "/user"
142
- core_share_config["searchGroupUrl"] = (
143
- core_share_config["searchUrl"] + "/user/group-all"
144
- )
129
+ core_share_config["searchGroupUrl"] = core_share_config["searchUrl"] + "/user/group-all"
145
130
 
146
131
  core_share_config["sessionsUrl"] = core_share_config["restUrlv1"] + "/sessions"
147
- core_share_config["tokenUrl"] = (
148
- core_share_config["ssoUrl"] + "/otdsws/oauth2/token"
149
- )
132
+ core_share_config["tokenUrl"] = core_share_config["ssoUrl"] + "/otdsws/oauth2/token"
150
133
  core_share_config["sessionsUrl"] = core_share_config["restUrlv1"] + "/sessions"
151
134
 
152
135
  # Tenant Admin User Authentication information (Session URL):
@@ -186,21 +169,27 @@ class CoreShare(object):
186
169
  # end method definition
187
170
 
188
171
  def config(self) -> dict:
189
- """Returns the configuration dictionary
172
+ """Return the configuration dictionary.
190
173
 
191
174
  Returns:
192
- dict: Configuration dictionary
175
+ dict:
176
+ The configuration dictionary.
177
+
193
178
  """
179
+
194
180
  return self._config
195
181
 
196
182
  # end method definition
197
183
 
198
184
  def credentials(self) -> dict:
199
- """Get credentials (username + password)
185
+ """Get credentials (username + password).
200
186
 
201
187
  Returns:
202
- dict: dictionary with username and password
188
+ dict:
189
+ A dictionary with username and password.
190
+
203
191
  """
192
+
204
193
  return {
205
194
  "username": self.config()["username"],
206
195
  "password": self.config()["password"],
@@ -208,15 +197,16 @@ class CoreShare(object):
208
197
 
209
198
  # end method definition
210
199
 
211
- def set_credentials(self, username: str = "admin", password: str = ""):
200
+ def set_credentials(self, username: str = "admin", password: str = "") -> None:
212
201
  """Set the credentials for Core Share based on username and password.
213
202
 
214
203
  Args:
215
204
  username (str, optional): Username. Defaults to "admin".
216
205
  password (str, optional): Password of the user. Defaults to "".
206
+
217
207
  """
218
208
 
219
- logger.info("Change Core Share credentials to user -> %s...", username)
209
+ self.logger.info("Change Core Share credentials to user -> %s...", username)
220
210
 
221
211
  self.config()["username"] = username
222
212
  self.config()["password"] = password
@@ -248,16 +238,27 @@ class CoreShare(object):
248
238
  # end method definition
249
239
 
250
240
  def request_header_admin(self, content_type: str = "application/json") -> dict:
251
- """Returns the request header used for Application calls.
252
- Consists of Bearer access token and Content Type
241
+ """Return the request header used for application calls that require administrator credentials.
242
+
243
+ Consists of Bearer access token and Content Type
253
244
 
254
245
  Args:
255
- content_type (str, optional): content type for the request
246
+ content_type (str, optional):
247
+ Custom content type for the request.
248
+ Typical values:
249
+ * application/json - Used for sending JSON-encoded data
250
+ * application/x-www-form-urlencoded - The default for HTML forms.
251
+ Data is sent as key-value pairs in the body of the request, similar to query parameters.
252
+ * multipart/form-data - Used for file uploads or when a form includes non-ASCII characters
253
+
256
254
  Return:
257
- dict: request header values
255
+ dict:
256
+ The request header values.
257
+
258
258
  """
259
259
 
260
260
  request_header = {
261
+ "User-Agent": USER_AGENT,
261
262
  "Authorization": "Bearer {}".format(self._access_token_admin),
262
263
  }
263
264
  if content_type:
@@ -268,16 +269,22 @@ class CoreShare(object):
268
269
  # end method definition
269
270
 
270
271
  def request_header_user(self, content_type: str = "application/json") -> dict:
271
- """Returns the request header used for Application calls.
272
- Consists of Bearer access token and Content Type
272
+ """Return the request header used for Application calls that require user (non-admin) credentials.
273
+
274
+ Consists of Bearer access token and Content Type
273
275
 
274
276
  Args:
275
- content_type (str, optional): content type for the request
277
+ content_type (str, optional):
278
+ The content type for the request.
279
+
276
280
  Return:
277
- dict: request header values
281
+ dict:
282
+ The request header values.
283
+
278
284
  """
279
285
 
280
286
  request_header = {
287
+ "User-Agent": USER_AGENT,
281
288
  "Authorization": "Bearer {}".format(self._access_token_user),
282
289
  }
283
290
  if content_type:
@@ -287,49 +294,261 @@ class CoreShare(object):
287
294
 
288
295
  # end method definition
289
296
 
297
+ def do_request(
298
+ self,
299
+ url: str,
300
+ method: str = "GET",
301
+ headers: dict | None = None,
302
+ data: dict | None = None,
303
+ json_data: dict | None = None,
304
+ files: dict | None = None,
305
+ timeout: int | None = REQUEST_TIMEOUT,
306
+ show_error: bool = True,
307
+ show_warning: bool = False,
308
+ warning_message: str = "",
309
+ failure_message: str = "",
310
+ success_message: str = "",
311
+ max_retries: int = REQUEST_MAX_RETRIES,
312
+ retry_forever: bool = False,
313
+ parse_request_response: bool = True,
314
+ user_credentials: bool = False,
315
+ verify: bool = True,
316
+ ) -> dict | None:
317
+ """Call an OTDS REST API in a safe way.
318
+
319
+ Args:
320
+ url (str):
321
+ The URL to send the request to.
322
+ method (str, optional):
323
+ HTTP method (GET, POST, etc.). Defaults to "GET".
324
+ headers (dict | None, optional):
325
+ Request Headers. Defaults to None.
326
+ data (dict | None, optional):
327
+ Request payload. Defaults to None
328
+ json_data (dict | None, optional):
329
+ Request payload for the JSON parameter. Defaults to None.
330
+ files (dict | None, optional):
331
+ Dictionary of {"name": file-tuple} for multipart encoding upload.
332
+ The file-tuple can be a 2-tuple ("filename", fileobj) or a 3-tuple ("filename", fileobj, "content_type")
333
+ timeout (int | None, optional):
334
+ Timeout for the request in seconds. Defaults to REQUEST_TIMEOUT.
335
+ show_error (bool, optional):
336
+ Whether or not an error should be logged in case of a failed REST call.
337
+ If False, then only a warning is logged. Defaults to True.
338
+ show_warning (bool, optional):
339
+ Whether or not an warning should be logged in case of a
340
+ failed REST call.
341
+ If False, then only a warning is logged. Defaults to True.
342
+ warning_message (str, optional):
343
+ Specific warning message. Defaults to "". If not given the error_message will be used.
344
+ failure_message (str, optional):
345
+ Specific error message. Defaults to "".
346
+ success_message (str, optional):
347
+ Specific success message. Defaults to "".
348
+ max_retries (int, optional):
349
+ How many retries on Connection errors? Default is REQUEST_MAX_RETRIES.
350
+ retry_forever (bool, optional):
351
+ Eventually wait forever - without timeout. Defaults to False.
352
+ parse_request_response (bool, optional):
353
+ If True the response.text will be interpreted as json and loaded into a dictionary.
354
+ True is the default.
355
+ user_credentials (bool, optional):
356
+ Defines if admin or user credentials are used for the REST API call.
357
+ Default = False = admin credentials
358
+ verify (bool, optional):
359
+ Specify whether or not SSL certificates should be verified when making an HTTPS request.
360
+ Default = True
361
+
362
+ Returns:
363
+ dict | None:
364
+ Response of OTDS REST API or None in case of an error.
365
+
366
+ """
367
+
368
+ if headers is None:
369
+ self.logger.error(
370
+ "Missing request header. Cannot send request to Core Share!",
371
+ )
372
+ return None
373
+
374
+ # In case of an expired session we reauthenticate and
375
+ # try 1 more time. Session expiration should not happen
376
+ # twice in a row:
377
+ retries = 0
378
+
379
+ while True:
380
+ try:
381
+ response = requests.request(
382
+ method=method,
383
+ url=url,
384
+ data=data,
385
+ json=json_data,
386
+ files=files,
387
+ headers=headers,
388
+ timeout=timeout,
389
+ verify=verify,
390
+ )
391
+
392
+ if response.ok:
393
+ if success_message:
394
+ self.logger.info(success_message)
395
+ if parse_request_response:
396
+ return self.parse_request_response(response)
397
+ else:
398
+ return response
399
+ # Check if Session has expired - then re-authenticate and try once more
400
+ elif response.status_code == 401 and retries == 0:
401
+ if user_credentials:
402
+ self.logger.debug(
403
+ "User session has expired - try to re-authenticate...",
404
+ )
405
+ self.authenticate_user(revalidate=True)
406
+ # Make sure to not change the content type:
407
+ headers = self.request_header_user(
408
+ content_type=headers.get("Content-Type", None),
409
+ )
410
+ else:
411
+ self.logger.warning(
412
+ "Admin session has expired - try to re-authenticate...",
413
+ )
414
+ self.authenticate_admin(revalidate=True)
415
+ # Make sure to not change the content type:
416
+ headers = self.request_header_admin(
417
+ content_type=headers.get("Content-Type", None),
418
+ )
419
+ retries += 1
420
+ else:
421
+ # Handle plain HTML responses to not pollute the logs
422
+ content_type = response.headers.get("content-type", None)
423
+ if content_type == "text/html":
424
+ response_text = "HTML content (only printed in debug log)"
425
+ elif "image" in content_type:
426
+ response_text = "Image content (not printed)"
427
+ else:
428
+ response_text = response.text
429
+
430
+ if show_error:
431
+ self.logger.error(
432
+ "%s; status -> %s/%s; error -> %s",
433
+ failure_message,
434
+ response.status_code,
435
+ HTTPStatus(response.status_code).phrase,
436
+ response_text,
437
+ )
438
+ elif show_warning:
439
+ self.logger.warning(
440
+ "%s; status -> %s/%s; warning -> %s",
441
+ warning_message if warning_message else failure_message,
442
+ response.status_code,
443
+ HTTPStatus(response.status_code).phrase,
444
+ response_text,
445
+ )
446
+ if content_type == "text/html":
447
+ self.logger.debug(
448
+ "%s; status -> %s/%s; warning -> %s",
449
+ failure_message,
450
+ response.status_code,
451
+ HTTPStatus(response.status_code).phrase,
452
+ response.text,
453
+ )
454
+ return None
455
+ except requests.exceptions.Timeout:
456
+ if retries <= max_retries:
457
+ self.logger.warning(
458
+ "Request timed out. Retrying in %s seconds...",
459
+ str(REQUEST_RETRY_DELAY),
460
+ )
461
+ retries += 1
462
+ time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
463
+ else:
464
+ self.logger.error(
465
+ "%s; timeout error.",
466
+ failure_message,
467
+ )
468
+ if retry_forever:
469
+ # If it fails after REQUEST_MAX_RETRIES retries we let it wait forever
470
+ self.logger.warning("Turn timeouts off and wait forever...")
471
+ timeout = None
472
+ else:
473
+ return None
474
+ except requests.exceptions.ConnectionError:
475
+ if retries <= max_retries:
476
+ self.logger.warning(
477
+ "Connection error. Retrying in %s seconds...",
478
+ str(REQUEST_RETRY_DELAY),
479
+ )
480
+ retries += 1
481
+ time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
482
+ else:
483
+ self.logger.error(
484
+ "%s; connection error.",
485
+ failure_message,
486
+ )
487
+ if retry_forever:
488
+ # If it fails after REQUEST_MAX_RETRIES retries we let it wait forever
489
+ self.logger.warning("Turn timeouts off and wait forever...")
490
+ timeout = None
491
+ time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
492
+ else:
493
+ return None
494
+ # end try
495
+ self.logger.debug(
496
+ "Retrying REST API %s call -> %s... (retry = %s)",
497
+ method,
498
+ url,
499
+ str(retries),
500
+ )
501
+ # end while True
502
+
503
+ # end method definition
504
+
290
505
  def parse_request_response(
291
506
  self,
292
507
  response_object: requests.Response,
293
508
  additional_error_message: str = "",
294
509
  show_error: bool = True,
295
510
  ) -> dict | None:
296
- """Converts the request response (JSon) to a Python dict in a safe way
297
- that also handles exceptions. It first tries to load the response.text
298
- via json.loads() that produces a dict output. Only if response.text is
299
- not set or is empty it just converts the response_object to a dict using
300
- the vars() built-in method.
511
+ """Convert the request response to a dict in a safe way.
512
+
513
+ This also handles exceptions. It first tries to load the response.text
514
+ via json.loads() that produces a dict output. Only if response.text is
515
+ not set or is empty it just converts the response_object to a dict using
516
+ the vars() built-in method.
301
517
 
302
518
  Args:
303
- response_object (object): this is reponse object delivered by the request call
304
- additional_error_message (str, optional): use a more specific error message
305
- in case of an error
306
- show_error (bool): True: write an error to the log file
307
- False: write a warning to the log file
519
+ response_object (object):
520
+ This is reponse object returned by the request call.
521
+ additional_error_message (str, optional):
522
+ Can be used to provide a more specific error message
523
+ in case an error occurs.
524
+ show_error (bool, optional):
525
+ True: write an error to the log file
526
+ False: write a warning to the log file
527
+
308
528
  Returns:
309
529
  dict: response information or None in case of an error
530
+
310
531
  """
311
532
 
312
533
  if not response_object:
313
534
  return None
314
535
 
315
536
  try:
316
- if response_object.text:
317
- dict_object = json.loads(response_object.text)
318
- else:
319
- dict_object = vars(response_object)
537
+ dict_object = json.loads(response_object.text) if response_object.text else vars(response_object)
320
538
  except json.JSONDecodeError as exception:
321
539
  if additional_error_message:
322
540
  message = "Cannot decode response as JSon. {}; error -> {}".format(
323
- additional_error_message, exception
541
+ additional_error_message,
542
+ exception,
324
543
  )
325
544
  else:
326
545
  message = "Cannot decode response as JSon; error -> {}".format(
327
- exception
546
+ exception,
328
547
  )
329
548
  if show_error:
330
- logger.error(message)
549
+ self.logger.error(message)
331
550
  else:
332
- logger.warning(message)
551
+ self.logger.warning(message)
333
552
  return None
334
553
  else:
335
554
  return dict_object
@@ -337,24 +556,34 @@ class CoreShare(object):
337
556
  # end method definition
338
557
 
339
558
  def lookup_result_value(
340
- self, response: dict, key: str, value: str, return_key: str
559
+ self,
560
+ response: dict,
561
+ key: str,
562
+ value: str,
563
+ return_key: str,
341
564
  ) -> str | None:
342
- """Lookup a property value based on a provided key / value pair in the
343
- response properties of an Extended ECM REST API call.
565
+ """Lookup a property value based on a provided key / value pair in the response of an Core Share REST API call.
344
566
 
345
567
  Args:
346
- response (dict): REST response from an OTCS REST Call
347
- key (str): property name (key)
348
- value (str): value to find in the item with the matching key
349
- return_key (str): determines which value to return based on the name of the dict key
568
+ response (dict):
569
+ REST response from an Core Share REST Call
570
+ key (str):
571
+ The property name (key).
572
+ value (str):
573
+ The value to find in the item with the matching key.
574
+ return_key (str):
575
+ Determines which value to return based on the name of the dict key.
576
+
350
577
  Returns:
351
- str: value of the property with the key defined in "return_key"
352
- or None if the lookup fails
578
+ str | None:
579
+ The value of the property with the key defined in "return_key"
580
+ or None if the lookup fails.
581
+
353
582
  """
354
583
 
355
584
  if not response:
356
585
  return None
357
- if not "results" in response:
586
+ if "results" not in response:
358
587
  return None
359
588
 
360
589
  results = response["results"]
@@ -370,16 +599,28 @@ class CoreShare(object):
370
599
  # end method definition
371
600
 
372
601
  def exist_result_item(
373
- self, response: dict, key: str, value: str, results_marker: str = "results"
602
+ self,
603
+ response: dict,
604
+ key: str,
605
+ value: str,
606
+ results_marker: str = "results",
374
607
  ) -> bool:
375
608
  """Check existence of key / value pair in the response properties of a Core Share API call.
376
609
 
377
610
  Args:
378
- response (dict): REST response from a Core Share API call
379
- key (str): property name (key)
380
- value (str): value to find in the item with the matching key
611
+ response (dict):
612
+ REST response from a Core Share API call
613
+ key (str):
614
+ A property name (key)
615
+ value (str):
616
+ The value to find in the item with the matching key.
617
+ results_marker (str, optional):
618
+ The name of the data structure for the results.
619
+
381
620
  Returns:
382
- bool: True if the value was found, False otherwise
621
+ bool:
622
+ True, if the value was found, False otherwise.
623
+
383
624
  """
384
625
 
385
626
  if not response:
@@ -394,7 +635,7 @@ class CoreShare(object):
394
635
  if value == result[key]:
395
636
  return True
396
637
  else:
397
- if not key in response:
638
+ if key not in response:
398
639
  return False
399
640
  if value == response[key]:
400
641
  return True
@@ -412,12 +653,18 @@ class CoreShare(object):
412
653
  """Get value of a result property with a given key of a Core Share API call.
413
654
 
414
655
  Args:
415
- response (dict or list): REST response from a Core Share REST Call
416
- key (str): property name (key)
417
- index (int, optional): Index to use (1st element has index 0).
418
- Defaults to 0.
656
+ response (dict or list):
657
+ REST response from a Core Share REST Call
658
+ key (str):
659
+ The property name (key).
660
+ index (int, optional):
661
+ Index to use (1st element has index 0).
662
+ Defaults to 0.
663
+
419
664
  Returns:
420
- str: value for the key, None otherwise
665
+ str:
666
+ The value for the key, None in case of an error.
667
+
421
668
  """
422
669
 
423
670
  if not response:
@@ -427,7 +674,7 @@ class CoreShare(object):
427
674
  if isinstance(response, list):
428
675
  if len(response) - 1 < index:
429
676
  return None
430
- if not key in response[index]:
677
+ if key not in response[index]:
431
678
  return None
432
679
  value = response[index][key]
433
680
  return value
@@ -437,17 +684,13 @@ class CoreShare(object):
437
684
  if "results" in response:
438
685
  # we expect results to be a list!
439
686
  values = response["results"]
440
- if (
441
- not values
442
- or not isinstance(values, list)
443
- or len(values) - 1 < index
444
- ):
687
+ if not values or not isinstance(values, list) or len(values) - 1 < index:
445
688
  return None
446
- if not key in values[index]:
689
+ if key not in values[index]:
447
690
  return None
448
691
  value = values[index][key]
449
692
  else: # simple response as dictionary - try to find key in response directly:
450
- if not key in response:
693
+ if key not in response:
451
694
  return None
452
695
  value = response[key]
453
696
 
@@ -464,15 +707,20 @@ class CoreShare(object):
464
707
  """Authenticate at Core Share as Tenant Admin.
465
708
 
466
709
  Args:
467
- revalidate (bool, optional): determinse if a re-athentication is enforced
468
- (e.g. if session has timed out with 401 error)
710
+ revalidate (bool, optional):
711
+ Defines whether or not a re-athentication is enforced
712
+ (e.g. if session has timed out with 401 error).
713
+
469
714
  Returns:
470
- str: Access token. Also stores access token in self._access_token. None in case of error
715
+ str:
716
+ The access token. Also stores access token in self._access_token.
717
+ None in case of error.
718
+
471
719
  """
472
720
 
473
721
  # Already authenticated and session still valid?
474
722
  if self._access_token_admin and not revalidate:
475
- logger.debug(
723
+ self.logger.debug(
476
724
  "Session still valid - return existing access token -> %s",
477
725
  str(self._access_token_admin),
478
726
  )
@@ -482,7 +730,10 @@ class CoreShare(object):
482
730
 
483
731
  request_header = REQUEST_LOGIN_HEADERS
484
732
 
485
- logger.debug("Requesting Core Share Admin Access Token from -> %s", request_url)
733
+ self.logger.debug(
734
+ "Requesting Core Share Admin Access Token from -> %s",
735
+ request_url,
736
+ )
486
737
 
487
738
  response = None
488
739
  self._access_token_admin = None
@@ -494,7 +745,7 @@ class CoreShare(object):
494
745
  timeout=REQUEST_TIMEOUT,
495
746
  )
496
747
  except requests.exceptions.ConnectionError as exception:
497
- logger.warning(
748
+ self.logger.warning(
498
749
  "Unable to connect to -> %s : %s",
499
750
  request_url,
500
751
  exception,
@@ -517,13 +768,14 @@ class CoreShare(object):
517
768
 
518
769
  # Store authentication access_token:
519
770
  self._access_token_admin = access_token
520
- logger.debug(
521
- "Tenant Admin Access Token -> %s", self._access_token_admin
771
+ self.logger.debug(
772
+ "Tenant Admin Access Token -> %s",
773
+ self._access_token_admin,
522
774
  )
523
775
  else:
524
776
  return None
525
777
  else:
526
- logger.error(
778
+ self.logger.error(
527
779
  "Failed to request a Core Share Tenant Admin Access Token; error -> %s",
528
780
  response.text,
529
781
  )
@@ -534,37 +786,38 @@ class CoreShare(object):
534
786
  # end method definition
535
787
 
536
788
  def authenticate_user(
537
- self, revalidate: bool = False, grant_type: str = "password"
789
+ self,
790
+ revalidate: bool = False,
791
+ grant_type: str = "password",
538
792
  ) -> str | None:
539
793
  """Authenticate at Core Share as Tenant Service User (TSU) with client ID and client secret.
540
794
 
541
795
  Args:
542
- revalidate (bool, optional): determinse if a re-athentication is enforced
543
- (e.g. if session has timed out with 401 error)
544
- grant_type (str, optional): Can either be "client_credentials" (default) or "password".
796
+ revalidate (bool, optional):
797
+ Defines whether or not a re-athentication is enforced
798
+ (e.g. if session has timed out with 401 error).
799
+ grant_type (str, optional):
800
+ Can either be "client_credentials" (default) or "password".
801
+
545
802
  Returns:
546
- str: Access token. Also stores access token in self._access_token. None in case of error
803
+ str:
804
+ The access token. Also stores access token in self._access_token.
805
+ None in case of error.
806
+
547
807
  """
548
808
 
549
809
  # Already authenticated and session still valid?
550
810
  if self._access_token_user and not revalidate:
551
- logger.debug(
811
+ self.logger.debug(
552
812
  "Session still valid - return existing access token -> %s",
553
813
  str(self._access_token_user),
554
814
  )
555
815
  return self._access_token_user
556
816
 
557
- if grant_type == "client_credentials":
558
- request_url = self.config()["authorizationUrlCredentials"]
559
- elif grant_type == "password":
560
- request_url = self.config()["authorizationUrlPassword"]
561
- else:
562
- logger.error("Illegal grant type - authorization not possible!")
563
- return None
564
-
565
817
  request_header = REQUEST_LOGIN_HEADERS
818
+ request_url = self.config()["tokenUrl"]
566
819
 
567
- logger.debug(
820
+ self.logger.debug(
568
821
  "Requesting Core Share Tenant Service User Access Token from -> %s",
569
822
  request_url,
570
823
  )
@@ -572,14 +825,25 @@ class CoreShare(object):
572
825
  response = None
573
826
  self._access_token_user = None
574
827
 
828
+ authentication_body_post = {
829
+ "grant_type": grant_type,
830
+ "client_id": self.config()["clientId"],
831
+ "client_secret": self.config()["clientSecret"],
832
+ }
833
+
834
+ if grant_type == "password":
835
+ authentication_body_post["username"] = self.config()["username"]
836
+ authentication_body_post["password"] = self.config()["password"]
837
+
575
838
  try:
576
839
  response = requests.post(
577
840
  request_url,
578
841
  headers=request_header,
842
+ data=authentication_body_post,
579
843
  timeout=REQUEST_TIMEOUT,
580
844
  )
581
845
  except requests.exceptions.ConnectionError as exception:
582
- logger.warning(
846
+ self.logger.warning(
583
847
  "Unable to connect to -> %s : %s",
584
848
  request_url,
585
849
  exception,
@@ -593,11 +857,12 @@ class CoreShare(object):
593
857
  else:
594
858
  # Store authentication access_token:
595
859
  self._access_token_user = authenticate_dict["access_token"]
596
- logger.debug(
597
- "Tenant Service User Access Token -> %s", self._access_token_user
860
+ self.logger.debug(
861
+ "Tenant Service User Access Token -> %s",
862
+ self._access_token_user,
598
863
  )
599
864
  else:
600
- logger.error(
865
+ self.logger.error(
601
866
  "Failed to request a Core Share Tenant Service User Access Token; error -> %s",
602
867
  response.text,
603
868
  )
@@ -611,11 +876,14 @@ class CoreShare(object):
611
876
  """Get Core Share groups.
612
877
 
613
878
  Args:
614
- offset (int, optional): index of first group (for pagination). Defaults to 0.
615
- count (int, optional): number of groups to return (page length). Defaults to 25.
879
+ offset (int, optional):
880
+ The index of first group (for pagination). Defaults to 0.
881
+ count (int, optional):
882
+ The number of groups to return (page length). Defaults to 25.
616
883
 
617
884
  Returns:
618
- dict | None: Dictionary with the Core Share group data or None if the request fails.
885
+ dict | None:
886
+ Dictionary with the Core Share group data or None if the request fails.
619
887
 
620
888
  Example response:
621
889
  {
@@ -640,6 +908,7 @@ class CoreShare(object):
640
908
  }
641
909
  ]
642
910
  }
911
+
643
912
  """
644
913
 
645
914
  if not self._access_token_user:
@@ -647,30 +916,20 @@ class CoreShare(object):
647
916
 
648
917
  request_header = self.request_header_user()
649
918
  request_url = self.config()["groupsUrl"] + "?offset={}&count={}".format(
650
- offset, count
919
+ offset,
920
+ count,
651
921
  )
652
922
 
653
- logger.debug("Get Core Share groups; calling -> %s", request_url)
923
+ self.logger.debug("Get Core Share groups; calling -> %s", request_url)
654
924
 
655
- retries = 0
656
- while True:
657
- response = requests.get(
658
- request_url, headers=request_header, timeout=REQUEST_TIMEOUT
659
- )
660
- if response.ok:
661
- return self.parse_request_response(response)
662
- elif response.status_code == 401 and retries == 0:
663
- logger.debug("Session has expired - try to re-authenticate...")
664
- self.authenticate_user(revalidate=True)
665
- request_header = self.request_header_user()
666
- retries += 1
667
- else:
668
- logger.error(
669
- "Failed to get Core Share groups; status -> %s; error -> %s",
670
- response.status_code,
671
- response.text,
672
- )
673
- return None
925
+ return self.do_request(
926
+ url=request_url,
927
+ method="GET",
928
+ headers=request_header,
929
+ timeout=REQUEST_TIMEOUT,
930
+ failure_message="Failed to get Core Share groups",
931
+ user_credentials=True,
932
+ )
674
933
 
675
934
  # end method definition
676
935
 
@@ -682,11 +941,14 @@ class CoreShare(object):
682
941
  """Add a new Core Share group. This requires a Tenent Admin authorization.
683
942
 
684
943
  Args:
685
- group_name (str): Name of the new Core Share group
686
- description (str): Description of the new Core Share group
944
+ group_name (str):
945
+ The name of the new Core Share group.
946
+ description (str, optional):
947
+ The description of the new Core Share group.
687
948
 
688
949
  Returns:
689
- dict | None: Dictionary with the Core Share Group data or None if the request fails.
950
+ dict | None:
951
+ Dictionary with the Core Share Group data or None if the request fails.
690
952
 
691
953
  Example response:
692
954
  {
@@ -706,6 +968,7 @@ class CoreShare(object):
706
968
  "isSync": false,
707
969
  "tenantId": "2157293035593927996"
708
970
  }
971
+
709
972
  """
710
973
 
711
974
  if not self._access_token_admin:
@@ -716,33 +979,21 @@ class CoreShare(object):
716
979
 
717
980
  payload = {"name": group_name, "description": description}
718
981
 
719
- logger.debug(
720
- "Adding Core Share group -> %s; calling -> %s", group_name, request_url
982
+ self.logger.debug(
983
+ "Adding Core Share group -> %s; calling -> %s",
984
+ group_name,
985
+ request_url,
721
986
  )
722
987
 
723
- retries = 0
724
- while True:
725
- response = requests.post(
726
- request_url,
727
- headers=request_header,
728
- data=json.dumps(payload),
729
- timeout=REQUEST_TIMEOUT,
730
- )
731
- if response.ok:
732
- return self.parse_request_response(response)
733
- elif response.status_code == 401 and retries == 0:
734
- logger.debug("Session has expired - try to re-authenticate...")
735
- self.authenticate_admin(revalidate=True)
736
- request_header = self.request_header_admin()
737
- retries += 1
738
- else:
739
- logger.error(
740
- "Failed to add Core Share group -> %s; status -> %s; error -> %s",
741
- group_name,
742
- response.status_code,
743
- response.text,
744
- )
745
- return None
988
+ return self.do_request(
989
+ url=request_url,
990
+ method="POST",
991
+ headers=request_header,
992
+ data=json.dumps(payload),
993
+ timeout=REQUEST_TIMEOUT,
994
+ failure_message="Failed to add Core Share group -> '{}'".format(group_name),
995
+ user_credentials=False,
996
+ )
746
997
 
747
998
  # end method definition
748
999
 
@@ -750,10 +1001,12 @@ class CoreShare(object):
750
1001
  """Get Core Share group members.
751
1002
 
752
1003
  Args:
753
- group_id (str): ID of the group to deliver the members for.
1004
+ group_id (str):
1005
+ The ID of the group to deliver the members for.
754
1006
 
755
1007
  Returns:
756
- dict | None: Dictionary with the Core Share group membership data or None if the request fails.
1008
+ dict | None:
1009
+ Dictionary with the Core Share group membership data or None if the request fails.
757
1010
 
758
1011
  Example response:
759
1012
  {
@@ -782,54 +1035,54 @@ class CoreShare(object):
782
1035
  ],
783
1036
  'count': 0
784
1037
  }
1038
+
785
1039
  """
786
1040
 
787
1041
  if not self._access_token_admin:
788
1042
  self.authenticate_admin()
789
1043
 
790
- request_header = self.request_header_user()
1044
+ request_header = self.request_header_admin()
791
1045
  request_url = self.config()["groupsUrl"] + "/{}".format(group_id) + "/members"
792
1046
 
793
- logger.debug(
794
- "Get members for Core Share group -> %s; calling -> %s",
1047
+ self.logger.debug(
1048
+ "Get members for Core Share group with ID -> %s; calling -> %s",
795
1049
  group_id,
796
1050
  request_url,
797
1051
  )
798
1052
 
799
- retries = 0
800
- while True:
801
- response = requests.get(
802
- request_url, headers=request_header, timeout=REQUEST_TIMEOUT
803
- )
804
- if response.ok:
805
- return self.parse_request_response(response)
806
- elif response.status_code == 401 and retries == 0:
807
- logger.debug("Session has expired - try to re-authenticate...")
808
- self.authenticate_admin(revalidate=True)
809
- request_header = self.request_header_admin()
810
- retries += 1
811
- else:
812
- logger.error(
813
- "Failed to get members of Core Share group -> %s; status -> %s; error -> %s",
814
- group_id,
815
- response.status_code,
816
- response.text,
817
- )
818
- return None
1053
+ return self.do_request(
1054
+ url=request_url,
1055
+ method="GET",
1056
+ headers=request_header,
1057
+ timeout=REQUEST_TIMEOUT,
1058
+ failure_message="Failed to get members of Core Share group -> '{}'".format(
1059
+ group_id,
1060
+ ),
1061
+ user_credentials=False,
1062
+ )
819
1063
 
820
1064
  # end method definition
821
1065
 
822
1066
  def add_group_member(
823
- self, group_id: str, user_id: str, is_group_admin: bool = False
1067
+ self,
1068
+ group_id: str,
1069
+ user_id: str,
1070
+ is_group_admin: bool = False,
824
1071
  ) -> list | None:
825
1072
  """Add a Core Share user to a Core Share group.
826
1073
 
827
1074
  Args:
828
- group_id (str): ID of the Core Share Group
829
- user_id (str): ID of the Core Share User
1075
+ group_id (str):
1076
+ ID of the Core Share group.
1077
+ user_id (str):
1078
+ ID of the Core Share user.
1079
+ is_group_admin (bool, optional):
1080
+ Whether or not the member is a group administrator.
1081
+ Default is False.
830
1082
 
831
1083
  Returns:
832
- list | None: Dictionary with the Core Share group membership or None if the request fails.
1084
+ list | None:
1085
+ Dictionary with the Core Share group membership or None if the request fails.
833
1086
 
834
1087
  Example Response ('errors' is only output if success = False):
835
1088
  [
@@ -863,12 +1116,13 @@ class CoreShare(object):
863
1116
  ]
864
1117
  }
865
1118
  ]
1119
+
866
1120
  """
867
1121
 
868
1122
  if not self._access_token_admin:
869
1123
  self.authenticate_admin()
870
1124
 
871
- request_header = self.request_header_user()
1125
+ request_header = self.request_header_admin()
872
1126
  request_url = self.config()["groupsUrl"] + "/{}".format(group_id) + "/members"
873
1127
 
874
1128
  user = self.get_user_by_id(user_id=user_id)
@@ -876,8 +1130,8 @@ class CoreShare(object):
876
1130
 
877
1131
  payload = {"members": [user_email], "specificGroupRole": is_group_admin}
878
1132
 
879
- logger.debug(
880
- "Add Core Share user -> %s (%s) as %s to Core Share group -> %s; calling -> %s",
1133
+ self.logger.debug(
1134
+ "Add Core Share user -> '%s' (%s) as %s to Core Share group with ID -> %s; calling -> %s",
881
1135
  user_email,
882
1136
  user_id,
883
1137
  "group member" if not is_group_admin else "group admin",
@@ -885,44 +1139,41 @@ class CoreShare(object):
885
1139
  request_url,
886
1140
  )
887
1141
 
888
- retries = 0
889
- while True:
890
- response = requests.post(
891
- request_url,
892
- headers=request_header,
893
- json=payload,
894
- timeout=REQUEST_TIMEOUT,
895
- )
896
- if response.ok:
897
- return self.parse_request_response(response)
898
- elif response.status_code == 401 and retries == 0:
899
- logger.debug("Session has expired - try to re-authenticate...")
900
- self.authenticate_admin(revalidate=True)
901
- request_header = self.request_header_admin()
902
- retries += 1
903
- else:
904
- logger.error(
905
- "Failed to add Core Share user -> %s to Core Share group -> %s; status -> %s; error -> %s",
906
- user_id,
907
- group_id,
908
- response.status_code,
909
- response.text,
910
- )
911
- return None
1142
+ return self.do_request(
1143
+ url=request_url,
1144
+ method="POST",
1145
+ headers=request_header,
1146
+ json_data=payload,
1147
+ timeout=REQUEST_TIMEOUT,
1148
+ failure_message="Failed to add Core Share user -> '{}' to Core Share group with ID -> {}".format(
1149
+ user_email,
1150
+ group_id,
1151
+ ),
1152
+ user_credentials=False,
1153
+ )
912
1154
 
913
1155
  # end method definition
914
1156
 
915
1157
  def remove_group_member(
916
- self, group_id: str, user_id: str, is_group_admin: bool = False
1158
+ self,
1159
+ group_id: str,
1160
+ user_id: str,
1161
+ is_group_admin: bool = False,
917
1162
  ) -> list | None:
918
1163
  """Remove a Core Share user from a Core Share group.
919
1164
 
920
1165
  Args:
921
- group_id (str): ID of the Core Share Group
922
- user_id (str): ID of the Core Share User
1166
+ group_id (str):
1167
+ The ID of the Core Share Group.
1168
+ user_id (str):
1169
+ The ID of the Core Share User.
1170
+ is_group_admin (bool, optional):
1171
+ True, if the member is a group admin.
1172
+ Default is False.
923
1173
 
924
1174
  Returns:
925
- list | None: Dictionary with the Core Share group membership or None if the request fails.
1175
+ list | None:
1176
+ Dictionary with the Core Share group membership or None if the request fails.
926
1177
 
927
1178
  Example Response ('errors' is only output if success = False):
928
1179
  [
@@ -937,12 +1188,13 @@ class CoreShare(object):
937
1188
  ]
938
1189
  }
939
1190
  ]
1191
+
940
1192
  """
941
1193
 
942
- if not self._access_token_user:
943
- self.authenticate_user()
1194
+ if not self._access_token_admin:
1195
+ self.authenticate_admin()
944
1196
 
945
- request_header = self.request_header_user()
1197
+ request_header = self.request_header_admin()
946
1198
  request_url = self.config()["groupsUrl"] + "/{}".format(group_id) + "/members"
947
1199
 
948
1200
  user = self.get_user_by_id(user_id=user_id)
@@ -950,8 +1202,8 @@ class CoreShare(object):
950
1202
 
951
1203
  payload = {"members": [user_email], "specificGroupRole": is_group_admin}
952
1204
 
953
- logger.debug(
954
- "Remove Core Share user -> %s (%s) as %s from Core Share group -> %s; calling -> %s",
1205
+ self.logger.debug(
1206
+ "Remove Core Share user -> '%s' (%s) as %s from Core Share group with ID -> %s; calling -> %s",
955
1207
  user_email,
956
1208
  user_id,
957
1209
  "group member" if not is_group_admin else "group admin",
@@ -959,30 +1211,19 @@ class CoreShare(object):
959
1211
  request_url,
960
1212
  )
961
1213
 
962
- retries = 0
963
- while True:
964
- response = requests.delete(
965
- request_url,
966
- headers=request_header,
967
- json=payload,
968
- timeout=REQUEST_TIMEOUT,
969
- )
970
- if response.ok:
971
- return self.parse_request_response(response)
972
- elif response.status_code == 401 and retries == 0:
973
- logger.debug("Session has expired - try to re-authenticate...")
974
- self.authenticate_user(revalidate=True)
975
- request_header = self.request_header_user()
976
- retries += 1
977
- else:
978
- logger.error(
979
- "Failed to remove Core Share user -> %s from Core Share group -> %s; status -> %s; error -> %s",
980
- user_id,
981
- group_id,
982
- response.status_code,
983
- response.text,
984
- )
985
- return None
1214
+ return self.do_request(
1215
+ url=request_url,
1216
+ method="DELETE",
1217
+ headers=request_header,
1218
+ json_data=payload,
1219
+ timeout=REQUEST_TIMEOUT,
1220
+ failure_message="Failed to remove Core Share user -> '{}' ({}) from Core Share group with ID -> {}".format(
1221
+ user_email,
1222
+ user_id,
1223
+ group_id,
1224
+ ),
1225
+ user_credentials=False,
1226
+ )
986
1227
 
987
1228
  # end method definition
988
1229
 
@@ -990,44 +1231,37 @@ class CoreShare(object):
990
1231
  """Get a Core Share group by its ID.
991
1232
 
992
1233
  Args:
993
- None
1234
+ group_id (str):
1235
+ The ID of the Core Share group.
994
1236
 
995
1237
  Returns:
996
- dict | None: Dictionary with the Core Share group data or None if the request fails.
1238
+ dict | None:
1239
+ Dictionary with the Core Share group data or None if the request fails.
997
1240
 
998
- Response example:
999
1241
  """
1000
1242
 
1001
- if not self._access_token_user:
1002
- self.authenticate_user()
1243
+ if not self._access_token_admin:
1244
+ self.authenticate_admin()
1003
1245
 
1004
- request_header = self.request_header_user()
1246
+ request_header = self.request_header_admin()
1005
1247
  request_url = self.config()["groupsUrl"] + "/" + group_id
1006
1248
 
1007
- logger.debug(
1008
- "Get Core Share group with ID -> %s; calling -> %s", group_id, request_url
1249
+ self.logger.debug(
1250
+ "Get Core Share group with ID -> %s; calling -> %s",
1251
+ group_id,
1252
+ request_url,
1009
1253
  )
1010
1254
 
1011
- retries = 0
1012
- while True:
1013
- response = requests.get(
1014
- request_url, headers=request_header, timeout=REQUEST_TIMEOUT
1015
- )
1016
- if response.ok:
1017
- return self.parse_request_response(response)
1018
- elif response.status_code == 401 and retries == 0:
1019
- logger.debug("Session has expired - try to re-authenticate...")
1020
- self.authenticate_user(revalidate=True)
1021
- request_header = self.request_header_user()
1022
- retries += 1
1023
- else:
1024
- logger.error(
1025
- "Failed to get Core Share group with ID -> %s; status -> %s; error -> %s",
1026
- group_id,
1027
- response.status_code,
1028
- response.text,
1029
- )
1030
- return None
1255
+ return self.do_request(
1256
+ url=request_url,
1257
+ method="GET",
1258
+ headers=request_header,
1259
+ timeout=REQUEST_TIMEOUT,
1260
+ failure_message="Failed to get Core Share group with ID -> {}".format(
1261
+ group_id,
1262
+ ),
1263
+ user_credentials=False,
1264
+ )
1031
1265
 
1032
1266
  # end method definition
1033
1267
 
@@ -1035,10 +1269,12 @@ class CoreShare(object):
1035
1269
  """Get Core Share group by its name.
1036
1270
 
1037
1271
  Args:
1038
- name (str): Name of the group to search.
1272
+ name (str):
1273
+ The name of the group to search.
1039
1274
 
1040
1275
  Returns:
1041
- dict | None: Dictionary with the Core Share group data or None if the request fails.
1276
+ dict | None:
1277
+ Dictionary with the Core Share group data or None if the request fails.
1042
1278
 
1043
1279
  Example result:
1044
1280
  {
@@ -1060,6 +1296,7 @@ class CoreShare(object):
1060
1296
  ],
1061
1297
  'total': 1
1062
1298
  }
1299
+
1063
1300
  """
1064
1301
 
1065
1302
  groups = self.search_groups(
@@ -1071,7 +1308,7 @@ class CoreShare(object):
1071
1308
  # end method definition
1072
1309
 
1073
1310
  def search_groups(self, query_string: str) -> dict | None:
1074
- """Search Core Share group(s) by name.
1311
+ """Search Core Share group(s) using a query string.
1075
1312
 
1076
1313
  Args:
1077
1314
  query_string(str): Query for the group name / property
@@ -1079,7 +1316,6 @@ class CoreShare(object):
1079
1316
  Returns:
1080
1317
  dict | None: Dictionary with the Core Share user data or None if the request fails.
1081
1318
 
1082
- Example response:
1083
1319
  """
1084
1320
 
1085
1321
  if not self._access_token_admin:
@@ -1088,30 +1324,22 @@ class CoreShare(object):
1088
1324
  request_header = self.request_header_admin()
1089
1325
  request_url = self.config()["searchGroupUrl"] + "?q=" + query_string
1090
1326
 
1091
- logger.debug(
1092
- "Search Core Share group by -> %s; calling -> %s", query_string, request_url
1327
+ self.logger.debug(
1328
+ "Search Core Share group by -> %s; calling -> %s",
1329
+ query_string,
1330
+ request_url,
1093
1331
  )
1094
1332
 
1095
- retries = 0
1096
- while True:
1097
- response = requests.get(
1098
- request_url, headers=request_header, timeout=REQUEST_TIMEOUT
1099
- )
1100
- if response.ok:
1101
- return self.parse_request_response(response)
1102
- elif response.status_code == 401 and retries == 0:
1103
- logger.debug("Session has expired - try to re-authenticate...")
1104
- self.authenticate_admin(revalidate=True)
1105
- request_header = self.request_header_admin()
1106
- retries += 1
1107
- else:
1108
- logger.error(
1109
- "Cannot find Core Share group with name / property -> %s; status -> %s; error -> %s",
1110
- query_string,
1111
- response.status_code,
1112
- response.text,
1113
- )
1114
- return None
1333
+ return self.do_request(
1334
+ url=request_url,
1335
+ method="GET",
1336
+ headers=request_header,
1337
+ timeout=REQUEST_TIMEOUT,
1338
+ failure_message="Cannot find Core Share group with name / property -> {}".format(
1339
+ query_string,
1340
+ ),
1341
+ user_credentials=False,
1342
+ )
1115
1343
 
1116
1344
  # end method definition
1117
1345
 
@@ -1122,7 +1350,8 @@ class CoreShare(object):
1122
1350
  None
1123
1351
 
1124
1352
  Returns:
1125
- dict | None: Dictionary with the Core Share user data or None if the request fails.
1353
+ dict | None:
1354
+ Dictionary with the Core Share user data or None if the request fails.
1126
1355
 
1127
1356
  Example response (it is a list!):
1128
1357
  [
@@ -1187,6 +1416,7 @@ class CoreShare(object):
1187
1416
  },
1188
1417
  ...
1189
1418
  ]
1419
+
1190
1420
  """
1191
1421
 
1192
1422
  if not self._access_token_admin:
@@ -1195,27 +1425,16 @@ class CoreShare(object):
1195
1425
  request_header = self.request_header_admin()
1196
1426
  request_url = self.config()["usersUrlv1"]
1197
1427
 
1198
- logger.debug("Get Core Share users; calling -> %s", request_url)
1428
+ self.logger.debug("Get Core Share users; calling -> %s", request_url)
1199
1429
 
1200
- retries = 0
1201
- while True:
1202
- response = requests.get(
1203
- request_url, headers=request_header, timeout=REQUEST_TIMEOUT
1204
- )
1205
- if response.ok:
1206
- return self.parse_request_response(response)
1207
- elif response.status_code == 401 and retries == 0:
1208
- logger.debug("Session has expired - try to re-authenticate...")
1209
- self.authenticate_admin(revalidate=True)
1210
- request_header = self.request_header_admin()
1211
- retries += 1
1212
- else:
1213
- logger.error(
1214
- "Failed to get Core Share users; status -> %s; error -> %s",
1215
- response.status_code,
1216
- response.text,
1217
- )
1218
- return None
1430
+ return self.do_request(
1431
+ url=request_url,
1432
+ method="GET",
1433
+ headers=request_header,
1434
+ timeout=REQUEST_TIMEOUT,
1435
+ failure_message="Failed to get Core Share users",
1436
+ user_credentials=False,
1437
+ )
1219
1438
 
1220
1439
  # end method definition
1221
1440
 
@@ -1223,10 +1442,12 @@ class CoreShare(object):
1223
1442
  """Get a Core Share user by its ID.
1224
1443
 
1225
1444
  Args:
1226
- None
1445
+ user_id (str):
1446
+ The ID of the user.
1227
1447
 
1228
1448
  Returns:
1229
- dict | None: Dictionary with the Core Share user data or None if the request fails.
1449
+ dict | None:
1450
+ Dictionary with the Core Share user data or None if the request fails.
1230
1451
 
1231
1452
  Response example:
1232
1453
  {
@@ -1287,6 +1508,7 @@ class CoreShare(object):
1287
1508
  'quota': 10737418240,
1288
1509
  'usage': 0
1289
1510
  }
1511
+
1290
1512
  """
1291
1513
 
1292
1514
  if not self._access_token_user:
@@ -1295,50 +1517,50 @@ class CoreShare(object):
1295
1517
  request_header = self.request_header_user()
1296
1518
  request_url = self.config()["usersUrlv1"] + "/" + user_id
1297
1519
 
1298
- logger.debug(
1299
- "Get Core Share user with ID -> %s; calling -> %s", user_id, request_url
1520
+ self.logger.debug(
1521
+ "Get Core Share user with ID -> %s; calling -> %s",
1522
+ user_id,
1523
+ request_url,
1300
1524
  )
1301
1525
 
1302
- retries = 0
1303
- while True:
1304
- response = requests.get(
1305
- request_url, headers=request_header, timeout=REQUEST_TIMEOUT
1306
- )
1307
- if response.ok:
1308
- return self.parse_request_response(response)
1309
- elif response.status_code == 401 and retries == 0:
1310
- logger.debug("Session has expired - try to re-authenticate...")
1311
- self.authenticate_user(revalidate=True)
1312
- request_header = self.request_header_user()
1313
- retries += 1
1314
- else:
1315
- logger.error(
1316
- "Failed to get Core Share user with ID -> %s; status -> %s; error -> %s",
1317
- user_id,
1318
- response.status_code,
1319
- response.text,
1320
- )
1321
- return None
1526
+ return self.do_request(
1527
+ url=request_url,
1528
+ method="GET",
1529
+ headers=request_header,
1530
+ timeout=REQUEST_TIMEOUT,
1531
+ failure_message="Failed to get Core Share user with ID -> {}".format(
1532
+ user_id,
1533
+ ),
1534
+ user_credentials=True,
1535
+ )
1322
1536
 
1323
1537
  # end method definition
1324
1538
 
1325
1539
  def get_user_by_name(
1326
- self, first_name: str, last_name: str, user_status: str = "internal-native"
1540
+ self,
1541
+ first_name: str,
1542
+ last_name: str,
1543
+ user_status: str = "internal-native",
1327
1544
  ) -> dict | None:
1328
1545
  """Get Core Share user by its first and last name.
1329
1546
 
1330
1547
  Args:
1331
- first_name (str): First name of the users to search.
1332
- last_name (str): Last name of the users to search.
1333
- user_status (str, optional): type of users. Possible values:
1334
- * internal-enabled
1335
- * internal-pending
1336
- * internal-locked
1337
- * internal-native (non-SSO)
1338
- * internal-sso
1548
+ first_name (str):
1549
+ First name of the users to search.
1550
+ last_name (str):
1551
+ Last name of the users to search.
1552
+ user_status (str, optional):
1553
+ Type of users. Possible values:
1554
+ * internal-enabled
1555
+ * internal-pending
1556
+ * internal-locked
1557
+ * internal-native (non-SSO)
1558
+ * internal-sso
1339
1559
 
1340
1560
  Returns:
1341
- dict | None: Dictionary with the Core Share user data or None if the request fails.
1561
+ dict | None:
1562
+ Dictionary with the Core Share user data or None if the request fails.
1563
+
1342
1564
  """
1343
1565
 
1344
1566
  # Search the users with this first and last name (and hope this is unique ;-).
@@ -1352,21 +1574,27 @@ class CoreShare(object):
1352
1574
  # end method definition
1353
1575
 
1354
1576
  def get_user_by_email(
1355
- self, email: str, user_status: str = "internal-native"
1577
+ self,
1578
+ email: str,
1579
+ user_status: str = "internal-native",
1356
1580
  ) -> dict | None:
1357
1581
  """Get Core Share user by its email address.
1358
1582
 
1359
1583
  Args:
1360
- email (str): Email address of the users to search.
1361
- user_status (str, optional): type of users. Possible values:
1362
- * internal-enabled
1363
- * internal-pending
1364
- * internal-locked
1365
- * internal-native (non-SSO)
1366
- * internal-sso
1584
+ email (str):
1585
+ Email address of the users to search.
1586
+ user_status (str, optional):
1587
+ Type of users. Possible values:
1588
+ * internal-enabled
1589
+ * internal-pending
1590
+ * internal-locked
1591
+ * internal-native (non-SSO)
1592
+ * internal-sso
1367
1593
 
1368
1594
  Returns:
1369
- dict | None: Dictionary with the Core Share user data or None if the request fails.
1595
+ dict | None:
1596
+ Dictionary with the Core Share user data or None if the request fails.
1597
+
1370
1598
  """
1371
1599
 
1372
1600
  # Search the users with this first and last name (and hope this is unique ;-).
@@ -1388,14 +1616,17 @@ class CoreShare(object):
1388
1616
  """Search Core Share user(s) by name / property. Needs to be a Tenant Administrator to do so.
1389
1617
 
1390
1618
  Args:
1391
- query_string (str): string to query the user(s)
1392
- user_status (str, optional): type of users. Possible values:
1393
- * internal-enabled
1394
- * internal-pending
1395
- * internal-locked
1396
- * internal-native (non-SSO)
1397
- * internal-sso
1398
- page_size (int, optional): max number of results per page. We set the default to 100 (Web UI uses 25)
1619
+ query_string (str):
1620
+ The string to query the user(s).
1621
+ user_status (str, optional):
1622
+ The type of users. Possible values:
1623
+ * internal-enabled
1624
+ * internal-pending
1625
+ * internal-locked
1626
+ * internal-native (non-SSO)
1627
+ * internal-sso
1628
+ page_size (int, optional):
1629
+ The maximum number of results per page. We set the default to 100 (Web UI uses 25)
1399
1630
 
1400
1631
  Returns:
1401
1632
  dict | None: Dictionary with the Core Share user data or None if the request fails.
@@ -1434,6 +1665,7 @@ class CoreShare(object):
1434
1665
  ...
1435
1666
  ]
1436
1667
  }
1668
+
1437
1669
  """
1438
1670
 
1439
1671
  if not self._access_token_admin:
@@ -1449,30 +1681,22 @@ class CoreShare(object):
1449
1681
  + str(page_size)
1450
1682
  )
1451
1683
 
1452
- logger.debug(
1453
- "Search Core Share user by -> %s; calling -> %s", query_string, request_url
1684
+ self.logger.debug(
1685
+ "Search Core Share user by -> %s; calling -> %s",
1686
+ query_string,
1687
+ request_url,
1454
1688
  )
1455
1689
 
1456
- retries = 0
1457
- while True:
1458
- response = requests.get(
1459
- request_url, headers=request_header, timeout=REQUEST_TIMEOUT
1460
- )
1461
- if response.ok:
1462
- return self.parse_request_response(response)
1463
- elif response.status_code == 401 and retries == 0:
1464
- logger.debug("Session has expired - try to re-authenticate...")
1465
- self.authenticate_admin(revalidate=True)
1466
- request_header = self.request_header_admin()
1467
- retries += 1
1468
- else:
1469
- logger.error(
1470
- "Failed to search Core Share user with name / property -> %s; status -> %s; error -> %s",
1471
- query_string,
1472
- response.status_code,
1473
- response.text,
1474
- )
1475
- return None
1690
+ return self.do_request(
1691
+ url=request_url,
1692
+ method="GET",
1693
+ headers=request_header,
1694
+ timeout=REQUEST_TIMEOUT,
1695
+ failure_message="Failed to search Core Share user with name / property -> {}".format(
1696
+ query_string,
1697
+ ),
1698
+ user_credentials=False,
1699
+ )
1476
1700
 
1477
1701
  # end method definition
1478
1702
 
@@ -1488,15 +1712,22 @@ class CoreShare(object):
1488
1712
  """Add a new Core Share user. This requires a Tenent Admin authorization.
1489
1713
 
1490
1714
  Args:
1491
- first_name (str): First name of the new user
1492
- last_name (str): Last name of the new user
1493
- email (str): Email of the new Core Share user
1494
- password (str | None, optional): Password of the new Core Share user
1495
- title (str | None, optional): Title of the user
1496
- company (str | None, optional): Name of the Company of the user
1715
+ first_name (str):
1716
+ First name of the new user
1717
+ last_name (str):
1718
+ Last name of the new user
1719
+ email (str):
1720
+ Email of the new Core Share user
1721
+ password (str | None, optional):
1722
+ Password of the new Core Share user
1723
+ title (str | None, optional):
1724
+ Title of the user
1725
+ company (str | None, optional):
1726
+ Name of the Company of the user
1497
1727
 
1498
1728
  Returns:
1499
- dict | None: Dictionary with the Core Share User data or None if the request fails.
1729
+ dict | None:
1730
+ Dictionary with the Core Share User data or None if the request fails.
1500
1731
 
1501
1732
  Example response:
1502
1733
  {
@@ -1535,6 +1766,7 @@ class CoreShare(object):
1535
1766
  "quota": 10737418240,
1536
1767
  "usage": 0
1537
1768
  }
1769
+
1538
1770
  """
1539
1771
 
1540
1772
  if not self._access_token_admin:
@@ -1557,38 +1789,26 @@ class CoreShare(object):
1557
1789
  if company:
1558
1790
  payload["company"] = company
1559
1791
 
1560
- logger.debug(
1792
+ self.logger.debug(
1561
1793
  "Adding Core Share user -> %s %s; calling -> %s",
1562
1794
  first_name,
1563
1795
  last_name,
1564
1796
  request_url,
1565
1797
  )
1566
1798
 
1567
- retries = 0
1568
- while True:
1569
- response = requests.post(
1570
- request_url,
1571
- headers=request_header,
1572
- json=payload,
1573
- timeout=REQUEST_TIMEOUT,
1574
- )
1575
- if response.ok:
1576
- return self.parse_request_response(response)
1577
- elif response.status_code == 401 and retries == 0:
1578
- logger.debug("Session has expired - try to re-authenticate...")
1579
- self.authenticate_admin(revalidate=True)
1580
- request_header = self.request_header_admin()
1581
- retries += 1
1582
- else:
1583
- logger.error(
1584
- "Failed to add Core Share user -> %s %s (%s); status -> %s; error -> %s",
1585
- first_name,
1586
- last_name,
1587
- email,
1588
- response.status_code,
1589
- response.text,
1590
- )
1591
- return None
1799
+ return self.do_request(
1800
+ url=request_url,
1801
+ method="POST",
1802
+ headers=request_header,
1803
+ json_data=payload,
1804
+ timeout=REQUEST_TIMEOUT,
1805
+ failure_message="Failed to add Core Share user -> '{} {}' ({})".format(
1806
+ first_name,
1807
+ last_name,
1808
+ email,
1809
+ ),
1810
+ user_credentials=False,
1811
+ )
1592
1812
 
1593
1813
  # end method definition
1594
1814
 
@@ -1596,10 +1816,13 @@ class CoreShare(object):
1596
1816
  """Resend the invite for a Core Share user.
1597
1817
 
1598
1818
  Args:
1599
- user_id (str): The Core Share user ID.
1819
+ user_id (str):
1820
+ The Core Share user ID.
1600
1821
 
1601
1822
  Returns:
1602
- dict: Response from the Core Share API.
1823
+ dict:
1824
+ Response from the Core Share API.
1825
+
1603
1826
  """
1604
1827
 
1605
1828
  if not self._access_token_admin:
@@ -1609,7 +1832,7 @@ class CoreShare(object):
1609
1832
 
1610
1833
  request_url = self.config()["usersUrlv1"] + "/{}".format(user_id)
1611
1834
 
1612
- logger.debug(
1835
+ self.logger.debug(
1613
1836
  "Resend invite for Core Share user with ID -> %s; calling -> %s",
1614
1837
  user_id,
1615
1838
  request_url,
@@ -1617,29 +1840,17 @@ class CoreShare(object):
1617
1840
 
1618
1841
  update_data = {"resend": True}
1619
1842
 
1620
- retries = 0
1621
- while True:
1622
- response = requests.put(
1623
- request_url,
1624
- json=update_data,
1625
- headers=request_header,
1626
- timeout=REQUEST_TIMEOUT,
1627
- )
1628
- if response.ok:
1629
- return self.parse_request_response(response)
1630
- elif response.status_code == 401 and retries == 0:
1631
- logger.debug("Admin Session has expired - try to re-authenticate...")
1632
- self.authenticate_admin(revalidate=True)
1633
- request_header = self.request_header_admin()
1634
- retries += 1
1635
- else:
1636
- logger.error(
1637
- "Failed to resend invite for Core Share user -> %s; status -> %s; error -> %s",
1638
- user_id,
1639
- response.status_code,
1640
- response.text,
1641
- )
1642
- return None
1843
+ return self.do_request(
1844
+ url=request_url,
1845
+ method="PUT",
1846
+ headers=request_header,
1847
+ json_data=update_data,
1848
+ timeout=REQUEST_TIMEOUT,
1849
+ failure_message="Failed to resend invite for Core Share user with ID -> {}".format(
1850
+ user_id,
1851
+ ),
1852
+ user_credentials=False,
1853
+ )
1643
1854
 
1644
1855
  # end method definition
1645
1856
 
@@ -1647,10 +1858,15 @@ class CoreShare(object):
1647
1858
  """Update a Core Share user.
1648
1859
 
1649
1860
  Args:
1650
- user_id (str): ID of the Core Share user.
1861
+ user_id (str):
1862
+ The ID of the Core Share user.
1863
+ update_data (dict):
1864
+ The updated user data.
1651
1865
 
1652
1866
  Returns:
1653
- dict: Response or None if the request has failed.
1867
+ dict:
1868
+ REST response or None if the REST call has failed.
1869
+
1654
1870
  """
1655
1871
 
1656
1872
  if not self._access_token_admin:
@@ -1660,40 +1876,28 @@ class CoreShare(object):
1660
1876
 
1661
1877
  request_url = self.config()["usersUrlv1"] + "/{}".format(user_id)
1662
1878
 
1663
- logger.debug(
1879
+ self.logger.debug(
1664
1880
  "Update data of Core Share user with ID -> %s; calling -> %s",
1665
1881
  user_id,
1666
1882
  request_url,
1667
1883
  )
1668
1884
 
1669
- if "email" in update_data and not "password" in update_data:
1670
- logger.warning(
1671
- "Trying to update the email without providing the password. This is likely to fail..."
1885
+ if "email" in update_data and "password" not in update_data:
1886
+ self.logger.warning(
1887
+ "Trying to update the email without providing the password. This is likely to fail...",
1672
1888
  )
1673
1889
 
1674
- retries = 0
1675
- while True:
1676
- response = requests.put(
1677
- request_url,
1678
- json=update_data,
1679
- headers=request_header,
1680
- timeout=REQUEST_TIMEOUT,
1681
- )
1682
- if response.ok:
1683
- return self.parse_request_response(response)
1684
- elif response.status_code == 401 and retries == 0:
1685
- logger.debug("Admin Session has expired - try to re-authenticate...")
1686
- self.authenticate_admin(revalidate=True)
1687
- request_header = self.request_header_admin()
1688
- retries += 1
1689
- else:
1690
- logger.error(
1691
- "Failed to update Core Share user -> %s; status -> %s; error -> %s",
1692
- user_id,
1693
- response.status_code,
1694
- response.text,
1695
- )
1696
- return None
1890
+ return self.do_request(
1891
+ url=request_url,
1892
+ method="PUT",
1893
+ headers=request_header,
1894
+ json_data=update_data,
1895
+ timeout=REQUEST_TIMEOUT,
1896
+ failure_message="Failed to update Core Share user with ID -> {}".format(
1897
+ user_id,
1898
+ ),
1899
+ user_credentials=False,
1900
+ )
1697
1901
 
1698
1902
  # end method definition
1699
1903
 
@@ -1701,13 +1905,17 @@ class CoreShare(object):
1701
1905
  """Add an access role to a Core Share user.
1702
1906
 
1703
1907
  Args:
1704
- user_id (str): The Core Share user ID.
1705
- role_id (int): The role ID:
1706
- * Content Manager = 5
1707
- * Group Admin = 3
1908
+ user_id (str):
1909
+ The Core Share user ID.
1910
+ role_id (int):
1911
+ The role ID:
1912
+ - Content Manager = 5
1913
+ - Group Admin = 3
1708
1914
 
1709
1915
  Returns:
1710
- dict: Response from the Core Share API.
1916
+ dict:
1917
+ Response from the Core Share API.
1918
+
1711
1919
  """
1712
1920
 
1713
1921
  if not self._access_token_admin:
@@ -1715,44 +1923,26 @@ class CoreShare(object):
1715
1923
 
1716
1924
  request_header = self.request_header_admin()
1717
1925
 
1718
- request_url = (
1719
- self.config()["usersUrlv1"]
1720
- + "/{}".format(user_id)
1721
- + "/roles/"
1722
- + str(role_id)
1723
- )
1926
+ request_url = self.config()["usersUrlv1"] + "/{}".format(user_id) + "/roles/" + str(role_id)
1724
1927
 
1725
- logger.debug(
1928
+ self.logger.debug(
1726
1929
  "Add access role -> %s to Core Share user with ID -> %s; calling -> %s",
1727
1930
  str(role_id),
1728
1931
  user_id,
1729
1932
  request_url,
1730
1933
  )
1731
1934
 
1732
- retries = 0
1733
- while True:
1734
- response = requests.put(
1735
- request_url,
1736
- # json=update_data,
1737
- headers=request_header,
1738
- timeout=REQUEST_TIMEOUT,
1739
- )
1740
- if response.ok:
1741
- return self.parse_request_response(response)
1742
- elif response.status_code == 401 and retries == 0:
1743
- logger.debug("Admin Session has expired - try to re-authenticate...")
1744
- self.authenticate_admin(revalidate=True)
1745
- request_header = self.request_header_admin()
1746
- retries += 1
1747
- else:
1748
- logger.error(
1749
- "Failed to add access role -> %s to Core Share user -> %s; status -> %s; error -> %s",
1750
- str(role_id),
1751
- user_id,
1752
- response.status_code,
1753
- response.text,
1754
- )
1755
- return None
1935
+ return self.do_request(
1936
+ url=request_url,
1937
+ method="PUT",
1938
+ headers=request_header,
1939
+ timeout=REQUEST_TIMEOUT,
1940
+ failure_message="Failed to add access role with ID -> {} to Core Share user with ID -> {}".format(
1941
+ role_id,
1942
+ user_id,
1943
+ ),
1944
+ user_credentials=False,
1945
+ )
1756
1946
 
1757
1947
  # end method definition
1758
1948
 
@@ -1760,13 +1950,17 @@ class CoreShare(object):
1760
1950
  """Remove an access role from a Core Share user.
1761
1951
 
1762
1952
  Args:
1763
- user_id (str): The Core Share user ID.
1764
- role_id (int): The role ID:
1765
- * Content Manager = 5
1766
- * Group Admin = 3
1953
+ user_id (str):
1954
+ The Core Share user ID.
1955
+ role_id (int):
1956
+ The role ID:
1957
+ * Content Manager = 5
1958
+ * Group Admin = 3
1767
1959
 
1768
1960
  Returns:
1769
- dict: Response from the Core Share API.
1961
+ dict:
1962
+ Response from the Core Share API.
1963
+
1770
1964
  """
1771
1965
 
1772
1966
  if not self._access_token_admin:
@@ -1774,44 +1968,26 @@ class CoreShare(object):
1774
1968
 
1775
1969
  request_header = self.request_header_admin()
1776
1970
 
1777
- request_url = (
1778
- self.config()["usersUrlv1"]
1779
- + "/{}".format(user_id)
1780
- + "/roles/"
1781
- + str(role_id)
1782
- )
1971
+ request_url = self.config()["usersUrlv1"] + "/{}".format(user_id) + "/roles/" + str(role_id)
1783
1972
 
1784
- logger.debug(
1785
- "Remove access role -> %s from Core Share user with ID -> %s; calling -> %s",
1973
+ self.logger.debug(
1974
+ "Remove access role with ID -> %s from Core Share user with ID -> %s; calling -> %s",
1786
1975
  str(role_id),
1787
1976
  user_id,
1788
1977
  request_url,
1789
1978
  )
1790
1979
 
1791
- retries = 0
1792
- while True:
1793
- response = requests.delete(
1794
- request_url,
1795
- # json=update_data,
1796
- headers=request_header,
1797
- timeout=REQUEST_TIMEOUT,
1798
- )
1799
- if response.ok:
1800
- return self.parse_request_response(response)
1801
- elif response.status_code == 401 and retries == 0:
1802
- logger.debug("Admin Session has expired - try to re-authenticate...")
1803
- self.authenticate_admin(revalidate=True)
1804
- request_header = self.request_header_admin()
1805
- retries += 1
1806
- else:
1807
- logger.error(
1808
- "Failed to remove access role -> %s from Core Share user -> %s; status -> %s; error -> %s",
1809
- str(role_id),
1810
- user_id,
1811
- response.status_code,
1812
- response.text,
1813
- )
1814
- return None
1980
+ return self.do_request(
1981
+ url=request_url,
1982
+ method="DELETE",
1983
+ headers=request_header,
1984
+ timeout=REQUEST_TIMEOUT,
1985
+ failure_message="Failed to remove access role with ID -> {} from Core Share user with ID -> {}".format(
1986
+ role_id,
1987
+ user_id,
1988
+ ),
1989
+ user_credentials=False,
1990
+ )
1815
1991
 
1816
1992
  # end method definition
1817
1993
 
@@ -1825,26 +2001,29 @@ class CoreShare(object):
1825
2001
  """Define the access roles of a Core Share user.
1826
2002
 
1827
2003
  Args:
1828
- user_id (str): ID of the Core Share user
1829
- is_content_manager (bool | None, optional): Assign Content Manager Role if True.
1830
- Removes Content Manager Role if False.
1831
- Does nothing if None.
1832
- Defaults to None.
1833
- is_group_admin (bool | None, optional): Assign Group Admin Role if True.
1834
- Removes Group Admin Role if False.
1835
- Does nothing if None.
1836
- Defaults to None.
1837
- is_admin (bool | None, optional): Makes user Admin if True.
1838
- Removes Admin rights if False.
1839
- Does nothing if None.
1840
- Defaults to None.
2004
+ user_id (str):
2005
+ The ID of the Core Share user.
2006
+ is_content_manager (bool | None, optional):
2007
+ Assign Content Manager Role if True.
2008
+ Removes Content Manager Role if False.
2009
+ Does nothing if None.
2010
+ Defaults to None.
2011
+ is_group_admin (bool | None, optional):
2012
+ Assign Group Admin Role if True.
2013
+ Removes Group Admin Role if False.
2014
+ Does nothing if None.
2015
+ Defaults to None.
2016
+ is_admin (bool | None, optional):
2017
+ Makes user Admin if True.
2018
+ Removes Admin rights if False.
2019
+ Does nothing if None.
2020
+ Defaults to None.
1841
2021
 
1842
2022
  Returns:
1843
- dict: Response from the Core Share API.
1844
- """
2023
+ dict:
2024
+ Response from the Core Share API.
1845
2025
 
1846
- CONTENT_MANAGER_ROLE_ID = 5
1847
- GROUP_ADMIN_ROLE_ID = 3
2026
+ """
1848
2027
 
1849
2028
  response = None
1850
2029
 
@@ -1858,21 +2037,25 @@ class CoreShare(object):
1858
2037
  if is_content_manager is not None:
1859
2038
  if is_content_manager:
1860
2039
  response = self.add_user_access_role(
1861
- user_id=user_id, role_id=CONTENT_MANAGER_ROLE_ID
2040
+ user_id=user_id,
2041
+ role_id=CONTENT_MANAGER_ROLE_ID,
1862
2042
  )
1863
2043
  else:
1864
2044
  response = self.remove_user_access_role(
1865
- user_id=user_id, role_id=CONTENT_MANAGER_ROLE_ID
2045
+ user_id=user_id,
2046
+ role_id=CONTENT_MANAGER_ROLE_ID,
1866
2047
  )
1867
2048
 
1868
2049
  if is_group_admin is not None:
1869
2050
  if is_group_admin:
1870
2051
  response = self.add_user_access_role(
1871
- user_id=user_id, role_id=GROUP_ADMIN_ROLE_ID
2052
+ user_id=user_id,
2053
+ role_id=GROUP_ADMIN_ROLE_ID,
1872
2054
  )
1873
2055
  else:
1874
2056
  response = self.remove_user_access_role(
1875
- user_id=user_id, role_id=GROUP_ADMIN_ROLE_ID
2057
+ user_id=user_id,
2058
+ role_id=GROUP_ADMIN_ROLE_ID,
1876
2059
  )
1877
2060
 
1878
2061
  return response
@@ -1880,17 +2063,25 @@ class CoreShare(object):
1880
2063
  # end method definition
1881
2064
 
1882
2065
  def update_user_password(
1883
- self, user_id: str, password: str, new_password: str
2066
+ self,
2067
+ user_id: str,
2068
+ password: str,
2069
+ new_password: str,
1884
2070
  ) -> dict:
1885
2071
  """Update the password of a Core Share user.
1886
2072
 
1887
2073
  Args:
1888
- user_id (str): The Core Share user ID.
1889
- password (str): Old user password.
1890
- new_password (str): New user password.
2074
+ user_id (str):
2075
+ The Core Share user ID.
2076
+ password (str):
2077
+ The old user password.
2078
+ new_password (str):
2079
+ The new user password.
1891
2080
 
1892
2081
  Returns:
1893
- dict: Response from the Core Share API.
2082
+ dict:
2083
+ Response from the Core Share API.
2084
+
1894
2085
  """
1895
2086
 
1896
2087
  if not self._access_token_admin:
@@ -1900,7 +2091,7 @@ class CoreShare(object):
1900
2091
 
1901
2092
  request_url = self.config()["usersUrlv1"] + "/{}".format(user_id)
1902
2093
 
1903
- logger.debug(
2094
+ self.logger.debug(
1904
2095
  "Update password of Core Share user with ID -> %s; calling -> %s",
1905
2096
  user_id,
1906
2097
  request_url,
@@ -1908,29 +2099,17 @@ class CoreShare(object):
1908
2099
 
1909
2100
  update_data = {"password": password, "newpassword": new_password}
1910
2101
 
1911
- retries = 0
1912
- while True:
1913
- response = requests.put(
1914
- request_url,
1915
- json=update_data,
1916
- headers=request_header,
1917
- timeout=REQUEST_TIMEOUT,
1918
- )
1919
- if response.ok:
1920
- return self.parse_request_response(response)
1921
- elif response.status_code == 401 and retries == 0:
1922
- logger.debug("Admin Session has expired - try to re-authenticate...")
1923
- self.authenticate_admin(revalidate=True)
1924
- request_header = self.request_header_admin()
1925
- retries += 1
1926
- else:
1927
- logger.error(
1928
- "Failed to update password of Core Share user -> %s; status -> %s; error -> %s",
1929
- user_id,
1930
- response.status_code,
1931
- response.text,
1932
- )
1933
- return None
2102
+ return self.do_request(
2103
+ url=request_url,
2104
+ method="PUT",
2105
+ headers=request_header,
2106
+ json_data=update_data,
2107
+ timeout=REQUEST_TIMEOUT,
2108
+ failure_message="Failed to update password of Core Share user with ID -> {}".format(
2109
+ user_id,
2110
+ ),
2111
+ user_credentials=False,
2112
+ )
1934
2113
 
1935
2114
  # end method definition
1936
2115
 
@@ -1938,14 +2117,22 @@ class CoreShare(object):
1938
2117
  self,
1939
2118
  user_id: str,
1940
2119
  photo_path: str,
2120
+ mime_type: str = "image/jpeg",
1941
2121
  ) -> dict | None:
1942
2122
  """Update the Core Share user photo.
1943
2123
 
1944
2124
  Args:
1945
- user_id (str): Core Share ID of the user
1946
- photo_path (str): file system path with the location of the photo
2125
+ user_id (str):
2126
+ The Core Share user ID.
2127
+ photo_path (str):
2128
+ The file system path with the location of the photo.
2129
+ mime_type (str, optional):
2130
+ The mime type of the photo. Default is "image/jpeg".
2131
+
1947
2132
  Returns:
1948
- dict | None: Dictionary with the Core Share User data or None if the request fails.
2133
+ dict | None:
2134
+ Dictionary with the Core Share User data or None if the request fails.
2135
+
1949
2136
  """
1950
2137
 
1951
2138
  if not self._access_token_user:
@@ -1953,65 +2140,59 @@ class CoreShare(object):
1953
2140
 
1954
2141
  # Check if the photo file exists
1955
2142
  if not os.path.isfile(photo_path):
1956
- logger.error("Photo file -> %s not found!", photo_path)
2143
+ self.logger.error("Photo file -> %s not found!", photo_path)
1957
2144
  return None
1958
2145
 
1959
2146
  try:
1960
2147
  # Read the photo file as binary data
1961
2148
  with open(photo_path, "rb") as image_file:
1962
2149
  photo_data = image_file.read()
1963
- except OSError as exception:
2150
+ except OSError:
1964
2151
  # Handle any errors that occurred while reading the photo file
1965
- logger.error(
1966
- "Error reading photo file -> %s; error -> %s", photo_path, exception
2152
+ self.logger.error(
2153
+ "Error reading photo file -> %s",
2154
+ photo_path,
1967
2155
  )
1968
2156
  return None
1969
2157
 
1970
2158
  request_url = self.config()["usersUrlv3"] + "/{}".format(user_id) + "/photo"
1971
2159
  files = {
1972
- "file": (photo_path, photo_data, "image/jpeg"),
2160
+ "file": (photo_path, photo_data, mime_type),
1973
2161
  }
1974
2162
 
1975
- logger.debug(
2163
+ self.logger.debug(
1976
2164
  "Update profile photo of Core Share user with ID -> %s; calling -> %s",
1977
2165
  user_id,
1978
2166
  request_url,
1979
2167
  )
1980
2168
 
1981
- retries = 0
1982
- while True:
1983
- response = requests.post(
1984
- request_url,
1985
- files=files,
1986
- headers=self.request_header_user(content_type=""),
1987
- verify=False,
1988
- timeout=REQUEST_TIMEOUT,
1989
- )
1990
- if response.ok:
1991
- return self.parse_request_response(response)
1992
- elif response.status_code == 401 and retries == 0:
1993
- logger.debug("Session has expired - try to re-authenticate...")
1994
- self.authenticate_user(revalidate=True)
1995
- retries += 1
1996
- else:
1997
- logger.error(
1998
- "Failed to update profile photo of Core Share user with ID -> %s; status -> %s; error -> %s",
1999
- user_id,
2000
- response.status_code,
2001
- response.text,
2002
- )
2003
- return None
2169
+ return self.do_request(
2170
+ url=request_url,
2171
+ method="POST",
2172
+ headers=self.request_header_user(content_type=""),
2173
+ files=files,
2174
+ timeout=REQUEST_TIMEOUT,
2175
+ failure_message="Failed to update profile photo of Core Share user with ID -> {}".format(
2176
+ user_id,
2177
+ ),
2178
+ user_credentials=True,
2179
+ verify=False,
2180
+ )
2004
2181
 
2005
2182
  # end method definition
2006
2183
 
2007
2184
  def get_folders(self, parent_id: str) -> list | None:
2008
- """Get Core Share folders under a given parent ID. This runs under user credentials (not admin!)
2185
+ """Get Core Share folders under a given parent ID.
2186
+
2187
+ This runs under user credentials (not admin!)
2009
2188
 
2010
2189
  Args:
2011
- parent_id (str): ID of the parent folder or the rootID of a user
2190
+ parent_id (str):
2191
+ ID of the parent folder or the root ID of a user
2012
2192
 
2013
2193
  Returns:
2014
- list | None: List with the Core Share folders data or None if the request fails.
2194
+ list | None:
2195
+ List with the Core Share folders data or None if the request fails.
2015
2196
 
2016
2197
  Example response (it is a list!):
2017
2198
  [
@@ -2061,10 +2242,11 @@ class CoreShare(object):
2061
2242
  'contentOriginator': {
2062
2243
  'id': '0D949C67-473D-448C-8F4B-B2CCA769F586',
2063
2244
  'name': 'IDEA-TE-QA',
2064
- 'imageUri': '/api/v1/tenants/2595192600759637225/contentOriginator/images/0D949C67-473D-448C-8F4B-B2CCA769F586'
2245
+ 'imageUri': '/api/v1/tenants/2595192600759637225/contentOriginator/images/0D949C67'
2065
2246
  }
2066
2247
  }
2067
2248
  ]
2249
+
2068
2250
  """
2069
2251
 
2070
2252
  if not self._access_token_user:
@@ -2078,32 +2260,22 @@ class CoreShare(object):
2078
2260
  + "?limit=25&order=lastModified:desc&filter=any"
2079
2261
  )
2080
2262
 
2081
- logger.debug(
2263
+ self.logger.debug(
2082
2264
  "Get Core Share folders under parent -> %s; calling -> %s",
2083
2265
  parent_id,
2084
2266
  request_url,
2085
2267
  )
2086
2268
 
2087
- retries = 0
2088
- while True:
2089
- response = requests.get(
2090
- request_url, headers=request_header, timeout=REQUEST_TIMEOUT
2091
- )
2092
- if response.ok:
2093
- return self.parse_request_response(response)
2094
- elif response.status_code == 401 and retries == 0:
2095
- logger.debug("Session has expired - try to re-authenticate...")
2096
- self.authenticate_user(revalidate=True)
2097
- request_header = self.request_header_user()
2098
- retries += 1
2099
- else:
2100
- logger.error(
2101
- "Failed to get Core Share folders under parent -> %s; status -> %s; error -> %s",
2102
- parent_id,
2103
- response.status_code,
2104
- response.text,
2105
- )
2106
- return None
2269
+ return self.do_request(
2270
+ url=request_url,
2271
+ method="GET",
2272
+ headers=request_header,
2273
+ timeout=REQUEST_TIMEOUT,
2274
+ failure_message="Failed to get Core Share folders under parent -> {}".format(
2275
+ parent_id,
2276
+ ),
2277
+ user_credentials=True,
2278
+ )
2107
2279
 
2108
2280
  # end method definition
2109
2281
 
@@ -2111,48 +2283,37 @@ class CoreShare(object):
2111
2283
  """Unshare Core Share folder with a given resource ID.
2112
2284
 
2113
2285
  Args:
2114
- resource_id (str): ID of the folder (resource) to unshare with all collaborators
2286
+ resource_id (str):
2287
+ The ID of the folder (resource) to unshare with all collaborators.
2115
2288
 
2116
2289
  Returns:
2117
- dict | None: Dictionary with the Core Share folders data or None if the request fails.
2290
+ dict | None:
2291
+ Dictionary with the Core Share folders data or None if the request fails.
2118
2292
 
2119
- Example response (it is a list!):
2120
2293
  """
2121
2294
 
2122
2295
  if not self._access_token_user:
2123
2296
  self.authenticate_user()
2124
2297
 
2125
2298
  request_header = self.request_header_user()
2126
- request_url = (
2127
- self.config()["foldersUrlv1"] + "/{}".format(resource_id) + "/collaborators"
2128
- )
2299
+ request_url = self.config()["foldersUrlv1"] + "/{}".format(resource_id) + "/collaborators"
2129
2300
 
2130
- logger.debug(
2301
+ self.logger.debug(
2131
2302
  "Unshare Core Share folder -> %s; calling -> %s",
2132
2303
  resource_id,
2133
2304
  request_url,
2134
2305
  )
2135
2306
 
2136
- retries = 0
2137
- while True:
2138
- response = requests.delete(
2139
- request_url, headers=request_header, timeout=REQUEST_TIMEOUT
2140
- )
2141
- if response.ok:
2142
- return self.parse_request_response(response)
2143
- elif response.status_code == 401 and retries == 0:
2144
- logger.debug("Session has expired - try to re-authenticate...")
2145
- self.authenticate_user(revalidate=True)
2146
- request_header = self.request_header_user()
2147
- retries += 1
2148
- else:
2149
- logger.error(
2150
- "Failed to unshare Core Share folder -> %s; status -> %s; error -> %s",
2151
- resource_id,
2152
- response.status_code,
2153
- response.text,
2154
- )
2155
- return None
2307
+ return self.do_request(
2308
+ url=request_url,
2309
+ method="DELETE",
2310
+ headers=request_header,
2311
+ timeout=REQUEST_TIMEOUT,
2312
+ failure_message="Failed to unshare Core Share folder with ID -> {}".format(
2313
+ resource_id,
2314
+ ),
2315
+ user_credentials=True,
2316
+ )
2156
2317
 
2157
2318
  # end method definition
2158
2319
 
@@ -2160,12 +2321,13 @@ class CoreShare(object):
2160
2321
  """Delete Core Share folder with a given resource ID.
2161
2322
 
2162
2323
  Args:
2163
- resource_id (str): ID of the folder (resource) to delete
2324
+ resource_id (str):
2325
+ The ID of the folder (resource) to delete.
2164
2326
 
2165
2327
  Returns:
2166
- dict | None: Dictionary with the Core Share request data or None if the request fails.
2328
+ dict | None:
2329
+ Dictionary with the Core Share request data or None if the request fails.
2167
2330
 
2168
- Example response (it is a list!):
2169
2331
  """
2170
2332
 
2171
2333
  if not self._access_token_user:
@@ -2176,35 +2338,23 @@ class CoreShare(object):
2176
2338
 
2177
2339
  payload = {"state": "deleted"}
2178
2340
 
2179
- logger.debug(
2341
+ self.logger.debug(
2180
2342
  "Delete Core Share folder -> %s; calling -> %s",
2181
2343
  resource_id,
2182
2344
  request_url,
2183
2345
  )
2184
2346
 
2185
- retries = 0
2186
- while True:
2187
- response = requests.put(
2188
- request_url,
2189
- headers=request_header,
2190
- data=json.dumps(payload),
2191
- timeout=REQUEST_TIMEOUT,
2192
- )
2193
- if response.ok:
2194
- return self.parse_request_response(response)
2195
- elif response.status_code == 401 and retries == 0:
2196
- logger.debug("Session has expired - try to re-authenticate...")
2197
- self.authenticate_user(revalidate=True)
2198
- request_header = self.request_header_user()
2199
- retries += 1
2200
- else:
2201
- logger.error(
2202
- "Failed to delete Core Share folder -> %s; status -> %s; error -> %s",
2203
- resource_id,
2204
- response.status_code,
2205
- response.text,
2206
- )
2207
- return None
2347
+ return self.do_request(
2348
+ url=request_url,
2349
+ method="PUT",
2350
+ headers=request_header,
2351
+ data=json.dumps(payload),
2352
+ timeout=REQUEST_TIMEOUT,
2353
+ failure_message="Failed to delete Core Share folder -> {}".format(
2354
+ resource_id,
2355
+ ),
2356
+ user_credentials=True,
2357
+ )
2208
2358
 
2209
2359
  # end method definition
2210
2360
 
@@ -2212,12 +2362,13 @@ class CoreShare(object):
2212
2362
  """Delete Core Share document with a given resource ID.
2213
2363
 
2214
2364
  Args:
2215
- resource_id (str): ID of the document (resource) to delete
2365
+ resource_id (str):
2366
+ The ID of the document (resource) to delete.
2216
2367
 
2217
2368
  Returns:
2218
- dict | None: Dictionary with the Core Share request data or None if the request fails.
2369
+ dict | None:
2370
+ Dictionary with the Core Share request data or None if the request fails.
2219
2371
 
2220
- Example response (it is a list!):
2221
2372
  """
2222
2373
 
2223
2374
  if not self._access_token_user:
@@ -2228,47 +2379,39 @@ class CoreShare(object):
2228
2379
 
2229
2380
  payload = {"state": "deleted"}
2230
2381
 
2231
- logger.debug(
2382
+ self.logger.debug(
2232
2383
  "Delete Core Share document -> %s; calling -> %s",
2233
2384
  resource_id,
2234
2385
  request_url,
2235
2386
  )
2236
2387
 
2237
- retries = 0
2238
- while True:
2239
- response = requests.put(
2240
- request_url,
2241
- headers=request_header,
2242
- data=json.dumps(payload),
2243
- timeout=REQUEST_TIMEOUT,
2244
- )
2245
- if response.ok:
2246
- return self.parse_request_response(response)
2247
- elif response.status_code == 401 and retries == 0:
2248
- logger.debug("Session has expired - try to re-authenticate...")
2249
- self.authenticate_user(revalidate=True)
2250
- request_header = self.request_header_user()
2251
- retries += 1
2252
- else:
2253
- logger.error(
2254
- "Failed to delete Core Share document -> %s; status -> %s; error -> %s",
2255
- resource_id,
2256
- response.status_code,
2257
- response.text,
2258
- )
2259
- return None
2388
+ return self.do_request(
2389
+ url=request_url,
2390
+ method="PUT",
2391
+ headers=request_header,
2392
+ data=json.dumps(payload),
2393
+ timeout=REQUEST_TIMEOUT,
2394
+ failure_message="Failed to delete Core Share document -> {}".format(
2395
+ resource_id,
2396
+ ),
2397
+ user_credentials=True,
2398
+ )
2260
2399
 
2261
2400
  # end method definition
2262
2401
 
2263
2402
  def leave_share(self, user_id: str, resource_id: str) -> dict | None:
2264
- """Remove a Core Share user from a share (i.e. the user leaves the share)
2403
+ """Remove a Core Share user from a share (i.e. the user leaves the share).
2265
2404
 
2266
2405
  Args:
2267
- user_id (str): Core Share ID of the user.
2268
- resource_id (str): Core Share ID of the shared folder.
2406
+ user_id (str):
2407
+ The Core Share user ID.
2408
+ resource_id (str):
2409
+ The Core Share ID of the shared folder.
2269
2410
 
2270
2411
  Returns:
2271
- dict | None: Reponse of the REST call or None in case of an error.
2412
+ dict | None:
2413
+ Reponse of the REST call or None in case of an error.
2414
+
2272
2415
  """
2273
2416
 
2274
2417
  if not self._access_token_user:
@@ -2276,45 +2419,29 @@ class CoreShare(object):
2276
2419
 
2277
2420
  request_header = self.request_header_user()
2278
2421
 
2279
- request_url = (
2280
- self.config()["foldersUrlv1"]
2281
- + "/{}".format(resource_id)
2282
- + "/collaborators/"
2283
- + str(user_id)
2284
- )
2422
+ request_url = self.config()["foldersUrlv1"] + "/{}".format(resource_id) + "/collaborators/" + str(user_id)
2285
2423
 
2286
2424
  payload = {"action": "LEAVE_SHARE"}
2287
2425
 
2288
- logger.debug(
2289
- "User -> %s leaves Core Share shared folder -> %s; calling -> %s",
2426
+ self.logger.debug(
2427
+ "User with ID -> %s leaves Core Share shared folder with ID -> %s; calling -> %s",
2290
2428
  user_id,
2291
2429
  resource_id,
2292
2430
  request_url,
2293
2431
  )
2294
2432
 
2295
- retries = 0
2296
- while True:
2297
- response = requests.delete(
2298
- request_url,
2299
- headers=request_header,
2300
- data=json.dumps(payload),
2301
- timeout=REQUEST_TIMEOUT,
2302
- )
2303
- if response.ok:
2304
- return self.parse_request_response(response)
2305
- elif response.status_code == 401 and retries == 0:
2306
- logger.debug("Session has expired - try to re-authenticate...")
2307
- self.authenticate_user(revalidate=True)
2308
- request_header = self.request_header_user()
2309
- retries += 1
2310
- else:
2311
- logger.error(
2312
- "Failed to leave Core Share folder -> %s; status -> %s; error -> %s",
2313
- resource_id,
2314
- response.status_code,
2315
- response.text,
2316
- )
2317
- return None
2433
+ return self.do_request(
2434
+ url=request_url,
2435
+ method="DELETE",
2436
+ headers=request_header,
2437
+ data=json.dumps(payload),
2438
+ timeout=REQUEST_TIMEOUT,
2439
+ failure_message="User with ID -> {} failed to leave Core Share folder with ID -> {}".format(
2440
+ user_id,
2441
+ resource_id,
2442
+ ),
2443
+ user_credentials=True,
2444
+ )
2318
2445
 
2319
2446
  # end method definition
2320
2447
 
@@ -2322,11 +2449,15 @@ class CoreShare(object):
2322
2449
  """Stop of share of a user.
2323
2450
 
2324
2451
  Args:
2325
- user_id (str): Core Share ID of the user.
2326
- resource_id (str): Core Share ID of the shared folder.
2452
+ user_id (str):
2453
+ The Core Share user ID.
2454
+ resource_id (str):
2455
+ The Core Share ID of the shared folder.
2327
2456
 
2328
2457
  Returns:
2329
- dict | None: Response of the REST call or None in case of an error.
2458
+ dict | None:
2459
+ Response of the REST call or None in case of an error.
2460
+
2330
2461
  """
2331
2462
 
2332
2463
  if not self._access_token_user:
@@ -2334,46 +2465,38 @@ class CoreShare(object):
2334
2465
 
2335
2466
  request_header = self.request_header_user()
2336
2467
 
2337
- request_url = (
2338
- self.config()["foldersUrlv1"] + "/{}".format(resource_id) + "/collaborators"
2339
- )
2468
+ request_url = self.config()["foldersUrlv1"] + "/{}".format(resource_id) + "/collaborators"
2340
2469
 
2341
- logger.debug(
2470
+ self.logger.debug(
2342
2471
  "User -> %s stops sharing Core Share shared folder -> %s; calling -> %s",
2343
2472
  user_id,
2344
2473
  resource_id,
2345
2474
  request_url,
2346
2475
  )
2347
2476
 
2348
- retries = 0
2349
- while True:
2350
- response = requests.delete(
2351
- request_url,
2352
- headers=request_header,
2353
- timeout=REQUEST_TIMEOUT,
2354
- )
2355
- if response.ok:
2356
- return self.parse_request_response(response)
2357
- elif response.status_code == 401 and retries == 0:
2358
- logger.debug("Session has expired - try to re-authenticate...")
2359
- self.authenticate_user(revalidate=True)
2360
- request_header = self.request_header_user()
2361
- retries += 1
2362
- else:
2363
- logger.error(
2364
- "Failed to stop sharing Core Share folder -> %s; status -> %s; error -> %s",
2365
- resource_id,
2366
- response.status_code,
2367
- response.text,
2368
- )
2369
- return None
2477
+ return self.do_request(
2478
+ url=request_url,
2479
+ method="DELETE",
2480
+ headers=request_header,
2481
+ timeout=REQUEST_TIMEOUT,
2482
+ failure_message="User with ID -> {} failed to stop sharing Core Share folder with ID -> {}".format(
2483
+ user_id,
2484
+ resource_id,
2485
+ ),
2486
+ user_credentials=True,
2487
+ )
2370
2488
 
2371
2489
  # end method definition
2372
2490
 
2373
2491
  def cleanup_user_files(
2374
- self, user_id: str, user_login: str, user_password: str
2492
+ self,
2493
+ user_id: str,
2494
+ user_login: str,
2495
+ user_password: str,
2375
2496
  ) -> bool:
2376
- """Cleanup files of a user. This handles different types of resources.
2497
+ """Cleanup files of a user.
2498
+
2499
+ This handles different types of resources.
2377
2500
  * Local resources - not shared
2378
2501
  * Resources shared by the user
2379
2502
  * Resources shared by other users or groups
@@ -2381,12 +2504,17 @@ class CoreShare(object):
2381
2504
  The Core Share admin is not entitled to do this.
2382
2505
 
2383
2506
  Args:
2384
- user_id (str): Core Share ID of the user
2385
- user_login (str): Core Share email (= login) of the user
2386
- user_password (str): Core Share password of the user
2507
+ user_id (str):
2508
+ The Core Share user ID.
2509
+ user_login (str):
2510
+ The Core Share email (= login) of the user.
2511
+ user_password (str):
2512
+ The Core Share password of the user.
2387
2513
 
2388
2514
  Returns:
2389
- bool: True = success, False in case of an error.
2515
+ bool:
2516
+ True = success, False in case of an error.
2517
+
2390
2518
  """
2391
2519
 
2392
2520
  user = self.get_user_by_id(user_id=user_id)
@@ -2395,13 +2523,13 @@ class CoreShare(object):
2395
2523
 
2396
2524
  is_confirmed = self.get_result_value(response=user, key="isConfirmed")
2397
2525
  if not is_confirmed:
2398
- logger.info(
2526
+ self.logger.info(
2399
2527
  "User -> %s is not yet confirmed - so it cannot have files to cleanup.",
2400
2528
  user_id,
2401
2529
  )
2402
2530
  return True
2403
2531
 
2404
- logger.info("Inpersonate as user -> %s to cleanup files...", user_login)
2532
+ self.logger.info("Inpersonate as user -> %s to cleanup files...", user_login)
2405
2533
 
2406
2534
  # Save admin credentials the class has been initialized with:
2407
2535
  admin_credentials = self.credentials()
@@ -2418,24 +2546,25 @@ class CoreShare(object):
2418
2546
  # Get all folders of the user:
2419
2547
  response = self.get_folders(parent_id=user_root_folder_id)
2420
2548
  if not response or not response["results"]:
2421
- logger.info("User -> %s has no items to cleanup!", user_id)
2549
+ self.logger.info("User -> %s has no items to cleanup!", user_id)
2422
2550
  else:
2423
2551
  items = response["results"]
2424
2552
  for item in items:
2425
2553
  if item["isShared"]:
2426
2554
  if item["owner"]["id"] == user_id:
2427
- logger.info(
2555
+ self.logger.info(
2428
2556
  "User -> %s stops sharing item -> %s (%s)...",
2429
2557
  user_id,
2430
2558
  item["name"],
2431
2559
  item["id"],
2432
2560
  )
2433
2561
  response = self.stop_share(
2434
- user_id=user_id, resource_id=item["id"]
2562
+ user_id=user_id,
2563
+ resource_id=item["id"],
2435
2564
  )
2436
2565
  if not response:
2437
2566
  success = False
2438
- logger.info(
2567
+ self.logger.info(
2439
2568
  "User -> %s deletes unshared item -> %s (%s)...",
2440
2569
  user_id,
2441
2570
  item["name"],
@@ -2445,19 +2574,20 @@ class CoreShare(object):
2445
2574
  if not response:
2446
2575
  success = False
2447
2576
  else:
2448
- logger.info(
2577
+ self.logger.info(
2449
2578
  "User -> %s leaves shared folder -> '%s' (%s)...",
2450
2579
  user_id,
2451
2580
  item["name"],
2452
2581
  item["id"],
2453
2582
  )
2454
2583
  response = self.leave_share(
2455
- user_id=user_id, resource_id=item["id"]
2584
+ user_id=user_id,
2585
+ resource_id=item["id"],
2456
2586
  )
2457
2587
  if not response:
2458
2588
  success = False
2459
2589
  else:
2460
- logger.info(
2590
+ self.logger.info(
2461
2591
  "User -> %s deletes local item -> '%s' (%s) of type -> '%s'...",
2462
2592
  user_id,
2463
2593
  item["name"],
@@ -2469,21 +2599,23 @@ class CoreShare(object):
2469
2599
  elif item["resourceType"] == "document":
2470
2600
  response = self.delete_document(item["id"])
2471
2601
  else:
2472
- logger.error(
2473
- "Unsupport resource type -> '%s'", item["resourceType"]
2602
+ self.logger.error(
2603
+ "Unsupport resource type -> '%s'",
2604
+ item["resourceType"],
2474
2605
  )
2475
2606
  response = None
2476
2607
  if not response:
2477
2608
  success = False
2478
2609
 
2479
- logger.info(
2610
+ self.logger.info(
2480
2611
  "End inpersonation and switch back to admin account -> %s...",
2481
2612
  admin_credentials["username"],
2482
2613
  )
2483
2614
 
2484
2615
  # Reset credentials to admin:
2485
2616
  self.set_credentials(
2486
- admin_credentials["username"], admin_credentials["password"]
2617
+ admin_credentials["username"],
2618
+ admin_credentials["password"],
2487
2619
  )
2488
2620
  # Authenticate as administrator the class has been initialized with:
2489
2621
  self.authenticate_user(revalidate=True)
@@ -2496,10 +2628,13 @@ class CoreShare(object):
2496
2628
  """Get (incoming) shares of a Core Share group.
2497
2629
 
2498
2630
  Args:
2499
- group_id (str): Core Share ID of a group
2631
+ group_id (str):
2632
+ The Core Share group ID.
2500
2633
 
2501
2634
  Returns:
2502
- dict | None: Incoming shares or None if the request fails.
2635
+ dict | None:
2636
+ Incoming shares or None if the request fails.
2637
+
2503
2638
  """
2504
2639
 
2505
2640
  if not self._access_token_admin:
@@ -2507,38 +2642,24 @@ class CoreShare(object):
2507
2642
 
2508
2643
  request_header = self.request_header_admin()
2509
2644
 
2510
- request_url = (
2511
- self.config()["groupsUrl"] + "/{}".format(group_id) + "/shares/incoming"
2512
- )
2645
+ request_url = self.config()["groupsUrl"] + "/{}".format(group_id) + "/shares/incoming"
2513
2646
 
2514
- logger.debug(
2647
+ self.logger.debug(
2515
2648
  "Get shares of Core Share group -> %s; calling -> %s",
2516
2649
  group_id,
2517
2650
  request_url,
2518
2651
  )
2519
2652
 
2520
- retries = 0
2521
- while True:
2522
- response = requests.get(
2523
- request_url,
2524
- headers=request_header,
2525
- timeout=REQUEST_TIMEOUT,
2526
- )
2527
- if response.ok:
2528
- return self.parse_request_response(response)
2529
- elif response.status_code == 401 and retries == 0:
2530
- logger.debug("Session has expired - try to re-authenticate...")
2531
- self.authenticate_admin(revalidate=True)
2532
- request_header = self.request_header_admin()
2533
- retries += 1
2534
- else:
2535
- logger.error(
2536
- "Failed to get shares of Core Share group -> %s; status -> %s; error -> %s",
2537
- group_id,
2538
- response.status_code,
2539
- response.text,
2540
- )
2541
- return None
2653
+ return self.do_request(
2654
+ url=request_url,
2655
+ method="GET",
2656
+ headers=request_header,
2657
+ timeout=REQUEST_TIMEOUT,
2658
+ failure_message="Failed to get shares of Core Share group -> {}".format(
2659
+ group_id,
2660
+ ),
2661
+ user_credentials=False,
2662
+ )
2542
2663
 
2543
2664
  # end method definition
2544
2665
 
@@ -2546,11 +2667,15 @@ class CoreShare(object):
2546
2667
  """Revoke sharing of a folder with a group.
2547
2668
 
2548
2669
  Args:
2549
- group_id (str): ID of the Core Share group
2550
- resource_id (str): ID of the Core share folder
2670
+ group_id (str):
2671
+ The Core Share group ID.
2672
+ resource_id (str):
2673
+ The ID of the Core share folder.
2551
2674
 
2552
2675
  Returns:
2553
- dict | None: Response or None if the request fails.
2676
+ dict | None:
2677
+ Response or None if the request fails.
2678
+
2554
2679
  """
2555
2680
 
2556
2681
  if not self._access_token_admin:
@@ -2559,74 +2684,64 @@ class CoreShare(object):
2559
2684
  request_header = self.request_header_admin()
2560
2685
 
2561
2686
  request_url = (
2562
- self.config()["foldersUrlv1"]
2563
- + "/{}".format(resource_id)
2564
- + "/collaboratorsAsAdmin/"
2565
- + str(group_id)
2687
+ self.config()["foldersUrlv1"] + "/{}".format(resource_id) + "/collaboratorsAsAdmin/" + str(group_id)
2566
2688
  )
2567
2689
 
2568
- logger.debug(
2690
+ self.logger.debug(
2569
2691
  "Revoke sharing of folder -> %s with group -> %s; calling -> %s",
2570
2692
  resource_id,
2571
2693
  group_id,
2572
2694
  request_url,
2573
2695
  )
2574
2696
 
2575
- retries = 0
2576
- while True:
2577
- response = requests.delete(
2578
- request_url,
2579
- headers=request_header,
2580
- timeout=REQUEST_TIMEOUT,
2581
- )
2582
- if response.ok:
2583
- return self.parse_request_response(response)
2584
- elif response.status_code == 401 and retries == 0:
2585
- logger.debug("Session has expired - try to re-authenticate...")
2586
- self.authenticate_admin(revalidate=True)
2587
- request_header = self.request_header_admin()
2588
- retries += 1
2589
- else:
2590
- logger.error(
2591
- "Failed to revoke sharing Core Share folder -> %s with group -> %s; status -> %s; error -> %s",
2592
- resource_id,
2593
- group_id,
2594
- response.status_code,
2595
- response.text,
2596
- )
2597
- return None
2697
+ return self.do_request(
2698
+ url=request_url,
2699
+ method="DELETE",
2700
+ headers=request_header,
2701
+ timeout=REQUEST_TIMEOUT,
2702
+ failure_message="Failed to revoke sharing Core Share folder with ID -> {} with group with ID -> {}".format(
2703
+ resource_id,
2704
+ group_id,
2705
+ ),
2706
+ user_credentials=False,
2707
+ )
2598
2708
 
2599
2709
  # end method definition
2600
2710
 
2601
2711
  def cleanup_group_shares(self, group_id: str) -> bool:
2602
2712
  """Cleanup all incoming shares of a group.
2603
- The Core Share admin is required to do this.
2713
+
2714
+ The Core Share admin is required to do this.
2604
2715
 
2605
2716
  Args:
2606
- group_id (str): Core Share ID of the group
2717
+ group_id (str):
2718
+ The Core Share group ID.
2607
2719
 
2608
2720
  Returns:
2609
- bool: True = success, False in case of an error.
2721
+ bool:
2722
+ True = success, False in case of an error.
2723
+
2610
2724
  """
2611
2725
 
2612
2726
  response = self.get_group_shares(group_id=group_id)
2613
2727
 
2614
2728
  if not response or not response["shares"]:
2615
- logger.info("Group -> %s has no shares to revoke!", group_id)
2729
+ self.logger.info("Group -> %s has no shares to revoke!", group_id)
2616
2730
  return True
2617
2731
 
2618
2732
  success = True
2619
2733
 
2620
2734
  items = response["shares"]
2621
2735
  for item in items:
2622
- logger.info(
2736
+ self.logger.info(
2623
2737
  "Revoke sharing of folder -> %s (%s) with group -> %s...",
2624
2738
  item["name"],
2625
2739
  item["id"],
2626
2740
  group_id,
2627
2741
  )
2628
2742
  response = self.revoke_group_share(
2629
- group_id=group_id, resource_id=item["id"]
2743
+ group_id=group_id,
2744
+ resource_id=item["id"],
2630
2745
  )
2631
2746
  if not response:
2632
2747
  success = False