pyegeria 0.2.4__py3-none-any.whl → 0.3.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.
- pyegeria/__init__.py +13 -8
- pyegeria/_client.py +164 -105
- pyegeria/_exceptions.py +2 -1
- pyegeria/_validators.py +2 -2
- pyegeria/automated_curation_omvs.py +2188 -0
- pyegeria/core_omag_server_config.py +38 -7
- pyegeria/full_omag_server_config.py +1075 -0
- pyegeria/glossary_omvs.py +204 -66
- pyegeria/gov_engine.py +92 -201
- pyegeria/governance_author.py +184 -0
- pyegeria/my_profile_omvs.py +976 -0
- pyegeria/platform_services.py +67 -35
- pyegeria/server_operations.py +92 -25
- pyegeria/utils.py +5 -17
- {pyegeria-0.2.4.dist-info → pyegeria-0.3.1.dist-info}/METADATA +22 -18
- pyegeria-0.3.1.dist-info/RECORD +21 -0
- {pyegeria-0.2.4.dist-info → pyegeria-0.3.1.dist-info}/WHEEL +2 -1
- pyegeria-0.3.1.dist-info/top_level.txt +1 -0
- pyegeria/config.toml +0 -11
- pyegeria/curation_omvs.py +0 -458
- pyegeria/exceptions.py +0 -382
- pyegeria-0.2.4.dist-info/RECORD +0 -19
- {pyegeria-0.2.4.dist-info/licenses → pyegeria-0.3.1.dist-info}/LICENSE +0 -0
pyegeria/__init__.py
CHANGED
@@ -11,29 +11,34 @@ the server platform and servers.
|
|
11
11
|
|
12
12
|
"""
|
13
13
|
|
14
|
-
|
14
|
+
|
15
15
|
try:
|
16
16
|
import tomllib
|
17
17
|
except ModuleNotFoundError:
|
18
18
|
import tomli as tomllib
|
19
19
|
|
20
|
-
from ._globals import is_debug, disable_ssl_warnings
|
20
|
+
from ._globals import is_debug, disable_ssl_warnings, max_paging_size
|
21
21
|
|
22
22
|
if disable_ssl_warnings:
|
23
23
|
from urllib3.exceptions import InsecureRequestWarning
|
24
24
|
from urllib3 import disable_warnings
|
25
25
|
disable_warnings(InsecureRequestWarning)
|
26
26
|
|
27
|
-
from .
|
28
|
-
|
29
|
-
from .utils import print_response
|
27
|
+
from ._exceptions import (InvalidParameterException, PropertyServerException, UserNotAuthorizedException,
|
28
|
+
print_exception_response)
|
29
|
+
from .utils import print_response, body_slimmer
|
30
30
|
from ._client import Client
|
31
|
-
|
31
|
+
from .automated_curation_omvs import AutomatedCuration
|
32
32
|
from .core_omag_server_config import CoreServerConfig
|
33
33
|
from .platform_services import Platform
|
34
34
|
from .registered_info import RegisteredInfo
|
35
35
|
from .glossary_omvs import GlossaryBrowser
|
36
|
-
from .
|
36
|
+
from ._validators import (validate_user_id, validate_name, validate_guid, validate_server_name, validate_search_string,
|
37
|
+
validate_url, is_json, validate_public)
|
38
|
+
# from .asset_catalog_omvs import AssetCatalog
|
37
39
|
from .gov_engine import GovEng
|
40
|
+
from .my_profile_omvs import MyProfile
|
41
|
+
from .full_omag_server_config import FullServerConfig
|
42
|
+
from .server_operations import ServerOps
|
38
43
|
|
39
|
-
__version__ = "0.
|
44
|
+
__version__ = "0.3"
|
pyegeria/_client.py
CHANGED
@@ -6,13 +6,18 @@ This is a simple class to create and manage a connection to an Egeria backend. I
|
|
6
6
|
different client capabilities. It also provides the common methods used to make restful self.session to Egeria.
|
7
7
|
|
8
8
|
"""
|
9
|
+
import asyncio
|
9
10
|
import inspect
|
10
11
|
import json
|
11
12
|
import os
|
12
|
-
import httpx
|
13
|
-
import asyncio
|
14
13
|
|
14
|
+
import httpx
|
15
15
|
|
16
|
+
from pyegeria._exceptions import (
|
17
|
+
OMAGCommonErrorCode,
|
18
|
+
InvalidParameterException,
|
19
|
+
PropertyServerException,
|
20
|
+
UserNotAuthorizedException, )
|
16
21
|
from pyegeria._globals import max_paging_size
|
17
22
|
from pyegeria._validators import (
|
18
23
|
validate_name,
|
@@ -21,15 +26,6 @@ from pyegeria._validators import (
|
|
21
26
|
validate_user_id,
|
22
27
|
is_json
|
23
28
|
)
|
24
|
-
from pyegeria.exceptions import (
|
25
|
-
OMAGCommonErrorCode,
|
26
|
-
InvalidParameterException,
|
27
|
-
PropertyServerException,
|
28
|
-
UserNotAuthorizedException, print_exception_response,
|
29
|
-
)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
29
|
|
34
30
|
...
|
35
31
|
|
@@ -77,17 +73,17 @@ class Client:
|
|
77
73
|
json_header = {"Content-Type": "application/json"}
|
78
74
|
|
79
75
|
def __init__(
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
76
|
+
self,
|
77
|
+
server_name: str,
|
78
|
+
platform_url: str,
|
79
|
+
user_id: str = None,
|
80
|
+
user_pwd: str = None,
|
81
|
+
verify_flag: bool = False,
|
82
|
+
api_key: str = None,
|
83
|
+
page_size: int = max_paging_size,
|
84
|
+
token: str = None,
|
85
|
+
token_src: str = None,
|
86
|
+
async_mode: bool = False
|
91
87
|
):
|
92
88
|
self.server_name = None
|
93
89
|
self.platform_url = None
|
@@ -113,15 +109,15 @@ class Client:
|
|
113
109
|
# self.token = token
|
114
110
|
|
115
111
|
if api_key is None:
|
116
|
-
api_key = os.environ.get("API_KEY",None)
|
112
|
+
api_key = os.environ.get("API_KEY", None)
|
117
113
|
self.api_key = api_key
|
118
114
|
|
119
115
|
self.headers = {
|
120
116
|
"Content-Type": "application/json",
|
121
|
-
|
117
|
+
}
|
122
118
|
self.text_headers = {
|
123
119
|
"Content-Type": "text/plain",
|
124
|
-
|
120
|
+
}
|
125
121
|
if self.api_key is not None:
|
126
122
|
self.headers["X-Api-Key"] = self.api_key
|
127
123
|
self.text_headers["X-Api-Key"] = self.api_key
|
@@ -146,7 +142,6 @@ class Client:
|
|
146
142
|
self.session = httpx.AsyncClient(verify=self.ssl_verify)
|
147
143
|
|
148
144
|
def __enter__(self):
|
149
|
-
print("entered client")
|
150
145
|
return self
|
151
146
|
|
152
147
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
@@ -156,17 +151,80 @@ class Client:
|
|
156
151
|
self.exc_val = exc_val
|
157
152
|
self.exc_tb = exc_tb
|
158
153
|
|
159
|
-
return False
|
154
|
+
return False # allows exceptions to propagate
|
155
|
+
|
156
|
+
async def _async_close_session(self) -> None:
|
157
|
+
"""Close the session"""
|
158
|
+
await self.session.aclose()
|
160
159
|
|
161
160
|
def close_session(self) -> None:
|
162
161
|
"""Close the session"""
|
163
|
-
|
162
|
+
loop = asyncio.get_event_loop()
|
163
|
+
loop.run_until_complete(self._async_close_session())
|
164
|
+
return
|
165
|
+
|
166
|
+
async def _async_create_egeria_bearer_token(self, user_id: str = None, password: str = None) -> str:
|
167
|
+
""" Create and set an Egeria Bearer Token for the user. Async version
|
168
|
+
Parameters
|
169
|
+
----------
|
170
|
+
user_id : str, opt
|
171
|
+
The user id to authenticate with. If None, then user_id from class instance used.
|
172
|
+
password : str, opt
|
173
|
+
The password for the user. If None, then user_pwd from class instance is used.
|
174
|
+
|
175
|
+
Returns
|
176
|
+
-------
|
177
|
+
token
|
178
|
+
The bearer token for the specified user.
|
179
|
+
|
180
|
+
Raises
|
181
|
+
------
|
182
|
+
InvalidParameterException
|
183
|
+
If the client passes incorrect parameters on the request - such as bad URLs or invalid values
|
184
|
+
PropertyServerException
|
185
|
+
Raised by the server when an issue arises in processing a valid request
|
186
|
+
NotAuthorizedException
|
187
|
+
The principle specified by the user_id does not have authorization for the requested action
|
188
|
+
Notes
|
189
|
+
-----
|
190
|
+
This routine creates a new bearer token for the user and updates the object with it.
|
191
|
+
It uses Egeria's mechanisms to create a token. This is useful if an Egeria token expires.
|
192
|
+
A bearer token from another source can be set with the set_bearer_token() method.
|
193
|
+
|
194
|
+
"""
|
195
|
+
if user_id is None:
|
196
|
+
validate_user_id(self.user_id)
|
197
|
+
user_id = self.user_id
|
198
|
+
if password is None:
|
199
|
+
validate_name(self.user_pwd)
|
200
|
+
password = self.user_pwd
|
201
|
+
|
202
|
+
url = f"{self.platform_url}/api/token"
|
203
|
+
data = {
|
204
|
+
"userId": user_id,
|
205
|
+
"password": password
|
206
|
+
}
|
207
|
+
async with httpx.AsyncClient(verify=self.ssl_verify) as client:
|
208
|
+
try:
|
209
|
+
response = await client.post(url, json=data, headers=self.headers)
|
210
|
+
token = response.text
|
211
|
+
except httpx.HTTPError as e:
|
212
|
+
print(e)
|
213
|
+
return "FAILED"
|
214
|
+
|
215
|
+
if token:
|
216
|
+
self.token_src = 'Egeria'
|
217
|
+
self.headers["Authorization"] = f"Bearer {token}"
|
218
|
+
self.text_headers["Authorization"] = f"Bearer {token}"
|
219
|
+
return token
|
220
|
+
else:
|
221
|
+
raise InvalidParameterException("No token returned - request issue")
|
164
222
|
|
165
|
-
def create_egeria_bearer_token(self,
|
223
|
+
def create_egeria_bearer_token(self, user_id: str = None, password: str = None) -> str:
|
166
224
|
""" Create and set an Egeria Bearer Token for the user
|
167
225
|
Parameters
|
168
226
|
----------
|
169
|
-
|
227
|
+
user_id : str
|
170
228
|
The user id to authenticate with.
|
171
229
|
password : str
|
172
230
|
The password for the user.
|
@@ -191,32 +249,56 @@ class Client:
|
|
191
249
|
A bearer token from another source can be set with the set_bearer_token() method.
|
192
250
|
|
193
251
|
"""
|
194
|
-
|
195
|
-
|
252
|
+
loop = asyncio.get_event_loop()
|
253
|
+
response = loop.run_until_complete(self._async_create_egeria_bearer_token(user_id, password))
|
254
|
+
return response
|
196
255
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
"password": password
|
201
|
-
}
|
202
|
-
with httpx.Client(verify=self.ssl_verify) as client:
|
203
|
-
response = client.post(url, json=data, headers=self.headers)
|
256
|
+
async def _async_refresh_egeria_bearer_token(self) -> None:
|
257
|
+
"""
|
258
|
+
Refreshes the Egeria bearer token. Async version.
|
204
259
|
|
205
|
-
token
|
206
|
-
if
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
260
|
+
This method is used to refresh the bearer token used for authentication with Egeria. It checks if the token
|
261
|
+
source is 'Egeria', and if the user ID and password are valid. If all conditions are met, it calls the
|
262
|
+
`create_egeria_bearer_token` method to create a new bearer token. Otherwise,
|
263
|
+
it raises an `InvalidParameterException`.
|
264
|
+
|
265
|
+
Parameters:
|
266
|
+
|
267
|
+
Returns:
|
268
|
+
None
|
213
269
|
|
214
|
-
|
270
|
+
Raises:
|
271
|
+
InvalidParameterException: If the token source is invalid.
|
272
|
+
"""
|
215
273
|
if (self.token_src == 'Egeria') and validate_user_id(self.user_id) and validate_name(self.user_pwd):
|
216
|
-
self.
|
274
|
+
await self._async_create_egeria_bearer_token(self.user_id, self.user_pwd)
|
217
275
|
else:
|
218
276
|
raise InvalidParameterException("Invalid token source")
|
219
|
-
|
277
|
+
|
278
|
+
def refresh_egeria_bearer_token(self) -> None:
|
279
|
+
"""
|
280
|
+
Refreshes the Egeria bearer token.
|
281
|
+
|
282
|
+
This method is used to refresh the bearer token used for authentication with Egeria. It checks if the token
|
283
|
+
source is 'Egeria', and if the user ID and password are valid. If all conditions are met, it calls the
|
284
|
+
`create_egeria_bearer_token` method to create a new bearer token. Otherwise,
|
285
|
+
it raises an `InvalidParameterException`.
|
286
|
+
|
287
|
+
Parameters:
|
288
|
+
|
289
|
+
Returns:
|
290
|
+
None
|
291
|
+
|
292
|
+
Raises:
|
293
|
+
InvalidParameterException: If the token source is invalid.
|
294
|
+
PropertyServerException
|
295
|
+
Raised by the server when an issue arises in processing a valid request
|
296
|
+
NotAuthorizedException
|
297
|
+
The principle specified by the user_id does not have authorization for the requested action
|
298
|
+
"""
|
299
|
+
loop = asyncio.get_event_loop()
|
300
|
+
loop.run_until_complete(self._async_refresh_egeria_bearer_token())
|
301
|
+
return
|
220
302
|
|
221
303
|
def set_bearer_token(self, token: str) -> None:
|
222
304
|
""" Retrieve and set a Bearer Token
|
@@ -247,31 +329,21 @@ class Client:
|
|
247
329
|
self.headers["Authorization"] = f"Bearer {token}"
|
248
330
|
self.text_headers["Authorization"] = f"Bearer {token}"
|
249
331
|
|
250
|
-
|
251
332
|
def get_token(self) -> str:
|
333
|
+
""" Retrieve and return the bearer token """
|
252
334
|
return self.text_headers["Authorization"]
|
253
335
|
|
254
|
-
def make_request(self, request_type: str, endpoint: str,
|
336
|
+
def make_request(self, request_type: str, endpoint: str, payload: str | dict = None,
|
255
337
|
time_out: int = 30) -> dict | str:
|
256
|
-
|
257
|
-
# if self.sync_mode:
|
258
|
-
# print(f"\nasync is: {self.sync_mode}")
|
259
|
-
# loop = asyncio.new_event_loop()
|
338
|
+
""" Make a request to the Egeria API"""
|
260
339
|
loop = asyncio.get_event_loop()
|
261
340
|
response = loop.run_until_complete(self._async_make_request(request_type, endpoint,
|
262
|
-
|
263
|
-
# asyncio.set_event_loop(loop)
|
264
|
-
# response = asyncio.run( self._async_make_request(request_type, endpoint,
|
265
|
-
# payload, time_out))
|
266
|
-
# else:
|
267
|
-
# response = await self.smart_make_request( request_type, endpoint,
|
268
|
-
# payload, time_out)
|
269
|
-
# # response = self.async_make_request(request_type, endpoint,payload, time_out)
|
341
|
+
payload, time_out))
|
270
342
|
return response
|
271
343
|
|
272
|
-
async def _async_make_request(
|
273
|
-
|
274
|
-
"""
|
344
|
+
async def _async_make_request(self, request_type: str, endpoint: str, payload: str | dict = None,
|
345
|
+
time_out: int = 30) -> dict | str:
|
346
|
+
""" Make a request to the Egeria API - Async Version
|
275
347
|
Function to make an API call via the self.session Library. Raise an exception if the HTTP response code
|
276
348
|
is not 200/201. IF there is a REST communication exception, raise InvalidParameterException.
|
277
349
|
|
@@ -288,39 +360,28 @@ class Client:
|
|
288
360
|
calling_frame = inspect.currentframe().f_back
|
289
361
|
caller_method = inspect.getframeinfo(calling_frame).function
|
290
362
|
|
291
|
-
|
292
363
|
try:
|
293
364
|
response = ""
|
294
365
|
if request_type == "GET":
|
295
|
-
|
296
|
-
# response = self.session.get(endpoint, params=payload, headers=self.headers)
|
297
|
-
# else:
|
298
|
-
response = await self.session.get(endpoint, params=payload, headers=self.headers)
|
366
|
+
response = await self.session.get(endpoint, params=payload, headers=self.headers, timeout=time_out)
|
299
367
|
|
300
368
|
elif request_type == "POST":
|
301
|
-
if
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
# timeout=time_out, data=payload)
|
369
|
+
if payload is None:
|
370
|
+
response = await self.session.post(endpoint, headers=self.headers, timeout=time_out)
|
371
|
+
elif type(payload) is str:
|
372
|
+
response = await self.session.post(endpoint, headers=self.text_headers, data=payload,
|
373
|
+
timeout=time_out)
|
307
374
|
else:
|
308
|
-
|
309
|
-
|
310
|
-
# else: response = self.session.post(endpoint, headers=self.headers,
|
311
|
-
# timeout=time_out, json=payload, )
|
375
|
+
response = await self.session.post(endpoint, headers=self.headers,
|
376
|
+
json=payload, timeout=time_out)
|
312
377
|
|
313
378
|
elif request_type == "POST-DATA":
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
# else: response = self.session.post(endpoint, headers=self.headers,
|
319
|
-
# timeout=time_out, data=payload )
|
379
|
+
if True:
|
380
|
+
response = await self.session.post(endpoint, headers=self.headers,
|
381
|
+
data=payload, timeout=time_out)
|
320
382
|
elif request_type == "DELETE":
|
321
|
-
|
322
|
-
|
323
|
-
# else: response = self.session.delete(endpoint, timeout=30, headers=self.headers)
|
383
|
+
if True:
|
384
|
+
response = await self.session.delete(endpoint, headers=self.headers, timeout=time_out)
|
324
385
|
|
325
386
|
status_code = response.status_code
|
326
387
|
|
@@ -376,7 +437,7 @@ class Client:
|
|
376
437
|
)
|
377
438
|
raise InvalidParameterException(exc_msg)
|
378
439
|
|
379
|
-
if response.status_code in (400, 401, 403, 404, 405):
|
440
|
+
if response.status_code in (400, 401, 403, 404, 405, 415):
|
380
441
|
# 4xx are client side errors - 400 bad request, 401 unauthorized
|
381
442
|
msg = OMAGCommonErrorCode.CLIENT_SIDE_REST_API_ERROR.value[
|
382
443
|
"message_template"
|
@@ -415,7 +476,7 @@ class Client:
|
|
415
476
|
},
|
416
477
|
}
|
417
478
|
)
|
418
|
-
if response.status_code in (401,403,405):
|
479
|
+
if response.status_code in (401, 403, 405):
|
419
480
|
raise UserNotAuthorizedException(exc_msg)
|
420
481
|
else:
|
421
482
|
raise InvalidParameterException(exc_msg)
|
@@ -423,8 +484,8 @@ class Client:
|
|
423
484
|
elif response.status_code in (500, 501, 502, 503, 504):
|
424
485
|
# server errors
|
425
486
|
msg = OMAGCommonErrorCode.EXCEPTION_RESPONSE_FROM_API.value[
|
426
|
-
|
427
|
-
|
487
|
+
"message_template"
|
488
|
+
].format(
|
428
489
|
str(response.status_code),
|
429
490
|
caller_method,
|
430
491
|
endpoint,
|
@@ -465,18 +526,19 @@ class Client:
|
|
465
526
|
except UserNotAuthorizedException:
|
466
527
|
raise
|
467
528
|
except (
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
529
|
+
httpx.NetworkError,
|
530
|
+
httpx.ProtocolError,
|
531
|
+
httpx.HTTPStatusError,
|
532
|
+
httpx.TimeoutException,
|
472
533
|
) as e:
|
473
534
|
if type(response) is str:
|
474
535
|
reason = response
|
475
|
-
else:
|
536
|
+
else:
|
537
|
+
reason = response.reason_phrase
|
476
538
|
|
477
539
|
msg = OMAGCommonErrorCode.CLIENT_SIDE_REST_API_ERROR.value[
|
478
|
-
|
479
|
-
|
540
|
+
"message_template"
|
541
|
+
].format(
|
480
542
|
e.args[0],
|
481
543
|
caller_method,
|
482
544
|
class_name,
|
@@ -504,9 +566,6 @@ class Client:
|
|
504
566
|
}
|
505
567
|
)
|
506
568
|
raise InvalidParameterException(exc_msg)
|
507
|
-
# finally:
|
508
|
-
# self.session.close()
|
509
|
-
|
510
569
|
|
511
570
|
|
512
571
|
if __name__ == "__main__":
|
pyegeria/_exceptions.py
CHANGED
@@ -322,7 +322,7 @@ class EgeriaException(Exception):
|
|
322
322
|
Define the Egeria exceptions raised during error handling. Modeled on the exceptions defined in the Egeria core.
|
323
323
|
|
324
324
|
"""
|
325
|
-
|
325
|
+
raw_error_message = ""
|
326
326
|
def __init__(self, response_body) -> None:
|
327
327
|
response_dict = json.loads(response_body)
|
328
328
|
self.response_class = response_dict["class"]
|
@@ -373,6 +373,7 @@ def print_exception_response(e: EgeriaException):
|
|
373
373
|
print(
|
374
374
|
f"\t\t Error Code: {e.exception_error_message_id} with http code {str(e.related_http_code)}"
|
375
375
|
)
|
376
|
+
# print(f"\t\t Raw Error Text is {e.raw_error_message}")
|
376
377
|
print(f"\t\t Class: {e.exception_class_name}")
|
377
378
|
print(f"\t\t Caller: {e.action_description}")
|
378
379
|
print(f"\t\t System Action: {e.exception_system_action}")
|
pyegeria/_validators.py
CHANGED
@@ -6,7 +6,7 @@ import inspect
|
|
6
6
|
import json
|
7
7
|
import validators
|
8
8
|
from json import JSONDecodeError
|
9
|
-
from pyegeria.
|
9
|
+
from pyegeria._exceptions import (
|
10
10
|
OMAGCommonErrorCode,
|
11
11
|
InvalidParameterException,
|
12
12
|
)
|
@@ -330,7 +330,7 @@ def validate_url(url: str) -> bool:
|
|
330
330
|
# The following hack allows localhost to be used as a hostname - which is disallowed by the
|
331
331
|
# validations package
|
332
332
|
if ('localhost' in url) and ('localhost.' not in url):
|
333
|
-
|
333
|
+
url = url.replace('localhost', '127.0.0.1')
|
334
334
|
|
335
335
|
result = validators.url(url)
|
336
336
|
if result is not True:
|