pyegeria 0.2.4__py3-none-any.whl → 0.3.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.
pyegeria/__init__.py CHANGED
@@ -11,29 +11,34 @@ the server platform and servers.
11
11
 
12
12
  """
13
13
 
14
- from importlib import resources
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 .exceptions import (InvalidParameterException, PropertyServerException, UserNotAuthorizedException,
28
- print_exception_response, )
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 .server_operations import ServerOps
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.1"
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
- self,
81
- server_name: str,
82
- platform_url: str,
83
- user_id: str = None,
84
- user_pwd: str = None,
85
- verify_flag: bool = False,
86
- api_key: str = None,
87
- page_size: int = max_paging_size,
88
- token: str = None,
89
- token_src: str = None,
90
- async_mode: bool = False
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 # allows exceptions to propagate
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
- self.session.aclose()
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, user_Id: str, password: str = None) -> str:
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
- user_Id : str
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
- validate_name(user_Id)
195
- validate_name(password)
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
- url = f"{self.platform_url}/api/token"
198
- data = {
199
- "userId": user_Id,
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 = response.text
206
- if token:
207
- self.token_src = 'Egeria'
208
- self.headers["Authorization"] = f"Bearer {token}"
209
- self.text_headers["Authorization"] = f"Bearer {token}"
210
- return token
211
- else:
212
- raise InvalidParameterException("No token returned - request issue")
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
- def refresh_egeria_bearer_token(self)-> None:
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.create_egeria_bearer_token(self.user_id, self.user_pwd)
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
- # todo - should I turn the above into a regular exception?
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, payload: str | dict = None,
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
- payload, time_out))
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
- self, request_type: str, endpoint: str, payload: str | dict = None, time_out: int = 30) -> dict | str:
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
- # if self.sync_mode:
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 type(payload) is str:
302
- # if True:
303
- response = await self.session.post(endpoint, headers=self.text_headers, data=payload)
304
- #
305
- # else: response = self.session.post(endpoint, headers=self.text_headers,
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
- if True:
309
- response = await self.session.post(endpoint, headers=self.headers, json=payload, )
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
- if True:
315
- response = await self.session.post(endpoint, headers=self.headers,
316
- data=payload )
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
- if True:
322
- response = await self.session.delete(endpoint, timeout=30, headers=self.headers)
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
- "message_template"
427
- ].format(
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
- httpx.NetworkError,
469
- httpx.ProtocolError,
470
- httpx.HTTPStatusError,
471
- httpx.TimeoutException,
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: reason = response.reason_phrase
536
+ else:
537
+ reason = response.reason_phrase
476
538
 
477
539
  msg = OMAGCommonErrorCode.CLIENT_SIDE_REST_API_ERROR.value[
478
- "message_template"
479
- ].format(
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.exceptions import (
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
- url = url.replace('localhost', '127.0.0.1')
333
+ url = url.replace('localhost', '127.0.0.1')
334
334
 
335
335
  result = validators.url(url)
336
336
  if result is not True: