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.
- pyxecm/__init__.py +6 -4
- pyxecm/avts.py +673 -246
- pyxecm/coreshare.py +686 -467
- pyxecm/customizer/__init__.py +16 -4
- pyxecm/customizer/__main__.py +58 -0
- pyxecm/customizer/api/__init__.py +5 -0
- pyxecm/customizer/api/__main__.py +6 -0
- pyxecm/customizer/api/app.py +914 -0
- pyxecm/customizer/api/auth.py +154 -0
- pyxecm/customizer/api/metrics.py +92 -0
- pyxecm/customizer/api/models.py +13 -0
- pyxecm/customizer/api/payload_list.py +865 -0
- pyxecm/customizer/api/settings.py +103 -0
- pyxecm/customizer/browser_automation.py +332 -139
- pyxecm/customizer/customizer.py +1007 -1130
- pyxecm/customizer/exceptions.py +35 -0
- pyxecm/customizer/guidewire.py +322 -0
- pyxecm/customizer/k8s.py +713 -378
- pyxecm/customizer/log.py +107 -0
- pyxecm/customizer/m365.py +2867 -909
- pyxecm/customizer/nhc.py +1169 -0
- pyxecm/customizer/openapi.py +258 -0
- pyxecm/customizer/payload.py +16817 -7467
- pyxecm/customizer/pht.py +699 -285
- pyxecm/customizer/salesforce.py +516 -342
- pyxecm/customizer/sap.py +58 -41
- pyxecm/customizer/servicenow.py +593 -371
- pyxecm/customizer/settings.py +442 -0
- pyxecm/customizer/successfactors.py +408 -346
- pyxecm/customizer/translate.py +83 -48
- pyxecm/helper/__init__.py +5 -2
- pyxecm/helper/assoc.py +83 -43
- pyxecm/helper/data.py +2406 -870
- pyxecm/helper/logadapter.py +27 -0
- pyxecm/helper/web.py +229 -101
- pyxecm/helper/xml.py +527 -171
- pyxecm/maintenance_page/__init__.py +5 -0
- pyxecm/maintenance_page/__main__.py +6 -0
- pyxecm/maintenance_page/app.py +51 -0
- pyxecm/maintenance_page/settings.py +28 -0
- pyxecm/maintenance_page/static/favicon.avif +0 -0
- pyxecm/maintenance_page/templates/maintenance.html +165 -0
- pyxecm/otac.py +234 -140
- pyxecm/otawp.py +1436 -557
- pyxecm/otcs.py +7716 -3161
- pyxecm/otds.py +2150 -919
- pyxecm/otiv.py +36 -21
- pyxecm/otmm.py +1272 -325
- pyxecm/otpd.py +231 -127
- pyxecm-2.0.0.dist-info/METADATA +145 -0
- pyxecm-2.0.0.dist-info/RECORD +54 -0
- {pyxecm-1.6.dist-info → pyxecm-2.0.0.dist-info}/WHEEL +1 -1
- pyxecm-1.6.dist-info/METADATA +0 -53
- pyxecm-1.6.dist-info/RECORD +0 -32
- {pyxecm-1.6.dist-info → pyxecm-2.0.0.dist-info/licenses}/LICENSE +0 -0
- {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()
|