pyxecm 3.0.1__py3-none-any.whl → 3.1.1__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 (52) hide show
  1. pyxecm/avts.py +4 -4
  2. pyxecm/coreshare.py +14 -15
  3. pyxecm/helper/data.py +2 -1
  4. pyxecm/helper/web.py +11 -11
  5. pyxecm/helper/xml.py +41 -10
  6. pyxecm/otac.py +1 -1
  7. pyxecm/otawp.py +19 -19
  8. pyxecm/otca.py +878 -70
  9. pyxecm/otcs.py +1716 -349
  10. pyxecm/otds.py +332 -153
  11. pyxecm/otkd.py +4 -4
  12. pyxecm/otmm.py +1 -1
  13. pyxecm/otpd.py +246 -30
  14. {pyxecm-3.0.1.dist-info → pyxecm-3.1.1.dist-info}/METADATA +2 -1
  15. pyxecm-3.1.1.dist-info/RECORD +82 -0
  16. pyxecm_api/app.py +45 -35
  17. pyxecm_api/auth/functions.py +2 -2
  18. pyxecm_api/auth/router.py +2 -3
  19. pyxecm_api/common/functions.py +67 -12
  20. pyxecm_api/settings.py +0 -8
  21. pyxecm_api/terminal/router.py +1 -1
  22. pyxecm_api/v1_csai/router.py +33 -18
  23. pyxecm_customizer/browser_automation.py +161 -79
  24. pyxecm_customizer/customizer.py +43 -25
  25. pyxecm_customizer/guidewire.py +422 -8
  26. pyxecm_customizer/k8s.py +23 -27
  27. pyxecm_customizer/knowledge_graph.py +498 -20
  28. pyxecm_customizer/m365.py +45 -44
  29. pyxecm_customizer/payload.py +1723 -1188
  30. pyxecm_customizer/payload_list.py +3 -0
  31. pyxecm_customizer/salesforce.py +122 -79
  32. pyxecm_customizer/servicenow.py +27 -7
  33. pyxecm_customizer/settings.py +3 -1
  34. pyxecm_customizer/successfactors.py +2 -2
  35. pyxecm_customizer/translate.py +1 -1
  36. pyxecm-3.0.1.dist-info/RECORD +0 -96
  37. pyxecm_api/agents/__init__.py +0 -7
  38. pyxecm_api/agents/app.py +0 -13
  39. pyxecm_api/agents/functions.py +0 -119
  40. pyxecm_api/agents/models.py +0 -10
  41. pyxecm_api/agents/otcm_knowledgegraph/__init__.py +0 -1
  42. pyxecm_api/agents/otcm_knowledgegraph/functions.py +0 -85
  43. pyxecm_api/agents/otcm_knowledgegraph/models.py +0 -61
  44. pyxecm_api/agents/otcm_knowledgegraph/router.py +0 -74
  45. pyxecm_api/agents/otcm_user_agent/__init__.py +0 -1
  46. pyxecm_api/agents/otcm_user_agent/models.py +0 -20
  47. pyxecm_api/agents/otcm_user_agent/router.py +0 -65
  48. pyxecm_api/agents/otcm_workspace_agent/__init__.py +0 -1
  49. pyxecm_api/agents/otcm_workspace_agent/models.py +0 -40
  50. pyxecm_api/agents/otcm_workspace_agent/router.py +0 -200
  51. {pyxecm-3.0.1.dist-info → pyxecm-3.1.1.dist-info}/WHEEL +0 -0
  52. {pyxecm-3.0.1.dist-info → pyxecm-3.1.1.dist-info}/entry_points.txt +0 -0
pyxecm/otkd.py CHANGED
@@ -50,8 +50,8 @@ REQUEST_UPLOAD_HEADERS = {
50
50
  # DO NOT set "Content-Type" manually
51
51
  }
52
52
 
53
- REQUEST_TIMEOUT = 60
54
- REQUEST_RETRY_DELAY = 20
53
+ REQUEST_TIMEOUT = 60.0
54
+ REQUEST_RETRY_DELAY = 20.0
55
55
  REQUEST_MAX_RETRIES = 2
56
56
 
57
57
  default_logger = logging.getLogger(MODULE_NAME)
@@ -301,7 +301,7 @@ class OTKD:
301
301
  data: dict | None = None,
302
302
  json_data: dict | None = None,
303
303
  files: dict | None = None,
304
- timeout: int | None = REQUEST_TIMEOUT,
304
+ timeout: float | None = REQUEST_TIMEOUT,
305
305
  show_error: bool = True,
306
306
  show_warning: bool = False,
307
307
  warning_message: str = "",
@@ -327,7 +327,7 @@ class OTKD:
327
327
  files (dict | None, optional):
328
328
  Dictionary of {"name": file-tuple} for multipart encoding upload.
329
329
  File-tuple can be a 2-tuple ("filename", fileobj) or a 3-tuple ("filename", fileobj, "content_type")
330
- timeout (int | None, optional):
330
+ timeout (float | None, optional):
331
331
  The timeout for the request in seconds. Defaults to REQUEST_TIMEOUT.
332
332
  show_error (bool, optional):
333
333
  Whether or not an error should be logged in case of a failed REST call.
pyxecm/otmm.py CHANGED
@@ -47,7 +47,7 @@ REQUEST_HEADERS = {
47
47
  "User-Agent": USER_AGENT,
48
48
  "Content-Type": "application/x-www-form-urlencoded",
49
49
  }
50
- REQUEST_TIMEOUT = 60
50
+ REQUEST_TIMEOUT = 60.0
51
51
 
52
52
  default_logger = logging.getLogger(MODULE_NAME)
53
53
 
pyxecm/otpd.py CHANGED
@@ -9,11 +9,46 @@ __email__ = "mdiefenb@opentext.com"
9
9
  import json
10
10
  import logging
11
11
  import os
12
+ import platform
13
+ import sys
14
+ import time
15
+ from http import HTTPStatus
16
+ from importlib.metadata import version
12
17
 
13
18
  import requests
14
19
  from requests.auth import HTTPBasicAuth
15
20
  from requests_toolbelt.multipart.encoder import MultipartEncoder
16
21
 
22
+ APP_NAME = "pyxecm"
23
+ APP_VERSION = version("pyxecm")
24
+ MODULE_NAME = APP_NAME + ".otpd"
25
+
26
+ PYTHON_VERSION = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
27
+ OS_INFO = f"{platform.system()} {platform.release()}"
28
+ ARCH_INFO = platform.machine()
29
+ REQUESTS_VERSION = requests.__version__
30
+
31
+ USER_AGENT = (
32
+ f"{APP_NAME}/{APP_VERSION} ({MODULE_NAME}/{APP_VERSION}; "
33
+ f"Python/{PYTHON_VERSION}; {OS_INFO}; {ARCH_INFO}; Requests/{REQUESTS_VERSION})"
34
+ )
35
+
36
+ REQUEST_HEADERS = {
37
+ "User-Agent": USER_AGENT,
38
+ "accept": "application/json;charset=utf-8",
39
+ "Content-Type": "application/json",
40
+ }
41
+
42
+ REQUEST_FORM_HEADERS = {
43
+ "User-Agent": USER_AGENT,
44
+ "accept": "application/json;charset=utf-8",
45
+ "Content-Type": "application/x-www-form-urlencoded",
46
+ }
47
+
48
+ REQUEST_TIMEOUT = 60.0
49
+ REQUEST_RETRY_DELAY = 20.0
50
+ REQUEST_MAX_RETRIES = 2
51
+
17
52
  default_logger = logging.getLogger("pyxecm.otpd")
18
53
 
19
54
  request_headers = {
@@ -93,15 +128,17 @@ class OTPD:
93
128
  otpd_base_url = protocol + "://" + otpd_config["hostname"]
94
129
  if str(port) not in ["80", "443"]:
95
130
  otpd_base_url += ":{}".format(port)
96
- otpd_base_url += "/ServerManager"
97
131
  otpd_config["baseUrl"] = otpd_base_url
98
132
 
99
- otpd_rest_url = otpd_base_url + "/api"
133
+ otpd_servermanager_url = otpd_base_url + "/ServerManager"
134
+ otpd_config["serverManagerUrl"] = otpd_servermanager_url
135
+
136
+ otpd_rest_url = otpd_servermanager_url + "/api"
100
137
  otpd_config["restUrl"] = otpd_rest_url
101
138
 
102
139
  otpd_config["settingsUrl"] = otpd_rest_url + "/v1/settings"
103
140
 
104
- otpd_config["importDatabaseUrl"] = otpd_base_url + "/servlet/import"
141
+ otpd_config["importDatabaseUrl"] = otpd_servermanager_url + "/servlet/import"
105
142
 
106
143
  self._config = otpd_config
107
144
 
@@ -174,32 +211,6 @@ class OTPD:
174
211
 
175
212
  # end method definition
176
213
 
177
- def base_url(self) -> str:
178
- """Return the base URL of PowerDocs.
179
-
180
- Returns:
181
- string:
182
- The base URL.
183
-
184
- """
185
-
186
- return self.config()["baseUrl"]
187
-
188
- # end method definition
189
-
190
- def rest_url(self) -> str:
191
- """Return the REST URL of PowerDocs.
192
-
193
- Returns:
194
- string:
195
- The REST URL.
196
-
197
- """
198
-
199
- return self.config()["restUrl"]
200
-
201
- # end method definition
202
-
203
214
  def parse_request_response(
204
215
  self,
205
216
  response_object: object,
@@ -270,7 +281,7 @@ class OTPD:
270
281
  return self._jsessionid
271
282
 
272
283
  auth_url = (
273
- self.base_url()
284
+ self.config()["serverManagerUrl"]
274
285
  + "/j_security_check?j_username="
275
286
  + self.config()["username"]
276
287
  + "&j_password="
@@ -482,3 +493,208 @@ class OTPD:
482
493
  return None
483
494
 
484
495
  # end method definition
496
+
497
+ def do_request(
498
+ self,
499
+ url: str,
500
+ method: str = "GET",
501
+ headers: dict | None = None,
502
+ data: dict | None = None,
503
+ json_data: dict | None = None,
504
+ files: dict | None = None,
505
+ timeout: float | None = REQUEST_TIMEOUT,
506
+ show_error: bool = True,
507
+ show_warning: bool = False,
508
+ warning_message: str = "",
509
+ failure_message: str = "",
510
+ success_message: str = "",
511
+ max_retries: int = REQUEST_MAX_RETRIES,
512
+ retry_forever: bool = False,
513
+ parse_request_response: bool = True,
514
+ ) -> dict | None:
515
+ """Call an OTDS REST API in a safe way.
516
+
517
+ Args:
518
+ url (str):
519
+ The URL to send the request to.
520
+ method (str, optional):
521
+ The HTTP method (GET, POST, etc.). Defaults to "GET".
522
+ headers (dict | None, optional):
523
+ The request headers. Defaults to None.
524
+ data (dict | None, optional):
525
+ Request payload. Defaults to None
526
+ json_data (dict | None, optional):
527
+ Request payload for the JSON parameter. Defaults to None.
528
+ files (dict | None, optional):
529
+ Dictionary of {"name": file-tuple} for multipart encoding upload.
530
+ File-tuple can be a 2-tuple ("filename", fileobj) or a 3-tuple ("filename", fileobj, "content_type")
531
+ timeout (int | None, optional):
532
+ The timeout for the request in seconds. Defaults to REQUEST_TIMEOUT.
533
+ show_error (bool, optional):
534
+ Whether or not an error should be logged in case of a failed REST call.
535
+ If False, then only a warning is logged. Defaults to True.
536
+ show_warning (bool, optional):
537
+ Whether or not an warning should be logged in case of a
538
+ failed REST call.
539
+ If False, then only a warning is logged. Defaults to True.
540
+ warning_message (str, optional):
541
+ Specific warning message. Defaults to "". If not given the error_message will be used.
542
+ failure_message (str, optional):
543
+ Specific error message. Defaults to "".
544
+ success_message (str, optional):
545
+ Specific success message. Defaults to "".
546
+ max_retries (int, optional):
547
+ How many retries on Connection errors? Default is REQUEST_MAX_RETRIES.
548
+ retry_forever (bool, optional):
549
+ Eventually wait forever - without timeout. Defaults to False.
550
+ parse_request_response (bool, optional):
551
+ Defines if the response.text should be interpreted as json and loaded into a dictionary.
552
+ True is the default.
553
+
554
+ Returns:
555
+ dict | None:
556
+ Response of OTDS REST API or None in case of an error.
557
+
558
+ """
559
+
560
+ if headers is None:
561
+ headers = REQUEST_HEADERS
562
+
563
+ # In case of an expired session we reauthenticate and
564
+ # try 1 more time. Session expiration should not happen
565
+ # twice in a row:
566
+ retries = 0
567
+
568
+ while True:
569
+ try:
570
+ response = requests.request(
571
+ method=method,
572
+ url=url,
573
+ data=data,
574
+ json=json_data,
575
+ files=files,
576
+ headers=headers,
577
+ timeout=timeout,
578
+ )
579
+
580
+ if response.ok:
581
+ if success_message:
582
+ self.logger.info(success_message)
583
+ if parse_request_response:
584
+ return self.parse_request_response(response)
585
+ else:
586
+ return response
587
+ else:
588
+ # Handle plain HTML responses to not pollute the logs
589
+ content_type = response.headers.get("content-type", None)
590
+ response_text = (
591
+ "HTML content (only printed in debug log)" if content_type == "text/html" else response.text
592
+ )
593
+
594
+ if show_error:
595
+ self.logger.error(
596
+ "%s; status -> %s/%s; error -> %s",
597
+ failure_message,
598
+ response.status_code,
599
+ HTTPStatus(response.status_code).phrase,
600
+ response_text,
601
+ )
602
+ elif show_warning:
603
+ self.logger.warning(
604
+ "%s; status -> %s/%s; warning -> %s",
605
+ warning_message if warning_message else failure_message,
606
+ response.status_code,
607
+ HTTPStatus(response.status_code).phrase,
608
+ response_text,
609
+ )
610
+ if content_type == "text/html":
611
+ self.logger.debug(
612
+ "%s; status -> %s/%s; warning -> %s",
613
+ failure_message,
614
+ response.status_code,
615
+ HTTPStatus(response.status_code).phrase,
616
+ response.text,
617
+ )
618
+ return None
619
+ except requests.exceptions.Timeout:
620
+ if retries <= max_retries:
621
+ self.logger.warning(
622
+ "Request timed out. Retrying in %s seconds...",
623
+ str(REQUEST_RETRY_DELAY),
624
+ )
625
+ retries += 1
626
+ time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
627
+ else:
628
+ self.logger.error(
629
+ "%s; timeout error.",
630
+ failure_message,
631
+ )
632
+ if retry_forever:
633
+ # If it fails after REQUEST_MAX_RETRIES retries we let it wait forever
634
+ self.logger.warning("Turn timeouts off and wait forever...")
635
+ timeout = None
636
+ else:
637
+ return None
638
+ except requests.exceptions.ConnectionError:
639
+ if retries <= max_retries:
640
+ self.logger.warning(
641
+ "Connection error. Retrying in %s seconds...",
642
+ str(REQUEST_RETRY_DELAY),
643
+ )
644
+ retries += 1
645
+ time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
646
+ else:
647
+ self.logger.error(
648
+ "%s; connection error.",
649
+ failure_message,
650
+ )
651
+ if retry_forever:
652
+ # If it fails after REQUEST_MAX_RETRIES retries we let it wait forever
653
+ self.logger.warning("Turn timeouts off and wait forever...")
654
+ timeout = None
655
+ time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
656
+ else:
657
+ return None
658
+ # end try
659
+ self.logger.info(
660
+ "Retrying REST API %s call -> %s... (retry = %s",
661
+ method,
662
+ url,
663
+ str(retries),
664
+ )
665
+ # end while True
666
+
667
+ # end method definition
668
+
669
+ def generate_document(self, payload: str) -> dict | None:
670
+ """Generate a PowerDocs document based on the provided XML payload.
671
+
672
+ Args:
673
+ payload (str):
674
+ The XML payload to generate the document.
675
+
676
+ Returns:
677
+ dict | None:
678
+ The request response or None in case of an error.
679
+
680
+ """
681
+
682
+ if not payload:
683
+ self.logger.error("Cannot generate PowerDocs document from empty payload!")
684
+ return None
685
+
686
+ url = self.config()["baseUrl"] + "/c4ApplicationServer/rest/document"
687
+
688
+ body = {"documentgeneration": payload}
689
+
690
+ response = self.do_request(
691
+ url=url,
692
+ method="POST",
693
+ headers=REQUEST_FORM_HEADERS,
694
+ data=body,
695
+ show_error=True,
696
+ failure_message="Failed to generate PowerDocs document",
697
+ parse_request_response=False,
698
+ )
699
+
700
+ return response
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyxecm
3
- Version: 3.0.1
3
+ Version: 3.1.1
4
4
  Summary: A Python library to interact with Opentext Content Management Rest API
5
5
  Project-URL: Homepage, https://github.com/opentext/pyxecm
6
6
  Author-email: Kai Gatzweiler <kgatzweiler@opentext.com>, "Dr. Marc Diefenbruch" <mdiefenb@opentext.com>
@@ -37,6 +37,7 @@ Requires-Dist: python-multipart>=0.0.20; extra == 'api'
37
37
  Requires-Dist: uvicorn>=0.35.0; extra == 'api'
38
38
  Provides-Extra: customizer
39
39
  Requires-Dist: kubernetes>=33.1.0; extra == 'customizer'
40
+ Requires-Dist: openpyxl>=3.1.5; extra == 'customizer'
40
41
  Requires-Dist: playwright>=1.53.0; extra == 'customizer'
41
42
  Requires-Dist: pydantic>=2.11.7; extra == 'customizer'
42
43
  Requires-Dist: python-hcl2>=7.2.1; extra == 'customizer'
@@ -0,0 +1,82 @@
1
+ pyxecm/__init__.py,sha256=KNvTFbDVHylf8Ub28osm5Dw0W-ck-94y27lIBuE32RE,441
2
+ pyxecm/avts.py,sha256=NEd1JdJCVmNohp_A-ZxAIMQZX5NCJFMhyKV6cdLFgTw,56960
3
+ pyxecm/coreshare.py,sha256=oz5SASlOC_e4-OIazFUrIA7gB6DfpC7RY4Ck7AHKcvQ,95999
4
+ pyxecm/otac.py,sha256=itsxIkIhIHdAiRCLRegmxCejE3kqpWxgwaJvAGNqzzg,22849
5
+ pyxecm/otawp.py,sha256=9kQghTIrnHP5I_GOmnAlvPhJk433b0dcdVzQHx0fSAM,112962
6
+ pyxecm/otca.py,sha256=_HJG84yFr00CvdgMlvTnmAYNs-6w1APbzUzZb5udkuk,99597
7
+ pyxecm/otcs.py,sha256=5YPdvkl8CBDMRNjVOkUDRce-GnBxaVGF98mAGakrvSM,819480
8
+ pyxecm/otds.py,sha256=gTyHwCSFVJbEia3h-xcPY0UL1fS58VxuI0l9uA3lC6c,191581
9
+ pyxecm/otiv.py,sha256=I5lt4sz7TN3W7BCstCKQY2WQnei-t0tXdM4QjRQRmWI,2358
10
+ pyxecm/otkd.py,sha256=3c37FwQ-KDBN48O1wZVxX46BiQDRtb2-KrXZ7HG54Fg,47488
11
+ pyxecm/otmm.py,sha256=MC4roK8SNG3RPj7Nb-iwn8siH9CLLmhwTmw-84i6zm4,80733
12
+ pyxecm/otpd.py,sha256=MXtgaRMEgVXBbc6hPm9tXq--NpzYAkSMnaL_p2H4b7E,23875
13
+ pyxecm/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ pyxecm/helper/__init__.py,sha256=B35HdIZC9wZ1m_sx4NOPggyFFJoXmFuA1T2tuPDU1W8,246
15
+ pyxecm/helper/assoc.py,sha256=yOh4GFZEkTfIaVVrHBr4bhMtIv-E76oDVWGqaUdVqyA,7321
16
+ pyxecm/helper/data.py,sha256=L92y9dagzJLwdfZW-ISUemkJEuGFo8kGz3r18c01GPc,123363
17
+ pyxecm/helper/logadapter.py,sha256=ExrIKpA2JKegkTIky0wDDTgPYSvVmwQLrDh5jko_6hQ,724
18
+ pyxecm/helper/otel_config.py,sha256=nMzNi3Jv2_kmYG4thzDpN95bqjy6MbRfV6nlpIZ6SjQ,907
19
+ pyxecm/helper/web.py,sha256=YqR72jWSeg3dOEdhN6b09s-kSnHbTjDIl56QC5NvNtE,13386
20
+ pyxecm/helper/xml.py,sha256=6FJ-ApiJmBBo6IwtnWj93DVB016ZTSlQIMXQNvFumOI,47091
21
+ pyxecm_api/__init__.py,sha256=8oXxEEFSu2afpyQURpxMA2qTZAB3MUdbBrndogDn5Oc,92
22
+ pyxecm_api/__main__.py,sha256=Q2tcaoSExPMUwGVBrE4AHT8DNvB4XIgwoN4EPDx5Rt8,117
23
+ pyxecm_api/app.py,sha256=OkBipbCPeDM8KO-LvehYKdSyXcXvVHSwz4vXx8CZdZY,7256
24
+ pyxecm_api/settings.py,sha256=Zu4LnBsCUm9bi7riE_uP2JynWkSmUjJtGrLmSZQXpRg,5535
25
+ pyxecm_api/auth/__init__.py,sha256=ranS8DEliiC4Mlo-bVva9Maj5q08I0I6NJflUFIvOvw,19
26
+ pyxecm_api/auth/functions.py,sha256=hdu4XPS975r8LsbH_3OKNQOEMvnbD40zhRh1nO1hIyQ,3331
27
+ pyxecm_api/auth/models.py,sha256=lKebaIHbALZ10quCCKQ3wf7w8V6k84tFXcPV1zbQsS0,271
28
+ pyxecm_api/auth/router.py,sha256=H58owZMM9lcskUITWSJPZYQr6IM66MyV7h9G36HPpQM,2140
29
+ pyxecm_api/common/__init__.py,sha256=ranS8DEliiC4Mlo-bVva9Maj5q08I0I6NJflUFIvOvw,19
30
+ pyxecm_api/common/functions.py,sha256=snu6bFNvky4JUCRbWdeE64wUjIH2NWVihXbf9nfTpsQ,5209
31
+ pyxecm_api/common/metrics.py,sha256=kOJ1DZveZJ7xFd1pB12Zbkq6Z-evaTivpgO_ujVbsxM,2707
32
+ pyxecm_api/common/models.py,sha256=c76ysWUt40A875DirsFMXttxwjHvBYvjuOVEXePSZ9k,456
33
+ pyxecm_api/common/router.py,sha256=BXi3SMvkswCIftoVkJIP_Wx8gd3WKJsE3rq6Pult50I,3482
34
+ pyxecm_api/terminal/__init__.py,sha256=RHlTzdGeOY0_dvvNZS_wq6uJcY1OatIUHwCxAUwklaE,43
35
+ pyxecm_api/terminal/router.py,sha256=cYL93inAB9qux-IFg-TpZjdRHWF_OSkbABlXyG69ukc,3582
36
+ pyxecm_api/v1_csai/__init__.py,sha256=ranS8DEliiC4Mlo-bVva9Maj5q08I0I6NJflUFIvOvw,19
37
+ pyxecm_api/v1_csai/models.py,sha256=c0VEJP09jsTW83dpHtZnooNvg3SZNuyMHEBCgWQDJ1s,489
38
+ pyxecm_api/v1_csai/router.py,sha256=5MuzlVzUTEjgBTE2yGtvplt2jekxS4zcl_lqUcz3LVU,5198
39
+ pyxecm_api/v1_csai/statics/bindings/utils.js,sha256=MRPHMxfDDJnqr7x4KRYWDOfSMYOhSGFdoElmQ-lWDLA,6311
40
+ pyxecm_api/v1_csai/statics/tom-select/tom-select.complete.min.js,sha256=f2dQEyWhXp7M5o3MsmgS6zJ_Gq4pXFxSxSTsYrPxPc0,44776
41
+ pyxecm_api/v1_csai/statics/tom-select/tom-select.css,sha256=JgqUGftl29aF9fEJ-ObePYuXmhVAUK-Tp2GiLTTt54Q,9328
42
+ pyxecm_api/v1_csai/statics/vis-9.1.2/vis-network.css,sha256=LoLURa1YeOqIFlJHDOYyYB-PVfG5nm6-zf-GFGAObQ4,220163
43
+ pyxecm_api/v1_csai/statics/vis-9.1.2/vis-network.min.js,sha256=HyDwc28yy5vt-PY4OyXP6i-Dnhq-gNboBAtNW-o3jGk,468813
44
+ pyxecm_api/v1_maintenance/__init__.py,sha256=ranS8DEliiC4Mlo-bVva9Maj5q08I0I6NJflUFIvOvw,19
45
+ pyxecm_api/v1_maintenance/functions.py,sha256=NDU08aHzPi7KXxKMA4KPHwc-LkpiqqwZU7Wif8yjaJk,3001
46
+ pyxecm_api/v1_maintenance/models.py,sha256=HcrhBg9hhRZg4Y6xuus9T8SCNIsL8ZxX1uuaRrBnFBw,271
47
+ pyxecm_api/v1_maintenance/router.py,sha256=gL_f_-9Slq7yzzOO1uS1aB8N3GduKnm1__7b6cSH7VI,2238
48
+ pyxecm_api/v1_otcs/__init__.py,sha256=ranS8DEliiC4Mlo-bVva9Maj5q08I0I6NJflUFIvOvw,19
49
+ pyxecm_api/v1_otcs/functions.py,sha256=YKC4b6nVtlnBGcu2WqSRLmS3faXagkAokrBMXUblkJE,2012
50
+ pyxecm_api/v1_otcs/router.py,sha256=VCtGmObA2TVJREwx4mA047SzSpsJlfxhRXA6DdlppZw,7055
51
+ pyxecm_api/v1_payload/__init__.py,sha256=ranS8DEliiC4Mlo-bVva9Maj5q08I0I6NJflUFIvOvw,19
52
+ pyxecm_api/v1_payload/functions.py,sha256=xA06ACS4NWUut0FXfrw6Ww-R94y43PVSESyEiZEhKAA,6295
53
+ pyxecm_api/v1_payload/models.py,sha256=eD9A2K23L_cGhBDTO1FGVGJMQ1COaYWmcr-ELE66tOA,1006
54
+ pyxecm_api/v1_payload/router.py,sha256=2twiLBeNNDGIEQ6SCZwLFgd8oYOzgbskS-Q3OL-lKe4,15441
55
+ pyxecm_customizer/__init__.py,sha256=x2NhDlNcubRC-jXzqT02j9kQGXBo36QAehjmcQuSbXw,721
56
+ pyxecm_customizer/__main__.py,sha256=QGQlJJAdLmJccArwPT8XEhBOMSJ-Z8gwLrtHPNfL8Ps,1407
57
+ pyxecm_customizer/browser_automation.py,sha256=gWYJVkVMOer7LyQNq_lwyCb0-Ixaf3VaWM7WrpqKMKY,71360
58
+ pyxecm_customizer/customizer.py,sha256=Nzz3wndPp9r5GVpOzLtdNUOuIsCZ2GYdIS1HhWdboBM,84938
59
+ pyxecm_customizer/exceptions.py,sha256=YXX0in-UGXfJb6Fei01JQetOtAHqUiITb49OTSaZRkE,909
60
+ pyxecm_customizer/guidewire.py,sha256=u_mGZU5Nedm-cGf5hUCWE38U-75qoqwi7cq-G3qHty0,67167
61
+ pyxecm_customizer/k8s.py,sha256=DcxLKKuvV3S4DYqP4jUygn32aeGtD0c1FOXumZzqqdY,55837
62
+ pyxecm_customizer/knowledge_graph.py,sha256=Jdm-WVftunoLztinWSPUNhRZ8DhRAVIEcQJJz57opGk,46429
63
+ pyxecm_customizer/log.py,sha256=2DmGF3b-ZIOAJCPSmZmxGD7BNxY4-mZm0H_X0HeJedE,916
64
+ pyxecm_customizer/m365.py,sha256=xQGdY9ti44e93c5tPbgCHjvrM-SjQr2F1iApJMb0-Pk,213957
65
+ pyxecm_customizer/payload.py,sha256=bMyXLMBlio1WuLgKfy9z8LICxFSKg05JDy8qrxRn6M4,1366630
66
+ pyxecm_customizer/payload_list.py,sha256=MCpCzarmQ-5u7FSW7djNOMSjv373Ltx-9rEDIuhEboI,28266
67
+ pyxecm_customizer/salesforce.py,sha256=lGkQcEYg5YIWc7SF2TELT6yfgrn4pnbCrf8wXGk-PMc,64557
68
+ pyxecm_customizer/sap.py,sha256=lD_riOZhYjbZ0_pUZyqhxP6guzBM__TcUjZhSgDowoE,6506
69
+ pyxecm_customizer/servicenow.py,sha256=K26DQiXd-SMMyft9CLCKtw_vb3oKf6VGdTEzm7XMqYI,66292
70
+ pyxecm_customizer/settings.py,sha256=SPm6sBFiMMuFquNwKBnkUcqPZLtVBQ5K6CGjsLJNaOc,21409
71
+ pyxecm_customizer/successfactors.py,sha256=CcWGXwYcqp4jJ805XQZfY50iXcCEiJg6uVUq5AjRqXs,38661
72
+ pyxecm_customizer/translate.py,sha256=TViU9Erp4lXzZi2jQSmBpjVWS0kIW-I_Y5C0z25z-tQ,4960
73
+ pyxecm_maintenance_page/__init__.py,sha256=09to4a8rygOIN6Z1SCN9tLtW1qPUC78Z-scDbpt0E-Q,136
74
+ pyxecm_maintenance_page/__main__.py,sha256=N_0tw2upUPQZhTYCaIr75LogyaEsp-mlZCon-lIyaxM,158
75
+ pyxecm_maintenance_page/app.py,sha256=pTOeZfgPPq6BT7P8naUjW-ZT9dXqwX6DWazIVL-9Fkc,1997
76
+ pyxecm_maintenance_page/settings.py,sha256=VRReZeNdza7i7lgnQ3wVojzoPDGXZnzr5rsMJY1EnHk,955
77
+ pyxecm_maintenance_page/static/favicon.avif,sha256=POuuPXKbjHVP3BjNLpFIx8MfkQg5z2LZA7sK6lejARg,1543
78
+ pyxecm_maintenance_page/templates/maintenance.html,sha256=0OAinv7jmj3Aa7GNCIoBLDGEMW1-_HdJfwWmkmb6Cs4,5581
79
+ pyxecm-3.1.1.dist-info/METADATA,sha256=9ZjC6c_J6K4UBCgohLC_Fz9THffRsXPPq41fPl5Wpoo,4566
80
+ pyxecm-3.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
81
+ pyxecm-3.1.1.dist-info/entry_points.txt,sha256=prc1mDdpd3bQk98VRBozI363mDUgSwDibDKXGNqKqgI,151
82
+ pyxecm-3.1.1.dist-info/RECORD,,
pyxecm_api/app.py CHANGED
@@ -8,7 +8,6 @@ __email__ = "mdiefenb@opentext.com"
8
8
 
9
9
  import logging
10
10
  import os
11
- import threading
12
11
  from collections.abc import AsyncGenerator
13
12
  from contextlib import asynccontextmanager
14
13
  from datetime import UTC, datetime
@@ -23,9 +22,6 @@ from prometheus_fastapi_instrumentator import Instrumentator
23
22
  from pyxecm.helper.otel_config import tracer
24
23
  from pyxecm_maintenance_page import run_maintenance_page
25
24
 
26
- from .agents.app import agent_routers
27
- from .agents.functions import register_all
28
- from .agents.otcm_knowledgegraph.functions import build_graph
29
25
  from .auth.router import router as auth_router
30
26
  from .common.functions import PAYLOAD_LIST
31
27
  from .common.metrics import payload_logs_by_payload, payload_logs_total
@@ -50,31 +46,20 @@ if os.path.isfile(os.path.join(api_settings.logfolder, api_settings.logfile)):
50
46
  elif not os.path.exists(api_settings.logfolder):
51
47
  os.makedirs(api_settings.logfolder)
52
48
 
53
- handlers = [logging.FileHandler(os.path.join(api_settings.logfolder, api_settings.logfile))]
54
- if api_settings.log_payload_processing:
55
- handlers.append(logging.StreamHandler())
56
-
57
- logging.basicConfig(
58
- format="%(asctime)s %(levelname)s [%(name)s] [%(threadName)s] %(message)s",
59
- datefmt="%d-%b-%Y %H:%M:%S",
60
- level=api_settings.loglevel,
61
- handlers=handlers,
62
- )
63
-
64
49
 
65
50
  @asynccontextmanager
66
51
  async def lifespan(
67
52
  app: FastAPI, # noqa: ARG001
68
53
  ) -> AsyncGenerator:
69
- """Lifespan Method for FASTAPI to handle the startup and shutdown process.
54
+ """Lifespan function for FASTAPI to handle the startup and shutdown process.
70
55
 
71
56
  Args:
72
57
  app (FastAPI):
73
- The application.
58
+ The FastAPI application.
74
59
 
75
60
  """
76
61
 
77
- logger.debug("Settings -> %s", api_settings)
62
+ logger.debug("API settings -> %s", api_settings)
78
63
 
79
64
  with tracer.start_as_current_span("import_payloads"):
80
65
  if api_settings.import_payload:
@@ -89,20 +74,18 @@ async def lifespan(
89
74
  # Optional Payload
90
75
  import_payload(payload_dir=api_settings.payload_dir_optional)
91
76
 
92
- logger.info("Starting maintenance_page thread...")
77
+ logger.info("Starting maintenance page thread...")
93
78
  if api_settings.maintenance_page:
94
79
  run_maintenance_page()
95
80
 
96
- if api_settings.csai_studio_integration:
97
- logger.info("Registering Content Aviator tools...")
98
- register_all()
99
- threading.Thread(name="KnowledgeGraph", target=build_graph).start()
100
-
101
81
  yield
82
+
102
83
  logger.info("Shutdown")
103
84
  PAYLOAD_LIST.stop_payload_processing()
104
85
 
105
86
 
87
+ # end function lifespan
88
+
106
89
  app = FastAPI(
107
90
  docs_url="/api",
108
91
  title=api_settings.title,
@@ -114,7 +97,7 @@ app = FastAPI(
114
97
  openapi_tags=[
115
98
  {
116
99
  "name": "auth",
117
- "description": "Authentication Endpoint - Users are authenticated against Opentext Directory Services",
100
+ "description": "Authentication Endpoint - Users are authenticated against OpenText Directory Services",
118
101
  },
119
102
  {
120
103
  "name": "payload",
@@ -156,8 +139,6 @@ if api_settings.csai:
156
139
  )
157
140
 
158
141
  app.include_router(router=v1_csai_router)
159
- for agent_router in agent_routers:
160
- app.include_router(prefix="/agents", router=agent_router)
161
142
 
162
143
 
163
144
  ## Add Prometheus Instrumentator for /metrics,
@@ -170,7 +151,7 @@ if api_settings.metrics:
170
151
 
171
152
  ## Start the API Server
172
153
  def run_api() -> None:
173
- """Start the FASTAPI Webserver."""
154
+ """Start the FastAPI Webserver."""
174
155
 
175
156
  # Check if Temp and Log dir exists
176
157
  if not os.path.exists(api_settings.temp_dir):
@@ -183,27 +164,56 @@ def run_api() -> None:
183
164
  customizer_start_time = datetime.now(UTC).strftime(
184
165
  "%Y-%m-%d_%H-%M",
185
166
  )
186
- api_settings.logfile = f"customizer_{customizer_start_time}.log"
167
+ api_settings.logfile = "customizer_{}.log".format(customizer_start_time)
187
168
 
188
169
  # Configure Logging for uvicorn
189
170
  log_config = uvicorn.config.LOGGING_CONFIG
190
171
 
191
172
  # Stdout
192
- log_config["formatters"]["pyxecm"] = {
173
+ log_config["formatters"]["standard"] = {
193
174
  "()": "uvicorn.logging.DefaultFormatter",
194
175
  "fmt": "%(levelprefix)s [%(name)s] [%(threadName)s] %(message)s",
195
176
  "use_colors": True,
196
177
  }
197
- log_config["handlers"]["pyxecm"] = {
198
- "formatter": "pyxecm",
178
+
179
+ log_config["formatters"]["logfile"] = {
180
+ "()": "uvicorn.logging.DefaultFormatter",
181
+ "fmt": "%(asctime)s %(levelname)s [%(name)s] [%(threadName)s] %(message)s",
182
+ "datefmt": "%d-%b-%Y %H:%M:%S",
183
+ "use_colors": True,
184
+ }
185
+
186
+ log_config["formatters"]["accesslog"] = {
187
+ "()": "uvicorn.logging.AccessFormatter",
188
+ "fmt": '%(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s',
189
+ "use_colors": False,
190
+ }
191
+
192
+ log_config["handlers"]["console"] = {
193
+ "formatter": "standard",
199
194
  "class": "logging.StreamHandler",
200
195
  "stream": "ext://sys.stdout",
201
196
  }
202
197
 
203
- log_config["loggers"]["pyxecm"] = {
204
- "handlers": ["pyxecm"],
198
+ log_config["handlers"]["file"] = {
199
+ "formatter": "logfile",
200
+ "class": "logging.FileHandler",
201
+ "filename": os.path.join(api_settings.logfolder, api_settings.logfile),
202
+ "mode": "a",
203
+ }
204
+
205
+ log_config["handlers"]["accesslog"] = {
206
+ "formatter": "accesslog",
207
+ "class": "logging.FileHandler",
208
+ "filename": os.path.join(api_settings.logfolder, "access.log"),
209
+ "mode": "a",
210
+ }
211
+
212
+ log_config["loggers"]["uvicorn.access"]["handlers"] = ["access", "accesslog"]
213
+
214
+ log_config["loggers"]["root"] = {
205
215
  "level": api_settings.loglevel,
206
- "propagate": False,
216
+ "handlers": ["console", "file"],
207
217
  }
208
218
 
209
219
  logger.info("Starting processing thread...")
@@ -27,8 +27,8 @@ def get_groups(response: dict, token: str) -> list:
27
27
  """Get the groups of the user.
28
28
 
29
29
  Args:
30
- response (_type_): _description_
31
- token (_type_): _description_
30
+ response (dict): _description_
31
+ token (str): _description_
32
32
 
33
33
  Returns:
34
34
  list: _description_
pyxecm_api/auth/router.py CHANGED
@@ -64,9 +64,8 @@ async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]) -> J
64
64
  async def read_users_me(current_user: Annotated[User, Depends(get_current_user)]) -> JSONResponse:
65
65
  """Get the current user.
66
66
 
67
- current_user:
68
- type: User
69
- description: The current user.
67
+ current_user (User):
68
+ The current user.
70
69
 
71
70
  """
72
71