pyxecm 1.5__py3-none-any.whl → 1.6__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.
- pyxecm/__init__.py +2 -0
- pyxecm/avts.py +1065 -0
- pyxecm/coreshare.py +467 -571
- pyxecm/customizer/customizer.py +160 -19
- pyxecm/customizer/k8s.py +139 -25
- pyxecm/customizer/m365.py +694 -1498
- pyxecm/customizer/payload.py +2306 -485
- pyxecm/customizer/pht.py +547 -124
- pyxecm/customizer/salesforce.py +378 -443
- pyxecm/customizer/servicenow.py +379 -133
- pyxecm/helper/assoc.py +20 -0
- pyxecm/helper/data.py +237 -33
- pyxecm/helper/xml.py +1 -1
- pyxecm/otawp.py +1810 -0
- pyxecm/otcs.py +3180 -2938
- pyxecm/otds.py +1591 -1875
- pyxecm/otmm.py +131 -11
- {pyxecm-1.5.dist-info → pyxecm-1.6.dist-info}/METADATA +3 -1
- pyxecm-1.6.dist-info/RECORD +32 -0
- {pyxecm-1.5.dist-info → pyxecm-1.6.dist-info}/WHEEL +1 -1
- pyxecm-1.5.dist-info/RECORD +0 -30
- {pyxecm-1.5.dist-info → pyxecm-1.6.dist-info}/LICENSE +0 -0
- {pyxecm-1.5.dist-info → pyxecm-1.6.dist-info}/top_level.txt +0 -0
pyxecm/customizer/m365.py
CHANGED
|
@@ -12,8 +12,10 @@ credentials_user: In some cases MS Graph APIs cannot be called via
|
|
|
12
12
|
application permissions (client_id, client_secret)
|
|
13
13
|
but requires a token of a user authenticated
|
|
14
14
|
with username + password
|
|
15
|
+
|
|
15
16
|
request_header: Returns the request header for MS Graph API calls
|
|
16
17
|
request_header_user: Returns the request header used for user specific calls
|
|
18
|
+
do_request: Call an M365 Graph API in a safe way
|
|
17
19
|
parse_request_response: Parse the REST API responses and convert
|
|
18
20
|
them to Python dict in a safe way
|
|
19
21
|
exist_result_item: Check if an dict item is in the response
|
|
@@ -102,9 +104,10 @@ import re
|
|
|
102
104
|
import time
|
|
103
105
|
import urllib.parse
|
|
104
106
|
import zipfile
|
|
105
|
-
from urllib.parse import quote
|
|
106
107
|
from datetime import datetime
|
|
107
108
|
|
|
109
|
+
from urllib.parse import quote
|
|
110
|
+
from http import HTTPStatus
|
|
108
111
|
import requests
|
|
109
112
|
|
|
110
113
|
from pyxecm.helper.web import HTTP
|
|
@@ -118,6 +121,8 @@ request_login_headers = {
|
|
|
118
121
|
}
|
|
119
122
|
|
|
120
123
|
REQUEST_TIMEOUT = 60
|
|
124
|
+
REQUEST_RETRY_DELAY = 20
|
|
125
|
+
REQUEST_MAX_RETRIES = 3
|
|
121
126
|
|
|
122
127
|
class M365(object):
|
|
123
128
|
"""Used to automate stettings in Microsoft 365 via the Graph API."""
|
|
@@ -268,6 +273,183 @@ class M365(object):
|
|
|
268
273
|
|
|
269
274
|
# end method definition
|
|
270
275
|
|
|
276
|
+
def do_request(
|
|
277
|
+
self,
|
|
278
|
+
url: str,
|
|
279
|
+
method: str = "GET",
|
|
280
|
+
headers: dict | None = None,
|
|
281
|
+
data: dict | None = None,
|
|
282
|
+
json_data: dict | None = None,
|
|
283
|
+
files: dict | None = None,
|
|
284
|
+
params: dict | None = None,
|
|
285
|
+
timeout: int | None = REQUEST_TIMEOUT,
|
|
286
|
+
show_error: bool = True,
|
|
287
|
+
show_warning: bool = False,
|
|
288
|
+
warning_message: str = "",
|
|
289
|
+
failure_message: str = "",
|
|
290
|
+
success_message: str = "",
|
|
291
|
+
max_retries: int = REQUEST_MAX_RETRIES,
|
|
292
|
+
retry_forever: bool = False,
|
|
293
|
+
parse_request_response: bool = True,
|
|
294
|
+
stream: bool = False,
|
|
295
|
+
) -> dict | None:
|
|
296
|
+
"""Call an M365 Graph API in a safe way
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
url (str): URL to send the request to.
|
|
300
|
+
method (str, optional): HTTP method (GET, POST, etc.). Defaults to "GET".
|
|
301
|
+
headers (dict | None, optional): Request Headers. Defaults to None.
|
|
302
|
+
data (dict | None, optional): Request payload. Defaults to None
|
|
303
|
+
files (dict | None, optional): Dictionary of {"name": file-tuple} for multipart encoding upload.
|
|
304
|
+
file-tuple can be a 2-tuple ("filename", fileobj) or a 3-tuple ("filename", fileobj, "content_type")
|
|
305
|
+
params (dict | None, optional): Add key-value pairs to the query string of the URL.
|
|
306
|
+
When you use the params parameter, requests automatically appends
|
|
307
|
+
the key-value pairs to the URL as part of the query string
|
|
308
|
+
timeout (int | None, optional): Timeout for the request in seconds. Defaults to REQUEST_TIMEOUT.
|
|
309
|
+
show_error (bool, optional): Whether or not an error should be logged in case of a failed REST call.
|
|
310
|
+
If False, then only a warning is logged. Defaults to True.
|
|
311
|
+
warning_message (str, optional): Specific warning message. Defaults to "". If not given the error_message will be used.
|
|
312
|
+
failure_message (str, optional): Specific error message. Defaults to "".
|
|
313
|
+
success_message (str, optional): Specific success message. Defaults to "".
|
|
314
|
+
max_retries (int, optional): How many retries on Connection errors? Default is REQUEST_MAX_RETRIES.
|
|
315
|
+
retry_forever (bool, optional): Eventually wait forever - without timeout. Defaults to False.
|
|
316
|
+
parse_request_response (bool, optional): should the response.text be interpreted as json and loaded into a dictionary. True is the default.
|
|
317
|
+
stream (bool, optional): parameter is used to control whether the response content should be immediately downloaded or streamed incrementally
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
dict | None: Response of OTDS REST API or None in case of an error.
|
|
321
|
+
"""
|
|
322
|
+
|
|
323
|
+
if headers is None:
|
|
324
|
+
logger.error("Missing request header. Cannot send request to Core Share!")
|
|
325
|
+
return None
|
|
326
|
+
|
|
327
|
+
# In case of an expired session we reauthenticate and
|
|
328
|
+
# try 1 more time. Session expiration should not happen
|
|
329
|
+
# twice in a row:
|
|
330
|
+
retries = 0
|
|
331
|
+
|
|
332
|
+
while True:
|
|
333
|
+
try:
|
|
334
|
+
response = requests.request(
|
|
335
|
+
method=method,
|
|
336
|
+
url=url,
|
|
337
|
+
data=data,
|
|
338
|
+
json=json_data,
|
|
339
|
+
files=files,
|
|
340
|
+
params=params,
|
|
341
|
+
headers=headers,
|
|
342
|
+
timeout=timeout,
|
|
343
|
+
stream=stream,
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
if response.ok:
|
|
347
|
+
if success_message:
|
|
348
|
+
logger.info(success_message)
|
|
349
|
+
if parse_request_response:
|
|
350
|
+
return self.parse_request_response(response)
|
|
351
|
+
else:
|
|
352
|
+
return response
|
|
353
|
+
# Check if Session has expired - then re-authenticate and try once more
|
|
354
|
+
elif response.status_code == 401 and retries == 0:
|
|
355
|
+
logger.debug("Session has expired - try to re-authenticate...")
|
|
356
|
+
self.authenticate(revalidate=True)
|
|
357
|
+
headers = self.request_header()
|
|
358
|
+
retries += 1
|
|
359
|
+
elif (
|
|
360
|
+
response.status_code in [502, 503, 504]
|
|
361
|
+
and retries < REQUEST_MAX_RETRIES
|
|
362
|
+
):
|
|
363
|
+
logger.warning(
|
|
364
|
+
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
365
|
+
response.status_code,
|
|
366
|
+
(retries + 1) * 60,
|
|
367
|
+
)
|
|
368
|
+
time.sleep((retries + 1) * 60)
|
|
369
|
+
retries += 1
|
|
370
|
+
else:
|
|
371
|
+
# Handle plain HTML responses to not pollute the logs
|
|
372
|
+
content_type = response.headers.get("content-type", None)
|
|
373
|
+
if content_type == "text/html":
|
|
374
|
+
response_text = "HTML content (only printed in debug log)"
|
|
375
|
+
else:
|
|
376
|
+
response_text = response.text
|
|
377
|
+
|
|
378
|
+
if show_error:
|
|
379
|
+
logger.error(
|
|
380
|
+
"%s; status -> %s/%s; error -> %s",
|
|
381
|
+
failure_message,
|
|
382
|
+
response.status_code,
|
|
383
|
+
HTTPStatus(response.status_code).phrase,
|
|
384
|
+
response_text,
|
|
385
|
+
)
|
|
386
|
+
elif show_warning:
|
|
387
|
+
logger.warning(
|
|
388
|
+
"%s; status -> %s/%s; warning -> %s",
|
|
389
|
+
warning_message if warning_message else failure_message,
|
|
390
|
+
response.status_code,
|
|
391
|
+
HTTPStatus(response.status_code).phrase,
|
|
392
|
+
response_text,
|
|
393
|
+
)
|
|
394
|
+
if content_type == "text/html":
|
|
395
|
+
logger.debug(
|
|
396
|
+
"%s; status -> %s/%s; warning -> %s",
|
|
397
|
+
failure_message,
|
|
398
|
+
response.status_code,
|
|
399
|
+
HTTPStatus(response.status_code).phrase,
|
|
400
|
+
response.text,
|
|
401
|
+
)
|
|
402
|
+
return None
|
|
403
|
+
except requests.exceptions.Timeout:
|
|
404
|
+
if retries <= max_retries:
|
|
405
|
+
logger.warning(
|
|
406
|
+
"Request timed out. Retrying in %s seconds...",
|
|
407
|
+
str(REQUEST_RETRY_DELAY),
|
|
408
|
+
)
|
|
409
|
+
retries += 1
|
|
410
|
+
time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
|
|
411
|
+
else:
|
|
412
|
+
logger.error(
|
|
413
|
+
"%s; timeout error",
|
|
414
|
+
failure_message,
|
|
415
|
+
)
|
|
416
|
+
if retry_forever:
|
|
417
|
+
# If it fails after REQUEST_MAX_RETRIES retries we let it wait forever
|
|
418
|
+
logger.warning("Turn timeouts off and wait forever...")
|
|
419
|
+
timeout = None
|
|
420
|
+
else:
|
|
421
|
+
return None
|
|
422
|
+
except requests.exceptions.ConnectionError:
|
|
423
|
+
if retries <= max_retries:
|
|
424
|
+
logger.warning(
|
|
425
|
+
"Connection error. Retrying in %s seconds...",
|
|
426
|
+
str(REQUEST_RETRY_DELAY),
|
|
427
|
+
)
|
|
428
|
+
retries += 1
|
|
429
|
+
time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
|
|
430
|
+
else:
|
|
431
|
+
logger.error(
|
|
432
|
+
"%s; connection error",
|
|
433
|
+
failure_message,
|
|
434
|
+
)
|
|
435
|
+
if retry_forever:
|
|
436
|
+
# If it fails after REQUEST_MAX_RETRIES retries we let it wait forever
|
|
437
|
+
logger.warning("Turn timeouts off and wait forever...")
|
|
438
|
+
timeout = None
|
|
439
|
+
time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
|
|
440
|
+
else:
|
|
441
|
+
return None
|
|
442
|
+
# end try
|
|
443
|
+
logger.debug(
|
|
444
|
+
"Retrying REST API %s call -> %s... (retry = %s)",
|
|
445
|
+
method,
|
|
446
|
+
url,
|
|
447
|
+
str(retries),
|
|
448
|
+
)
|
|
449
|
+
# end while True
|
|
450
|
+
|
|
451
|
+
# end method definition
|
|
452
|
+
|
|
271
453
|
def parse_request_response(
|
|
272
454
|
self,
|
|
273
455
|
response_object: requests.Response,
|
|
@@ -518,7 +700,7 @@ class M365(object):
|
|
|
518
700
|
logger.debug("User Access Token -> %s", access_token)
|
|
519
701
|
else:
|
|
520
702
|
logger.error(
|
|
521
|
-
"Failed to request an M365 Access Token for user -> %s; error -> %s",
|
|
703
|
+
"Failed to request an M365 Access Token for user -> '%s'; error -> %s",
|
|
522
704
|
username,
|
|
523
705
|
authenticate_response.text,
|
|
524
706
|
)
|
|
@@ -535,42 +717,21 @@ class M365(object):
|
|
|
535
717
|
"""Get list all all users in M365 tenant
|
|
536
718
|
|
|
537
719
|
Returns:
|
|
538
|
-
dict: Dictionary of all users.
|
|
720
|
+
dict: Dictionary of all M365 users.
|
|
539
721
|
"""
|
|
540
722
|
|
|
541
723
|
request_url = self.config()["usersUrl"]
|
|
542
724
|
request_header = self.request_header()
|
|
543
725
|
|
|
544
|
-
logger.debug("Get list of all users; calling -> %s", request_url)
|
|
726
|
+
logger.debug("Get list of all M365 users; calling -> %s", request_url)
|
|
545
727
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
554
|
-
elif response.status_code == 401 and retries == 0:
|
|
555
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
556
|
-
self.authenticate(revalidate=True)
|
|
557
|
-
request_header = self.request_header()
|
|
558
|
-
retries += 1
|
|
559
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
560
|
-
logger.warning(
|
|
561
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
562
|
-
response.status_code,
|
|
563
|
-
(retries + 1) * 60,
|
|
564
|
-
)
|
|
565
|
-
time.sleep((retries + 1) * 60)
|
|
566
|
-
retries += 1
|
|
567
|
-
else:
|
|
568
|
-
logger.error(
|
|
569
|
-
"Failed to get list of users; status -> %s; error -> %s",
|
|
570
|
-
response.status_code,
|
|
571
|
-
response.text,
|
|
572
|
-
)
|
|
573
|
-
return None
|
|
728
|
+
return self.do_request(
|
|
729
|
+
url=request_url,
|
|
730
|
+
method="GET",
|
|
731
|
+
headers=request_header,
|
|
732
|
+
timeout=REQUEST_TIMEOUT,
|
|
733
|
+
failure_message="Failed to get list of M365 users!",
|
|
734
|
+
)
|
|
574
735
|
|
|
575
736
|
# end method definition
|
|
576
737
|
|
|
@@ -631,38 +792,14 @@ class M365(object):
|
|
|
631
792
|
|
|
632
793
|
logger.debug("Get M365 user -> %s; calling -> %s", user_email, request_url)
|
|
633
794
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
elif response.status_code == 401 and retries == 0:
|
|
643
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
644
|
-
self.authenticate(revalidate=True)
|
|
645
|
-
request_header = self.request_header()
|
|
646
|
-
retries += 1
|
|
647
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
648
|
-
logger.warning(
|
|
649
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
650
|
-
response.status_code,
|
|
651
|
-
(retries + 1) * 60,
|
|
652
|
-
)
|
|
653
|
-
time.sleep((retries + 1) * 60)
|
|
654
|
-
retries += 1
|
|
655
|
-
else:
|
|
656
|
-
if show_error:
|
|
657
|
-
logger.error(
|
|
658
|
-
"Failed to get M365 user -> %s; status -> %s; error -> %s",
|
|
659
|
-
user_email,
|
|
660
|
-
response.status_code,
|
|
661
|
-
response.text,
|
|
662
|
-
)
|
|
663
|
-
else:
|
|
664
|
-
logger.debug("M365 User -> %s not found.", user_email)
|
|
665
|
-
return None
|
|
795
|
+
return self.do_request(
|
|
796
|
+
url=request_url,
|
|
797
|
+
method="GET",
|
|
798
|
+
headers=request_header,
|
|
799
|
+
timeout=REQUEST_TIMEOUT,
|
|
800
|
+
failure_message="Failed to get M365 user -> '{}'".format(user_email),
|
|
801
|
+
show_error=show_error,
|
|
802
|
+
)
|
|
666
803
|
|
|
667
804
|
# end method definition
|
|
668
805
|
|
|
@@ -714,38 +851,14 @@ class M365(object):
|
|
|
714
851
|
|
|
715
852
|
logger.debug("Adding M365 user -> %s; calling -> %s", email, request_url)
|
|
716
853
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
if response.ok:
|
|
726
|
-
return self.parse_request_response(response)
|
|
727
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
728
|
-
elif response.status_code == 401 and retries == 0:
|
|
729
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
730
|
-
self.authenticate(revalidate=True)
|
|
731
|
-
request_header = self.request_header()
|
|
732
|
-
retries += 1
|
|
733
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
734
|
-
logger.warning(
|
|
735
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
736
|
-
response.status_code,
|
|
737
|
-
(retries + 1) * 60,
|
|
738
|
-
)
|
|
739
|
-
time.sleep((retries + 1) * 60)
|
|
740
|
-
retries += 1
|
|
741
|
-
else:
|
|
742
|
-
logger.error(
|
|
743
|
-
"Failed to add M365 user -> %s; status -> %s; error -> %s",
|
|
744
|
-
email,
|
|
745
|
-
response.status_code,
|
|
746
|
-
response.text,
|
|
747
|
-
)
|
|
748
|
-
return None
|
|
854
|
+
return self.do_request(
|
|
855
|
+
url=request_url,
|
|
856
|
+
method="POST",
|
|
857
|
+
headers=request_header,
|
|
858
|
+
data=json.dumps(user_post_body),
|
|
859
|
+
timeout=REQUEST_TIMEOUT,
|
|
860
|
+
failure_message="Failed to add M365 user -> '{}'".format(email),
|
|
861
|
+
)
|
|
749
862
|
|
|
750
863
|
# end method definition
|
|
751
864
|
|
|
@@ -770,39 +883,16 @@ class M365(object):
|
|
|
770
883
|
request_url,
|
|
771
884
|
)
|
|
772
885
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
784
|
-
elif response.status_code == 401 and retries == 0:
|
|
785
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
786
|
-
self.authenticate(revalidate=True)
|
|
787
|
-
request_header = self.request_header()
|
|
788
|
-
retries += 1
|
|
789
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
790
|
-
logger.warning(
|
|
791
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
792
|
-
response.status_code,
|
|
793
|
-
(retries + 1) * 60,
|
|
794
|
-
)
|
|
795
|
-
time.sleep((retries + 1) * 60)
|
|
796
|
-
retries += 1
|
|
797
|
-
else:
|
|
798
|
-
logger.error(
|
|
799
|
-
"Failed to update M365 user -> %s with -> %s; status -> %s; error -> %s",
|
|
800
|
-
user_id,
|
|
801
|
-
str(updated_settings),
|
|
802
|
-
response.status_code,
|
|
803
|
-
response.text,
|
|
804
|
-
)
|
|
805
|
-
return None
|
|
886
|
+
return self.do_request(
|
|
887
|
+
url=request_url,
|
|
888
|
+
method="PATCH",
|
|
889
|
+
headers=request_header,
|
|
890
|
+
json_data=updated_settings,
|
|
891
|
+
timeout=REQUEST_TIMEOUT,
|
|
892
|
+
failure_message="Failed to update M365 user -> '{}' with -> {}".format(
|
|
893
|
+
user_id, updated_settings
|
|
894
|
+
),
|
|
895
|
+
)
|
|
806
896
|
|
|
807
897
|
# end method definition
|
|
808
898
|
|
|
@@ -831,35 +921,13 @@ class M365(object):
|
|
|
831
921
|
request_url = self.config()["usersUrl"] + "/" + user_id + "/licenseDetails"
|
|
832
922
|
request_header = self.request_header()
|
|
833
923
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
842
|
-
elif response.status_code == 401 and retries == 0:
|
|
843
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
844
|
-
self.authenticate(revalidate=True)
|
|
845
|
-
request_header = self.request_header()
|
|
846
|
-
retries += 1
|
|
847
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
848
|
-
logger.warning(
|
|
849
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
850
|
-
response.status_code,
|
|
851
|
-
(retries + 1) * 60,
|
|
852
|
-
)
|
|
853
|
-
time.sleep((retries + 1) * 60)
|
|
854
|
-
retries += 1
|
|
855
|
-
else:
|
|
856
|
-
logger.error(
|
|
857
|
-
"Failed to get M365 licenses of user -> %s; status -> %s; error -> %s",
|
|
858
|
-
user_id,
|
|
859
|
-
response.status_code,
|
|
860
|
-
response.text,
|
|
861
|
-
)
|
|
862
|
-
return None
|
|
924
|
+
return self.do_request(
|
|
925
|
+
url=request_url,
|
|
926
|
+
method="GET",
|
|
927
|
+
headers=request_header,
|
|
928
|
+
timeout=REQUEST_TIMEOUT,
|
|
929
|
+
failure_message="Failed to get M365 licenses of user -> {}".format(user_id),
|
|
930
|
+
)
|
|
863
931
|
|
|
864
932
|
# end method definition
|
|
865
933
|
|
|
@@ -897,39 +965,16 @@ class M365(object):
|
|
|
897
965
|
request_url,
|
|
898
966
|
)
|
|
899
967
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
911
|
-
elif response.status_code == 401 and retries == 0:
|
|
912
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
913
|
-
self.authenticate(revalidate=True)
|
|
914
|
-
request_header = self.request_header()
|
|
915
|
-
retries += 1
|
|
916
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
917
|
-
logger.warning(
|
|
918
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
919
|
-
response.status_code,
|
|
920
|
-
(retries + 1) * 60,
|
|
921
|
-
)
|
|
922
|
-
time.sleep((retries + 1) * 60)
|
|
923
|
-
retries += 1
|
|
924
|
-
else:
|
|
925
|
-
logger.error(
|
|
926
|
-
"Failed to add M365 license -> %s to M365 user -> %s; status -> %s; error -> %s",
|
|
927
|
-
sku_id,
|
|
928
|
-
user_id,
|
|
929
|
-
response.status_code,
|
|
930
|
-
response.text,
|
|
931
|
-
)
|
|
932
|
-
return None
|
|
968
|
+
return self.do_request(
|
|
969
|
+
url=request_url,
|
|
970
|
+
method="POST",
|
|
971
|
+
headers=request_header,
|
|
972
|
+
json_data=license_post_body,
|
|
973
|
+
timeout=REQUEST_TIMEOUT,
|
|
974
|
+
failure_message="Failed to add M365 license -> {} to M365 user -> {}".format(
|
|
975
|
+
sku_id, user_id
|
|
976
|
+
),
|
|
977
|
+
)
|
|
933
978
|
|
|
934
979
|
# end method definition
|
|
935
980
|
|
|
@@ -950,42 +995,27 @@ class M365(object):
|
|
|
950
995
|
|
|
951
996
|
logger.debug("Get photo of user -> %s; calling -> %s", user_id, request_url)
|
|
952
997
|
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
(retries + 1) * 60,
|
|
971
|
-
)
|
|
972
|
-
time.sleep((retries + 1) * 60)
|
|
973
|
-
retries += 1
|
|
974
|
-
else:
|
|
975
|
-
if show_error:
|
|
976
|
-
logger.error(
|
|
977
|
-
"Failed to get photo of user -> %s; status -> %s; error -> %s",
|
|
978
|
-
user_id,
|
|
979
|
-
response.status_code,
|
|
980
|
-
response.text,
|
|
981
|
-
)
|
|
982
|
-
else:
|
|
983
|
-
logger.debug("M365 User -> %s does not yet have a photo.", user_id)
|
|
984
|
-
return None
|
|
998
|
+
response = self.do_request(
|
|
999
|
+
url=request_url,
|
|
1000
|
+
method="GET",
|
|
1001
|
+
headers=request_header,
|
|
1002
|
+
timeout=REQUEST_TIMEOUT,
|
|
1003
|
+
failure_message="Failed to get photo of M365 user -> {}".format(user_id),
|
|
1004
|
+
warning_message="M365 User -> {} does not yet have a photo.".format(
|
|
1005
|
+
user_id
|
|
1006
|
+
),
|
|
1007
|
+
show_error=show_error,
|
|
1008
|
+
parse_request_response=False, # the response is NOT JSON!
|
|
1009
|
+
)
|
|
1010
|
+
|
|
1011
|
+
if response and response.ok and response.content:
|
|
1012
|
+
return response.content # this is the actual image - not json!
|
|
1013
|
+
|
|
1014
|
+
return None
|
|
985
1015
|
|
|
986
1016
|
# end method definition
|
|
987
1017
|
|
|
988
|
-
def download_user_photo(self, user_id: str, photo_path: str) -> str:
|
|
1018
|
+
def download_user_photo(self, user_id: str, photo_path: str) -> str | None:
|
|
989
1019
|
"""Download the M365 user photo and save it to the local file system
|
|
990
1020
|
|
|
991
1021
|
Args:
|
|
@@ -1005,64 +1035,48 @@ class M365(object):
|
|
|
1005
1035
|
request_url,
|
|
1006
1036
|
)
|
|
1007
1037
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1038
|
+
response = self.do_request(
|
|
1039
|
+
url=request_url,
|
|
1040
|
+
method="GET",
|
|
1041
|
+
headers=request_header,
|
|
1042
|
+
timeout=REQUEST_TIMEOUT,
|
|
1043
|
+
failure_message="Failed to download photo for user with ID -> {}".format(
|
|
1044
|
+
user_id
|
|
1045
|
+
),
|
|
1046
|
+
stream=True,
|
|
1047
|
+
parse_request_response=False,
|
|
1048
|
+
)
|
|
1049
|
+
|
|
1050
|
+
if response and response.ok:
|
|
1051
|
+
content_type = response.headers.get("Content-Type", "image/png")
|
|
1052
|
+
if content_type == "image/jpeg":
|
|
1053
|
+
file_extension = "jpg"
|
|
1054
|
+
elif content_type == "image/png":
|
|
1055
|
+
file_extension = "png"
|
|
1056
|
+
else:
|
|
1057
|
+
file_extension = "img" # Default extension if type is unknown
|
|
1058
|
+
file_path = os.path.join(
|
|
1059
|
+
photo_path, "{}.{}".format(user_id, file_extension)
|
|
1015
1060
|
)
|
|
1016
|
-
if response.ok:
|
|
1017
|
-
content_type = response.headers.get("Content-Type", "image/png")
|
|
1018
|
-
if content_type == "image/jpeg":
|
|
1019
|
-
file_extension = "jpg"
|
|
1020
|
-
elif content_type == "image/png":
|
|
1021
|
-
file_extension = "png"
|
|
1022
|
-
else:
|
|
1023
|
-
file_extension = "img" # Default extension if type is unknown
|
|
1024
|
-
file_path = os.path.join(
|
|
1025
|
-
photo_path, "{}.{}".format(user_id, file_extension)
|
|
1026
|
-
)
|
|
1027
1061
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
)
|
|
1037
|
-
return file_path
|
|
1038
|
-
except OSError as exception:
|
|
1039
|
-
logger.error(
|
|
1040
|
-
"Error saving photo for user with ID -> %s; error -> %s",
|
|
1041
|
-
user_id,
|
|
1042
|
-
exception,
|
|
1043
|
-
)
|
|
1044
|
-
return None
|
|
1045
|
-
elif response.status_code == 401 and retries == 0:
|
|
1046
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
1047
|
-
self.authenticate(revalidate=True)
|
|
1048
|
-
request_header = self.request_header("application/json")
|
|
1049
|
-
retries += 1
|
|
1050
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
1051
|
-
logger.warning(
|
|
1052
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
1053
|
-
response.status_code,
|
|
1054
|
-
(retries + 1) * 60,
|
|
1062
|
+
try:
|
|
1063
|
+
with open(file_path, "wb") as file:
|
|
1064
|
+
for chunk in response.iter_content(chunk_size=8192):
|
|
1065
|
+
file.write(chunk)
|
|
1066
|
+
logger.info(
|
|
1067
|
+
"Photo for M365 user with ID -> %s saved to -> '%s'",
|
|
1068
|
+
user_id,
|
|
1069
|
+
file_path,
|
|
1055
1070
|
)
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
else:
|
|
1071
|
+
return file_path
|
|
1072
|
+
except OSError as exception:
|
|
1059
1073
|
logger.error(
|
|
1060
|
-
"
|
|
1074
|
+
"Error saving photo for user with ID -> %s; error -> %s",
|
|
1061
1075
|
user_id,
|
|
1062
|
-
|
|
1063
|
-
response.text,
|
|
1076
|
+
exception,
|
|
1064
1077
|
)
|
|
1065
|
-
|
|
1078
|
+
|
|
1079
|
+
return None
|
|
1066
1080
|
|
|
1067
1081
|
# end method definition
|
|
1068
1082
|
|
|
@@ -1105,36 +1119,16 @@ class M365(object):
|
|
|
1105
1119
|
request_url,
|
|
1106
1120
|
)
|
|
1107
1121
|
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
self.authenticate(revalidate=True)
|
|
1119
|
-
request_header = self.request_header()
|
|
1120
|
-
retries += 1
|
|
1121
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
1122
|
-
logger.warning(
|
|
1123
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
1124
|
-
response.status_code,
|
|
1125
|
-
(retries + 1) * 60,
|
|
1126
|
-
)
|
|
1127
|
-
time.sleep((retries + 1) * 60)
|
|
1128
|
-
retries += 1
|
|
1129
|
-
else:
|
|
1130
|
-
logger.error(
|
|
1131
|
-
"Failed to update user with ID -> %s with photo -> %s; status -> %s; error -> %s",
|
|
1132
|
-
user_id,
|
|
1133
|
-
photo_path,
|
|
1134
|
-
response.status_code,
|
|
1135
|
-
response.text,
|
|
1136
|
-
)
|
|
1137
|
-
return None
|
|
1122
|
+
return self.do_request(
|
|
1123
|
+
url=request_url,
|
|
1124
|
+
method="PUT",
|
|
1125
|
+
headers=request_header,
|
|
1126
|
+
data=data,
|
|
1127
|
+
timeout=REQUEST_TIMEOUT,
|
|
1128
|
+
failure_message="Failed to update M365 user with ID -> {} with photo -> '{}'".format(
|
|
1129
|
+
user_id, photo_path
|
|
1130
|
+
),
|
|
1131
|
+
)
|
|
1138
1132
|
|
|
1139
1133
|
# end method definition
|
|
1140
1134
|
|
|
@@ -1152,37 +1146,14 @@ class M365(object):
|
|
|
1152
1146
|
|
|
1153
1147
|
logger.debug("Get list of all M365 groups; calling -> %s", request_url)
|
|
1154
1148
|
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
if response.ok:
|
|
1164
|
-
return self.parse_request_response(response)
|
|
1165
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
1166
|
-
elif response.status_code == 401 and retries == 0:
|
|
1167
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
1168
|
-
self.authenticate(revalidate=True)
|
|
1169
|
-
request_header = self.request_header()
|
|
1170
|
-
retries += 1
|
|
1171
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
1172
|
-
logger.warning(
|
|
1173
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
1174
|
-
response.status_code,
|
|
1175
|
-
(retries + 1) * 60,
|
|
1176
|
-
)
|
|
1177
|
-
time.sleep((retries + 1) * 60)
|
|
1178
|
-
retries += 1
|
|
1179
|
-
else:
|
|
1180
|
-
logger.error(
|
|
1181
|
-
"Failed to get list of M365 groups; status -> %s; error -> %s",
|
|
1182
|
-
response.status_code,
|
|
1183
|
-
response.text,
|
|
1184
|
-
)
|
|
1185
|
-
return None
|
|
1149
|
+
return self.do_request(
|
|
1150
|
+
url=request_url,
|
|
1151
|
+
method="GET",
|
|
1152
|
+
headers=request_header,
|
|
1153
|
+
params={"$top": str(max_number)},
|
|
1154
|
+
timeout=REQUEST_TIMEOUT,
|
|
1155
|
+
failure_message="Failed to get list of M365 groups",
|
|
1156
|
+
)
|
|
1186
1157
|
|
|
1187
1158
|
# end method definition
|
|
1188
1159
|
|
|
@@ -1247,40 +1218,16 @@ class M365(object):
|
|
|
1247
1218
|
request_url = self.config()["groupsUrl"] + "?" + encoded_query
|
|
1248
1219
|
request_header = self.request_header()
|
|
1249
1220
|
|
|
1250
|
-
logger.debug("Get M365 group -> %s; calling -> %s", group_name, request_url)
|
|
1221
|
+
logger.debug("Get M365 group -> '%s'; calling -> %s", group_name, request_url)
|
|
1251
1222
|
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
elif response.status_code == 401 and retries == 0:
|
|
1261
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
1262
|
-
self.authenticate(revalidate=True)
|
|
1263
|
-
request_header = self.request_header()
|
|
1264
|
-
retries += 1
|
|
1265
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
1266
|
-
logger.warning(
|
|
1267
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
1268
|
-
response.status_code,
|
|
1269
|
-
(retries + 1) * 60,
|
|
1270
|
-
)
|
|
1271
|
-
time.sleep((retries + 1) * 60)
|
|
1272
|
-
retries += 1
|
|
1273
|
-
else:
|
|
1274
|
-
if show_error:
|
|
1275
|
-
logger.error(
|
|
1276
|
-
"Failed to get M365 group -> %s; status -> %s; error -> %s",
|
|
1277
|
-
group_name,
|
|
1278
|
-
response.status_code,
|
|
1279
|
-
response.text,
|
|
1280
|
-
)
|
|
1281
|
-
else:
|
|
1282
|
-
logger.debug("M365 Group -> %s not found.", group_name)
|
|
1283
|
-
return None
|
|
1223
|
+
return self.do_request(
|
|
1224
|
+
url=request_url,
|
|
1225
|
+
method="GET",
|
|
1226
|
+
headers=request_header,
|
|
1227
|
+
timeout=REQUEST_TIMEOUT,
|
|
1228
|
+
failure_message="Failed to get M365 group -> '{}'".format(group_name),
|
|
1229
|
+
show_error=show_error,
|
|
1230
|
+
)
|
|
1284
1231
|
|
|
1285
1232
|
# end method definition
|
|
1286
1233
|
|
|
@@ -1345,41 +1292,17 @@ class M365(object):
|
|
|
1345
1292
|
request_url = self.config()["groupsUrl"]
|
|
1346
1293
|
request_header = self.request_header()
|
|
1347
1294
|
|
|
1348
|
-
logger.debug("Adding M365 group -> %s; calling -> %s", name, request_url)
|
|
1349
|
-
logger.debug("M365 group attributes -> %s", group_post_body)
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
if response.ok:
|
|
1360
|
-
return self.parse_request_response(response)
|
|
1361
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
1362
|
-
elif response.status_code == 401 and retries == 0:
|
|
1363
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
1364
|
-
self.authenticate(revalidate=True)
|
|
1365
|
-
request_header = self.request_header()
|
|
1366
|
-
retries += 1
|
|
1367
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
1368
|
-
logger.warning(
|
|
1369
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
1370
|
-
response.status_code,
|
|
1371
|
-
(retries + 1) * 60,
|
|
1372
|
-
)
|
|
1373
|
-
time.sleep((retries + 1) * 60)
|
|
1374
|
-
retries += 1
|
|
1375
|
-
else:
|
|
1376
|
-
logger.error(
|
|
1377
|
-
"Failed to add M365 group -> %s; status -> %s; error -> %s",
|
|
1378
|
-
name,
|
|
1379
|
-
response.status_code,
|
|
1380
|
-
response.text,
|
|
1381
|
-
)
|
|
1382
|
-
return None
|
|
1295
|
+
logger.debug("Adding M365 group -> '%s'; calling -> %s", name, request_url)
|
|
1296
|
+
logger.debug("M365 group attributes -> %s", str(group_post_body))
|
|
1297
|
+
|
|
1298
|
+
return self.do_request(
|
|
1299
|
+
url=request_url,
|
|
1300
|
+
method="POST",
|
|
1301
|
+
headers=request_header,
|
|
1302
|
+
data=json.dumps(group_post_body),
|
|
1303
|
+
timeout=REQUEST_TIMEOUT,
|
|
1304
|
+
failure_message="Failed to add M365 group -> '{}'".format(name),
|
|
1305
|
+
)
|
|
1383
1306
|
|
|
1384
1307
|
# end method definition
|
|
1385
1308
|
|
|
@@ -1416,36 +1339,15 @@ class M365(object):
|
|
|
1416
1339
|
request_url,
|
|
1417
1340
|
)
|
|
1418
1341
|
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
1429
|
-
self.authenticate(revalidate=True)
|
|
1430
|
-
request_header = self.request_header()
|
|
1431
|
-
retries += 1
|
|
1432
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
1433
|
-
logger.warning(
|
|
1434
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
1435
|
-
response.status_code,
|
|
1436
|
-
(retries + 1) * 60,
|
|
1437
|
-
)
|
|
1438
|
-
time.sleep((retries + 1) * 60)
|
|
1439
|
-
retries += 1
|
|
1440
|
-
else:
|
|
1441
|
-
logger.error(
|
|
1442
|
-
"Failed to get members of M365 group -> %s (%s); status -> %s; error -> %s",
|
|
1443
|
-
group_name,
|
|
1444
|
-
group_id,
|
|
1445
|
-
response.status_code,
|
|
1446
|
-
response.text,
|
|
1447
|
-
)
|
|
1448
|
-
return None
|
|
1342
|
+
return self.do_request(
|
|
1343
|
+
url=request_url,
|
|
1344
|
+
method="GET",
|
|
1345
|
+
headers=request_header,
|
|
1346
|
+
timeout=REQUEST_TIMEOUT,
|
|
1347
|
+
failure_message="Failed to get members of M365 group -> '{}' ({})".format(
|
|
1348
|
+
group_name, group_id
|
|
1349
|
+
),
|
|
1350
|
+
)
|
|
1449
1351
|
|
|
1450
1352
|
# end method definition
|
|
1451
1353
|
|
|
@@ -1473,40 +1375,16 @@ class M365(object):
|
|
|
1473
1375
|
request_url,
|
|
1474
1376
|
)
|
|
1475
1377
|
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
1488
|
-
if response.status_code == 401 and retries == 0:
|
|
1489
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
1490
|
-
self.authenticate(revalidate=True)
|
|
1491
|
-
request_header = self.request_header()
|
|
1492
|
-
retries += 1
|
|
1493
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
1494
|
-
logger.warning(
|
|
1495
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
1496
|
-
response.status_code,
|
|
1497
|
-
(retries + 1) * 60,
|
|
1498
|
-
)
|
|
1499
|
-
time.sleep((retries + 1) * 60)
|
|
1500
|
-
retries += 1
|
|
1501
|
-
else:
|
|
1502
|
-
logger.error(
|
|
1503
|
-
"Failed to add member -> %s to M365 group -> %s; status -> %s; error -> %s",
|
|
1504
|
-
member_id,
|
|
1505
|
-
group_id,
|
|
1506
|
-
response.status_code,
|
|
1507
|
-
response.text,
|
|
1508
|
-
)
|
|
1509
|
-
return None
|
|
1378
|
+
return self.do_request(
|
|
1379
|
+
url=request_url,
|
|
1380
|
+
method="POST",
|
|
1381
|
+
headers=request_header,
|
|
1382
|
+
data=json.dumps(group_member_post_body),
|
|
1383
|
+
timeout=REQUEST_TIMEOUT,
|
|
1384
|
+
failure_message="Failed to add member -> {} to M365 group -> {}".format(
|
|
1385
|
+
member_id, group_id
|
|
1386
|
+
),
|
|
1387
|
+
)
|
|
1510
1388
|
|
|
1511
1389
|
# end method definition
|
|
1512
1390
|
|
|
@@ -1536,42 +1414,21 @@ class M365(object):
|
|
|
1536
1414
|
request_url,
|
|
1537
1415
|
)
|
|
1538
1416
|
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
if
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
retries += 1
|
|
1555
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
1556
|
-
logger.warning(
|
|
1557
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
1558
|
-
response.status_code,
|
|
1559
|
-
(retries + 1) * 60,
|
|
1560
|
-
)
|
|
1561
|
-
time.sleep((retries + 1) * 60)
|
|
1562
|
-
retries += 1
|
|
1563
|
-
else:
|
|
1564
|
-
# MS Graph API returns an error if the member is not in the
|
|
1565
|
-
# group. This is typically not what we want. We just return False.
|
|
1566
|
-
if show_error:
|
|
1567
|
-
logger.error(
|
|
1568
|
-
"Failed to check if user -> %s is in group -> %s; status -> %s; error -> %s",
|
|
1569
|
-
member_id,
|
|
1570
|
-
group_id,
|
|
1571
|
-
response.status_code,
|
|
1572
|
-
response.text,
|
|
1573
|
-
)
|
|
1574
|
-
return False
|
|
1417
|
+
response = self.do_request(
|
|
1418
|
+
url=request_url,
|
|
1419
|
+
method="GET",
|
|
1420
|
+
headers=request_header,
|
|
1421
|
+
timeout=REQUEST_TIMEOUT,
|
|
1422
|
+
failure_message="Failed to check if user -> {} is in group -> {}".format(
|
|
1423
|
+
member_id, group_id
|
|
1424
|
+
),
|
|
1425
|
+
show_error=show_error,
|
|
1426
|
+
)
|
|
1427
|
+
|
|
1428
|
+
if not response or not "value" in response or len(response["value"]) == 0:
|
|
1429
|
+
return False
|
|
1430
|
+
|
|
1431
|
+
return True
|
|
1575
1432
|
|
|
1576
1433
|
# end method definition
|
|
1577
1434
|
|
|
@@ -1608,36 +1465,15 @@ class M365(object):
|
|
|
1608
1465
|
request_url,
|
|
1609
1466
|
)
|
|
1610
1467
|
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
1621
|
-
self.authenticate(revalidate=True)
|
|
1622
|
-
request_header = self.request_header()
|
|
1623
|
-
retries += 1
|
|
1624
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
1625
|
-
logger.warning(
|
|
1626
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
1627
|
-
response.status_code,
|
|
1628
|
-
(retries + 1) * 60,
|
|
1629
|
-
)
|
|
1630
|
-
time.sleep((retries + 1) * 60)
|
|
1631
|
-
retries += 1
|
|
1632
|
-
else:
|
|
1633
|
-
logger.error(
|
|
1634
|
-
"Failed to get owners of M365 group -> %s (%s); status -> %s; error -> %s",
|
|
1635
|
-
group_name,
|
|
1636
|
-
group_id,
|
|
1637
|
-
response.status_code,
|
|
1638
|
-
response.text,
|
|
1639
|
-
)
|
|
1640
|
-
return None
|
|
1468
|
+
return self.do_request(
|
|
1469
|
+
url=request_url,
|
|
1470
|
+
method="GET",
|
|
1471
|
+
headers=request_header,
|
|
1472
|
+
timeout=REQUEST_TIMEOUT,
|
|
1473
|
+
failure_message="Failed to get owners of M365 group -> '{}' ({})".format(
|
|
1474
|
+
group_name, group_id
|
|
1475
|
+
),
|
|
1476
|
+
)
|
|
1641
1477
|
|
|
1642
1478
|
# end method definition
|
|
1643
1479
|
|
|
@@ -1665,39 +1501,16 @@ class M365(object):
|
|
|
1665
1501
|
request_url,
|
|
1666
1502
|
)
|
|
1667
1503
|
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
1679
|
-
elif response.status_code == 401 and retries == 0:
|
|
1680
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
1681
|
-
self.authenticate(revalidate=True)
|
|
1682
|
-
request_header = self.request_header()
|
|
1683
|
-
retries += 1
|
|
1684
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
1685
|
-
logger.warning(
|
|
1686
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
1687
|
-
response.status_code,
|
|
1688
|
-
(retries + 1) * 60,
|
|
1689
|
-
)
|
|
1690
|
-
time.sleep((retries + 1) * 60)
|
|
1691
|
-
retries += 1
|
|
1692
|
-
else:
|
|
1693
|
-
logger.error(
|
|
1694
|
-
"Failed to add owner -> %s to M365 group -> %s; status -> %s; error -> %s",
|
|
1695
|
-
owner_id,
|
|
1696
|
-
group_id,
|
|
1697
|
-
response.status_code,
|
|
1698
|
-
response.text,
|
|
1699
|
-
)
|
|
1700
|
-
return None
|
|
1504
|
+
return self.do_request(
|
|
1505
|
+
url=request_url,
|
|
1506
|
+
method="POST",
|
|
1507
|
+
headers=request_header,
|
|
1508
|
+
data=json.dumps(group_member_post_body),
|
|
1509
|
+
timeout=REQUEST_TIMEOUT,
|
|
1510
|
+
failure_message="Failed to add owner -> {} to M365 group -> {}".format(
|
|
1511
|
+
owner_id, group_id
|
|
1512
|
+
),
|
|
1513
|
+
)
|
|
1701
1514
|
|
|
1702
1515
|
# end method definition
|
|
1703
1516
|
|
|
@@ -1751,35 +1564,13 @@ class M365(object):
|
|
|
1751
1564
|
|
|
1752
1565
|
logger.debug("Purging deleted item -> %s; calling -> %s", item_id, request_url)
|
|
1753
1566
|
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
1762
|
-
elif response.status_code == 401 and retries == 0:
|
|
1763
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
1764
|
-
self.authenticate(revalidate=True)
|
|
1765
|
-
request_header = self.request_header()
|
|
1766
|
-
retries += 1
|
|
1767
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
1768
|
-
logger.warning(
|
|
1769
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
1770
|
-
response.status_code,
|
|
1771
|
-
(retries + 1) * 60,
|
|
1772
|
-
)
|
|
1773
|
-
time.sleep((retries + 1) * 60)
|
|
1774
|
-
retries += 1
|
|
1775
|
-
else:
|
|
1776
|
-
logger.error(
|
|
1777
|
-
"Failed to purge deleted item -> %s; status -> %s; error -> %s",
|
|
1778
|
-
item_id,
|
|
1779
|
-
response.status_code,
|
|
1780
|
-
response.text,
|
|
1781
|
-
)
|
|
1782
|
-
return None
|
|
1567
|
+
return self.do_request(
|
|
1568
|
+
url=request_url,
|
|
1569
|
+
method="DELETE",
|
|
1570
|
+
headers=request_header,
|
|
1571
|
+
timeout=REQUEST_TIMEOUT,
|
|
1572
|
+
failure_message="Failed to purge deleted item -> {}".format(item_id),
|
|
1573
|
+
)
|
|
1783
1574
|
|
|
1784
1575
|
# end method definition
|
|
1785
1576
|
|
|
@@ -1810,39 +1601,27 @@ class M365(object):
|
|
|
1810
1601
|
request_url,
|
|
1811
1602
|
)
|
|
1812
1603
|
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1604
|
+
response = self.do_request(
|
|
1605
|
+
url=request_url,
|
|
1606
|
+
method="GET",
|
|
1607
|
+
headers=request_header,
|
|
1608
|
+
timeout=REQUEST_TIMEOUT,
|
|
1609
|
+
failure_message="Failed to check if M365 Group -> '{}' has a M365 Team connected".format(
|
|
1610
|
+
group_name
|
|
1611
|
+
),
|
|
1612
|
+
parse_request_response=False,
|
|
1613
|
+
)
|
|
1818
1614
|
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
retries += 1
|
|
1830
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
1831
|
-
logger.warning(
|
|
1832
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
1833
|
-
response.status_code,
|
|
1834
|
-
(retries + 1) * 60,
|
|
1835
|
-
)
|
|
1836
|
-
time.sleep((retries + 1) * 60)
|
|
1837
|
-
retries += 1
|
|
1838
|
-
else:
|
|
1839
|
-
logger.error(
|
|
1840
|
-
"Failed to check if M365 Group -> %s has a M365 Team connected; status -> %s; error -> %s",
|
|
1841
|
-
group_name,
|
|
1842
|
-
response.status_code,
|
|
1843
|
-
response.text,
|
|
1844
|
-
)
|
|
1845
|
-
return False
|
|
1615
|
+
if response and response.status_code == 200: # Group has a Team assigned!
|
|
1616
|
+
logger.debug("Group -> %s has a M365 Team connected.", group_name)
|
|
1617
|
+
return True
|
|
1618
|
+
elif (
|
|
1619
|
+
not response or response.status_code == 404
|
|
1620
|
+
): # Group does not have a Team assigned!
|
|
1621
|
+
logger.debug("Group -> %s has no M365 Team connected.", group_name)
|
|
1622
|
+
return False
|
|
1623
|
+
|
|
1624
|
+
return False
|
|
1846
1625
|
|
|
1847
1626
|
# end method definition
|
|
1848
1627
|
|
|
@@ -1902,35 +1681,13 @@ class M365(object):
|
|
|
1902
1681
|
request_url,
|
|
1903
1682
|
)
|
|
1904
1683
|
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
1913
|
-
elif response.status_code == 401 and retries == 0:
|
|
1914
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
1915
|
-
self.authenticate(revalidate=True)
|
|
1916
|
-
request_header = self.request_header()
|
|
1917
|
-
retries += 1
|
|
1918
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
1919
|
-
logger.warning(
|
|
1920
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
1921
|
-
response.status_code,
|
|
1922
|
-
(retries + 1) * 60,
|
|
1923
|
-
)
|
|
1924
|
-
time.sleep((retries + 1) * 60)
|
|
1925
|
-
retries += 1
|
|
1926
|
-
else:
|
|
1927
|
-
logger.error(
|
|
1928
|
-
"Failed to get M365 Team -> %s; status -> %s; error -> %s",
|
|
1929
|
-
name,
|
|
1930
|
-
response.status_code,
|
|
1931
|
-
response.text,
|
|
1932
|
-
)
|
|
1933
|
-
return None
|
|
1684
|
+
return self.do_request(
|
|
1685
|
+
url=request_url,
|
|
1686
|
+
method="GET",
|
|
1687
|
+
headers=request_header,
|
|
1688
|
+
timeout=REQUEST_TIMEOUT,
|
|
1689
|
+
failure_message="Failed to get M365 Team -> '{}'".format(name),
|
|
1690
|
+
)
|
|
1934
1691
|
|
|
1935
1692
|
# end method definition
|
|
1936
1693
|
|
|
@@ -1972,41 +1729,17 @@ class M365(object):
|
|
|
1972
1729
|
request_url = self.config()["teamsUrl"]
|
|
1973
1730
|
request_header = self.request_header()
|
|
1974
1731
|
|
|
1975
|
-
logger.debug("Adding M365 Team -> %s; calling -> %s", name, request_url)
|
|
1976
|
-
logger.debug("M365 Team attributes -> %s", team_post_body)
|
|
1732
|
+
logger.debug("Adding M365 Team -> '%s'; calling -> %s", name, request_url)
|
|
1733
|
+
logger.debug("M365 Team attributes -> %s", str(team_post_body))
|
|
1977
1734
|
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
if response.ok:
|
|
1987
|
-
return self.parse_request_response(response)
|
|
1988
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
1989
|
-
elif response.status_code == 401 and retries == 0:
|
|
1990
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
1991
|
-
self.authenticate(revalidate=True)
|
|
1992
|
-
request_header = self.request_header()
|
|
1993
|
-
retries += 1
|
|
1994
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
1995
|
-
logger.warning(
|
|
1996
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
1997
|
-
response.status_code,
|
|
1998
|
-
(retries + 1) * 60,
|
|
1999
|
-
)
|
|
2000
|
-
time.sleep((retries + 1) * 60)
|
|
2001
|
-
retries += 1
|
|
2002
|
-
else:
|
|
2003
|
-
logger.error(
|
|
2004
|
-
"Failed to add M365 Team -> '%s'; status -> %s; error -> %s",
|
|
2005
|
-
name,
|
|
2006
|
-
response.status_code,
|
|
2007
|
-
response.text,
|
|
2008
|
-
)
|
|
2009
|
-
return None
|
|
1735
|
+
return self.do_request(
|
|
1736
|
+
url=request_url,
|
|
1737
|
+
method="POST",
|
|
1738
|
+
data=json.dumps(team_post_body),
|
|
1739
|
+
headers=request_header,
|
|
1740
|
+
timeout=REQUEST_TIMEOUT,
|
|
1741
|
+
failure_message="Failed to add M365 Team -> '{}'".format(name),
|
|
1742
|
+
)
|
|
2010
1743
|
|
|
2011
1744
|
# end method definition
|
|
2012
1745
|
|
|
@@ -2029,30 +1762,13 @@ class M365(object):
|
|
|
2029
1762
|
request_url,
|
|
2030
1763
|
)
|
|
2031
1764
|
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
2040
|
-
elif response.status_code == 401 and retries == 0:
|
|
2041
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
2042
|
-
self.authenticate(revalidate=True)
|
|
2043
|
-
request_header = self.request_header()
|
|
2044
|
-
retries += 1
|
|
2045
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
2046
|
-
logger.warning(
|
|
2047
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
2048
|
-
response.status_code,
|
|
2049
|
-
(retries + 1) * 60,
|
|
2050
|
-
)
|
|
2051
|
-
time.sleep((retries + 1) * 60)
|
|
2052
|
-
retries += 1
|
|
2053
|
-
else:
|
|
2054
|
-
logger.error("Failed to delete M365 Team with ID -> %s", team_id)
|
|
2055
|
-
return None
|
|
1765
|
+
return self.do_request(
|
|
1766
|
+
url=request_url,
|
|
1767
|
+
method="DELETE",
|
|
1768
|
+
headers=request_header,
|
|
1769
|
+
timeout=REQUEST_TIMEOUT,
|
|
1770
|
+
failure_message="Failed to delete M365 Team with ID -> {}".format(team_id),
|
|
1771
|
+
)
|
|
2056
1772
|
|
|
2057
1773
|
# end method definition
|
|
2058
1774
|
|
|
@@ -2083,36 +1799,13 @@ class M365(object):
|
|
|
2083
1799
|
request_url,
|
|
2084
1800
|
)
|
|
2085
1801
|
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
break
|
|
2094
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
2095
|
-
elif response.status_code == 401 and retries == 0:
|
|
2096
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
2097
|
-
self.authenticate(revalidate=True)
|
|
2098
|
-
request_header = self.request_header()
|
|
2099
|
-
retries += 1
|
|
2100
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
2101
|
-
logger.warning(
|
|
2102
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
2103
|
-
response.status_code,
|
|
2104
|
-
(retries + 1) * 60,
|
|
2105
|
-
)
|
|
2106
|
-
time.sleep((retries + 1) * 60)
|
|
2107
|
-
retries += 1
|
|
2108
|
-
else:
|
|
2109
|
-
logger.error(
|
|
2110
|
-
"Failed to get list of M365 Teams to delete; status -> %s; error -> %s",
|
|
2111
|
-
response.status_code,
|
|
2112
|
-
response.text,
|
|
2113
|
-
)
|
|
2114
|
-
existing_teams = None
|
|
2115
|
-
break
|
|
1802
|
+
existing_teams = self.do_request(
|
|
1803
|
+
url=request_url,
|
|
1804
|
+
method="GET",
|
|
1805
|
+
headers=request_header,
|
|
1806
|
+
timeout=REQUEST_TIMEOUT,
|
|
1807
|
+
failure_message="Failed to get list of M365 Teams to delete",
|
|
1808
|
+
)
|
|
2116
1809
|
|
|
2117
1810
|
if existing_teams:
|
|
2118
1811
|
data = existing_teams.get("value")
|
|
@@ -2124,7 +1817,7 @@ class M365(object):
|
|
|
2124
1817
|
|
|
2125
1818
|
if not response:
|
|
2126
1819
|
logger.error(
|
|
2127
|
-
"Failed to delete M365 Team -> %s (%s)", name, team_id
|
|
1820
|
+
"Failed to delete M365 Team -> '%s' (%s)", name, team_id
|
|
2128
1821
|
)
|
|
2129
1822
|
continue
|
|
2130
1823
|
counter += 1
|
|
@@ -2243,35 +1936,15 @@ class M365(object):
|
|
|
2243
1936
|
request_url,
|
|
2244
1937
|
)
|
|
2245
1938
|
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
2256
|
-
self.authenticate(revalidate=True)
|
|
2257
|
-
request_header = self.request_header()
|
|
2258
|
-
retries += 1
|
|
2259
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
2260
|
-
logger.warning(
|
|
2261
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
2262
|
-
response.status_code,
|
|
2263
|
-
(retries + 1) * 60,
|
|
2264
|
-
)
|
|
2265
|
-
time.sleep((retries + 1) * 60)
|
|
2266
|
-
retries += 1
|
|
2267
|
-
else:
|
|
2268
|
-
logger.error(
|
|
2269
|
-
"Failed to get Channels for M365 Team -> %s; status -> %s; error -> %s",
|
|
2270
|
-
name,
|
|
2271
|
-
response.status_code,
|
|
2272
|
-
response.text,
|
|
2273
|
-
)
|
|
2274
|
-
return None
|
|
1939
|
+
return self.do_request(
|
|
1940
|
+
url=request_url,
|
|
1941
|
+
method="GET",
|
|
1942
|
+
headers=request_header,
|
|
1943
|
+
timeout=REQUEST_TIMEOUT,
|
|
1944
|
+
failure_message="Failed to get Channels for M365 Team -> '{}' ({})".format(
|
|
1945
|
+
name, team_id
|
|
1946
|
+
),
|
|
1947
|
+
)
|
|
2275
1948
|
|
|
2276
1949
|
# end method definition
|
|
2277
1950
|
|
|
@@ -2346,38 +2019,15 @@ class M365(object):
|
|
|
2346
2019
|
request_url,
|
|
2347
2020
|
)
|
|
2348
2021
|
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
2359
|
-
self.authenticate(revalidate=True)
|
|
2360
|
-
request_header = self.request_header()
|
|
2361
|
-
retries += 1
|
|
2362
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
2363
|
-
logger.warning(
|
|
2364
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
2365
|
-
response.status_code,
|
|
2366
|
-
(retries + 1) * 60,
|
|
2367
|
-
)
|
|
2368
|
-
time.sleep((retries + 1) * 60)
|
|
2369
|
-
retries += 1
|
|
2370
|
-
else:
|
|
2371
|
-
logger.error(
|
|
2372
|
-
"Failed to get Tabs for M365 Team -> %s (%s) and Channel -> %s (%s); status -> %s; error -> %s",
|
|
2373
|
-
team_name,
|
|
2374
|
-
team_id,
|
|
2375
|
-
channel_name,
|
|
2376
|
-
channel_id,
|
|
2377
|
-
response.status_code,
|
|
2378
|
-
response.text,
|
|
2379
|
-
)
|
|
2380
|
-
return None
|
|
2022
|
+
return self.do_request(
|
|
2023
|
+
url=request_url,
|
|
2024
|
+
method="GET",
|
|
2025
|
+
headers=request_header,
|
|
2026
|
+
timeout=REQUEST_TIMEOUT,
|
|
2027
|
+
failure_message="Failed to get Tabs for M365 Team -> '{}' ({}) and Channel -> '{}' ({})".format(
|
|
2028
|
+
team_name, team_id, channel_name, channel_id
|
|
2029
|
+
),
|
|
2030
|
+
)
|
|
2381
2031
|
|
|
2382
2032
|
# end method definition
|
|
2383
2033
|
|
|
@@ -2435,39 +2085,24 @@ class M365(object):
|
|
|
2435
2085
|
filter_expression,
|
|
2436
2086
|
request_url,
|
|
2437
2087
|
)
|
|
2088
|
+
failure_message = (
|
|
2089
|
+
"Failed to get list of M365 Teams apps using filter -> {}".format(
|
|
2090
|
+
filter_expression
|
|
2091
|
+
)
|
|
2092
|
+
)
|
|
2438
2093
|
else:
|
|
2439
2094
|
logger.debug("Get list of all MS Teams Apps; calling -> %s", request_url)
|
|
2095
|
+
failure_message = "Failed to get list of M365 Teams apps"
|
|
2440
2096
|
|
|
2441
2097
|
request_header = self.request_header()
|
|
2442
2098
|
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
2451
|
-
elif response.status_code == 401 and retries == 0:
|
|
2452
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
2453
|
-
self.authenticate(revalidate=True)
|
|
2454
|
-
request_header = self.request_header()
|
|
2455
|
-
retries += 1
|
|
2456
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
2457
|
-
logger.warning(
|
|
2458
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
2459
|
-
response.status_code,
|
|
2460
|
-
(retries + 1) * 60,
|
|
2461
|
-
)
|
|
2462
|
-
time.sleep((retries + 1) * 60)
|
|
2463
|
-
retries += 1
|
|
2464
|
-
else:
|
|
2465
|
-
logger.error(
|
|
2466
|
-
"Failed to get list of M365 Teams apps; status -> %s; error -> %s",
|
|
2467
|
-
response.status_code,
|
|
2468
|
-
response.text,
|
|
2469
|
-
)
|
|
2470
|
-
return None
|
|
2099
|
+
return self.do_request(
|
|
2100
|
+
url=request_url,
|
|
2101
|
+
method="GET",
|
|
2102
|
+
headers=request_header,
|
|
2103
|
+
timeout=REQUEST_TIMEOUT,
|
|
2104
|
+
failure_message=failure_message,
|
|
2105
|
+
)
|
|
2471
2106
|
|
|
2472
2107
|
# end method definition
|
|
2473
2108
|
|
|
@@ -2514,34 +2149,13 @@ class M365(object):
|
|
|
2514
2149
|
|
|
2515
2150
|
request_header = self.request_header()
|
|
2516
2151
|
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
2525
|
-
elif response.status_code == 401 and retries == 0:
|
|
2526
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
2527
|
-
self.authenticate(revalidate=True)
|
|
2528
|
-
request_header = self.request_header()
|
|
2529
|
-
retries += 1
|
|
2530
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
2531
|
-
logger.warning(
|
|
2532
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
2533
|
-
response.status_code,
|
|
2534
|
-
(retries + 1) * 60,
|
|
2535
|
-
)
|
|
2536
|
-
time.sleep((retries + 1) * 60)
|
|
2537
|
-
retries += 1
|
|
2538
|
-
else:
|
|
2539
|
-
logger.error(
|
|
2540
|
-
"Failed to get list of M365 Teams apps; status -> %s; error -> %s",
|
|
2541
|
-
response.status_code,
|
|
2542
|
-
response.text,
|
|
2543
|
-
)
|
|
2544
|
-
return None
|
|
2152
|
+
return self.do_request(
|
|
2153
|
+
url=request_url,
|
|
2154
|
+
method="GET",
|
|
2155
|
+
headers=request_header,
|
|
2156
|
+
timeout=REQUEST_TIMEOUT,
|
|
2157
|
+
failure_message="Failed to get M365 Teams app with ID -> {}".format(app_id),
|
|
2158
|
+
)
|
|
2545
2159
|
|
|
2546
2160
|
# end method definition
|
|
2547
2161
|
|
|
@@ -2579,35 +2193,15 @@ class M365(object):
|
|
|
2579
2193
|
|
|
2580
2194
|
request_header = self.request_header()
|
|
2581
2195
|
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
2592
|
-
self.authenticate(revalidate=True)
|
|
2593
|
-
request_header = self.request_header()
|
|
2594
|
-
retries += 1
|
|
2595
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
2596
|
-
logger.warning(
|
|
2597
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
2598
|
-
response.status_code,
|
|
2599
|
-
(retries + 1) * 60,
|
|
2600
|
-
)
|
|
2601
|
-
time.sleep((retries + 1) * 60)
|
|
2602
|
-
retries += 1
|
|
2603
|
-
else:
|
|
2604
|
-
logger.error(
|
|
2605
|
-
"Failed to get list of M365 Teams Apps for user -> %s; status -> %s; error -> %s",
|
|
2606
|
-
user_id,
|
|
2607
|
-
response.status_code,
|
|
2608
|
-
response.text,
|
|
2609
|
-
)
|
|
2610
|
-
return None
|
|
2196
|
+
return self.do_request(
|
|
2197
|
+
url=request_url,
|
|
2198
|
+
method="GET",
|
|
2199
|
+
headers=request_header,
|
|
2200
|
+
timeout=REQUEST_TIMEOUT,
|
|
2201
|
+
failure_message="Failed to get M365 Teams apps for user -> {}".format(
|
|
2202
|
+
user_id
|
|
2203
|
+
),
|
|
2204
|
+
)
|
|
2611
2205
|
|
|
2612
2206
|
# end method definition
|
|
2613
2207
|
|
|
@@ -2645,35 +2239,15 @@ class M365(object):
|
|
|
2645
2239
|
|
|
2646
2240
|
request_header = self.request_header()
|
|
2647
2241
|
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
2658
|
-
self.authenticate(revalidate=True)
|
|
2659
|
-
request_header = self.request_header()
|
|
2660
|
-
retries += 1
|
|
2661
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
2662
|
-
logger.warning(
|
|
2663
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
2664
|
-
response.status_code,
|
|
2665
|
-
(retries + 1) * 60,
|
|
2666
|
-
)
|
|
2667
|
-
time.sleep((retries + 1) * 60)
|
|
2668
|
-
retries += 1
|
|
2669
|
-
else:
|
|
2670
|
-
logger.error(
|
|
2671
|
-
"Failed to get list of M365 Teams Apps for M365 Team -> %s; status -> %s; error -> %s",
|
|
2672
|
-
team_id,
|
|
2673
|
-
response.status_code,
|
|
2674
|
-
response.text,
|
|
2675
|
-
)
|
|
2676
|
-
return None
|
|
2242
|
+
return self.do_request(
|
|
2243
|
+
url=request_url,
|
|
2244
|
+
method="GET",
|
|
2245
|
+
headers=request_header,
|
|
2246
|
+
timeout=REQUEST_TIMEOUT,
|
|
2247
|
+
failure_message="Failed to get list of M365 Teams apps for M365 Team -> {}".format(
|
|
2248
|
+
team_id
|
|
2249
|
+
),
|
|
2250
|
+
)
|
|
2677
2251
|
|
|
2678
2252
|
# end method definition
|
|
2679
2253
|
|
|
@@ -2770,7 +2344,7 @@ class M365(object):
|
|
|
2770
2344
|
|
|
2771
2345
|
# Here we need the credentials of an authenticated user!
|
|
2772
2346
|
# (not the application credentials (client_id, client_secret))
|
|
2773
|
-
request_header = self.request_header_user("application/zip")
|
|
2347
|
+
request_header = self.request_header_user(content_type="application/zip")
|
|
2774
2348
|
|
|
2775
2349
|
with open(app_path, "rb") as f:
|
|
2776
2350
|
app_data = f.read()
|
|
@@ -2790,48 +2364,16 @@ class M365(object):
|
|
|
2790
2364
|
request_url,
|
|
2791
2365
|
)
|
|
2792
2366
|
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
2805
|
-
if response.status_code == 401 and retries == 0:
|
|
2806
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
2807
|
-
self.authenticate(revalidate=True)
|
|
2808
|
-
request_header = self.request_header()
|
|
2809
|
-
retries += 1
|
|
2810
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
2811
|
-
logger.warning(
|
|
2812
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
2813
|
-
response.status_code,
|
|
2814
|
-
(retries + 1) * 60,
|
|
2815
|
-
)
|
|
2816
|
-
time.sleep((retries + 1) * 60)
|
|
2817
|
-
retries += 1
|
|
2818
|
-
else:
|
|
2819
|
-
if update_existing_app:
|
|
2820
|
-
logger.warning(
|
|
2821
|
-
"Failed to update existing M365 Teams app -> '%s' (may be because it is not a new version); status -> %s; error -> %s",
|
|
2822
|
-
app_path,
|
|
2823
|
-
response.status_code,
|
|
2824
|
-
response.text,
|
|
2825
|
-
)
|
|
2826
|
-
|
|
2827
|
-
else:
|
|
2828
|
-
logger.error(
|
|
2829
|
-
"Failed to upload new M365 Teams app -> '%s'; status -> %s; error -> %s",
|
|
2830
|
-
app_path,
|
|
2831
|
-
response.status_code,
|
|
2832
|
-
response.text,
|
|
2833
|
-
)
|
|
2834
|
-
return None
|
|
2367
|
+
return self.do_request(
|
|
2368
|
+
url=request_url,
|
|
2369
|
+
method="POST",
|
|
2370
|
+
headers=request_header,
|
|
2371
|
+
data=app_data,
|
|
2372
|
+
timeout=REQUEST_TIMEOUT,
|
|
2373
|
+
failure_message="Failed to update existing M365 Teams app -> '{}' (may be because it is not a new version)".format(
|
|
2374
|
+
app_path
|
|
2375
|
+
),
|
|
2376
|
+
)
|
|
2835
2377
|
|
|
2836
2378
|
# end method definition
|
|
2837
2379
|
|
|
@@ -2920,56 +2462,26 @@ class M365(object):
|
|
|
2920
2462
|
|
|
2921
2463
|
logger.debug(
|
|
2922
2464
|
"Assign M365 Teams app -> '%s' (%s) to M365 user -> %s; calling -> %s",
|
|
2923
|
-
app_name,
|
|
2924
|
-
app_internal_id,
|
|
2925
|
-
user_id,
|
|
2926
|
-
request_url,
|
|
2927
|
-
)
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
request_header = self.request_header()
|
|
2944
|
-
retries += 1
|
|
2945
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
2946
|
-
logger.warning(
|
|
2947
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
2948
|
-
response.status_code,
|
|
2949
|
-
(retries + 1) * 60,
|
|
2950
|
-
)
|
|
2951
|
-
time.sleep((retries + 1) * 60)
|
|
2952
|
-
retries += 1
|
|
2953
|
-
else:
|
|
2954
|
-
if show_error:
|
|
2955
|
-
logger.error(
|
|
2956
|
-
"Failed to assign M365 Teams app -> '%s' (%s) to M365 user -> %s; status -> %s; error -> %s",
|
|
2957
|
-
app_name,
|
|
2958
|
-
app_internal_id,
|
|
2959
|
-
user_id,
|
|
2960
|
-
response.status_code,
|
|
2961
|
-
response.text,
|
|
2962
|
-
)
|
|
2963
|
-
else:
|
|
2964
|
-
logger.warning(
|
|
2965
|
-
"Failed to assign M365 Teams app -> '%s' (%s) to M365 user -> %s (could be because the app is assigned organization-wide); status -> %s; warning -> %s",
|
|
2966
|
-
app_name,
|
|
2967
|
-
app_internal_id,
|
|
2968
|
-
user_id,
|
|
2969
|
-
response.status_code,
|
|
2970
|
-
response.text,
|
|
2971
|
-
)
|
|
2972
|
-
return None
|
|
2465
|
+
app_name,
|
|
2466
|
+
app_internal_id,
|
|
2467
|
+
user_id,
|
|
2468
|
+
request_url,
|
|
2469
|
+
)
|
|
2470
|
+
|
|
2471
|
+
return self.do_request(
|
|
2472
|
+
url=request_url,
|
|
2473
|
+
method="POST",
|
|
2474
|
+
headers=request_header,
|
|
2475
|
+
json_data=post_body,
|
|
2476
|
+
timeout=REQUEST_TIMEOUT,
|
|
2477
|
+
failure_message="Failed to assign M365 Teams app -> '{}' ({}) to M365 user -> {}".format(
|
|
2478
|
+
app_name, app_internal_id, user_id
|
|
2479
|
+
),
|
|
2480
|
+
warning_message="Failed to assign M365 Teams app -> '{}' ({}) to M365 user -> {} (could be because the app is assigned organization-wide)".format(
|
|
2481
|
+
app_name, app_internal_id, user_id
|
|
2482
|
+
),
|
|
2483
|
+
show_error=show_error,
|
|
2484
|
+
)
|
|
2973
2485
|
|
|
2974
2486
|
# end method definition
|
|
2975
2487
|
|
|
@@ -3025,37 +2537,15 @@ class M365(object):
|
|
|
3025
2537
|
request_url,
|
|
3026
2538
|
)
|
|
3027
2539
|
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
3038
|
-
self.authenticate(revalidate=True)
|
|
3039
|
-
request_header = self.request_header()
|
|
3040
|
-
retries += 1
|
|
3041
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
3042
|
-
logger.warning(
|
|
3043
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
3044
|
-
response.status_code,
|
|
3045
|
-
(retries + 1) * 60,
|
|
3046
|
-
)
|
|
3047
|
-
time.sleep((retries + 1) * 60)
|
|
3048
|
-
retries += 1
|
|
3049
|
-
else:
|
|
3050
|
-
logger.error(
|
|
3051
|
-
"Failed to upgrade M365 Teams app -> '%s' (%s) of M365 user -> %s; status -> %s; error -> %s",
|
|
3052
|
-
app_name,
|
|
3053
|
-
app_installation_id,
|
|
3054
|
-
user_id,
|
|
3055
|
-
response.status_code,
|
|
3056
|
-
response.text,
|
|
3057
|
-
)
|
|
3058
|
-
return None
|
|
2540
|
+
return self.do_request(
|
|
2541
|
+
url=request_url,
|
|
2542
|
+
method="POST",
|
|
2543
|
+
headers=request_header,
|
|
2544
|
+
timeout=REQUEST_TIMEOUT,
|
|
2545
|
+
failure_message="Failed to upgrade M365 Teams app -> '{}' ({}) of M365 user -> {}".format(
|
|
2546
|
+
app_name, app_installation_id, user_id
|
|
2547
|
+
),
|
|
2548
|
+
)
|
|
3059
2549
|
|
|
3060
2550
|
# end method definition
|
|
3061
2551
|
|
|
@@ -3107,37 +2597,15 @@ class M365(object):
|
|
|
3107
2597
|
request_url,
|
|
3108
2598
|
)
|
|
3109
2599
|
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
3120
|
-
self.authenticate(revalidate=True)
|
|
3121
|
-
request_header = self.request_header()
|
|
3122
|
-
retries += 1
|
|
3123
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
3124
|
-
logger.warning(
|
|
3125
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
3126
|
-
response.status_code,
|
|
3127
|
-
(retries + 1) * 60,
|
|
3128
|
-
)
|
|
3129
|
-
time.sleep((retries + 1) * 60)
|
|
3130
|
-
retries += 1
|
|
3131
|
-
else:
|
|
3132
|
-
logger.error(
|
|
3133
|
-
"Failed to remove M365 Teams app -> '%s' (%s) from M365 user -> %s; status -> %s; error -> %s",
|
|
3134
|
-
app_name,
|
|
3135
|
-
app_installation_id,
|
|
3136
|
-
user_id,
|
|
3137
|
-
response.status_code,
|
|
3138
|
-
response.text,
|
|
3139
|
-
)
|
|
3140
|
-
return None
|
|
2600
|
+
return self.do_request(
|
|
2601
|
+
url=request_url,
|
|
2602
|
+
method="DELETE",
|
|
2603
|
+
headers=request_header,
|
|
2604
|
+
timeout=REQUEST_TIMEOUT,
|
|
2605
|
+
failure_message="Failed to remove M365 Teams app -> '{}' ({}) from M365 user -> {}".format(
|
|
2606
|
+
app_name, app_installation_id, user_id
|
|
2607
|
+
),
|
|
2608
|
+
)
|
|
3141
2609
|
|
|
3142
2610
|
# end method definition
|
|
3143
2611
|
|
|
@@ -3168,40 +2636,16 @@ class M365(object):
|
|
|
3168
2636
|
request_url,
|
|
3169
2637
|
)
|
|
3170
2638
|
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
3182
|
-
elif response.status_code == 401 and retries == 0:
|
|
3183
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
3184
|
-
self.authenticate(revalidate=True)
|
|
3185
|
-
request_header = self.request_header()
|
|
3186
|
-
retries += 1
|
|
3187
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
3188
|
-
logger.warning(
|
|
3189
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
3190
|
-
response.status_code,
|
|
3191
|
-
(retries + 1) * 60,
|
|
3192
|
-
)
|
|
3193
|
-
time.sleep((retries + 1) * 60)
|
|
3194
|
-
retries += 1
|
|
3195
|
-
else:
|
|
3196
|
-
logger.error(
|
|
3197
|
-
"Failed to assign M365 Teams app -> '%s' (%s) to M365 Team -> %s; status -> %s; error -> %s",
|
|
3198
|
-
self.config()["teamsAppName"],
|
|
3199
|
-
app_id,
|
|
3200
|
-
team_id,
|
|
3201
|
-
response.status_code,
|
|
3202
|
-
response.text,
|
|
3203
|
-
)
|
|
3204
|
-
return None
|
|
2639
|
+
return self.do_request(
|
|
2640
|
+
url=request_url,
|
|
2641
|
+
method="POST",
|
|
2642
|
+
headers=request_header,
|
|
2643
|
+
json_data=post_body,
|
|
2644
|
+
timeout=REQUEST_TIMEOUT,
|
|
2645
|
+
failure_message="Failed to assign M365 Teams app -> '{}' ({}) to M365 Team -> {}".format(
|
|
2646
|
+
self.config()["teamsAppName"], app_id, team_id
|
|
2647
|
+
),
|
|
2648
|
+
)
|
|
3205
2649
|
|
|
3206
2650
|
# end method definition
|
|
3207
2651
|
|
|
@@ -3249,37 +2693,15 @@ class M365(object):
|
|
|
3249
2693
|
request_url,
|
|
3250
2694
|
)
|
|
3251
2695
|
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
3262
|
-
self.authenticate(revalidate=True)
|
|
3263
|
-
request_header = self.request_header()
|
|
3264
|
-
retries += 1
|
|
3265
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
3266
|
-
logger.warning(
|
|
3267
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
3268
|
-
response.status_code,
|
|
3269
|
-
(retries + 1) * 60,
|
|
3270
|
-
)
|
|
3271
|
-
time.sleep((retries + 1) * 60)
|
|
3272
|
-
retries += 1
|
|
3273
|
-
else:
|
|
3274
|
-
logger.error(
|
|
3275
|
-
"Failed to upgrade M365 Teams app -> '%s' (%s) of M365 team with ID -> %s; status -> %s; error -> %s",
|
|
3276
|
-
app_name,
|
|
3277
|
-
app_installation_id,
|
|
3278
|
-
team_id,
|
|
3279
|
-
response.status_code,
|
|
3280
|
-
response.text,
|
|
3281
|
-
)
|
|
3282
|
-
return None
|
|
2696
|
+
return self.do_request(
|
|
2697
|
+
url=request_url,
|
|
2698
|
+
method="POST",
|
|
2699
|
+
headers=request_header,
|
|
2700
|
+
timeout=REQUEST_TIMEOUT,
|
|
2701
|
+
failure_message="Failed to upgrade M365 Teams app -> '{}' ({}) of M365 team with ID -> {}".format(
|
|
2702
|
+
app_name, app_installation_id, team_id
|
|
2703
|
+
),
|
|
2704
|
+
)
|
|
3283
2705
|
|
|
3284
2706
|
# end method definition
|
|
3285
2707
|
|
|
@@ -3364,42 +2786,16 @@ class M365(object):
|
|
|
3364
2786
|
request_url,
|
|
3365
2787
|
)
|
|
3366
2788
|
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
3378
|
-
elif response.status_code == 401 and retries == 0:
|
|
3379
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
3380
|
-
self.authenticate(revalidate=True)
|
|
3381
|
-
request_header = self.request_header()
|
|
3382
|
-
retries += 1
|
|
3383
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
3384
|
-
logger.warning(
|
|
3385
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
3386
|
-
response.status_code,
|
|
3387
|
-
(retries + 1) * 60,
|
|
3388
|
-
)
|
|
3389
|
-
time.sleep((retries + 1) * 60)
|
|
3390
|
-
retries += 1
|
|
3391
|
-
else:
|
|
3392
|
-
logger.error(
|
|
3393
|
-
"Failed to add Tab for M365 Team -> '%s' (%s) and Channel -> '%s' (%s); status -> %s; error -> %s; tab config -> %s",
|
|
3394
|
-
team_name,
|
|
3395
|
-
team_id,
|
|
3396
|
-
channel_name,
|
|
3397
|
-
channel_id,
|
|
3398
|
-
response.status_code,
|
|
3399
|
-
response.text,
|
|
3400
|
-
str(tab_config),
|
|
3401
|
-
)
|
|
3402
|
-
return None
|
|
2789
|
+
return self.do_request(
|
|
2790
|
+
url=request_url,
|
|
2791
|
+
method="POST",
|
|
2792
|
+
headers=request_header,
|
|
2793
|
+
json_data=tab_config,
|
|
2794
|
+
timeout=REQUEST_TIMEOUT,
|
|
2795
|
+
failure_message="Failed to add Tab for M365 Team -> '{}' ({}) and Channel -> '{}' ({})".format(
|
|
2796
|
+
team_name, team_id, channel_name, channel_id
|
|
2797
|
+
),
|
|
2798
|
+
)
|
|
3403
2799
|
|
|
3404
2800
|
# end method definition
|
|
3405
2801
|
|
|
@@ -3506,43 +2902,16 @@ class M365(object):
|
|
|
3506
2902
|
request_url,
|
|
3507
2903
|
)
|
|
3508
2904
|
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
3520
|
-
elif response.status_code == 401 and retries == 0:
|
|
3521
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
3522
|
-
self.authenticate(revalidate=True)
|
|
3523
|
-
request_header = self.request_header()
|
|
3524
|
-
retries += 1
|
|
3525
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
3526
|
-
logger.warning(
|
|
3527
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
3528
|
-
response.status_code,
|
|
3529
|
-
(retries + 1) * 60,
|
|
3530
|
-
)
|
|
3531
|
-
time.sleep((retries + 1) * 60)
|
|
3532
|
-
retries += 1
|
|
3533
|
-
else:
|
|
3534
|
-
logger.error(
|
|
3535
|
-
"Failed to update Tab -> '%s' (%s) for M365 Team -> '%s' (%s) and Channel -> '%s' (%s); status -> %s; error -> %s",
|
|
3536
|
-
tab_name,
|
|
3537
|
-
tab_id,
|
|
3538
|
-
team_name,
|
|
3539
|
-
team_id,
|
|
3540
|
-
channel_name,
|
|
3541
|
-
channel_id,
|
|
3542
|
-
response.status_code,
|
|
3543
|
-
response.text,
|
|
3544
|
-
)
|
|
3545
|
-
return None
|
|
2905
|
+
return self.do_request(
|
|
2906
|
+
url=request_url,
|
|
2907
|
+
method="PATCH",
|
|
2908
|
+
headers=request_header,
|
|
2909
|
+
json_data=tab_config,
|
|
2910
|
+
timeout=REQUEST_TIMEOUT,
|
|
2911
|
+
failure_message="Failed to update Tab -> '{}' ({}) for M365 Team -> '{}' ({}) and Channel -> '{}' ({})".format(
|
|
2912
|
+
tab_name, tab_id, team_name, team_id, channel_name, channel_id
|
|
2913
|
+
),
|
|
2914
|
+
)
|
|
3546
2915
|
|
|
3547
2916
|
# end method definition
|
|
3548
2917
|
|
|
@@ -3633,49 +3002,23 @@ class M365(object):
|
|
|
3633
3002
|
request_url,
|
|
3634
3003
|
)
|
|
3635
3004
|
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
elif response.status_code == 401 and retries == 0:
|
|
3654
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
3655
|
-
self.authenticate(revalidate=True)
|
|
3656
|
-
request_header = self.request_header()
|
|
3657
|
-
retries += 1
|
|
3658
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
3659
|
-
logger.warning(
|
|
3660
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
3661
|
-
response.status_code,
|
|
3662
|
-
(retries + 1) * 60,
|
|
3663
|
-
)
|
|
3664
|
-
time.sleep((retries + 1) * 60)
|
|
3665
|
-
retries += 1
|
|
3666
|
-
else:
|
|
3667
|
-
logger.error(
|
|
3668
|
-
"Failed to delete Tab -> '%s' (%s) for M365 Team -> '%s' (%s) and Channel -> '%s' (%s); status -> %s; error -> %s",
|
|
3669
|
-
tab_name,
|
|
3670
|
-
tab_id,
|
|
3671
|
-
team_name,
|
|
3672
|
-
team_id,
|
|
3673
|
-
channel_name,
|
|
3674
|
-
channel_id,
|
|
3675
|
-
response.status_code,
|
|
3676
|
-
response.text,
|
|
3677
|
-
)
|
|
3678
|
-
return False
|
|
3005
|
+
response = self.do_request(
|
|
3006
|
+
url=request_url,
|
|
3007
|
+
method="DELETE",
|
|
3008
|
+
headers=request_header,
|
|
3009
|
+
timeout=REQUEST_TIMEOUT,
|
|
3010
|
+
failure_message="Failed to delete Tab -> '{}' ({}) for M365 Team -> '{}' ({}) and Channel -> '{}' ({})".format(
|
|
3011
|
+
tab_name, tab_id, team_name, team_id, channel_name, channel_id
|
|
3012
|
+
),
|
|
3013
|
+
parse_request_response=False,
|
|
3014
|
+
)
|
|
3015
|
+
|
|
3016
|
+
if response and response.ok:
|
|
3017
|
+
break
|
|
3018
|
+
else:
|
|
3019
|
+
return False
|
|
3020
|
+
# end for tab in tab_list
|
|
3021
|
+
|
|
3679
3022
|
return True
|
|
3680
3023
|
|
|
3681
3024
|
# end method definition
|
|
@@ -3778,36 +3121,16 @@ class M365(object):
|
|
|
3778
3121
|
request_url,
|
|
3779
3122
|
)
|
|
3780
3123
|
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
self.authenticate(revalidate=True)
|
|
3792
|
-
request_header = self.request_header()
|
|
3793
|
-
retries += 1
|
|
3794
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
3795
|
-
logger.warning(
|
|
3796
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
3797
|
-
response.status_code,
|
|
3798
|
-
(retries + 1) * 60,
|
|
3799
|
-
)
|
|
3800
|
-
time.sleep((retries + 1) * 60)
|
|
3801
|
-
retries += 1
|
|
3802
|
-
else:
|
|
3803
|
-
logger.error(
|
|
3804
|
-
"Failed to assign label -> '%s' to M365 user -> '%s'; status -> %s; error -> %s",
|
|
3805
|
-
label_name,
|
|
3806
|
-
user_email,
|
|
3807
|
-
response.status_code,
|
|
3808
|
-
response.text,
|
|
3809
|
-
)
|
|
3810
|
-
return None
|
|
3124
|
+
return self.do_request(
|
|
3125
|
+
url=request_url,
|
|
3126
|
+
method="POST",
|
|
3127
|
+
headers=request_header,
|
|
3128
|
+
json_data=body,
|
|
3129
|
+
timeout=REQUEST_TIMEOUT,
|
|
3130
|
+
failure_message="Failed to assign label -> '{}' to M365 user -> '{}'".format(
|
|
3131
|
+
label_name, user_email
|
|
3132
|
+
),
|
|
3133
|
+
)
|
|
3811
3134
|
|
|
3812
3135
|
# end method definition
|
|
3813
3136
|
|
|
@@ -3832,7 +3155,7 @@ class M365(object):
|
|
|
3832
3155
|
|
|
3833
3156
|
# request_header = self.request_header()
|
|
3834
3157
|
|
|
3835
|
-
logger.debug("Install Outlook Add-in from '%s' (NOT IMPLEMENTED)", app_path)
|
|
3158
|
+
logger.debug("Install Outlook Add-in from -> '%s' (NOT IMPLEMENTED)", app_path)
|
|
3836
3159
|
|
|
3837
3160
|
response = None
|
|
3838
3161
|
|
|
@@ -3864,35 +3187,15 @@ class M365(object):
|
|
|
3864
3187
|
request_url,
|
|
3865
3188
|
)
|
|
3866
3189
|
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
3877
|
-
self.authenticate(revalidate=True)
|
|
3878
|
-
request_header = self.request_header()
|
|
3879
|
-
retries += 1
|
|
3880
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
3881
|
-
logger.warning(
|
|
3882
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
3883
|
-
response.status_code,
|
|
3884
|
-
(retries + 1) * 60,
|
|
3885
|
-
)
|
|
3886
|
-
time.sleep((retries + 1) * 60)
|
|
3887
|
-
retries += 1
|
|
3888
|
-
else:
|
|
3889
|
-
logger.error(
|
|
3890
|
-
"Cannot find Azure App Registration -> '%s'; status -> %s; error -> %s",
|
|
3891
|
-
app_registration_name,
|
|
3892
|
-
response.status_code,
|
|
3893
|
-
response.text,
|
|
3894
|
-
)
|
|
3895
|
-
return None
|
|
3190
|
+
return self.do_request(
|
|
3191
|
+
url=request_url,
|
|
3192
|
+
method="GET",
|
|
3193
|
+
headers=request_header,
|
|
3194
|
+
timeout=REQUEST_TIMEOUT,
|
|
3195
|
+
failure_message="Cannot find Azure App Registration -> '{}'".format(
|
|
3196
|
+
app_registration_name
|
|
3197
|
+
),
|
|
3198
|
+
)
|
|
3896
3199
|
|
|
3897
3200
|
# end method definition
|
|
3898
3201
|
|
|
@@ -3963,38 +3266,16 @@ class M365(object):
|
|
|
3963
3266
|
request_url = self.config()["applicationsUrl"]
|
|
3964
3267
|
request_header = self.request_header()
|
|
3965
3268
|
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
3977
|
-
elif response.status_code == 401 and retries == 0:
|
|
3978
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
3979
|
-
self.authenticate(revalidate=True)
|
|
3980
|
-
request_header = self.request_header()
|
|
3981
|
-
retries += 1
|
|
3982
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
3983
|
-
logger.warning(
|
|
3984
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
3985
|
-
response.status_code,
|
|
3986
|
-
(retries + 1) * 60,
|
|
3987
|
-
)
|
|
3988
|
-
time.sleep((retries + 1) * 60)
|
|
3989
|
-
retries += 1
|
|
3990
|
-
else:
|
|
3991
|
-
logger.error(
|
|
3992
|
-
"Cannot add App Registration -> '%s'; status -> %s; error -> %s",
|
|
3993
|
-
app_registration_name,
|
|
3994
|
-
response.status_code,
|
|
3995
|
-
response.text,
|
|
3996
|
-
)
|
|
3997
|
-
return None
|
|
3269
|
+
return self.do_request(
|
|
3270
|
+
url=request_url,
|
|
3271
|
+
method="POST",
|
|
3272
|
+
headers=request_header,
|
|
3273
|
+
json_data=app_registration_data,
|
|
3274
|
+
timeout=REQUEST_TIMEOUT,
|
|
3275
|
+
failure_message="Cannot add App Registration -> '{}'".format(
|
|
3276
|
+
app_registration_name
|
|
3277
|
+
),
|
|
3278
|
+
)
|
|
3998
3279
|
|
|
3999
3280
|
# end method definition
|
|
4000
3281
|
|
|
@@ -4035,39 +3316,16 @@ class M365(object):
|
|
|
4035
3316
|
request_url,
|
|
4036
3317
|
)
|
|
4037
3318
|
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
# Check if Session has expired - then re-authenticate and try once more
|
|
4049
|
-
elif response.status_code == 401 and retries == 0:
|
|
4050
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
4051
|
-
self.authenticate(revalidate=True)
|
|
4052
|
-
request_header = self.request_header()
|
|
4053
|
-
retries += 1
|
|
4054
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
4055
|
-
logger.warning(
|
|
4056
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
4057
|
-
response.status_code,
|
|
4058
|
-
(retries + 1) * 60,
|
|
4059
|
-
)
|
|
4060
|
-
time.sleep((retries + 1) * 60)
|
|
4061
|
-
retries += 1
|
|
4062
|
-
else:
|
|
4063
|
-
logger.error(
|
|
4064
|
-
"Cannot update App Registration -> '%s' (%s); status -> %s; error -> %s",
|
|
4065
|
-
app_registration_name,
|
|
4066
|
-
app_registration_id,
|
|
4067
|
-
response.status_code,
|
|
4068
|
-
response.text,
|
|
4069
|
-
)
|
|
4070
|
-
return None
|
|
3319
|
+
return self.do_request(
|
|
3320
|
+
url=request_url,
|
|
3321
|
+
method="PATCH",
|
|
3322
|
+
headers=request_header,
|
|
3323
|
+
json_data=app_registration_data,
|
|
3324
|
+
timeout=REQUEST_TIMEOUT,
|
|
3325
|
+
failure_message="Cannot update App Registration -> '{}' ({})".format(
|
|
3326
|
+
app_registration_name, app_registration_id
|
|
3327
|
+
),
|
|
3328
|
+
)
|
|
4071
3329
|
|
|
4072
3330
|
# end method definition
|
|
4073
3331
|
|
|
@@ -4109,67 +3367,40 @@ class M365(object):
|
|
|
4109
3367
|
request_header = self.request_header()
|
|
4110
3368
|
|
|
4111
3369
|
logger.debug(
|
|
4112
|
-
"
|
|
3370
|
+
"Get mails for user -> %s from -> '%s' with subject -> '%s'; calling -> %s",
|
|
4113
3371
|
user_id,
|
|
4114
3372
|
sender,
|
|
4115
3373
|
subject,
|
|
4116
3374
|
request_url,
|
|
4117
3375
|
)
|
|
4118
3376
|
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
self.authenticate(revalidate=True)
|
|
4143
|
-
request_header = self.request_header()
|
|
4144
|
-
retries += 1
|
|
4145
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
4146
|
-
logger.warning(
|
|
4147
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
4148
|
-
response.status_code,
|
|
4149
|
-
(retries + 1) * 60,
|
|
4150
|
-
)
|
|
4151
|
-
time.sleep((retries + 1) * 60)
|
|
4152
|
-
retries += 1
|
|
4153
|
-
else:
|
|
4154
|
-
if show_error:
|
|
4155
|
-
logger.error(
|
|
4156
|
-
"Cannot retrieve emails for user -> %s; status -> %s; error -> %s",
|
|
4157
|
-
user_id,
|
|
4158
|
-
response.status_code,
|
|
4159
|
-
response.text,
|
|
4160
|
-
)
|
|
4161
|
-
else:
|
|
4162
|
-
logger.warning(
|
|
4163
|
-
"Cannot retrieve emails for user -> %s; status -> %s; warning -> %s",
|
|
4164
|
-
user_id,
|
|
4165
|
-
response.status_code,
|
|
4166
|
-
response.text,
|
|
4167
|
-
)
|
|
4168
|
-
return None
|
|
3377
|
+
response = self.do_request(
|
|
3378
|
+
url=request_url,
|
|
3379
|
+
method="GET",
|
|
3380
|
+
headers=request_header,
|
|
3381
|
+
timeout=REQUEST_TIMEOUT,
|
|
3382
|
+
failure_message="Cannot retrieve emails for user -> {}".format(user_id),
|
|
3383
|
+
show_error=show_error,
|
|
3384
|
+
)
|
|
3385
|
+
|
|
3386
|
+
if response and "value" in response:
|
|
3387
|
+
messages = response["value"]
|
|
3388
|
+
|
|
3389
|
+
# Filter the messages by sender and subject in code
|
|
3390
|
+
filtered_messages = [
|
|
3391
|
+
msg
|
|
3392
|
+
for msg in messages
|
|
3393
|
+
if msg.get("from", {}).get("emailAddress", {}).get("address") == sender
|
|
3394
|
+
and subject in msg.get("subject", "")
|
|
3395
|
+
]
|
|
3396
|
+
response["value"] = filtered_messages
|
|
3397
|
+
return response
|
|
3398
|
+
|
|
3399
|
+
return None
|
|
4169
3400
|
|
|
4170
3401
|
# end method definition
|
|
4171
3402
|
|
|
4172
|
-
def get_mail_body(self, user_id: str, email_id: str) -> str:
|
|
3403
|
+
def get_mail_body(self, user_id: str, email_id: str) -> str | None:
|
|
4173
3404
|
"""Get full email body for a given email ID
|
|
4174
3405
|
This requires Mail.Read Application permissions for the Azure App being used.
|
|
4175
3406
|
|
|
@@ -4177,7 +3408,7 @@ class M365(object):
|
|
|
4177
3408
|
user_id (str): M365 ID of the user
|
|
4178
3409
|
email_id (str): M365 ID of the email
|
|
4179
3410
|
Returns:
|
|
4180
|
-
str: Email body or None of the request fails.
|
|
3411
|
+
str | None: Email body or None of the request fails.
|
|
4181
3412
|
"""
|
|
4182
3413
|
|
|
4183
3414
|
request_url = (
|
|
@@ -4191,36 +3422,22 @@ class M365(object):
|
|
|
4191
3422
|
|
|
4192
3423
|
request_header = self.request_header()
|
|
4193
3424
|
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
response.status_code,
|
|
4211
|
-
(retries + 1) * 60,
|
|
4212
|
-
)
|
|
4213
|
-
time.sleep((retries + 1) * 60)
|
|
4214
|
-
retries += 1
|
|
4215
|
-
else:
|
|
4216
|
-
logger.error(
|
|
4217
|
-
"Cannot retrieve emails body for user -> %s and email -> %s; status -> %s; error -> %s",
|
|
4218
|
-
user_id,
|
|
4219
|
-
email_id,
|
|
4220
|
-
response.status_code,
|
|
4221
|
-
response.text,
|
|
4222
|
-
)
|
|
4223
|
-
return None
|
|
3425
|
+
response = self.do_request(
|
|
3426
|
+
url=request_url,
|
|
3427
|
+
method="GET",
|
|
3428
|
+
headers=request_header,
|
|
3429
|
+
timeout=REQUEST_TIMEOUT,
|
|
3430
|
+
failure_message="Cannot get email body for user -> {} and email with ID -> {}".format(
|
|
3431
|
+
user_id,
|
|
3432
|
+
email_id,
|
|
3433
|
+
),
|
|
3434
|
+
parse_request_response=False,
|
|
3435
|
+
)
|
|
3436
|
+
|
|
3437
|
+
if response and response.ok and response.content:
|
|
3438
|
+
return response.content.decode("utf-8")
|
|
3439
|
+
|
|
3440
|
+
return None
|
|
4224
3441
|
|
|
4225
3442
|
# end method definition
|
|
4226
3443
|
|
|
@@ -4310,36 +3527,15 @@ class M365(object):
|
|
|
4310
3527
|
|
|
4311
3528
|
request_header = self.request_header()
|
|
4312
3529
|
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
4323
|
-
self.authenticate(revalidate=True)
|
|
4324
|
-
request_header = self.request_header()
|
|
4325
|
-
retries += 1
|
|
4326
|
-
elif response.status_code in [502, 503, 504] and retries < 3:
|
|
4327
|
-
logger.warning(
|
|
4328
|
-
"M365 Graph API delivered server side error -> %s; retrying in %s seconds...",
|
|
4329
|
-
response.status_code,
|
|
4330
|
-
(retries + 1) * 60,
|
|
4331
|
-
)
|
|
4332
|
-
time.sleep((retries + 1) * 60)
|
|
4333
|
-
retries += 1
|
|
4334
|
-
else:
|
|
4335
|
-
logger.error(
|
|
4336
|
-
"Cannot delete email -> %s from inbox of user -> %s; status -> %s; error -> %s",
|
|
4337
|
-
email_id,
|
|
4338
|
-
user_id,
|
|
4339
|
-
response.status_code,
|
|
4340
|
-
response.text,
|
|
4341
|
-
)
|
|
4342
|
-
return None
|
|
3530
|
+
return self.do_request(
|
|
3531
|
+
url=request_url,
|
|
3532
|
+
method="DELETE",
|
|
3533
|
+
headers=request_header,
|
|
3534
|
+
timeout=REQUEST_TIMEOUT,
|
|
3535
|
+
failure_message="Cannot delete email with ID -> {} from inbox of user -> {}".format(
|
|
3536
|
+
email_id, user_id
|
|
3537
|
+
),
|
|
3538
|
+
)
|
|
4343
3539
|
|
|
4344
3540
|
# end method definition
|
|
4345
3541
|
|
|
@@ -4521,8 +3717,8 @@ class M365(object):
|
|
|
4521
3717
|
password_submit_xpath,
|
|
4522
3718
|
)
|
|
4523
3719
|
success = False
|
|
4524
|
-
#
|
|
4525
|
-
# which
|
|
3720
|
+
# The Terms of service dialog has some weird animation
|
|
3721
|
+
# which require a short wait time. It seems it is required!
|
|
4526
3722
|
time.sleep(1)
|
|
4527
3723
|
terms_accept_button = browser_automation_object.find_elem(
|
|
4528
3724
|
find_elem=terms_of_service_xpath,
|