pyxecm 1.6__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pyxecm might be problematic. Click here for more details.

Files changed (56) hide show
  1. pyxecm/__init__.py +6 -4
  2. pyxecm/avts.py +673 -246
  3. pyxecm/coreshare.py +686 -467
  4. pyxecm/customizer/__init__.py +16 -4
  5. pyxecm/customizer/__main__.py +58 -0
  6. pyxecm/customizer/api/__init__.py +5 -0
  7. pyxecm/customizer/api/__main__.py +6 -0
  8. pyxecm/customizer/api/app.py +914 -0
  9. pyxecm/customizer/api/auth.py +154 -0
  10. pyxecm/customizer/api/metrics.py +92 -0
  11. pyxecm/customizer/api/models.py +13 -0
  12. pyxecm/customizer/api/payload_list.py +865 -0
  13. pyxecm/customizer/api/settings.py +103 -0
  14. pyxecm/customizer/browser_automation.py +332 -139
  15. pyxecm/customizer/customizer.py +1007 -1130
  16. pyxecm/customizer/exceptions.py +35 -0
  17. pyxecm/customizer/guidewire.py +322 -0
  18. pyxecm/customizer/k8s.py +713 -378
  19. pyxecm/customizer/log.py +107 -0
  20. pyxecm/customizer/m365.py +2867 -909
  21. pyxecm/customizer/nhc.py +1169 -0
  22. pyxecm/customizer/openapi.py +258 -0
  23. pyxecm/customizer/payload.py +16817 -7467
  24. pyxecm/customizer/pht.py +699 -285
  25. pyxecm/customizer/salesforce.py +516 -342
  26. pyxecm/customizer/sap.py +58 -41
  27. pyxecm/customizer/servicenow.py +593 -371
  28. pyxecm/customizer/settings.py +442 -0
  29. pyxecm/customizer/successfactors.py +408 -346
  30. pyxecm/customizer/translate.py +83 -48
  31. pyxecm/helper/__init__.py +5 -2
  32. pyxecm/helper/assoc.py +83 -43
  33. pyxecm/helper/data.py +2406 -870
  34. pyxecm/helper/logadapter.py +27 -0
  35. pyxecm/helper/web.py +229 -101
  36. pyxecm/helper/xml.py +527 -171
  37. pyxecm/maintenance_page/__init__.py +5 -0
  38. pyxecm/maintenance_page/__main__.py +6 -0
  39. pyxecm/maintenance_page/app.py +51 -0
  40. pyxecm/maintenance_page/settings.py +28 -0
  41. pyxecm/maintenance_page/static/favicon.avif +0 -0
  42. pyxecm/maintenance_page/templates/maintenance.html +165 -0
  43. pyxecm/otac.py +234 -140
  44. pyxecm/otawp.py +1436 -557
  45. pyxecm/otcs.py +7716 -3161
  46. pyxecm/otds.py +2150 -919
  47. pyxecm/otiv.py +36 -21
  48. pyxecm/otmm.py +1272 -325
  49. pyxecm/otpd.py +231 -127
  50. pyxecm-2.0.0.dist-info/METADATA +145 -0
  51. pyxecm-2.0.0.dist-info/RECORD +54 -0
  52. {pyxecm-1.6.dist-info → pyxecm-2.0.0.dist-info}/WHEEL +1 -1
  53. pyxecm-1.6.dist-info/METADATA +0 -53
  54. pyxecm-1.6.dist-info/RECORD +0 -32
  55. {pyxecm-1.6.dist-info → pyxecm-2.0.0.dist-info/licenses}/LICENSE +0 -0
  56. {pyxecm-1.6.dist-info → pyxecm-2.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,258 @@
1
+ """Module to implement OpenAPI Support for OTCS."""
2
+
3
+ __author__ = "Dr. Marc Diefenbruch"
4
+ __copyright__ = "Copyright (C) 2024-2025, OpenText"
5
+ __credits__ = ["Kai-Philip Gatzweiler"]
6
+ __maintainer__ = "Dr. Marc Diefenbruch"
7
+ __email__ = "mdiefenb@opentext.com"
8
+
9
+ import logging
10
+ import os
11
+ import sys
12
+ import tempfile
13
+
14
+ import httpx
15
+ import uvicorn
16
+ from decouple import config
17
+ from fastapi import FastAPI, HTTPException, Request
18
+ from pydantic import BaseModel, Field, HttpUrl
19
+
20
+ from pyxecm import OTCS
21
+
22
+ app = FastAPI()
23
+
24
+ PROTOCOL = config("PROTOCOL", default="https")
25
+ DOMAIN = config("DOMAIN", "master.terrarium.cloud")
26
+ HOSTNAME = config("OTCS_HOST", f"otcs-admin.{DOMAIN}")
27
+ PUBLIC_URL = PROTOCOL + "://" + HOSTNAME
28
+ USERNAME = config("USERNAME")
29
+ PASSWORD = config("PASSWORD")
30
+ PORT = config("PORT", default=443, cast=int)
31
+ BASE_PATH = config("BASE_PATH", default="/cs/cs")
32
+ REST_API = PUBLIC_URL + BASE_PATH + "/api"
33
+
34
+ otcs_object = OTCS(
35
+ protocol=PROTOCOL,
36
+ hostname=HOSTNAME,
37
+ port=PORT,
38
+ public_url=PUBLIC_URL,
39
+ username=USERNAME,
40
+ password=PASSWORD,
41
+ thread_number=1,
42
+ download_dir=os.path.join(tempfile.gettempdir(), "ollie"),
43
+ base_path=BASE_PATH,
44
+ )
45
+
46
+ cookie = otcs_object.authenticate(wait_for_ready=False)
47
+ if not cookie:
48
+ exception = "Authentication failed - exit"
49
+ sys.exit(1)
50
+
51
+ logging.basicConfig(
52
+ format="%(asctime)s %(levelname)s [%(name)s] [%(threadName)s] %(message)s",
53
+ datefmt="%d-%b-%Y %H:%M:%S",
54
+ level=logging.INFO,
55
+ handlers=[
56
+ logging.FileHandler(os.path.join(tempfile.gettempdir(), "customizing_api.log")),
57
+ logging.StreamHandler(sys.stdout),
58
+ ],
59
+ )
60
+
61
+
62
+ @app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
63
+ async def proxy(request: Request, path: str) -> dict | HTTPException:
64
+ """Proxy the request to the backend.
65
+
66
+ Args:
67
+ request (Request): _description_
68
+ path (str): _description_
69
+
70
+ Raises:
71
+ HTTPException: _description_
72
+
73
+ Returns:
74
+ _type_: _description_
75
+
76
+ """
77
+ async with httpx.AsyncClient() as client:
78
+ backend_url = f"{REST_API}/{path}"
79
+
80
+ # Forward headers, query params, and body
81
+ headers = dict(request.headers) | cookie
82
+ query_params = request.query_params
83
+ body = await request.body()
84
+
85
+ try:
86
+ response = await client.request(
87
+ method=request.method,
88
+ url=backend_url,
89
+ headers=headers,
90
+ params=query_params,
91
+ content=body,
92
+ )
93
+ return response.json() # Return the backend response
94
+ except httpx.RequestError as e:
95
+ raise HTTPException(status_code=502, detail=f"Backend error: {e}") from e
96
+
97
+
98
+ class UserData(BaseModel):
99
+ """Response data for users.
100
+
101
+ Args:
102
+ BaseModel: pydantic base model
103
+
104
+ """
105
+
106
+ birth_date: str | None = Field(
107
+ None,
108
+ title="Birth Date",
109
+ description="The user's date of birth, if available.",
110
+ )
111
+ business_email: str | None = Field(
112
+ None,
113
+ title="Business Email",
114
+ description="The user's business email address.",
115
+ )
116
+ business_fax: str | None = Field(
117
+ None,
118
+ title="Business Fax",
119
+ description="The user's business fax number, if available.",
120
+ )
121
+ business_phone: str | None = Field(
122
+ None,
123
+ title="Business Phone",
124
+ description="The user's business phone number, if available.",
125
+ )
126
+ cell_phone: str | None = Field(
127
+ None,
128
+ title="Cell Phone",
129
+ description="The user's cell phone number, if available.",
130
+ )
131
+ deleted: bool = Field(
132
+ ...,
133
+ title="Deleted",
134
+ description="Indicates whether the user's record is deleted.",
135
+ )
136
+ display_language: str | None = Field(
137
+ None,
138
+ title="Display Language",
139
+ description="The preferred display language of the user.",
140
+ )
141
+ first_name: str = Field(
142
+ ...,
143
+ title="First Name",
144
+ description="The user's first name.",
145
+ )
146
+ gender: str | None = Field(
147
+ None,
148
+ title="Gender",
149
+ description="The gender of the user, if specified.",
150
+ )
151
+ group_id: int = Field(
152
+ ...,
153
+ title="Group ID",
154
+ description="The unique identifier for the group the user belongs to.",
155
+ )
156
+ home_address_1: str | None = Field(
157
+ None,
158
+ title="Home Address Line 1",
159
+ description="The first line of the user's home address, if available.",
160
+ )
161
+ home_address_2: str | None = Field(
162
+ None,
163
+ title="Home Address Line 2",
164
+ description="The second line of the user's home address, if available.",
165
+ )
166
+ home_fax: str | None = Field(
167
+ None,
168
+ title="Home Fax",
169
+ description="The user's home fax number, if available.",
170
+ )
171
+ home_phone: str | None = Field(
172
+ None,
173
+ title="Home Phone",
174
+ description="The users's home phone number, if available.",
175
+ )
176
+ user_id: int = Field(
177
+ ...,
178
+ title="User ID",
179
+ description="The unique identifier for the user.",
180
+ )
181
+ initials: str | None = Field(
182
+ None,
183
+ title="Initials",
184
+ description="The user's initials, if specified.",
185
+ )
186
+ last_name: str = Field(..., title="Last Name", description="The user's last name.")
187
+ middle_name: str | None = Field(
188
+ None,
189
+ title="Middle Name",
190
+ description="The user's middle name, if specified.",
191
+ )
192
+ name: str = Field(
193
+ ...,
194
+ title="Username",
195
+ description="The user's username or login name.",
196
+ )
197
+ name_formatted: str = Field(
198
+ ...,
199
+ title="Formatted Name",
200
+ description="The user's full name in formatted form.",
201
+ )
202
+ photo_id: int = Field(
203
+ ...,
204
+ title="Photo ID",
205
+ description="The unique identifier for the user's photo.",
206
+ )
207
+ photo_url: HttpUrl | None = Field(
208
+ None,
209
+ title="Photo URL",
210
+ description="The URL to the user's photo, if available.",
211
+ )
212
+ type: int = Field(
213
+ ...,
214
+ title="Type",
215
+ description="The type of member, represented as an integer.",
216
+ )
217
+ type_name: str = Field(
218
+ ...,
219
+ title="Type Name",
220
+ description="The type of user, represented as a descriptive name.",
221
+ )
222
+
223
+
224
+ class UserResponse(BaseModel):
225
+ """User Response Model."""
226
+
227
+ results: list[UserData] = Field(
228
+ ...,
229
+ title="Results",
230
+ description="A list of user data.",
231
+ )
232
+
233
+
234
+ @app.get("/v2/members/{user_id}", response_model=UserResponse)
235
+ async def get_user(user_id: int) -> dict | list:
236
+ """Get the user.
237
+
238
+ Args:
239
+ user_id (int): _description_
240
+
241
+ Returns:
242
+ dict | list: _description_
243
+
244
+ """
245
+ backend_url = f"{REST_API}/v2/members/{user_id}"
246
+ async with httpx.AsyncClient() as client:
247
+ response = await client.get(backend_url)
248
+ response.raise_for_status()
249
+ return response.json()
250
+
251
+
252
+ def run_api() -> None:
253
+ """Start the OpenAPI Proxy Server."""
254
+ uvicorn.run("pyxecm.customizer.openapi:app", host="0.0.0.0", port=8000) # noqa: S104
255
+
256
+
257
+ if __name__ == "__main__":
258
+ run_api()