pyegeria 5.4.0.20__py3-none-any.whl → 5.4.0.23__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.
- commands/cat/.DS_Store +0 -0
- commands/cat/.env +8 -0
- commands/cat/debug_log.log +0 -0
- commands/cat/list_collections.py +15 -6
- commands/cat/list_format_set.py +90 -85
- commands/cat/logs/pyegeria.log +136 -0
- commands/cli/debug_log.log +0 -0
- commands/ops/logs/pyegeria.log +0 -0
- md_processing/.DS_Store +0 -0
- md_processing/dr-egeria-outbox/Collections-2025-08-12-13-30-37.md +163 -0
- md_processing/dr-egeria-outbox/Collections-2025-08-12-13-35-58.md +474 -0
- md_processing/dr_egeria_inbox/Derive-Dr-Gov-Defs.md +8 -0
- md_processing/dr_egeria_inbox/Dr.Egeria Templates.md +873 -0
- md_processing/dr_egeria_inbox/arch_test.md +57 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro.md +254 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro_more_terms.md +696 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part1.md +254 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part2.md +298 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part3.md +608 -0
- md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part4.md +94 -0
- md_processing/dr_egeria_inbox/archive/freddie_intro.md +284 -0
- md_processing/dr_egeria_inbox/archive/freddie_intro_orig.md +275 -0
- md_processing/dr_egeria_inbox/archive/test-term.md +110 -0
- md_processing/dr_egeria_inbox/cat_test.md +100 -0
- md_processing/dr_egeria_inbox/collections.md +39 -0
- md_processing/dr_egeria_inbox/data_designer_debug.log +6 -0
- md_processing/dr_egeria_inbox/data_designer_out.md +60 -0
- md_processing/dr_egeria_inbox/data_designer_search_test.md +11 -0
- md_processing/dr_egeria_inbox/data_field.md +54 -0
- md_processing/dr_egeria_inbox/data_spec.md +77 -0
- md_processing/dr_egeria_inbox/data_spec_test.md +2406 -0
- md_processing/dr_egeria_inbox/data_test.md +179 -0
- md_processing/dr_egeria_inbox/data_test2.md +429 -0
- md_processing/dr_egeria_inbox/data_test3.md +462 -0
- md_processing/dr_egeria_inbox/dr_egeria_data_designer_1.md +124 -0
- md_processing/dr_egeria_inbox/dr_egeria_intro_categories.md +168 -0
- md_processing/dr_egeria_inbox/dr_egeria_intro_part1.md +280 -0
- md_processing/dr_egeria_inbox/dr_egeria_intro_part2.md +313 -0
- md_processing/dr_egeria_inbox/dr_egeria_intro_part3.md +1073 -0
- md_processing/dr_egeria_inbox/dr_egeria_isc1.md +44 -0
- md_processing/dr_egeria_inbox/generated_help_report.md +9 -0
- md_processing/dr_egeria_inbox/glossary_list.md +5 -0
- md_processing/dr_egeria_inbox/glossary_search_test.md +40 -0
- md_processing/dr_egeria_inbox/glossary_test1.md +324 -0
- md_processing/dr_egeria_inbox/gov_def.md +424 -0
- md_processing/dr_egeria_inbox/gov_def2.md +447 -0
- md_processing/dr_egeria_inbox/product.md +50 -0
- md_processing/dr_egeria_inbox/rel.md +8 -0
- md_processing/dr_egeria_inbox/sb.md +119 -0
- md_processing/dr_egeria_inbox/solution-components.md +136 -0
- md_processing/dr_egeria_inbox/solution_blueprints.md +118 -0
- md_processing/dr_egeria_inbox/synonym_test.md +42 -0
- md_processing/dr_egeria_inbox/t2.md +268 -0
- md_processing/dr_egeria_outbox/.obsidian/app.json +1 -0
- md_processing/dr_egeria_outbox/.obsidian/appearance.json +1 -0
- md_processing/dr_egeria_outbox/.obsidian/community-plugins.json +6 -0
- md_processing/dr_egeria_outbox/.obsidian/core-plugins.json +31 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/data.json +10 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/main.js +4459 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/manifest.json +10 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/data.json +3 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/main.js +153 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/manifest.json +11 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/styles.css +1 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/main.js +500 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/manifest.json +12 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/styles.css +1 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/main.js +37 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/manifest.json +11 -0
- md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/styles.css +220 -0
- md_processing/dr_egeria_outbox/.obsidian/types.json +28 -0
- md_processing/dr_egeria_outbox/.obsidian/workspace.json +220 -0
- md_processing/dr_egeria_outbox/Untitled.canvas +1 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-07-18 15:00-product.md +62 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-07-18 15:13-product.md +62 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-07-20 13:23-product.md +47 -0
- md_processing/dr_egeria_outbox/friday/processed-2025-08-01 11:55-data_test3.md +503 -0
- md_processing/dr_egeria_outbox/monday/processed-2025-07-14 12:38-data_designer_out.md +663 -0
- md_processing/dr_egeria_outbox/monday/processed-2025-07-21 10:52-generated_help_report.md +2744 -0
- md_processing/dr_egeria_outbox/monday/processed-2025-07-21 18:38-collections.md +62 -0
- md_processing/dr_egeria_outbox/monday/processed-2025-08-01 11:34-gov_def.md +444 -0
- md_processing/dr_egeria_outbox/processed-2025-08-03 16:05-glossary_list.md +37 -0
- md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 14:55-product.md +77 -0
- md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 15:05-product.md +75 -0
- md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 15:11-product.md +74 -0
- md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 20:40-collections.md +49 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 15:00-Derive-Dr-Gov-Defs.md +719 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:13-Derive-Dr-Gov-Defs.md +41 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:14-Derive-Dr-Gov-Defs.md +33 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:50-Derive-Dr-Gov-Defs.md +192 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 22:08-gov_def2.md +486 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 22:10-gov_def2.md +486 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 08:53-gov_def2.md +486 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 08:54-gov_def2.md +486 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:03-gov_def2.md +486 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:06-gov_def2.md +486 -0
- md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:10-gov_def2.md +486 -0
- md_processing/dr_egeria_outbox/tuesday/processed-2025-07-16 19:15-gov_def2.md +527 -0
- md_processing/dr_egeria_outbox/tuesday/processed-2025-07-17 12:08-gov_def2.md +527 -0
- md_processing/dr_egeria_outbox/tuesday/processed-2025-07-17 14:27-gov_def2.md +485 -0
- md_processing/md_processing_utils/debug_log.log +0 -0
- md_processing/md_processing_utils/solution_architect_log.log +0 -0
- pyegeria/.DS_Store +0 -0
- pyegeria/__init__.py +2 -2
- pyegeria/_client_new.py +392 -98
- pyegeria/_exceptions_new.py +16 -13
- pyegeria/_output_format_models.py +22 -17
- pyegeria/_output_formats.py +107 -34
- pyegeria/collection_manager.py +703 -1429
- pyegeria/collection_manager_omvs.py +48 -19
- pyegeria/egeria_cat_client.py +1 -1
- pyegeria/egeria_client.py +6 -0
- pyegeria/egeria_tech_client.py +6 -1
- pyegeria/governance_officer.py +2515 -0
- pyegeria/models.py +23 -6
- pyegeria/output_formatter.py +298 -79
- {pyegeria-5.4.0.20.dist-info → pyegeria-5.4.0.23.dist-info}/METADATA +1 -1
- {pyegeria-5.4.0.20.dist-info → pyegeria-5.4.0.23.dist-info}/RECORD +121 -19
- {pyegeria-5.4.0.20.dist-info → pyegeria-5.4.0.23.dist-info}/LICENSE +0 -0
- {pyegeria-5.4.0.20.dist-info → pyegeria-5.4.0.23.dist-info}/WHEEL +0 -0
- {pyegeria-5.4.0.20.dist-info → pyegeria-5.4.0.23.dist-info}/entry_points.txt +0 -0
pyegeria/_client_new.py
CHANGED
@@ -12,33 +12,31 @@ import inspect
|
|
12
12
|
import json
|
13
13
|
import os
|
14
14
|
import re
|
15
|
-
from
|
15
|
+
from collections.abc import Callable
|
16
|
+
from typing import Any
|
16
17
|
|
17
18
|
import httpcore
|
18
19
|
import httpx
|
20
|
+
from httpx import AsyncClient, Response, HTTPStatusError
|
19
21
|
# from venv import logger
|
20
22
|
from loguru import logger
|
21
|
-
|
22
|
-
from httpx import AsyncClient, Response, HTTPStatusError
|
23
23
|
from pydantic import TypeAdapter
|
24
24
|
|
25
|
-
from pyegeria.utils import body_slimmer
|
26
25
|
from pyegeria._exceptions_new import (
|
27
|
-
|
28
|
-
|
29
|
-
PyegeriaUnauthorizedException, PyegeriaClientException
|
26
|
+
PyegeriaAPIException, PyegeriaConnectionException, PyegeriaInvalidParameterException,
|
27
|
+
PyegeriaUnknownException, PyegeriaClientException
|
30
28
|
)
|
31
29
|
from pyegeria._globals import enable_ssl_check, max_paging_size, NO_ELEMENTS_FOUND
|
32
30
|
from pyegeria._validators import (
|
33
|
-
is_json,
|
34
31
|
validate_name,
|
35
32
|
validate_server_name,
|
36
33
|
validate_url,
|
37
34
|
validate_user_id,
|
38
|
-
)
|
35
|
+
)
|
39
36
|
from pyegeria.models import SearchStringRequestBody, FilterRequestBody, GetRequestBody, NewElementRequestBody, \
|
40
37
|
TemplateRequestBody, UpdateStatusRequestBody, UpdateElementRequestBody, NewRelationshipRequestBody, \
|
41
|
-
DeleteRequestBody
|
38
|
+
DeleteRequestBody, UpdateRelationshipRequestBody, ResultsRequestBody
|
39
|
+
from pyegeria.utils import body_slimmer
|
42
40
|
|
43
41
|
...
|
44
42
|
|
@@ -84,16 +82,16 @@ class Client2:
|
|
84
82
|
json_header = {"Content-Type": "application/json"}
|
85
83
|
|
86
84
|
def __init__(
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
85
|
+
self,
|
86
|
+
server_name: str,
|
87
|
+
platform_url: str,
|
88
|
+
user_id: str = None,
|
89
|
+
user_pwd: str = None,
|
90
|
+
token: str = None,
|
91
|
+
token_src: str = None,
|
92
|
+
api_key: str = None,
|
93
|
+
page_size: int = max_paging_size,
|
94
|
+
):
|
97
95
|
self.server_name = validate_server_name(server_name)
|
98
96
|
self.platform_url = validate_url(platform_url)
|
99
97
|
self.user_id = user_id
|
@@ -121,10 +119,10 @@ class Client2:
|
|
121
119
|
|
122
120
|
self.headers = {
|
123
121
|
"Content-Type": "application/json",
|
124
|
-
|
122
|
+
}
|
125
123
|
self.text_headers = {
|
126
124
|
"Content-Type": "text/plain",
|
127
|
-
|
125
|
+
}
|
128
126
|
if self.api_key is not None:
|
129
127
|
self.headers["X-Api-Key"] = self.api_key
|
130
128
|
self.text_headers["X-Api-Key"] = self.api_key
|
@@ -150,6 +148,8 @@ class Client2:
|
|
150
148
|
self._new_relationship_request_adapter = TypeAdapter(NewRelationshipRequestBody)
|
151
149
|
self._delete_request_adapter = TypeAdapter(DeleteRequestBody)
|
152
150
|
self._template_request_adapter = TypeAdapter(TemplateRequestBody)
|
151
|
+
self._update_relationship_request_adapter = TypeAdapter(UpdateRelationshipRequestBody)
|
152
|
+
self._results_request_adapter = TypeAdapter(ResultsRequestBody)
|
153
153
|
|
154
154
|
def __enter__(self):
|
155
155
|
return self
|
@@ -178,8 +178,8 @@ class Client2:
|
|
178
178
|
return
|
179
179
|
|
180
180
|
async def _async_create_egeria_bearer_token(
|
181
|
-
|
182
|
-
|
181
|
+
self, user_id: str = None, password: str = None
|
182
|
+
) -> str:
|
183
183
|
"""Create and set an Egeria Bearer Token for the user. Async version
|
184
184
|
Parameters
|
185
185
|
----------
|
@@ -235,8 +235,8 @@ class Client2:
|
|
235
235
|
raise PyegeriaInvalidParameterException(None, None, additional_info)
|
236
236
|
|
237
237
|
def create_egeria_bearer_token(
|
238
|
-
|
239
|
-
|
238
|
+
self, user_id: str = None, password: str = None
|
239
|
+
) -> str:
|
240
240
|
"""Create and set an Egeria Bearer Token for the user
|
241
241
|
Parameters
|
242
242
|
----------
|
@@ -268,7 +268,7 @@ class Client2:
|
|
268
268
|
loop = asyncio.get_event_loop()
|
269
269
|
response = loop.run_until_complete(
|
270
270
|
self._async_create_egeria_bearer_token(user_id, password)
|
271
|
-
|
271
|
+
)
|
272
272
|
return response
|
273
273
|
|
274
274
|
async def _async_refresh_egeria_bearer_token(self) -> str:
|
@@ -289,19 +289,18 @@ class Client2:
|
|
289
289
|
InvalidParameterException: If the token source is invalid.
|
290
290
|
"""
|
291
291
|
if (
|
292
|
-
|
293
|
-
|
294
|
-
|
292
|
+
(self.token_src == "Egeria")
|
293
|
+
and validate_user_id(self.user_id)
|
294
|
+
and validate_name(self.user_pwd)
|
295
295
|
):
|
296
296
|
token = await self._async_create_egeria_bearer_token(
|
297
297
|
self.user_id, self.user_pwd
|
298
|
-
|
298
|
+
)
|
299
299
|
return token
|
300
300
|
else:
|
301
301
|
additional_info = {"reason": "Invalid token source"}
|
302
302
|
raise PyegeriaInvalidParameterException(None, None, additional_info)
|
303
303
|
|
304
|
-
|
305
304
|
def refresh_egeria_bearer_token(self) -> None:
|
306
305
|
"""
|
307
306
|
Refreshes the Egeria bearer token.
|
@@ -373,16 +372,15 @@ class Client2:
|
|
373
372
|
else:
|
374
373
|
logger.info(f"Got response from {origin_url}\n status_code: {response.status_code}")
|
375
374
|
|
376
|
-
|
377
375
|
# @logger.catch
|
378
376
|
def make_request(
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
377
|
+
self,
|
378
|
+
request_type: str,
|
379
|
+
endpoint: str,
|
380
|
+
payload: str | dict = None,
|
381
|
+
time_out: int = 30,
|
382
|
+
is_json: bool = True,
|
383
|
+
) -> Response | str:
|
386
384
|
"""Make a request to the Egeria API."""
|
387
385
|
try:
|
388
386
|
loop = asyncio.get_running_loop()
|
@@ -390,19 +388,20 @@ class Client2:
|
|
390
388
|
coro = self._async_make_request(request_type, endpoint, payload, time_out, is_json)
|
391
389
|
return asyncio.run_coroutine_threadsafe(coro, loop).result()
|
392
390
|
else:
|
393
|
-
return loop.run_until_complete(
|
391
|
+
return loop.run_until_complete(
|
392
|
+
self._async_make_request(request_type, endpoint, payload, time_out, is_json))
|
394
393
|
except RuntimeError:
|
395
394
|
# No running loop exists; run the coroutine
|
396
395
|
return asyncio.run(self._async_make_request(request_type, endpoint, payload, time_out, is_json))
|
397
396
|
|
398
397
|
async def _async_make_request(
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
398
|
+
self,
|
399
|
+
request_type: str,
|
400
|
+
endpoint: str,
|
401
|
+
payload: str | dict = None,
|
402
|
+
time_out: int = 30,
|
403
|
+
is_json: bool = True,
|
404
|
+
) -> Response | str:
|
406
405
|
"""Make a request to the Egeria API - Async Version
|
407
406
|
Function to make an API call via the self.session Library. Raise an exception if the HTTP response code
|
408
407
|
is not 200/201. IF there is a REST communication exception, raise InvalidParameterException.
|
@@ -427,13 +426,13 @@ class Client2:
|
|
427
426
|
if request_type == "GET":
|
428
427
|
response = await self.session.get(
|
429
428
|
endpoint, params=payload, headers=self.headers, timeout=time_out
|
430
|
-
|
429
|
+
)
|
431
430
|
|
432
431
|
elif request_type == "POST":
|
433
432
|
if payload is None:
|
434
433
|
response = await self.session.post(
|
435
434
|
endpoint, headers=self.headers, timeout=time_out
|
436
|
-
|
435
|
+
)
|
437
436
|
elif type(payload) is dict:
|
438
437
|
response = await self.session.post(
|
439
438
|
endpoint, json=payload, headers=self.headers, timeout=time_out
|
@@ -449,7 +448,7 @@ class Client2:
|
|
449
448
|
headers=self.headers,
|
450
449
|
content=payload,
|
451
450
|
timeout=time_out,
|
452
|
-
|
451
|
+
)
|
453
452
|
else:
|
454
453
|
# response = await self.session.post(
|
455
454
|
# endpoint, headers=self.headers, json=payload, timeout=time_out)
|
@@ -460,14 +459,13 @@ class Client2:
|
|
460
459
|
if True:
|
461
460
|
response = await self.session.post(
|
462
461
|
endpoint, headers=self.headers, data=payload, timeout=time_out
|
463
|
-
|
462
|
+
)
|
464
463
|
elif request_type == "DELETE":
|
465
464
|
if True:
|
466
465
|
response = await self.session.delete(
|
467
466
|
endpoint, headers=self.headers, timeout=time_out
|
468
|
-
|
467
|
+
)
|
469
468
|
response.raise_for_status()
|
470
|
-
|
471
469
|
|
472
470
|
status_code = response.status_code
|
473
471
|
|
@@ -517,11 +515,9 @@ class Client2:
|
|
517
515
|
exc_info=True)
|
518
516
|
context['caught_exception'] = e
|
519
517
|
raise PyegeriaInvalidParameterException(
|
520
|
-
response, context,e
|
518
|
+
response, context, e=e
|
521
519
|
)
|
522
520
|
|
523
|
-
|
524
|
-
|
525
521
|
def build_global_guid_lists(self) -> None:
|
526
522
|
global template_guids, integration_guids
|
527
523
|
|
@@ -541,7 +537,8 @@ class Client2:
|
|
541
537
|
# get tech type details
|
542
538
|
display_name = tech_type["name"]
|
543
539
|
|
544
|
-
url = f"{self.platform_url}/servers/
|
540
|
+
url = (f"{self.platform_url}/servers/"
|
541
|
+
f"{self.server_name}/api/open-metadata/automated-curation/technology-types/by-name")
|
545
542
|
body = {"filter": display_name}
|
546
543
|
response = self.make_request("POST", url, body)
|
547
544
|
details = response.json().get("element", "no type found")
|
@@ -568,16 +565,17 @@ class Client2:
|
|
568
565
|
resource_type = resource["relatedElement"]["type"]["typeName"]
|
569
566
|
if resource_type == "IntegrationConnector":
|
570
567
|
integration_guids[display_name] = resource_guid
|
571
|
-
# print(f"Added {display_name} integration connector with GUID {integration_guids[
|
568
|
+
# print(f"Added {display_name} integration connector with GUID {integration_guids[
|
569
|
+
# display_name]}")
|
572
570
|
|
573
571
|
async def __async_get_guid__(
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
572
|
+
self,
|
573
|
+
guid: str = None,
|
574
|
+
display_name: str = None,
|
575
|
+
property_name: str = "qualifiedName",
|
576
|
+
qualified_name: str = None,
|
577
|
+
tech_type: str = None,
|
578
|
+
) -> str:
|
581
579
|
"""Helper function to return a server_guid - one of server_guid, qualified_name or display_name should
|
582
580
|
contain information. If all are None, an exception will be thrown. If all contain
|
583
581
|
values, server_guid will be used first, followed by qualified_name. If the tech_type is supplied and the
|
@@ -600,7 +598,7 @@ class Client2:
|
|
600
598
|
"forLineage": False,
|
601
599
|
"forDuplicateProcessing": False,
|
602
600
|
"effectiveTime": None,
|
603
|
-
|
601
|
+
}
|
604
602
|
url = (
|
605
603
|
f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/classification-manager/"
|
606
604
|
f"elements/guid-by-unique-name?forLineage=false&forDuplicateProcessing=false"
|
@@ -624,7 +622,7 @@ class Client2:
|
|
624
622
|
"forLineage": False,
|
625
623
|
"forDuplicateProcessing": False,
|
626
624
|
"effectiveTime": None,
|
627
|
-
|
625
|
+
}
|
628
626
|
url = (
|
629
627
|
f"{self.platform_url}/servers/{view_server}/api/open-metadata/classification-manager/"
|
630
628
|
f"elements/guid-by-unique-name?forLineage=false&forDuplicateProcessing=false"
|
@@ -640,7 +638,7 @@ class Client2:
|
|
640
638
|
"forLineage": False,
|
641
639
|
"forDuplicateProcessing": False,
|
642
640
|
"effectiveTime": None,
|
643
|
-
|
641
|
+
}
|
644
642
|
url = (
|
645
643
|
f"{self.platform_url}/servers/{view_server}/api/open-metadata/classification-manager/"
|
646
644
|
f"elements/guid-by-unique-name?forLineage=false&forDuplicateProcessing=false"
|
@@ -649,20 +647,21 @@ class Client2:
|
|
649
647
|
result = await self._async_make_request("POST", url, body_slimmer(body))
|
650
648
|
return result.json().get("guid", NO_ELEMENTS_FOUND)
|
651
649
|
else:
|
652
|
-
additional_info = {
|
653
|
-
|
654
|
-
|
655
|
-
|
650
|
+
additional_info = {
|
651
|
+
"reason": "Neither server_guid nor server_name were provided - please provide.",
|
652
|
+
"parameters": (f"GUID={guid}, display_name={display_name}, property_name={property_name},"
|
653
|
+
f"qualified_name={qualified_name}, tech_type={tech_type}")
|
654
|
+
}
|
656
655
|
raise PyegeriaInvalidParameterException(None, None, additional_info)
|
657
656
|
|
658
657
|
def __get_guid__(
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
658
|
+
self,
|
659
|
+
guid: str = None,
|
660
|
+
display_name: str = None,
|
661
|
+
property_name: str = "qualifiedName",
|
662
|
+
qualified_name: str = None,
|
663
|
+
tech_type: str = None,
|
664
|
+
) -> str:
|
666
665
|
"""Helper function to return a server_guid - one of server_guid, qualified_name or display_name should
|
667
666
|
contain information. If all are None, an exception will be thrown. If all contain
|
668
667
|
values, server_guid will be used first, followed by qualified_name. If the tech_type is supplied and the
|
@@ -677,8 +676,8 @@ class Client2:
|
|
677
676
|
result = loop.run_until_complete(
|
678
677
|
self.__async_get_guid__(
|
679
678
|
guid, display_name, property_name, qualified_name, tech_type
|
679
|
+
)
|
680
680
|
)
|
681
|
-
)
|
682
681
|
return result
|
683
682
|
|
684
683
|
def __create_qualified_name__(self, type: str, display_name: str, local_qualifier: str = None,
|
@@ -686,9 +685,9 @@ class Client2:
|
|
686
685
|
"""Helper function to create a qualified name for a given type and display name.
|
687
686
|
If present, the local qualifier will be prepended to the qualified name."""
|
688
687
|
EGERIA_LOCAL_QUALIFIER = os.environ.get("EGERIA_LOCAL_QUALIFIER", local_qualifier)
|
689
|
-
display_name = re.sub(r'\s','-',display_name.strip())
|
688
|
+
display_name = re.sub(r'\s', '-', display_name.strip()) # This changes spaces between words to -; removing
|
690
689
|
if display_name is None:
|
691
|
-
additional_info = {"reason": "Display name is missing - please provide.",}
|
690
|
+
additional_info = {"reason": "Display name is missing - please provide.", }
|
692
691
|
raise PyegeriaInvalidParameterException(additional_info=additional_info)
|
693
692
|
q_name = f"{type}::{display_name}"
|
694
693
|
if EGERIA_LOCAL_QUALIFIER:
|
@@ -697,7 +696,6 @@ class Client2:
|
|
697
696
|
q_name = f"{q_name}::{version_identifier}"
|
698
697
|
return q_name
|
699
698
|
|
700
|
-
|
701
699
|
async def _async_get_element_by_guid_(self, element_guid: str) -> dict | str:
|
702
700
|
"""
|
703
701
|
Simplified, internal version of get_element_by_guid found in Classification Manager.
|
@@ -726,7 +724,7 @@ class Client2:
|
|
726
724
|
body = {
|
727
725
|
"class": "EffectiveTimeQueryRequestBody",
|
728
726
|
"effectiveTime": None,
|
729
|
-
|
727
|
+
}
|
730
728
|
|
731
729
|
url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/classification-manager/elements/"
|
732
730
|
f"{element_guid}?forLineage=false&forDuplicateProcessing=false")
|
@@ -737,22 +735,318 @@ class Client2:
|
|
737
735
|
|
738
736
|
return elements
|
739
737
|
|
740
|
-
def validate_new_element_request(self, body: dict | NewElementRequestBody,
|
741
|
-
|
742
|
-
if body
|
738
|
+
def validate_new_element_request(self, body: dict | NewElementRequestBody,
|
739
|
+
prop: list[str]) -> NewElementRequestBody | None:
|
740
|
+
if isinstance(body, NewElementRequestBody):
|
741
|
+
# if body.properties.class_ in prop:
|
743
742
|
validated_body = body
|
744
|
-
|
745
|
-
raise PyegeriaInvalidParameterException(additional_info=
|
746
|
-
|
743
|
+
# else:
|
744
|
+
# raise PyegeriaInvalidParameterException(additional_info=
|
745
|
+
# {"reason": "unexpected property class name"})
|
747
746
|
|
748
|
-
|
749
|
-
|
747
|
+
elif isinstance(body, dict):
|
748
|
+
# if body.get("properties", {}).get("class", "") == prop:
|
750
749
|
validated_body = self._new_element_request_adapter.validate_python(body)
|
750
|
+
# else:
|
751
|
+
# raise PyegeriaInvalidParameterException(additional_info=
|
752
|
+
# {"reason": "unexpected property class name"})
|
751
753
|
else:
|
752
|
-
|
753
|
-
|
754
|
+
return None
|
755
|
+
return validated_body
|
756
|
+
|
757
|
+
def validate_new_relationship_request(self, body: dict | NewRelationshipRequestBody,
|
758
|
+
prop: str = None) -> NewRelationshipRequestBody | None:
|
759
|
+
if isinstance(body, NewElementRequestBody):
|
760
|
+
if (prop and body.properties.class_ == prop) or (prop is None):
|
761
|
+
validated_body = body
|
762
|
+
else:
|
763
|
+
raise PyegeriaInvalidParameterException(additional_info=
|
764
|
+
{"reason": "unexpected property class name"})
|
765
|
+
|
766
|
+
elif isinstance(body, dict):
|
767
|
+
if body.get("properties", {}).get("class", "") == prop:
|
768
|
+
validated_body = self._new_relationship_request_adapter.validate_python(body)
|
769
|
+
else:
|
770
|
+
raise PyegeriaInvalidParameterException(additional_info=
|
771
|
+
{"reason": "unexpected property class name"})
|
772
|
+
else:
|
773
|
+
return None
|
774
|
+
|
775
|
+
return validated_body
|
776
|
+
|
777
|
+
def validate_delete_request(self, body: dict | DeleteRequestBody,
|
778
|
+
cascade_delete: bool = False) -> DeleteRequestBody | None:
|
779
|
+
if isinstance(body, DeleteRequestBody):
|
780
|
+
validated_body = body
|
781
|
+
elif isinstance(body, dict):
|
782
|
+
validated_body = self._delete_request_adapter.validate_python(body)
|
783
|
+
else: # handle case where body not provided
|
784
|
+
body= {
|
785
|
+
"class": "DeleteRequestBody",
|
786
|
+
"cascadeDelete": cascade_delete
|
787
|
+
}
|
788
|
+
validated_body= DeleteRequestBody.model_validate(body)
|
789
|
+
return validated_body
|
790
|
+
|
791
|
+
def validate_update_element_request(self, body: dict | UpdateElementRequestBody,
|
792
|
+
prop: list[str]) -> UpdateElementRequestBody | None:
|
793
|
+
if isinstance(body, UpdateElementRequestBody):
|
794
|
+
if body.properties.class_ in prop:
|
795
|
+
validated_body = body
|
796
|
+
else:
|
797
|
+
raise PyegeriaInvalidParameterException(additional_info=
|
798
|
+
{"reason": "unexpected property class name"})
|
799
|
+
|
800
|
+
elif isinstance(body, dict):
|
801
|
+
# if body.get("properties", {}).get("class", "") in prop:
|
802
|
+
validated_body = self._update_element_request_adapter.validate_python(body)
|
803
|
+
# else:
|
804
|
+
# raise PyegeriaInvalidParameterException(additional_info=
|
805
|
+
# {"reason": "unexpected property class name"})
|
806
|
+
else:
|
807
|
+
validated_body = None
|
808
|
+
return validated_body
|
809
|
+
|
810
|
+
def validate_update_status_request(self, status: str = None, body: dict | UpdateStatusRequestBody = None,
|
811
|
+
prop: list[str] = None) -> UpdateStatusRequestBody | None:
|
812
|
+
if isinstance(body, UpdateStatusRequestBody):
|
813
|
+
validated_body = body
|
814
|
+
|
815
|
+
elif isinstance(body, dict):
|
816
|
+
validated_body = self._update_element_request_adapter.validate_python(body)
|
817
|
+
|
818
|
+
elif status:
|
819
|
+
body = {
|
820
|
+
"class": "UpdateStatusRequestBody",
|
821
|
+
"status": status
|
822
|
+
}
|
823
|
+
validated_body = UpdateStatusRequestBody.validate_python(body)
|
824
|
+
else:
|
825
|
+
raise PyegeriaInvalidParameterException(additional_info={"reason": "invalid parameters"})
|
826
|
+
|
827
|
+
return validated_body
|
828
|
+
|
829
|
+
def validate_update_relationship_request(self, body: dict | UpdateRelationshipRequestBody,
|
830
|
+
prop: [str]) -> UpdateRelationshipRequestBody | None:
|
831
|
+
if isinstance(body, UpdateRelationshipRequestBody):
|
832
|
+
# if body.properties.class_ == prop:
|
833
|
+
validated_body = body
|
834
|
+
# else:
|
835
|
+
# raise PyegeriaInvalidParameterException(additional_info=
|
836
|
+
# {"reason": "unexpected property class name"})
|
837
|
+
|
838
|
+
elif isinstance(body, dict):
|
839
|
+
# if body.get("properties", {}).get("class", "") == prop:
|
840
|
+
validated_body = self._update_relationship_request_adapter.validate_python(body)
|
841
|
+
# else:
|
842
|
+
# raise PyegeriaInvalidParameterException(additional_info=
|
843
|
+
# {"reason": "unexpected property class name"})
|
844
|
+
else:
|
845
|
+
validated_body = None
|
846
|
+
return validated_body
|
847
|
+
|
848
|
+
async def _async_find_request(self, url: str, _type: str, _gen_output: Callable[..., Any],
|
849
|
+
search_string: str = '*', classification_names: list[str] = None,
|
850
|
+
metadata_element_types: list[str] = None,
|
851
|
+
starts_with: bool = True, ends_with: bool = False, ignore_case: bool = False,
|
852
|
+
start_from: int = 0, page_size: int = 0, output_format: str = 'JSON',
|
853
|
+
output_format_set: str | dict = None,
|
854
|
+
body: dict | SearchStringRequestBody = None) -> Any:
|
855
|
+
|
856
|
+
if isinstance(body, SearchStringRequestBody):
|
857
|
+
validated_body = body
|
858
|
+
elif isinstance(body, dict):
|
859
|
+
validated_body = self._search_string_request_adapter.validate_python(body)
|
860
|
+
else:
|
861
|
+
search_string = None if search_string is "*" else search_string
|
862
|
+
body = {
|
863
|
+
"class": "SearchStringRequestBody",
|
864
|
+
"search_string": search_string,
|
865
|
+
"starts_with": starts_with,
|
866
|
+
"ends_with": ends_with,
|
867
|
+
"ignore_case": ignore_case,
|
868
|
+
"start_from": start_from,
|
869
|
+
"page_size": page_size,
|
870
|
+
"include_only_classified_elements": classification_names,
|
871
|
+
"metadata_element_subtype_names": metadata_element_types,
|
872
|
+
}
|
873
|
+
validated_body = SearchStringRequestBody.model_validate(body)
|
874
|
+
|
875
|
+
# classification_names = validated_body.include_only_classified_elements
|
876
|
+
# element_type_name = classification_names[0] if classification_names else _type
|
877
|
+
|
878
|
+
json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
|
879
|
+
|
880
|
+
response = await self._async_make_request("POST", url, json_body)
|
881
|
+
elements = response.json().get("elements", NO_ELEMENTS_FOUND)
|
882
|
+
if type(elements) is str:
|
883
|
+
logger.info(NO_ELEMENTS_FOUND)
|
884
|
+
return NO_ELEMENTS_FOUND
|
885
|
+
|
886
|
+
if output_format != 'JSON': # return a simplified markdown representation
|
887
|
+
# logger.info(f"Found elements, output format: {output_format} and output_format_set: {output_format_set}")
|
888
|
+
return _gen_output(elements, search_string, _type,
|
889
|
+
output_format, output_format_set)
|
890
|
+
return elements
|
891
|
+
|
892
|
+
async def _async_get_name_request(self, url: str, _type: str, _gen_output: Callable[..., Any],
|
893
|
+
filter_string: str, classification_names: list[str] = None,
|
894
|
+
start_from: int = 0, page_size: int = 0, output_format: str = 'JSON',
|
895
|
+
output_format_set: str | dict = None,
|
896
|
+
body: dict | FilterRequestBody = None) -> Any:
|
897
|
+
|
898
|
+
if isinstance(body, FilterRequestBody):
|
899
|
+
validated_body = body
|
900
|
+
elif isinstance(body, dict):
|
901
|
+
validated_body = self._filter_request_adapter.validate_python(body)
|
902
|
+
else:
|
903
|
+
filter_string = None if filter_string is "*" else filter_string
|
904
|
+
body = {
|
905
|
+
"class": "FilterRequestBody",
|
906
|
+
"filter": filter_string,
|
907
|
+
"start_from": start_from,
|
908
|
+
"page_size": page_size,
|
909
|
+
"include_only_classified_elements": classification_names,
|
910
|
+
}
|
911
|
+
validated_body = FilterRequestBody.model_validate(body)
|
912
|
+
|
913
|
+
# classification_names = validated_body.include_only_classified_elements
|
914
|
+
# element_type_name = classification_names[0] if classification_names else _type
|
915
|
+
|
916
|
+
json_body = validated_body.model_dump_json(indent=2)
|
917
|
+
|
918
|
+
response = await self._async_make_request("POST", url, json_body)
|
919
|
+
elements = response.json().get("elements", NO_ELEMENTS_FOUND)
|
920
|
+
if type(elements) is str:
|
921
|
+
logger.info(NO_ELEMENTS_FOUND)
|
922
|
+
return NO_ELEMENTS_FOUND
|
923
|
+
|
924
|
+
if output_format != 'JSON': # return a simplified markdown representation
|
925
|
+
logger.info(f"Found elements, output format: {output_format} and output_format_set: {output_format_set}")
|
926
|
+
return _gen_output(elements, filter_string, _type,
|
927
|
+
output_format, output_format_set)
|
928
|
+
return elements
|
929
|
+
|
930
|
+
async def _async_get_guid_request(self, url: str, _type: str, _gen_output: Callable[..., Any],
|
931
|
+
output_format: str = 'JSON', output_format_set: str | dict = None,
|
932
|
+
body: dict | GetRequestBody = None) -> Any:
|
933
|
+
|
934
|
+
if isinstance(body, GetRequestBody):
|
935
|
+
validated_body = body
|
936
|
+
elif isinstance(body, dict):
|
937
|
+
validated_body = self._filter_request_adapter.validate_python(body)
|
938
|
+
else:
|
939
|
+
body = {
|
940
|
+
"class": "GetRequestBody",
|
941
|
+
|
942
|
+
}
|
943
|
+
validated_body = GetRequestBody.model_validate(body)
|
944
|
+
|
945
|
+
json_body = validated_body.model_dump_json(indent=2)
|
946
|
+
|
947
|
+
response = await self._async_make_request("POST", url, json_body)
|
948
|
+
elements = response.json().get("element", NO_ELEMENTS_FOUND)
|
949
|
+
if type(elements) is str:
|
950
|
+
logger.info(NO_ELEMENTS_FOUND)
|
951
|
+
return NO_ELEMENTS_FOUND
|
952
|
+
|
953
|
+
if output_format != 'JSON': # return a simplified markdown representation
|
954
|
+
logger.info(f"Found elements, output format: {output_format} and output_format_set: {output_format_set}")
|
955
|
+
return _gen_output(elements, "GUID", _type, output_format, output_format_set)
|
956
|
+
return elements
|
957
|
+
|
958
|
+
async def _async_get_results_body_request(self, url: str, _type: str, _gen_output: Callable[..., Any],
|
959
|
+
start_from: int = 0, page_size: int = 0, output_format: str = 'JSON',
|
960
|
+
output_format_set: str | dict = None,
|
961
|
+
body: dict | ResultsRequestBody = None) -> Any:
|
962
|
+
if isinstance(body, ResultsRequestBody):
|
963
|
+
validated_body = body
|
964
|
+
elif isinstance(body, dict):
|
965
|
+
validated_body = self._results_request_adapter.validate_python(body)
|
966
|
+
else:
|
967
|
+
body = {
|
968
|
+
"class": "ResultsRequestBody",
|
969
|
+
"start_from": start_from,
|
970
|
+
"page_size": page_size,
|
971
|
+
}
|
972
|
+
validated_body = ResultsRequestBody.model_validate(body)
|
973
|
+
|
974
|
+
json_body = validated_body.model_dump_json(indent=2)
|
975
|
+
|
976
|
+
response = await self._async_make_request("POST", url, json_body)
|
977
|
+
elements = response.json().get("elements", None)
|
978
|
+
if elements is None:
|
979
|
+
elements = response.json().get("element", NO_ELEMENTS_FOUND)
|
980
|
+
|
981
|
+
if type(elements) is str:
|
982
|
+
logger.info(NO_ELEMENTS_FOUND)
|
983
|
+
return NO_ELEMENTS_FOUND
|
984
|
+
|
985
|
+
if output_format != 'JSON': # return a simplified markdown representation
|
986
|
+
logger.info(f"Found elements, output format: {output_format} and output_format_set: {output_format_set}")
|
987
|
+
return _gen_output(elements, "Members", _type,
|
988
|
+
output_format, output_format_set)
|
989
|
+
return elements
|
990
|
+
|
991
|
+
async def _async_create_element_body_request(self, url: str, prop: list[str],
|
992
|
+
body: dict | NewElementRequestBody = None) -> str:
|
993
|
+
validated_body = self.validate_new_element_request(body, prop)
|
994
|
+
json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
|
995
|
+
logger.info(json_body)
|
996
|
+
response = await self._async_make_request("POST", url, json_body)
|
997
|
+
logger.info(response.json())
|
998
|
+
return response.json().get("guid")
|
999
|
+
|
1000
|
+
async def _async_update_element_body_request(self, url: str, prop: list[str],
|
1001
|
+
body: dict | UpdateElementRequestBody = None) -> None:
|
1002
|
+
validated_body = self.validate_update_element_request(body, prop)
|
1003
|
+
json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
|
1004
|
+
logger.info(json_body)
|
1005
|
+
response = await self._async_make_request("POST", url, json_body)
|
1006
|
+
logger.info(response.json())
|
1007
|
+
|
1008
|
+
async def _async_update_status_request(self, url: str, status: str = None,
|
1009
|
+
body: dict | UpdateStatusRequestBody = None) -> None:
|
1010
|
+
validated_body = self.validate_update_status_request(status, body)
|
1011
|
+
json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
|
1012
|
+
logger.info(json_body)
|
1013
|
+
response = await self._async_make_request("POST", url, json_body)
|
1014
|
+
logger.info(response.json())
|
1015
|
+
|
1016
|
+
async def _async_new_relationship_request(self, url: str, prop: list[str],
|
1017
|
+
body: dict | NewRelationshipRequestBody = None) -> None:
|
1018
|
+
validated_body = self.validate_new_relationship_request(body, prop)
|
1019
|
+
if validated_body:
|
1020
|
+
json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
|
1021
|
+
logger.info(json_body)
|
1022
|
+
await self._async_make_request("POST", url, json_body)
|
1023
|
+
else:
|
1024
|
+
await self._async_make_request("POST", url)
|
1025
|
+
|
1026
|
+
async def _async_delete_request(self, url: str, body: dict | DeleteRequestBody = None,
|
1027
|
+
cascade_delete: bool = False) -> None:
|
1028
|
+
validated_body = self.validate_delete_request(body, cascade_delete)
|
1029
|
+
if validated_body:
|
1030
|
+
json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
|
1031
|
+
logger.info(json_body)
|
1032
|
+
await self._async_make_request("POST", url, json_body)
|
1033
|
+
else:
|
1034
|
+
await self._async_make_request("POST", url)
|
1035
|
+
|
1036
|
+
|
1037
|
+
|
1038
|
+
|
1039
|
+
|
1040
|
+
async def _async_update_relationship_request(self, url: str, prop: str,
|
1041
|
+
body: dict | UpdateRelationshipRequestBody = None) -> None:
|
1042
|
+
validated_body = self.validate_update_relationship_request(body, prop)
|
1043
|
+
if validated_body:
|
1044
|
+
json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
|
1045
|
+
logger.info(json_body)
|
1046
|
+
await self._async_make_request("POST", url, json_body)
|
1047
|
+
else:
|
1048
|
+
await self._async_make_request("POST", url)
|
1049
|
+
|
754
1050
|
|
755
|
-
else:
|
756
|
-
raise TypeError("Invalid parameter type")
|
757
1051
|
if __name__ == "__main__":
|
758
|
-
print("Main-__client")
|
1052
|
+
print("Main-__client")
|