pyegeria 5.4.3.2__py3-none-any.whl → 5.4.3.4__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.
Files changed (53) hide show
  1. commands/cat/debug_log.2025-09-01_07-02-58_818650.log.zip +0 -0
  2. commands/cat/debug_log.2025-09-02_07-44-39_567276.log.zip +0 -0
  3. commands/cat/debug_log.2025-09-03_07-45-21_986388.log.zip +0 -0
  4. commands/cat/debug_log.log +5379 -8107
  5. commands/cat/list_format_set.py +2 -2
  6. commands/tech/list_information_supply_chains.py +1 -1
  7. commands/tech/list_solution_blueprints.py +1 -1
  8. commands/tech/list_solution_components.py +1 -1
  9. commands/tech/list_solution_roles.py +1 -1
  10. md_processing/__init__.py +0 -4
  11. md_processing/data/commands.json +1258 -615
  12. md_processing/dr_egeria.py +6 -9
  13. md_processing/dr_egeria_inbox/data_spec_test.md +44 -418
  14. md_processing/dr_egeria_inbox/gov_def.md +239 -3
  15. md_processing/dr_egeria_inbox/product.md +13 -5
  16. md_processing/dr_egeria_outbox/monday/processed-2025-09-01 14:03-product.md +209 -0
  17. md_processing/dr_egeria_outbox/monday/processed-2025-09-01 14:24-product.md +263 -0
  18. md_processing/dr_egeria_outbox/monday/processed-2025-09-01 16:03-data_spec_test.md +2374 -0
  19. md_processing/dr_egeria_outbox/monday/processed-2025-09-01 16:05-data_spec_test.md +2374 -0
  20. md_processing/dr_egeria_outbox/monday/processed-2025-09-02 08:28-data_spec_test.md +2321 -0
  21. md_processing/dr_egeria_outbox/monday/processed-2025-09-02 08:37-data_spec_test.md +2304 -0
  22. md_processing/dr_egeria_outbox/monday/processed-2025-09-02 08:56-data_spec_test.md +2324 -0
  23. md_processing/dr_egeria_outbox/monday/processed-2025-09-02 09:00-data_spec_test.md +2324 -0
  24. md_processing/md_commands/data_designer_commands.py +170 -570
  25. md_processing/md_commands/product_manager_commands.py +1 -1
  26. md_processing/md_processing_utils/common_md_utils.py +55 -13
  27. md_processing/md_processing_utils/extraction_utils.py +14 -7
  28. md_processing/md_processing_utils/md_processing_constants.py +1 -1
  29. pyegeria/___external_references.py +3255 -0
  30. pyegeria/__init__.py +1 -1
  31. pyegeria/_client_new.py +9 -7
  32. pyegeria/_output_formats.py +124 -3
  33. pyegeria/collection_manager.py +17 -56
  34. pyegeria/config.py +10 -1
  35. pyegeria/data_designer.py +172 -124
  36. pyegeria/egeria_client.py +1 -1
  37. pyegeria/egeria_tech_client.py +1 -1
  38. pyegeria/glossary_manager.py +71 -85
  39. pyegeria/governance_officer.py +26 -29
  40. pyegeria/output_formatter.py +127 -1
  41. pyegeria/project_manager.py +33 -36
  42. pyegeria/{solution_architect_omvs.py → solution_architect.py} +443 -388
  43. {pyegeria-5.4.3.2.dist-info → pyegeria-5.4.3.4.dist-info}/METADATA +1 -1
  44. {pyegeria-5.4.3.2.dist-info → pyegeria-5.4.3.4.dist-info}/RECORD +47 -41
  45. md_processing/dr_egeria_outbox/friday/processed-2025-08-29 16:30-output_tests.md +0 -103
  46. md_processing/dr_egeria_outbox/friday/processed-2025-08-29 16:40-output_tests.md +0 -115
  47. md_processing/dr_egeria_outbox/friday/processed-2025-08-30 21:15-glossary_test1.md +0 -326
  48. md_processing/dr_egeria_outbox/friday/processed-2025-08-31 13:27-glossary_test1.md +0 -369
  49. md_processing/dr_egeria_outbox/friday/processed-2025-08-31 13:33-glossary_test1.md +0 -392
  50. md_processing/dr_egeria_outbox/friday/processed-2025-08-31 20:57-glossary_test1.md +0 -400
  51. {pyegeria-5.4.3.2.dist-info → pyegeria-5.4.3.4.dist-info}/LICENSE +0 -0
  52. {pyegeria-5.4.3.2.dist-info → pyegeria-5.4.3.4.dist-info}/WHEEL +0 -0
  53. {pyegeria-5.4.3.2.dist-info → pyegeria-5.4.3.4.dist-info}/entry_points.txt +0 -0
@@ -12,13 +12,17 @@ import sys
12
12
  from typing import Any, Callable, Dict, List, Optional, Tuple, Union
13
13
 
14
14
  from httpx import Response
15
+ from loguru import logger
16
+
17
+ from pyegeria.models import NewElementRequestBody, TemplateRequestBody, UpdateElementRequestBody, \
18
+ NewRelationshipRequestBody, DeleteRequestBody, UpdateStatusRequestBody
15
19
  from pyegeria.output_formatter import make_preamble, make_md_attribute, generate_output, extract_mermaid_only, \
16
20
  extract_basic_dict, MD_SEPARATOR
17
21
  from pyegeria import validate_guid
18
22
  from pyegeria.governance_officer import GovernanceOfficer
19
- from pyegeria._client import Client, max_paging_size
20
- from pyegeria._globals import NO_ELEMENTS_FOUND
21
- from pyegeria.utils import body_slimmer
23
+ from pyegeria._client_new import Client2, max_paging_size
24
+ from pyegeria._globals import NO_ELEMENTS_FOUND, NO_GUID_RETURNED
25
+ from pyegeria.utils import body_slimmer, dynamic_catch
22
26
  from pyegeria._exceptions import (InvalidParameterException)
23
27
 
24
28
  sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
@@ -53,7 +57,7 @@ def base_path(client, view_server: str):
53
57
  return f"{client.platform_url}/servers/{view_server}/api/open-metadata/metadata-explorer"
54
58
 
55
59
 
56
- class SolutionArchitect(Client):
60
+ class SolutionArchitect(Client2):
57
61
  """SolutionArchitect is a class that extends the Client class. The Solution Architect OMVS provides APIs for
58
62
  searching for architectural elements such as information supply chains, solution blueprints, solution components,
59
63
  and component implementations.
@@ -82,7 +86,7 @@ class SolutionArchitect(Client):
82
86
  self.user_pwd = user_pwd
83
87
  self.solution_architect_command_root: str = (f"{self.platform_url}/servers/{self.view_server}"
84
88
  f"/api/open-metadata/solution-architect")
85
- Client.__init__(self, view_server, platform_url, user_id=user_id, user_pwd=user_pwd, token=token, )
89
+ Client2.__init__(self, view_server, platform_url, user_id=user_id, user_pwd=user_pwd, token=token, )
86
90
  self.url_marker = "solution-architect"
87
91
  #
88
92
  # Extract properties functions
@@ -830,12 +834,13 @@ class SolutionArchitect(Client):
830
834
  # Default case
831
835
  return None
832
836
 
833
- async def _async_create_info_supply_chain(self, body: dict) -> str:
837
+ @dynamic_catch
838
+ async def _async_create_info_supply_chain(self, body: dict | NewElementRequestBody) -> str:
834
839
  """Create an information supply. Async version.
835
840
 
836
841
  Parameters
837
842
  ----------
838
- body: dict
843
+ body: dict | NewElementRequestBody
839
844
  A dictionary containing the definition of the supply chain to create.
840
845
 
841
846
  Returns
@@ -887,6 +892,7 @@ class SolutionArchitect(Client):
887
892
  "description": "add description here",
888
893
  "scope": "add scope of this information supply chain's applicability.",
889
894
  "purposes": ["purpose1", "purpose2"],
895
+ "estimatedVolumetrics": {},
890
896
  "additionalProperties": {
891
897
  "property1": "propertyValue1",
892
898
  "property2": "propertyValue2"
@@ -900,16 +906,15 @@ class SolutionArchitect(Client):
900
906
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
901
907
  f"information-supply-chains")
902
908
 
903
- response = await self._async_make_request("POST", url, body_slimmer(body))
904
-
905
- return response.json().get("guid", "Supply Chain not created")
909
+ return await self._async_create_element_body_request(url, ["InformationSupplyChainProperties"], body)
906
910
 
907
- def create_info_supply_chain(self, body: dict) -> str:
911
+ @dynamic_catch
912
+ def create_info_supply_chain(self, body: dict | NewElementRequestBody) -> str:
908
913
  """Create an information supply.
909
914
 
910
915
  Parameters
911
916
  ----------
912
- body: dict
917
+ body: dict | NewElementRequestBody
913
918
  A dictionary containing the definition of the supply chain to create.
914
919
 
915
920
  Returns
@@ -961,6 +966,7 @@ class SolutionArchitect(Client):
961
966
  "description": "add description here",
962
967
  "scope": "add scope of this information supply chain's applicability.",
963
968
  "purposes": ["purpose1", "purpose2"],
969
+ "estimatedVolumetrics": {},
964
970
  "additionalProperties": {
965
971
  "property1": "propertyValue1",
966
972
  "property2": "propertyValue2"
@@ -975,7 +981,8 @@ class SolutionArchitect(Client):
975
981
  response = loop.run_until_complete(self._async_create_info_supply_chain(body))
976
982
  return response
977
983
 
978
- async def _async_create_info_supply_chain_from_template(self, body: dict) -> str:
984
+ @dynamic_catch
985
+ async def _async_create_info_supply_chain_from_template(self, body: dict | TemplateRequestBody) -> str:
979
986
  """ Create a new metadata element to represent an information supply chain using an existing metadata element
980
987
  as a template. The template defines additional classifications and relationships that should be added to
981
988
  the new element. Async Version.
@@ -983,7 +990,7 @@ class SolutionArchitect(Client):
983
990
 
984
991
  Parameters
985
992
  ----------
986
- body: dict
993
+ body: dict | NewElementRequestBody
987
994
  A dictionary containing the definition of the supply chain to create.
988
995
 
989
996
  Returns
@@ -1049,18 +1056,27 @@ class SolutionArchitect(Client):
1049
1056
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
1050
1057
  f"information-supply-chains/from-template")
1051
1058
 
1052
- response = await self._async_make_request("POST", url, body_slimmer(body))
1059
+ if isinstance(body, TemplateRequestBody):
1060
+ validated_body = body
1053
1061
 
1054
- return response.json().get("guid", "Supply Chain not created")
1062
+ elif isinstance(body, dict):
1063
+ validated_body = self._template_request_adapter.validate_python(body)
1055
1064
 
1056
- def create_info_supply_chain_from_template(self, body: dict) -> str:
1065
+ json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
1066
+ logger.info(json_body)
1067
+ resp = await self._async_make_request("POST", url, json_body, is_json=True)
1068
+ logger.info(f"Create Supply Chain from template with GUID: {resp.json().get('guid')}")
1069
+ return resp.json().get("guid", NO_GUID_RETURNED)
1070
+
1071
+ @dynamic_catch
1072
+ def create_info_supply_chain_from_template(self, body: dict| TemplateRequestBody) -> str:
1057
1073
  """ Create a new metadata element to represent an information supply chain using an existing metadata element
1058
1074
  as a template. The template defines additional classifications and relationships that should be added to
1059
1075
  the new element.
1060
1076
 
1061
1077
  Parameters
1062
1078
  ----------
1063
- body: dict
1079
+ body: dict | TemplateRequestBody
1064
1080
  A dictionary containing the definition of the supply chain to create.
1065
1081
 
1066
1082
  Returns
@@ -1128,18 +1144,17 @@ class SolutionArchitect(Client):
1128
1144
  response = loop.run_until_complete(self._async_create_info_supply_chain_from_template(body))
1129
1145
  return response
1130
1146
 
1131
- async def _async_update_info_supply_chain(self, guid: str, body: dict,
1132
- replace_all_properties: bool = False) -> None:
1147
+ @dynamic_catch
1148
+ async def _async_update_info_supply_chain(self, guid: str, body: dict | UpdateElementRequestBody,) -> None:
1133
1149
  """ Update the properties of an information supply chain. Async Version.
1134
1150
 
1135
1151
  Parameters
1136
1152
  ----------
1137
1153
  guid: str
1138
1154
  guid of the information supply chain to update.
1139
- body: dict
1155
+ body: dict | UpdateElementRequestBody
1140
1156
  A dictionary containing the updates to the supply chain.
1141
- replace_all_properties: bool, optional
1142
- Whether to replace all properties with those provided in the body or to merge with existing properties.
1157
+
1143
1158
 
1144
1159
  Returns
1145
1160
  -------
@@ -1184,24 +1199,22 @@ class SolutionArchitect(Client):
1184
1199
  }
1185
1200
  }
1186
1201
  """
1187
- validate_guid(guid)
1188
- replace_all_properties_s = str(replace_all_properties).lower()
1202
+
1189
1203
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
1190
- f"information-supply-chains/{guid}/update?replaceAllProperties={replace_all_properties_s}")
1204
+ f"information-supply-chains/{guid}/update")
1191
1205
 
1192
- await self._async_make_request("POST", url, body_slimmer(body))
1206
+ await self._async_update_element_body_request(url, ["InformationSupplyChainProperties"],body)
1193
1207
 
1194
- def update_info_supply_chain(self, guid: str, body: dict, replace_all_properties: bool = False) -> None:
1208
+ @dynamic_catch
1209
+ def update_info_supply_chain(self, guid: str, body: dict | UpdateElementRequestBody) -> None:
1195
1210
  """ Update the properties of an information supply chain. Async Version.
1196
1211
 
1197
1212
  Parameters
1198
1213
  ----------
1199
1214
  guid: str
1200
1215
  guid of the information supply chain to update.
1201
- body: dict
1216
+ body: dict | UpdateElementRequestBody
1202
1217
  A dictionary containing the updates to the supply chain.
1203
- replace_all_properties: bool, optional
1204
- Whether to replace all properties with those provided in the body or to merge with existing properties.
1205
1218
 
1206
1219
  Returns
1207
1220
  -------
@@ -1247,10 +1260,10 @@ class SolutionArchitect(Client):
1247
1260
  }
1248
1261
  """
1249
1262
  loop = asyncio.get_event_loop()
1250
- loop.run_until_complete(self._async_update_info_supply_chain(guid, body, replace_all_properties))
1263
+ loop.run_until_complete(self._async_update_info_supply_chain(guid, body))
1251
1264
 
1252
-
1253
- async def _async_link_peer_info_supply_chain(self, peer1_guid: str, peer2_guid: str, body: dict = None) -> None:
1265
+ @dynamic_catch
1266
+ async def _async_link_peer_info_supply_chains(self, peer1_guid: str, peer2_guid: str, body: dict | NewRelationshipRequestBody = None) -> None:
1254
1267
  """ Connect two peer information supply chains. The linked elements are of type 'Referenceable' to
1255
1268
  allow significant data stores to be included in the definition of the information supply chain.
1256
1269
  Request body is optional. Async Version.
@@ -1261,7 +1274,7 @@ class SolutionArchitect(Client):
1261
1274
  guid of the first information supply chain to link.
1262
1275
  peer2_guid: str
1263
1276
  guid of the second information supply chain to link.
1264
- body: dict, optional
1277
+ body: dict | NewRelationshipRequestBody, optional
1265
1278
  The body describing the link between the two chains.
1266
1279
 
1267
1280
  Returns
@@ -1282,7 +1295,7 @@ class SolutionArchitect(Client):
1282
1295
 
1283
1296
  Body structure:
1284
1297
  {
1285
- "class" : "RelationshipRequestBody",
1298
+ "class" : "NewRelationshipRequestBody",
1286
1299
  "externalSourceGUID": "add guid here",
1287
1300
  "externalSourceName": "add qualified name here",
1288
1301
  "effectiveTime" : "{{$isoTimestamp}}",
@@ -1297,17 +1310,15 @@ class SolutionArchitect(Client):
1297
1310
  }
1298
1311
  }
1299
1312
  """
1300
- validate_guid(peer1_guid)
1301
- validate_guid(peer2_guid)
1313
+
1302
1314
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
1303
1315
  f"information-supply-chains/{peer1_guid}/peer-links/{peer2_guid}/attach")
1304
1316
 
1305
- if body:
1306
- await self._async_make_request("POST", url, body_slimmer(body))
1307
- else:
1308
- await self._async_make_request("POST", url)
1317
+ await self._async_new_relationship_request(url, ["InformationSupplyChainLinkProperties"], body)
1318
+ logger.info(f"Linked Supply Chains {peer1_guid} -> {peer2_guid}")
1309
1319
 
1310
- def link_peer_info_supply_chain(self, peer1_guid: str, peer2_guid: str, body: dict) -> None:
1320
+ @dynamic_catch
1321
+ def link_peer_info_supply_chains(self, peer1_guid: str, peer2_guid: str, body: dict | NewRelationshipRequestBody) -> None:
1311
1322
  """ Connect two peer information supply chains. The linked elements are of type 'Referenceable' to
1312
1323
  allow significant data stores to be included in the definition of the information supply chain.
1313
1324
  Request body is optional.
@@ -1318,7 +1329,7 @@ class SolutionArchitect(Client):
1318
1329
  guid of the first information supply chain to link.
1319
1330
  peer2_guid: str
1320
1331
  guid of the second information supply chain to link.
1321
- body: dict
1332
+ body: dict | NewRelationshipRequestBody, optional
1322
1333
  The body describing the link between the two chains.
1323
1334
 
1324
1335
  Returns
@@ -1355,10 +1366,11 @@ class SolutionArchitect(Client):
1355
1366
  }
1356
1367
  """
1357
1368
  loop = asyncio.get_event_loop()
1358
- loop.run_until_complete(self._async_link_peer_info_supply_chain(peer1_guid, peer2_guid, body))
1369
+ loop.run_until_complete(self._async_link_peer_info_supply_chains(peer1_guid, peer2_guid, body))
1359
1370
 
1371
+ @dynamic_catch
1360
1372
  async def _async_unlink_peer_info_supply_chains(self, peer1_guid: str, peer2_guid: str,
1361
- body: dict = None) -> None:
1373
+ body: dict | DeleteRequestBody = None) -> None:
1362
1374
  """ Detach two peers in an information supply chain from one another. The linked elements are of type
1363
1375
  'Referenceable' to allow significant data stores to be included in the definition of the information
1364
1376
  supply chain. Request body is optional. Async Version.
@@ -1369,7 +1381,7 @@ class SolutionArchitect(Client):
1369
1381
  guid of the first information supply chain to link.
1370
1382
  peer2_guid: str
1371
1383
  guid of the second information supply chain to link.
1372
- body: dict, optional
1384
+ body: dict | DeleteRequestBody, optional
1373
1385
  The body describing the link between the two segments.
1374
1386
 
1375
1387
  Returns
@@ -1390,7 +1402,7 @@ class SolutionArchitect(Client):
1390
1402
 
1391
1403
  Body structure:
1392
1404
  {
1393
- "class": "MetadataSourceRequestBody",
1405
+ "class": "DeleteRequestBody",
1394
1406
  "externalSourceGUID": "add guid here",
1395
1407
  "externalSourceName": "add qualified name here",
1396
1408
  "effectiveTime": {{isotime}},
@@ -1403,11 +1415,11 @@ class SolutionArchitect(Client):
1403
1415
  validate_guid(peer2_guid)
1404
1416
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
1405
1417
  f"information-supply-chains/{peer1_guid}/peer-links/{peer2_guid}/detach")
1406
- if body:
1407
- await self._async_make_request("POST", url, body_slimmer(body))
1408
- else:
1409
- await self._async_make_request("POST", url)
1418
+ await self._async_delete_request(url, body)
1419
+ logger.info(
1420
+ f"Detached supply chains {peer1_guid} -> {peer2_guid}")
1410
1421
 
1422
+ @dynamic_catch
1411
1423
  def unlink_peer_info_supply_chains(self, peer1_guid: str, peer2_guid: str,
1412
1424
  body: dict = None) -> None:
1413
1425
  """ Detach two peers in an information supply chain from one another. The linked elements are of type
@@ -1452,7 +1464,9 @@ class SolutionArchitect(Client):
1452
1464
  loop = asyncio.get_event_loop()
1453
1465
  loop.run_until_complete(self._async_unlink_peer_info_supply_chains(peer1_guid,peer2_guid, body))
1454
1466
 
1455
- async def _async_compose_info_supply_chains(self, chain_guid: str, nested_chain_guid: str, body: dict = None) -> None:
1467
+ @dynamic_catch
1468
+ async def _async_compose_info_supply_chains(self, chain_guid: str, nested_chain_guid: str,
1469
+ body: dict | NewRelationshipRequestBody = None) -> None:
1456
1470
  """ Connect a nested information supply chain to its parent. Request body is optional.
1457
1471
  Async Version.
1458
1472
 
@@ -1462,7 +1476,7 @@ class SolutionArchitect(Client):
1462
1476
  guid of the first information supply chain to link.
1463
1477
  nested_chain_guid: str
1464
1478
  guid of the second information supply chain to link.
1465
- body: dict, optional
1479
+ body: dict | NewRelationshipRequestBody, optional
1466
1480
  The body describing the link between the two chains.
1467
1481
 
1468
1482
  Returns
@@ -1483,7 +1497,7 @@ class SolutionArchitect(Client):
1483
1497
 
1484
1498
  Body structure:
1485
1499
  {
1486
- "class" : "RelationshipRequestBody",
1500
+ "class" : "NewRelationshipRequestBody",
1487
1501
  "externalSourceGUID": "add guid here",
1488
1502
  "externalSourceName": "add qualified name here",
1489
1503
  "effectiveTime" : "{{$isoTimestamp}}",
@@ -1502,21 +1516,21 @@ class SolutionArchitect(Client):
1502
1516
  validate_guid(nested_chain_guid)
1503
1517
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
1504
1518
  f"information-supply-chains/{chain_guid}/compositions/{nested_chain_guid}/attach")
1505
- if body:
1506
- await self._async_make_request("POST", url, body_slimmer(body))
1507
- else:
1508
- await self._async_make_request("POST", url)
1509
1519
 
1510
- def compose_info_supply_chains(self, chain_guid: str, nested_chain_guid: str, body: dict = None) -> None:
1520
+ await self._async_new_relationship_request(url, ["InformationSupplyChainCompositionProperties"], body)
1521
+ logger.info(f"Linked {chain_guid} -> {nested_chain_guid}")
1522
+
1523
+ @dynamic_catch
1524
+ def compose_info_supply_chains(self, chain_guid: str, nested_chain_guid: str, body: dict | NewRelationshipRequestBody = None) -> None:
1511
1525
  """ Connect a nested information supply chain to its parent. Request body is optional.
1512
1526
 
1513
1527
  Parameters
1514
1528
  ----------
1515
1529
  chain_guid: str
1516
- guid of the first information supply chain to link.
1530
+ guid of the first information supply chain to link.
1517
1531
  nested_chain_guid: str
1518
1532
  guid of the second information supply chain to link.
1519
- body: dict, optional
1533
+ body: dict | NewRelationshipRequestBody, optional
1520
1534
  The body describing the link between the two chains.
1521
1535
 
1522
1536
  Returns
@@ -1537,7 +1551,7 @@ class SolutionArchitect(Client):
1537
1551
 
1538
1552
  Body structure:
1539
1553
  {
1540
- "class" : "RelationshipRequestBody",
1554
+ "class" : "NewRelationshipRequestBody",
1541
1555
  "externalSourceGUID": "add guid here",
1542
1556
  "externalSourceName": "add qualified name here",
1543
1557
  "effectiveTime" : "{{$isoTimestamp}}",
@@ -1555,6 +1569,8 @@ class SolutionArchitect(Client):
1555
1569
  loop = asyncio.get_event_loop()
1556
1570
  loop.run_until_complete(self._async_compose_info_supply_chains(chain_guid, nested_chain_guid, body))
1557
1571
 
1572
+
1573
+ @dynamic_catch
1558
1574
  async def _async_decompose_info_supply_chains(self, chain_guid: str, nested_chain_guid: str,
1559
1575
  body: dict = None) -> None:
1560
1576
  """ Detach two peers in an information supply chain from one another. Request body is optional. Async Version.
@@ -1600,10 +1616,12 @@ class SolutionArchitect(Client):
1600
1616
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
1601
1617
  f"information-supply-chains/{chain_guid}/compositions/{nested_chain_guid}/detach")
1602
1618
 
1603
- await self._async_make_request("POST", url, body_slimmer(body))
1619
+ await self._async_delete_request(url, body)
1620
+ logger.info(f"Removed composition of {nested_chain_guid} -> {chain_guid}")
1604
1621
 
1622
+ @dynamic_catch
1605
1623
  def decompose_info_supply_chains(self, chain_guid: str, nested_chain_guid: str,
1606
- body: dict = None) -> None:
1624
+ body: dict | DeleteRequestBody = None) -> None:
1607
1625
  """ Detach two peers in an information supply chain from one another. Request body is optional.
1608
1626
 
1609
1627
  Parameters
@@ -1612,7 +1630,7 @@ class SolutionArchitect(Client):
1612
1630
  guid of the first information supply chain to link.
1613
1631
  nested_chain_guid: str
1614
1632
  guid of the second information supply chain to link.
1615
- body: dict, optional
1633
+ body: dict | DeleteRequestBody, optional
1616
1634
  The body describing the link between the two segments.
1617
1635
 
1618
1636
  Returns
@@ -1633,7 +1651,7 @@ class SolutionArchitect(Client):
1633
1651
 
1634
1652
  Body structure:
1635
1653
  {
1636
- "class": "MetadataSourceRequestBody",
1654
+ "class": "DeleteRequestBody",
1637
1655
  "externalSourceGUID": "add guid here",
1638
1656
  "externalSourceName": "add qualified name here",
1639
1657
  "effectiveTime": {{isotime}},
@@ -1645,8 +1663,8 @@ class SolutionArchitect(Client):
1645
1663
  loop.run_until_complete(self._async_decompose_info_supply_chains(chain_guid,
1646
1664
  nested_chain_guid, body))
1647
1665
 
1648
-
1649
- async def _async_delete_info_supply_chain(self, guid: str, body: dict = None, cascade_delete: bool = False) -> None:
1666
+ @dynamic_catch
1667
+ async def _async_delete_info_supply_chain(self, guid: str, body: dict | DeleteRequestBody = None, cascade_delete: bool = False) -> None:
1650
1668
  """Delete an information supply chain. Async Version.
1651
1669
 
1652
1670
  Parameters
@@ -1662,8 +1680,6 @@ class SolutionArchitect(Client):
1662
1680
  -------
1663
1681
  None
1664
1682
 
1665
- Raises
1666
- ------
1667
1683
  Raises
1668
1684
  ------
1669
1685
  InvalidParameterException
@@ -1678,7 +1694,7 @@ class SolutionArchitect(Client):
1678
1694
 
1679
1695
  Body structure:
1680
1696
  {
1681
- "class": "MetadataSourceRequestBody",
1697
+ "class": "DeleteRequestBody",
1682
1698
  "externalSourceGUID": "add guid here",
1683
1699
  "externalSourceName": "add qualified name here",
1684
1700
  "effectiveTime": {{isotime}},
@@ -1686,25 +1702,23 @@ class SolutionArchitect(Client):
1686
1702
  "forDuplicateProcessing": false
1687
1703
  }
1688
1704
  """
1689
- validate_guid(guid)
1690
- cascaded_s = str(cascade_delete).lower()
1705
+
1691
1706
 
1692
1707
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
1693
- f"information-supply-chains/{guid}/delete?cascadedDelete={cascaded_s}")
1694
- try:
1695
- await self._async_make_request("POST", url, body_slimmer(body))
1696
- except (InvalidParameterException) as e:
1697
- if e.exception_error_message_id == 'OMAG-REPOSITORY-HANDLER-404-007':
1698
- print("The GUID does not exist")
1699
-
1700
- def delete_info_supply_chain(self, guid: str, body: dict = None, cascade_delete: bool = False) -> None:
1708
+ f"information-supply-chains/{guid}/delete")
1709
+
1710
+ await self._async_delete_request(url, body, cascade_delete=cascade_delete)
1711
+ logger.info(f"Deleted Info Supply Chain {guid} with cascade {cascade_delete}")
1712
+
1713
+ @dynamic_catch
1714
+ def delete_info_supply_chain(self, guid: str, body: dict | DeleteRequestBody= None, cascade_delete: bool = False) -> None:
1701
1715
  """ Delete an information supply chain.
1702
1716
 
1703
1717
  Parameters
1704
1718
  ----------
1705
1719
  guid: str
1706
1720
  guid of the information supply chain to delete.
1707
- body: dict, optional
1721
+ body: dict | DeleteRequestBody, optional
1708
1722
  A dictionary containing parameters of the deletion.
1709
1723
  cascade_delete: bool, optional
1710
1724
  If true, the child objects will also be deleted.
@@ -1726,7 +1740,7 @@ class SolutionArchitect(Client):
1726
1740
 
1727
1741
  Body structure:
1728
1742
  {
1729
- "class": "MetadataSourceRequestBody",
1743
+ "class": "DeleteRequestBody",
1730
1744
  "externalSourceGUID": "add guid here",
1731
1745
  "externalSourceName": "add qualified name here",
1732
1746
  "effectiveTime": {{isotime}},
@@ -2195,7 +2209,8 @@ class SolutionArchitect(Client):
2195
2209
  # Blueprints
2196
2210
  #
2197
2211
 
2198
- async def _async_create_solution_blueprint(self, body: dict) -> str:
2212
+ @dynamic_catch
2213
+ async def _async_create_solution_blueprint(self, body: dict | NewElementRequestBody) -> str:
2199
2214
  """ Create a solution blueprint. To set a lifecycle status
2200
2215
  use a NewSolutionElementRequestBody which has a default status of DRAFT. Using a
2201
2216
  NewElementRequestBody sets the status to ACTIVE.
@@ -2203,7 +2218,7 @@ class SolutionArchitect(Client):
2203
2218
 
2204
2219
  Parameters
2205
2220
  ----------
2206
- body: dict
2221
+ body: dict | NewElementRequestBody
2207
2222
  A dictionary containing the definition of the blueprint to create.
2208
2223
 
2209
2224
  Returns
@@ -2251,6 +2266,8 @@ class SolutionArchitect(Client):
2251
2266
  "displayName": "add short name here",
2252
2267
  "description": "add description here",
2253
2268
  "versionIdentifier": "add version here",
2269
+ "userDefinedStatus" : "add status here",
2270
+ "lifecycleStatus": "DRAFT"
2254
2271
  "additionalProperties": {
2255
2272
  "property1": "propertyValue1",
2256
2273
  "property2": "propertyValue2"
@@ -2308,18 +2325,17 @@ class SolutionArchitect(Client):
2308
2325
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
2309
2326
  f"solution-blueprints")
2310
2327
 
2311
- response = await self._async_make_request("POST", url, body_slimmer(body))
2328
+ return await self._async_create_element_body_request(url, ["SolutionBlueprintProperties"], body)
2312
2329
 
2313
- return response.json().get("guid", "Blueprint not created")
2314
-
2315
- def create_solution_blueprint(self, body: dict) -> str:
2330
+ @dynamic_catch
2331
+ def create_solution_blueprint(self, body: dict | NewElementRequestBody) -> str:
2316
2332
  """ Create a solution blueprint. To set a lifecycle status
2317
2333
  use a NewSolutionElementRequestBody which has a default status of DRAFT. Using a
2318
2334
  NewElementRequestBody sets the status to ACTIVE.
2319
2335
 
2320
2336
  Parameters
2321
2337
  ----------
2322
- body: dict
2338
+ body: dict | NewElementRequestBody
2323
2339
  A dictionary containing the definition of the blueprint to create.
2324
2340
 
2325
2341
  Returns
@@ -2420,13 +2436,14 @@ class SolutionArchitect(Client):
2420
2436
  "forDuplicateProcessing" : false
2421
2437
  }
2422
2438
 
2423
- """
2439
+ """
2424
2440
 
2425
2441
  loop = asyncio.get_event_loop()
2426
2442
  response = loop.run_until_complete(self._async_create_solution_blueprint(body))
2427
2443
  return response
2428
2444
 
2429
- async def _async_create_solution_blueprint_from_template(self, body: dict) -> str:
2445
+ @dynamic_catch
2446
+ async def _async_create_solution_blueprint_from_template(self, body: dict | TemplateRequestBody) -> str:
2430
2447
  """ Create a new solution blueprint using an existing metadata element
2431
2448
  as a template. The template defines additional classifications and relationships that should be added to
2432
2449
  the new element. Async Version.
@@ -2434,7 +2451,7 @@ class SolutionArchitect(Client):
2434
2451
 
2435
2452
  Parameters
2436
2453
  ----------
2437
- body: dict
2454
+ body: dict | TemplateRequestBody
2438
2455
  A dictionary containing the definition of the solution blueprint to create.
2439
2456
 
2440
2457
  Returns
@@ -2499,19 +2516,28 @@ class SolutionArchitect(Client):
2499
2516
  """
2500
2517
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
2501
2518
  f"solution-blueprints/from-template")
2519
+ if isinstance(body, TemplateRequestBody):
2520
+ validated_body = body
2502
2521
 
2503
- response = await self._async_make_request("POST", url, body_slimmer(body))
2522
+ elif isinstance(body, dict):
2523
+ validated_body = self._template_request_adapter.validate_python(body)
2504
2524
 
2505
- return response.json().get("guid", "Blueprint not created")
2525
+ json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
2526
+ logger.info(json_body)
2527
+ resp = await self._async_make_request("POST", url, json_body, is_json=True)
2528
+ logger.info(f"Create Blueprint from template with GUID: {resp.json().get('guid')}")
2529
+ return resp.json().get("guid", NO_GUID_RETURNED)
2506
2530
 
2507
- def create_solution_blueprint_from_template(self, body: dict) -> str:
2531
+
2532
+ @dynamic_catch
2533
+ def create_solution_blueprint_from_template(self, body: dict | TemplateRequestBody) -> str:
2508
2534
  """ Create a new solution blueprint using an existing metadata element
2509
2535
  as a template. The template defines additional classifications and relationships that should be added to
2510
2536
  the new element.
2511
2537
 
2512
2538
  Parameters
2513
2539
  ----------
2514
- body: dict
2540
+ body: dict | TemplateRequestBody
2515
2541
  A dictionary containing the definition of the solution blueprint to create.
2516
2542
 
2517
2543
  Returns
@@ -2519,8 +2545,6 @@ class SolutionArchitect(Client):
2519
2545
 
2520
2546
  str - guid of the supply chain created.
2521
2547
 
2522
- Raises
2523
- ------
2524
2548
  Raises
2525
2549
  ------
2526
2550
  InvalidParameterException
@@ -2578,26 +2602,21 @@ class SolutionArchitect(Client):
2578
2602
  response = loop.run_until_complete(self._async_create_solution_blueprint_from_template(body))
2579
2603
  return response
2580
2604
 
2581
- async def _async_update_solution_blueprint(self, guid: str, body: dict,
2582
- replace_all_properties: bool = False) -> None:
2583
- """ Update the properties of a solution blueprint. Async Version.
2605
+ @dynamic_catch
2606
+ async def _async_update_solution_blueprint(self, guid: str, body: dict | UpdateElementRequestBody) -> None:
2607
+ """ Update a solution blueprint. Async Version.
2584
2608
 
2585
2609
  Parameters
2586
2610
  ----------
2587
2611
  guid: str
2588
2612
  guid of the information supply chain to update.
2589
- body: dict
2613
+ body: dict | UpdateElementRequestBody
2590
2614
  A dictionary containing the updates to the supply chain.
2591
- replace_all_properties: bool, optional
2592
- Whether to replace all properties with those provided in the body or to merge with existing properties.
2593
-
2594
2615
  Returns
2595
2616
  -------
2596
2617
 
2597
2618
  None
2598
2619
 
2599
- Raises
2600
- ------
2601
2620
  Raises
2602
2621
  ------
2603
2622
  InvalidParameterException
@@ -2633,24 +2652,22 @@ class SolutionArchitect(Client):
2633
2652
  }
2634
2653
  }
2635
2654
  """
2636
- validate_guid(guid)
2637
- replace_all_properties_s = str(replace_all_properties).lower()
2655
+
2638
2656
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
2639
- f"solution-blueprints/{guid}/update?replaceAllProperties={replace_all_properties_s}")
2657
+ f"solution-blueprints/{guid}/update")
2640
2658
 
2641
- await self._async_make_request("POST", url, body_slimmer(body))
2659
+ await self._async_update_element_body_request(url, ["SolutionBlueprintProperties"],body)
2642
2660
 
2643
- def update_solution_blueprint(self, guid: str, body: dict, replace_all_properties: bool = False) -> None:
2661
+ @dynamic_catch
2662
+ def update_solution_blueprint(self, guid: str, body: dict | UpdateElementRequestBody) -> None:
2644
2663
  """ Update the properties of a solution blueprint. Async Version.
2645
2664
 
2646
2665
  Parameters
2647
2666
  ----------
2648
2667
  guid: str
2649
2668
  guid of the information supply chain to update.
2650
- body: dict
2651
- A dictionary containing the updates to the supply chain.
2652
- replace_all_properties: bool, optional
2653
- Whether to replace all properties with those provided in the body or to merge with existing properties.
2669
+ body: dict | UpdateElementRequestBody
2670
+ A dictionary containing the updates to the blueprint.
2654
2671
 
2655
2672
  Returns
2656
2673
  -------
@@ -2696,18 +2713,110 @@ class SolutionArchitect(Client):
2696
2713
  }
2697
2714
  """
2698
2715
  loop = asyncio.get_event_loop()
2699
- loop.run_until_complete(self._async_update_solution_blueprint(guid, body, replace_all_properties))
2716
+ loop.run_until_complete(self._async_update_solution_blueprint(guid, body))
2717
+
2718
+
2719
+ @dynamic_catch
2720
+ async def _async_delete_solution_blueprint(self, guid: str, body: dict | DeleteRequestBody, cascade: bool = False) -> None:
2721
+ """ Delete a solution blueprint. Async Version.
2722
+ Parameters
2723
+ ----------
2724
+ guid: str
2725
+ guid of the information supply chain to delete.
2726
+ body: dict, optional
2727
+ A dictionary containing parameters of the deletion.
2728
+ cascade: bool, optional
2729
+ If true, the child objects will also be deleted.
2700
2730
 
2701
- async def _async_update_solution_element_status(self, guid: str, body: dict,
2702
- replace_all_properties: bool = False) -> None:
2731
+ Returns
2732
+ -------
2733
+ None
2734
+
2735
+ Raises
2736
+ ------
2737
+ InvalidParameterException
2738
+ one of the parameters is null or invalid or
2739
+ PropertyServerException
2740
+ There is a problem adding the element properties to the metadata repository or
2741
+ UserNotAuthorizedException
2742
+ the requesting user is not authorized to issue this request.
2743
+
2744
+ Notes
2745
+ ----
2746
+
2747
+ Body structure:
2748
+ {
2749
+ "class": "DeleteRequestBody",
2750
+ "externalSourceGUID": "add guid here",
2751
+ "externalSourceName": "add qualified name here",
2752
+ "effectiveTime": {{isotime}},
2753
+ "forLineage": false,
2754
+ "forDuplicateProcessing": false
2755
+ }
2756
+ """
2757
+
2758
+ url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
2759
+ f"information-supply-chains/{guid}/delete")
2760
+
2761
+ await self._async_delete_request(url, body, cascade_delete=cascade)
2762
+ logger.info(f"Deleted Info Supply Chain {guid} with cascade {cascade}")
2763
+
2764
+ @dynamic_catch
2765
+ def delete_solution_blueprint(self, guid: str, body: dict | DeleteRequestBody = None,
2766
+ cascade: bool = False) -> None:
2767
+ """ Delete an Solution Blueprint.
2768
+
2769
+ Parameters
2770
+ ----------
2771
+ guid: str
2772
+ guid of the information supply chain to delete.
2773
+ body: dict | DeleteRequestBody, optional
2774
+ A dictionary containing parameters of the deletion.
2775
+ cascade: bool, optional
2776
+ If true, the child objects will also be deleted.
2777
+ Returns
2778
+ -------
2779
+ None
2780
+
2781
+ Raises
2782
+ ------
2783
+ InvalidParameterException
2784
+ one of the parameters is null or invalid or
2785
+ PropertyServerException
2786
+ There is a problem adding the element properties to the metadata repository or
2787
+ UserNotAuthorizedException
2788
+ the requesting user is not authorized to issue this request.
2789
+
2790
+ Notes
2791
+ ----
2792
+
2793
+ Body structure:
2794
+ {
2795
+ "class": "DeleteRequestBody",
2796
+ "externalSourceGUID": "add guid here",
2797
+ "externalSourceName": "add qualified name here",
2798
+ "effectiveTime": {{isotime}},
2799
+ "forLineage": false,
2800
+ "forDuplicateProcessing": false
2801
+ }
2802
+ """
2803
+ loop = asyncio.get_event_loop()
2804
+ loop.run_until_complete(self._async_delete_info_supply_chain(guid, body, cascade))
2805
+
2806
+
2807
+ @dynamic_catch
2808
+ async def _async_update_solution_element_status(self, guid: str, status: str= None, body: dict| UpdateStatusRequestBody = None,
2809
+ ) -> None:
2703
2810
  """ Update the properties of a blueprint, solution component, or solution port. Async Version.
2704
2811
 
2705
2812
  Parameters
2706
2813
  ----------
2707
2814
  guid: str
2708
2815
  guid of the information supply chain to update.
2709
- body: dict
2816
+ body: dict | UpdateStatusRequestBody, optional
2710
2817
  A dictionary containing the updates to the supply chain.
2818
+ status: str, optional
2819
+ The status to update the supply chain to.
2711
2820
 
2712
2821
  Returns
2713
2822
  -------
@@ -2728,7 +2837,7 @@ class SolutionArchitect(Client):
2728
2837
 
2729
2838
  Body structure:
2730
2839
  {
2731
- "class" : "SolutionElementStatusRequestBody",
2840
+ "class" : "UpdateStatusRequestBody",
2732
2841
  "status" : "APPROVED",
2733
2842
  "externalSourceGUID": "add guid here",
2734
2843
  "externalSourceName": "add qualified name here",
@@ -2737,14 +2846,14 @@ class SolutionArchitect(Client):
2737
2846
  "forDuplicateProcessing" : false
2738
2847
  }
2739
2848
  """
2740
- validate_guid(guid)
2741
- replace_all_properties_s = str(replace_all_properties).lower()
2849
+
2742
2850
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
2743
- f"solution-blueprints/{guid}/update?replaceAllProperties={replace_all_properties_s}")
2851
+ f"solution-blueprints/{guid}/update")
2744
2852
 
2745
- await self._async_make_request("POST", url, body_slimmer(body))
2853
+ await self._async_update_status_request(url, status, body)
2746
2854
 
2747
- def update_solution_element_status(self, guid: str, body: dict) -> None:
2855
+ @dynamic_catch
2856
+ def update_solution_element_status(self, guid: str, status: str = None, body: dict| UpdateStatusRequestBody = None) -> None:
2748
2857
  """ Update the status of a blueprint, solution component, or solution port.
2749
2858
 
2750
2859
  Parameters
@@ -2782,12 +2891,12 @@ class SolutionArchitect(Client):
2782
2891
  }
2783
2892
  """
2784
2893
  loop = asyncio.get_event_loop()
2785
- loop.run_until_complete(self._async_update_solution_element_status(guid, body))
2786
-
2894
+ loop.run_until_complete(self._async_update_solution_element_status(guid = guid, status= status, body=body))
2787
2895
 
2788
2896
 
2897
+ @dynamic_catch
2789
2898
  async def _async_link_solution_component_to_blueprint(self, blueprint_guid: str, component_guid: str,
2790
- body: dict) -> None:
2899
+ body: dict | NewRelationshipRequestBody) -> None:
2791
2900
  """ Connect a solution component to a blueprint. Async Version.
2792
2901
 
2793
2902
  Parameters
@@ -2796,7 +2905,7 @@ class SolutionArchitect(Client):
2796
2905
  guid of the blueprint to connect to.
2797
2906
  component_guid: str
2798
2907
  guid of the component to link.
2799
- body: dict
2908
+ body: dict | NewRelationshipRequestBody
2800
2909
  The body describing the link.
2801
2910
 
2802
2911
  Returns
@@ -2817,7 +2926,7 @@ class SolutionArchitect(Client):
2817
2926
 
2818
2927
  Body structure:
2819
2928
  {
2820
- "class" : "RelationshipRequestBody",
2929
+ "class" : "NewRelationshipRequestBody",
2821
2930
  "properties": {
2822
2931
  "class": "SolutionBlueprintCompositionProperties",
2823
2932
  "role": "Add role that the component plays in the solution blueprint here",
@@ -2833,14 +2942,15 @@ class SolutionArchitect(Client):
2833
2942
  }
2834
2943
 
2835
2944
  """
2836
- validate_guid(blueprint_guid)
2837
- validate_guid(component_guid)
2945
+
2838
2946
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
2839
2947
  f"solution-blueprints/{blueprint_guid}/solution-components/{component_guid}/attach")
2840
2948
 
2841
- await self._async_make_request("POST", url, body_slimmer(body))
2949
+ await self._async_new_relationship_request(url, ["SolutionComponentCompositionProperties"], body)
2950
+ logger.info(f"Linked component to blueprint {component_guid} -> {blueprint_guid}")
2842
2951
 
2843
- def link_solution_component_to_blueprint(self, blueprint_guid: str, component_guid: str, body: dict) -> None:
2952
+ @dynamic_catch
2953
+ def link_solution_component_to_blueprint(self, blueprint_guid: str, component_guid: str, body: dict | NewRelationshipRequestBody) -> None:
2844
2954
  """ Connect a solution component to a blueprint.
2845
2955
 
2846
2956
  Parameters
@@ -2849,7 +2959,7 @@ class SolutionArchitect(Client):
2849
2959
  guid of the blueprint to connect to.
2850
2960
  component_guid: str
2851
2961
  guid of the component to link.
2852
- body: dict
2962
+ body: dict | NewRelationshipRequestBody
2853
2963
  The body describing the link.
2854
2964
 
2855
2965
  Returns
@@ -2888,8 +2998,9 @@ class SolutionArchitect(Client):
2888
2998
  loop = asyncio.get_event_loop()
2889
2999
  loop.run_until_complete(self._async_link_solution_component_to_blueprint(blueprint_guid, component_guid, body))
2890
3000
 
3001
+ @dynamic_catch
2891
3002
  async def _async_detach_solution_component_from_blueprint(self, blueprint_guid: str, component_guid: str,
2892
- body: dict = None) -> None:
3003
+ body: dict | DeleteRequestBody = None) -> None:
2893
3004
  """ Detach a solution component from a solution blueprint.
2894
3005
  Async Version.
2895
3006
 
@@ -2929,15 +3040,17 @@ class SolutionArchitect(Client):
2929
3040
  }
2930
3041
 
2931
3042
  """
2932
- validate_guid(blueprint_guid)
2933
- validate_guid(component_guid)
3043
+
2934
3044
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
2935
3045
  f"solution-blueprints/{blueprint_guid}/solution-components/{component_guid}/detach")
2936
3046
 
2937
- await self._async_make_request("POST", url, body_slimmer(body))
3047
+ await self._async_delete_request(url, body)
3048
+ logger.info(
3049
+ f"Detached component from blueprint {component_guid} -> {blueprint_guid}")
2938
3050
 
3051
+ @dynamic_catch
2939
3052
  def detach_solution_component_from_blueprint(self, blueprint_guid: str, component_guid: str,
2940
- body: dict = None) -> None:
3053
+ body: dict | DeleteRequestBody = None) -> None:
2941
3054
  """ Detach a solution component from a solution blueprint.
2942
3055
 
2943
3056
  Parameters
@@ -2980,8 +3093,10 @@ class SolutionArchitect(Client):
2980
3093
  loop.run_until_complete(
2981
3094
  self._async_detach_solution_component_from_blueprint(blueprint_guid, component_guid, body))
2982
3095
 
3096
+
3097
+ @dynamic_catch
2983
3098
  async def _async_delete_solution_blueprint(self, blueprint_guid: str, cascade_delete: bool = False,
2984
- body: dict = None) -> None:
3099
+ body: dict | DeleteRequestBody = None) -> None:
2985
3100
  """Delete a solution blueprint. Async Version.
2986
3101
 
2987
3102
  Parameters
@@ -3022,15 +3137,14 @@ class SolutionArchitect(Client):
3022
3137
  }
3023
3138
  """
3024
3139
  validate_guid(blueprint_guid)
3025
- cascaded_s = str(cascade_delete).lower()
3140
+
3026
3141
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
3027
- f"solution-blueprints/{blueprint_guid}/delete?cascadeDelete={cascaded_s}")
3028
- if body:
3029
- await self._async_make_request("POST", url, body_slimmer(body))
3030
- else:
3031
- await self._async_make_request("POST", url)
3142
+ f"solution-blueprints/{blueprint_guid}/delete")
3143
+ await self._async_delete_request(url, body, cascade_delete=cascade_delete)
3144
+ logger.info(f"Deleted Blueprint {blueprint_guid} with cascade {cascade_delete}")
3032
3145
 
3033
- def delete_solution_blueprint(self, blueprint_guid: str, cascade_delete: bool = False, body: dict = None) -> None:
3146
+ @dynamic_catch
3147
+ def delete_solution_blueprint(self, blueprint_guid: str, cascade_delete: bool = False, body: dict | DeleteRequestBody = None) -> None:
3034
3148
  """ Delete a solution blueprint.
3035
3149
  Parameters
3036
3150
  ----------
@@ -3071,6 +3185,8 @@ class SolutionArchitect(Client):
3071
3185
  loop.run_until_complete(self._async_delete_solution_blueprint(blueprint_guid, cascade_delete, body))
3072
3186
 
3073
3187
 
3188
+
3189
+
3074
3190
  async def _async_find_solution_blueprints(self, search_filter: str = "*", starts_with: bool = True,
3075
3191
  ends_with: bool = False, ignore_case: bool = False, start_from: int = 0,
3076
3192
  page_size: int = max_paging_size, body: dict = None,
@@ -3467,7 +3583,8 @@ class SolutionArchitect(Client):
3467
3583
  # Components
3468
3584
  #
3469
3585
 
3470
- async def _async_create_solution_component(self, body: dict) -> str:
3586
+ @dynamic_catch
3587
+ async def _async_create_solution_component(self, body: dict | NewElementRequestBody) -> str:
3471
3588
  """Create a solution component. To set a lifecycle status
3472
3589
  use a NewSolutionElementRequestBody which has a default status of DRAFT. Using a
3473
3590
  NewElementRequestBody sets the status to ACTIVE.
@@ -3475,7 +3592,8 @@ class SolutionArchitect(Client):
3475
3592
 
3476
3593
  Parameters
3477
3594
  ----------
3478
- body: dict
3595
+ body: dict | NewElementRequestBody
3596
+ A dictionary containing parameters of the creation.
3479
3597
  A dictionary containing the definition of the component to create.
3480
3598
 
3481
3599
  Returns
@@ -3500,7 +3618,7 @@ class SolutionArchitect(Client):
3500
3618
 
3501
3619
  Body structure:
3502
3620
  {
3503
- "class": "NewSolutionComponentRequestBody",
3621
+ "class": "NewElementRequestBody",
3504
3622
  "externalSourceGUID": "add guid here",
3505
3623
  "externalSourceName": "add qualified name here",
3506
3624
  "effectiveTime": {{isotime}},
@@ -3545,61 +3663,22 @@ class SolutionArchitect(Client):
3545
3663
  "forDuplicateProcessing" : false
3546
3664
  }
3547
3665
 
3548
- Without lifecycle:
3549
- {
3550
- "class" : "NewElementRequestBody",
3551
- "anchorGUID" : "add guid here",
3552
- "isOwnAnchor": false,
3553
- "parentGUID": "add guid here",
3554
- "parentRelationshipTypeName": "add type name here",
3555
- "parentRelationshipProperties": {
3556
- "class": "ElementProperties",
3557
- "propertyValueMap" : {
3558
- "description" : {
3559
- "class": "PrimitiveTypePropertyValue",
3560
- "typeName": "string",
3561
- "primitiveValue" : "New description"
3562
- }
3563
- }
3564
- },
3565
- "parentAtEnd1": false,
3566
- "properties": {
3567
- "class" : "SolutionComponentProperties",
3568
- "qualifiedName": "add unique name here",
3569
- "displayName": "add short name here",
3570
- "description": "add description here",
3571
- "solutionComponentType": "add optional type for this component",
3572
- "versionIdentifier": "add version for this component",
3573
- "plannedDeployedImplementationType": "add details of the type of implementation for this component",
3574
- "additionalProperties": {
3575
- "property1" : "propertyValue1",
3576
- "property2" : "propertyValue2"
3577
- },
3578
- "effectiveFrom": "{{$isoTimestamp}}",
3579
- "effectiveTo": "{{$isoTimestamp}}"
3580
- },
3581
- "externalSourceGUID": "add guid here",
3582
- "externalSourceName": "add qualified name here",
3583
- "effectiveTime" : "{{$isoTimestamp}}",
3584
- "forLineage" : false,
3585
- "forDuplicateProcessing" : false
3586
- }
3666
+
3587
3667
  """
3588
3668
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
3589
3669
  f"solution-components")
3590
3670
 
3591
- response = await self._async_make_request("POST", url, body_slimmer(body))
3671
+ return await self._async_create_element_body_request(url, ["SolutionComponentProperties"], body)
3592
3672
 
3593
- return response.json().get("guid", "Solution component not created")
3594
-
3595
- def create_solution_component(self, body: dict) -> str:
3673
+ @dynamic_catch
3674
+ def create_solution_component(self, body: dict | NewElementRequestBody) -> str:
3596
3675
  """Create a solution component. To set a lifecycle status
3597
3676
  use a NewSolutionElementRequestBody which has a default status of DRAFT. Using a
3598
3677
  NewElementRequestBody sets the status to ACTIVE.
3599
3678
 
3600
3679
  Parameters
3601
3680
  ----------
3602
- body: dict
3681
+ body: dict | NewElementRequestBody
3603
3682
  A dictionary containing the definition of the component to create.
3604
3683
 
3605
3684
  Returns
@@ -3624,7 +3703,7 @@ class SolutionArchitect(Client):
3624
3703
 
3625
3704
  Body structure:
3626
3705
  {
3627
- "class": "NewSolutionComponentRequestBody",
3706
+ "class": "NewElementRequestBody",
3628
3707
  "externalSourceGUID": "add guid here",
3629
3708
  "externalSourceName": "add qualified name here",
3630
3709
  "effectiveTime": {{isotime}},
@@ -3669,52 +3748,15 @@ class SolutionArchitect(Client):
3669
3748
  "forDuplicateProcessing" : false
3670
3749
  }
3671
3750
 
3672
- Without lifecycle:
3673
- {
3674
- "class" : "NewElementRequestBody",
3675
- "anchorGUID" : "add guid here",
3676
- "isOwnAnchor": false,
3677
- "parentGUID": "add guid here",
3678
- "parentRelationshipTypeName": "add type name here",
3679
- "parentRelationshipProperties": {
3680
- "class": "ElementProperties",
3681
- "propertyValueMap" : {
3682
- "description" : {
3683
- "class": "PrimitiveTypePropertyValue",
3684
- "typeName": "string",
3685
- "primitiveValue" : "New description"
3686
- }
3687
- }
3688
- },
3689
- "parentAtEnd1": false,
3690
- "properties": {
3691
- "class" : "SolutionComponentProperties",
3692
- "qualifiedName": "add unique name here",
3693
- "displayName": "add short name here",
3694
- "description": "add description here",
3695
- "solutionComponentType": "add optional type for this component",
3696
- "versionIdentifier": "add version for this component",
3697
- "plannedDeployedImplementationType": "add details of the type of implementation for this component",
3698
- "additionalProperties": {
3699
- "property1" : "propertyValue1",
3700
- "property2" : "propertyValue2"
3701
- },
3702
- "effectiveFrom": "{{$isoTimestamp}}",
3703
- "effectiveTo": "{{$isoTimestamp}}"
3704
- },
3705
- "externalSourceGUID": "add guid here",
3706
- "externalSourceName": "add qualified name here",
3707
- "effectiveTime" : "{{$isoTimestamp}}",
3708
- "forLineage" : false,
3709
- "forDuplicateProcessing" : false
3710
- }
3751
+
3711
3752
  """
3712
3753
 
3713
3754
  loop = asyncio.get_event_loop()
3714
3755
  response = loop.run_until_complete(self._async_create_solution_component(body))
3715
3756
  return response
3716
3757
 
3717
- async def _async_create_solution_component_from_template(self, body: dict) -> str:
3758
+ @dynamic_catch
3759
+ async def _async_create_solution_component_from_template(self, body: dict| TemplateRequestBody) -> str:
3718
3760
  """ Create a new solution component using an existing metadata element
3719
3761
  as a template. The template defines additional classifications and relationships that should be added to
3720
3762
  the new element. Async Version.
@@ -3722,7 +3764,7 @@ class SolutionArchitect(Client):
3722
3764
 
3723
3765
  Parameters
3724
3766
  ----------
3725
- body: dict
3767
+ body: dict | TemplateRequestBody
3726
3768
  A dictionary containing the definition of the solution component to create.
3727
3769
 
3728
3770
  Returns
@@ -3786,11 +3828,20 @@ class SolutionArchitect(Client):
3786
3828
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
3787
3829
  f"solution-components/from-template")
3788
3830
 
3789
- response = await self._async_make_request("POST", url, body_slimmer(body))
3831
+ if isinstance(body, TemplateRequestBody):
3832
+ validated_body = body
3833
+
3834
+ elif isinstance(body, dict):
3835
+ validated_body = self._template_request_adapter.validate_python(body)
3790
3836
 
3791
- return response.json().get("guid", "Component not created")
3837
+ json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
3838
+ logger.info(json_body)
3839
+ resp = await self._async_make_request("POST", url, json_body, is_json=True)
3840
+ logger.info(f"Create Solution Component from template with GUID: {resp.json().get('guid')}")
3841
+ return resp.json().get("guid", NO_GUID_RETURNED)
3792
3842
 
3793
- def create_solution_component_from_template(self, body: dict) -> str:
3843
+ @dynamic_catch
3844
+ def create_solution_component_from_template(self, body: dict| TemplateRequestBody) -> str:
3794
3845
  """ Create a new solution component using an existing metadata element
3795
3846
  as a template. The template defines additional classifications and relationships that should be
3796
3847
  added to
@@ -3799,7 +3850,7 @@ class SolutionArchitect(Client):
3799
3850
 
3800
3851
  Parameters
3801
3852
  ----------
3802
- body: dict
3853
+ body: dict| TemplateRequestBody
3803
3854
  A dictionary containing the definition of the solution component to create.
3804
3855
 
3805
3856
  Returns
@@ -3865,26 +3916,22 @@ class SolutionArchitect(Client):
3865
3916
  response = loop.run_until_complete(self._async_create_solution_component_from_template(body))
3866
3917
  return response
3867
3918
 
3868
- async def _async_update_solution_component(self, guid: str, body: dict,
3869
- replace_all_properties: bool = False) -> None:
3919
+ @dynamic_catch
3920
+ async def _async_update_solution_component(self, guid: str, body: dict | UpdateElementRequestBody,
3921
+ ) -> None:
3870
3922
  """ Update the properties of a solution component. Async Version.
3871
3923
 
3872
3924
  Parameters
3873
3925
  ----------
3874
3926
  guid: str
3875
3927
  guid of the solution component to update.
3876
- body: dict
3928
+ body: dict | UpdateElementRequestBody
3877
3929
  A dictionary containing the updates to the component.
3878
- replace_all_properties: bool, optional
3879
- Whether to replace all properties with those provided in the body or to merge with existing properties.
3880
3930
 
3881
3931
  Returns
3882
3932
  -------
3883
-
3884
3933
  None
3885
3934
 
3886
- Raises
3887
- ------
3888
3935
  Raises
3889
3936
  ------
3890
3937
  InvalidParameterException
@@ -3923,32 +3970,26 @@ class SolutionArchitect(Client):
3923
3970
  }
3924
3971
 
3925
3972
  """
3926
- validate_guid(guid)
3927
- replace_all_properties_s = str(replace_all_properties).lower()
3973
+
3928
3974
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
3929
- f"solution-components/{guid}/update?replaceAllProperties={replace_all_properties_s}")
3975
+ f"solution-components/{guid}/update")
3930
3976
 
3931
- await self._async_make_request("POST", url, body_slimmer(body))
3977
+ await self._async_update_element_body_request(url, ["SolutionComponentProperties"], body)
3932
3978
 
3933
- def update_solution_component(self, guid: str, body: dict, replace_all_properties: bool = False) -> None:
3979
+ @dynamic_catch
3980
+ def update_solution_component(self, guid: str, body: dict | UpdateElementRequestBody) -> None:
3934
3981
  """ Update the properties of a solution component. Async Version.
3935
3982
 
3936
3983
  Parameters
3937
3984
  ----------
3938
3985
  guid: str
3939
3986
  guid of the solution component to update.
3940
- body: dict
3987
+ body: dict | UpdateElementRequestBody
3941
3988
  A dictionary containing the updates to the component.
3942
- replace_all_properties: bool, optional
3943
- Whether to replace all properties with those provided in the body or to merge with existing properties.
3944
-
3945
3989
  Returns
3946
3990
  -------
3947
-
3948
3991
  None
3949
3992
 
3950
- Raises
3951
- ------
3952
3993
  Raises
3953
3994
  ------
3954
3995
  InvalidParameterException
@@ -3988,9 +4029,10 @@ class SolutionArchitect(Client):
3988
4029
 
3989
4030
  """
3990
4031
  loop = asyncio.get_event_loop()
3991
- loop.run_until_complete(self._async_update_solution_component(guid, body, replace_all_properties))
4032
+ loop.run_until_complete(self._async_update_solution_component(guid, body))
3992
4033
 
3993
- async def _async_link_subcomponent(self, component_guid: str, sub_component_guid: str, body: dict) -> None:
4034
+ @dynamic_catch
4035
+ async def _async_link_subcomponent(self, component_guid: str, sub_component_guid: str, body: dict | NewRelationshipRequestBody) -> None:
3994
4036
  """ Attach a solution component to a solution component. Async Version.
3995
4037
 
3996
4038
  Parameters
@@ -3999,7 +4041,7 @@ class SolutionArchitect(Client):
3999
4041
  guid of the blueprint to connect to.
4000
4042
  sub_component_guid: str
4001
4043
  guid of the component to link.
4002
- body: dict
4044
+ body: dict | NewRelationshipRequestBody
4003
4045
  The body describing the link.
4004
4046
 
4005
4047
  Returns
@@ -4020,25 +4062,23 @@ class SolutionArchitect(Client):
4020
4062
 
4021
4063
  Body structure:
4022
4064
  {
4023
- "class" : "RelationshipRequestBody",
4065
+ "class": "NewRelationshipRequestBody",
4024
4066
  "externalSourceGUID": "add guid here",
4025
4067
  "externalSourceName": "add qualified name here",
4026
- "effectiveTime" : "{{$isoTimestamp}}",
4027
- "forLineage" : false,
4028
- "forDuplicateProcessing" : false
4068
+ "effectiveTime": "{{$isoTimestamp}}",
4069
+ "forLineage": false,
4070
+ "forDuplicateProcessing": false
4029
4071
  }
4030
4072
  """
4031
- validate_guid(component_guid)
4032
- validate_guid(sub_component_guid)
4073
+
4033
4074
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
4034
4075
  f"solution-components/{component_guid}/subcomponents/{sub_component_guid}/attach")
4035
- if body:
4036
- await self._async_make_request("POST", url, body_slimmer(body))
4037
- else:
4038
- await self._async_make_request("POST", url)
4039
4076
 
4077
+ await self._async_new_relationship_request(url, ["SolutionCompositionProperties"], body)
4078
+ logger.info(f"Linked Subcomponent {component_guid} -> {sub_component_guid}")
4040
4079
 
4041
- def link_subcomponent(self, component_guid: str, sub_component_guid: str, body: dict) -> None:
4080
+ @dynamic_catch
4081
+ def link_subcomponent(self, component_guid: str, sub_component_guid: str, body: dict | NewRelationshipRequestBody) -> None:
4042
4082
  """ Attach a solution component to a solution component.
4043
4083
 
4044
4084
  Parameters
@@ -4047,7 +4087,7 @@ class SolutionArchitect(Client):
4047
4087
  guid of the blueprint to connect to.
4048
4088
  sub_component_guid: str
4049
4089
  guid of the component to link.
4050
- body: dict
4090
+ body: dict | NewRelationshipRequestBody
4051
4091
  The body describing the link.
4052
4092
 
4053
4093
  Returns
@@ -4068,7 +4108,7 @@ class SolutionArchitect(Client):
4068
4108
 
4069
4109
  Body structure:
4070
4110
  {
4071
- "class" : "RelationshipRequestBody",
4111
+ "class" : "NewRelationshipRequestBody",
4072
4112
  "externalSourceGUID": "add guid here",
4073
4113
  "externalSourceName": "add qualified name here",
4074
4114
  "effectiveTime" : "{{$isoTimestamp}}",
@@ -4078,9 +4118,9 @@ class SolutionArchitect(Client):
4078
4118
  """
4079
4119
  loop = asyncio.get_event_loop()
4080
4120
  loop.run_until_complete(self._async_link_subcomponent(component_guid, sub_component_guid, body))
4081
-
4121
+ @dynamic_catch
4082
4122
  async def _async_detach_sub_component(self, parent_component_guid: str, member_component_guid: str,
4083
- body: dict = None) -> None:
4123
+ body: dict |DeleteRequestBody = None) -> None:
4084
4124
  """ Detach a solution component from a solution component.
4085
4125
  Async Version.
4086
4126
 
@@ -4111,7 +4151,7 @@ class SolutionArchitect(Client):
4111
4151
 
4112
4152
  Body structure:
4113
4153
  {
4114
- "class": "MetadataSourceRequestBody",
4154
+ "class": "DeleteRequestBody",
4115
4155
  "externalSourceGUID": "add guid here",
4116
4156
  "externalSourceName": "add qualified name here",
4117
4157
  "effectiveTime": {{isotime}},
@@ -4120,14 +4160,16 @@ class SolutionArchitect(Client):
4120
4160
  }
4121
4161
 
4122
4162
  """
4123
- validate_guid(parent_component_guid)
4124
- validate_guid(member_component_guid)
4163
+
4125
4164
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
4126
4165
  f"solution-components/{parent_component_guid}/subcomponents/{member_component_guid}/detach")
4127
4166
 
4128
- await self._async_make_request("POST", url, body_slimmer(body))
4167
+ await self._async_delete_request(url, body)
4168
+ logger.info(
4169
+ f"Detached components {parent_component_guid} -> {member_component_guid}")
4129
4170
 
4130
- def detach_sub_component(self, parent_component_guid: str, member_component_guid: str, body: dict = None) -> None:
4171
+ @dynamic_catch
4172
+ def detach_sub_component(self, parent_component_guid: str, member_component_guid: str, body: dict| DeleteRequestBody = None) -> None:
4131
4173
  """ Detach a solution component from a solution component.
4132
4174
  Async Version.
4133
4175
 
@@ -4137,7 +4179,7 @@ class SolutionArchitect(Client):
4137
4179
  guid of the parent component to disconnect from.
4138
4180
  member_component_guid: str
4139
4181
  guid of the member (child) component to disconnect.
4140
- body: dict
4182
+ body: dict | DeleteRequestBody
4141
4183
  The body describing the request.
4142
4184
 
4143
4185
  Returns
@@ -4158,7 +4200,7 @@ class SolutionArchitect(Client):
4158
4200
 
4159
4201
  Body structure:
4160
4202
  {
4161
- "class": "MetadataSourceRequestBody",
4203
+ "class": "DeleteRequestBody",
4162
4204
  "externalSourceGUID": "add guid here",
4163
4205
  "externalSourceName": "add qualified name here",
4164
4206
  "effectiveTime": {{isotime}},
@@ -4170,7 +4212,8 @@ class SolutionArchitect(Client):
4170
4212
  loop = asyncio.get_event_loop()
4171
4213
  loop.run_until_complete(self._async_detach_sub_component(parent_component_guid, member_component_guid, body))
4172
4214
 
4173
- async def _async_link_solution_linking_wire(self, component1_guid: str, component2_guid: str, body: dict) -> None:
4215
+ @dynamic_catch
4216
+ async def _async_link_solution_linking_wire(self, component1_guid: str, component2_guid: str, body: dict | NewRelationshipRequestBody) -> None:
4174
4217
  """ Attach a solution component to a solution component as a peer in a solution. Async Version.
4175
4218
 
4176
4219
  Parameters
@@ -4179,9 +4222,10 @@ class SolutionArchitect(Client):
4179
4222
  GUID of the first component to link.
4180
4223
  component2_guid: str
4181
4224
  GUID of the second component to link.
4182
- body: dict
4225
+ body: dict | NewRelationshipRequestBody
4183
4226
  The body describing the link.
4184
4227
 
4228
+
4185
4229
  Returns
4186
4230
  -------
4187
4231
  None
@@ -4200,7 +4244,7 @@ class SolutionArchitect(Client):
4200
4244
 
4201
4245
  Body structure:
4202
4246
  {
4203
- "class": "RelationshipRequestBody",
4247
+ "class": "NewRelationshipRequestBody",
4204
4248
  "externalSourceGUID": "add guid here",
4205
4249
  "externalSourceName": "add qualified name here",
4206
4250
  "properties": {
@@ -4214,16 +4258,14 @@ class SolutionArchitect(Client):
4214
4258
  "forDuplicateProcessing": false
4215
4259
  }
4216
4260
  """
4217
- validate_guid(component1_guid)
4218
- validate_guid(component2_guid)
4261
+
4219
4262
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
4220
4263
  f"solution-components/{component1_guid}/wired-to/{component2_guid}/attach")
4221
- if body:
4222
- await self._async_make_request("POST", url, body_slimmer(body))
4223
- else:
4224
- await self._async_make_request("POST", url)
4264
+ await self._async_new_relationship_request(url, ["SolutionLinkingWireProperties"], body)
4265
+ logger.info(f"Linked Solution Linking wires between {component1_guid} -> {component2_guid}")
4225
4266
 
4226
- def link_solution_linking_wire(self, component1_guid: str, component2_guid: str, body: dict) -> None:
4267
+ @dynamic_catch
4268
+ def link_solution_linking_wire(self, component1_guid: str, component2_guid: str, body: dict | NewRelationshipRequestBody) -> None:
4227
4269
  """ Attach a solution component to a solution component as a peer in a solution.
4228
4270
 
4229
4271
  Parameters
@@ -4232,7 +4274,7 @@ class SolutionArchitect(Client):
4232
4274
  GUID of the first component to link.
4233
4275
  component2_guid: str
4234
4276
  GUID of the second component to link.
4235
- body: dict
4277
+ body: dict | NewRelationshipRequestBody
4236
4278
  The body describing the link.
4237
4279
 
4238
4280
  Returns
@@ -4253,7 +4295,7 @@ class SolutionArchitect(Client):
4253
4295
 
4254
4296
  Body structure:
4255
4297
  {
4256
- "class": "RelationshipRequestBody",
4298
+ "class": "NewRelationshipRequestBody",
4257
4299
  "externalSourceGUID": "add guid here",
4258
4300
  "externalSourceName": "add qualified name here",
4259
4301
  "properties": {
@@ -4270,8 +4312,9 @@ class SolutionArchitect(Client):
4270
4312
  loop = asyncio.get_event_loop()
4271
4313
  loop.run_until_complete(self._async_link_solution_linking_wire(component1_guid, component2_guid, body))
4272
4314
 
4315
+ @dynamic_catch
4273
4316
  async def _async_detach_solution_linking_wire(self, component1_guid: str, component2_guid: str,
4274
- body: dict = None) -> None:
4317
+ body: dict | DeleteRequestBody = None) -> None:
4275
4318
  """ Detach a solution component from a peer solution component.
4276
4319
  Async Version.
4277
4320
 
@@ -4281,7 +4324,7 @@ class SolutionArchitect(Client):
4281
4324
  GUID of the first component to unlink.
4282
4325
  component2_guid: str
4283
4326
  GUID of the second component to unlink.
4284
- body: dict
4327
+ body: dict | DeleteRequestBody
4285
4328
  The body describing the request.
4286
4329
 
4287
4330
  Returns
@@ -4302,7 +4345,7 @@ class SolutionArchitect(Client):
4302
4345
 
4303
4346
  Body structure:
4304
4347
  {
4305
- "class" : "MetadataSourceRequestBody",
4348
+ "class" : "DeleteRequestBody",
4306
4349
  "externalSourceGUID": "add guid here",
4307
4350
  "externalSourceName": "add qualified name here",
4308
4351
  "effectiveTime" : "{{$isoTimestamp}}",
@@ -4311,14 +4354,16 @@ class SolutionArchitect(Client):
4311
4354
  }
4312
4355
 
4313
4356
  """
4314
- validate_guid(component1_guid)
4315
- validate_guid(component2_guid)
4357
+
4316
4358
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
4317
4359
  f"solution-components/{component1_guid}/wired-to/{component2_guid}/detach")
4318
4360
 
4319
- await self._async_make_request("POST", url, body_slimmer(body))
4361
+ await self._async_delete_request(url, body)
4362
+ logger.info(
4363
+ f"Detached solution linking wire between {component1_guid} -> {component2_guid}")
4320
4364
 
4321
- def detach_solution_linking_wire(self, component1_guid: str, component2_guid: str, body: dict = None) -> None:
4365
+ @dynamic_catch
4366
+ def detach_solution_linking_wire(self, component1_guid: str, component2_guid: str, body: dict | DeleteRequestBody = None) -> None:
4322
4367
  """ Detach a solution component from a peer solution component.
4323
4368
  Async Version.
4324
4369
 
@@ -4328,7 +4373,8 @@ class SolutionArchitect(Client):
4328
4373
  GUID of the first component to unlink.
4329
4374
  component2_guid: str
4330
4375
  GUID of the second component to unlink.
4331
- body: dict
4376
+ body: dict | DeleteRequestBody
4377
+ The body describing the request.
4332
4378
  The body describing the request.
4333
4379
 
4334
4380
  Returns
@@ -4349,7 +4395,7 @@ class SolutionArchitect(Client):
4349
4395
 
4350
4396
  Body structure:
4351
4397
  {
4352
- "class" : "MetadataSourceRequestBody",
4398
+ "class" : "DeleteRequestBody",
4353
4399
  "externalSourceGUID": "add guid here",
4354
4400
  "externalSourceName": "add qualified name here",
4355
4401
  "effectiveTime" : "{{$isoTimestamp}}",
@@ -4361,9 +4407,9 @@ class SolutionArchitect(Client):
4361
4407
  loop = asyncio.get_event_loop()
4362
4408
  loop.run_until_complete(self._async_detach_solution_linking_wire(component1_guid, component2_guid, body))
4363
4409
 
4364
-
4410
+ @dynamic_catch
4365
4411
  async def _async_delete_solution_component(self, solution_component_guid: str, cascade_delete: bool = False,
4366
- body: dict = None) -> None:
4412
+ body: dict | DeleteRequestBody= None) -> None:
4367
4413
  """Delete a solution component. Async Version.
4368
4414
 
4369
4415
  Parameters
@@ -4372,7 +4418,7 @@ class SolutionArchitect(Client):
4372
4418
  guid of the component to delete.
4373
4419
  cascade_delete: bool, optional, default: False
4374
4420
  Cascade the delete to dependent objects?
4375
- body: dict, optional
4421
+ body: dict | DeleteRequestBody, optional
4376
4422
  A dictionary containing parameters for the deletion.
4377
4423
 
4378
4424
  Returns
@@ -4393,7 +4439,7 @@ class SolutionArchitect(Client):
4393
4439
 
4394
4440
  Body structure:
4395
4441
  {
4396
- "class": "MetadataSourceRequestBody",
4442
+ "class": "DeleteRequestBody",
4397
4443
  "externalSourceGUID": "add guid here",
4398
4444
  "externalSourceName": "add qualified name here",
4399
4445
  "effectiveTime": {{isotime}},
@@ -4401,18 +4447,16 @@ class SolutionArchitect(Client):
4401
4447
  "forDuplicateProcessing": false
4402
4448
  }
4403
4449
  """
4404
- validate_guid(solution_component_guid)
4405
- cascaded_s = str(cascade_delete).lower()
4450
+
4406
4451
 
4407
4452
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
4408
- f"solution-components/{solution_component_guid}/delete?cascadeDelete={cascade_delete}")
4409
- if body:
4410
- await self._async_make_request("POST", url, body_slimmer(body))
4411
- else:
4412
- await self._async_make_request("POST", url)
4453
+ f"solution-components/{solution_component_guid}/delete")
4454
+ await self._async_delete_request(url, body, cascade_delete=cascade_delete)
4455
+ logger.info(f"Deleted Solution Component {solution_component_guid} with cascade {cascade_delete}")
4413
4456
 
4457
+ @dynamic_catch
4414
4458
  def delete_solution_component(self, solution_component_guid: str, cascade_delete: bool = False,
4415
- body: dict = None) -> None:
4459
+ body: dict | DeleteRequestBody = None) -> None:
4416
4460
  """Delete a solution component.
4417
4461
  Parameters
4418
4462
  ----------
@@ -4420,7 +4464,7 @@ class SolutionArchitect(Client):
4420
4464
  guid of the component to delete.
4421
4465
  cascade_delete: bool, optional, default: False
4422
4466
  Cascade the delete to dependent objects?
4423
- body: dict, optional
4467
+ body: dict | DeleteRequestBody, optional
4424
4468
  A dictionary containing parameters for the deletion.
4425
4469
 
4426
4470
  Returns
@@ -4441,7 +4485,7 @@ class SolutionArchitect(Client):
4441
4485
 
4442
4486
  Body structure:
4443
4487
  {
4444
- "class": "MetadataSourceRequestBody",
4488
+ "class": "DeleteRequestBody",
4445
4489
  "externalSourceGUID": "add guid here",
4446
4490
  "externalSourceName": "add qualified name here",
4447
4491
  "effectiveTime": {{isotime}},
@@ -5018,22 +5062,20 @@ class SolutionArchitect(Client):
5018
5062
  #
5019
5063
  # Roles
5020
5064
  #
5021
-
5022
- async def _async_create_solution_role(self, body: dict) -> str:
5065
+ @dynamic_catch
5066
+ async def _async_create_solution_role(self, body: dict | NewElementRequestBody) -> str:
5023
5067
  """ Create a solution role. Async version.
5024
5068
 
5025
5069
  Parameters
5026
5070
  ----------
5027
- body: dict
5028
- A dictionary containing the definition of the role to create.
5071
+ body: dict | NewElementRequestBody
5072
+ A dictionary containing the properties of the solution role to create.
5029
5073
 
5030
5074
  Returns
5031
5075
  -------
5032
5076
 
5033
5077
  str - guid of the role created.
5034
5078
 
5035
- Raises
5036
- ------
5037
5079
  Raises
5038
5080
  ------
5039
5081
  InvalidParameterException
@@ -5087,17 +5129,16 @@ class SolutionArchitect(Client):
5087
5129
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
5088
5130
  f"solution-roles")
5089
5131
 
5090
- response = await self._async_make_request("POST", url, body_slimmer(body))
5091
-
5092
- return response.json().get("guid", "Solution role not created")
5132
+ return await self._async_create_element_body_request(url, ["SolutionRoleProperties"], body)
5093
5133
 
5094
- def create_solution_role(self, body: dict) -> str:
5134
+ @dynamic_catch
5135
+ def create_solution_role(self, body: dict| NewElementRequestBody) -> str:
5095
5136
  """Create a solution role. Async version.
5096
5137
 
5097
5138
  Parameters
5098
5139
  ----------
5099
- body: dict
5100
- A dictionary containing the definition of the role to create.
5140
+ body: dict | NewElementRequestBody
5141
+ A dictionary containing the properties of the solution role to create.
5101
5142
 
5102
5143
  Returns
5103
5144
  -------
@@ -5162,7 +5203,8 @@ class SolutionArchitect(Client):
5162
5203
  response = loop.run_until_complete(self._async_create_solution_role(body))
5163
5204
  return response
5164
5205
 
5165
- async def _async_create_solution_role_from_template(self, body: dict) -> str:
5206
+ @dynamic_catch
5207
+ async def _async_create_solution_role_from_template(self, body: dict | TemplateRequestBody) -> str:
5166
5208
  """ Create a new metadata element to represent a solution role using an existing metadata element as a template.
5167
5209
  The template defines additional classifications and relationships that should be added to the new element.
5168
5210
  Async Version.
@@ -5170,8 +5212,9 @@ class SolutionArchitect(Client):
5170
5212
 
5171
5213
  Parameters
5172
5214
  ----------
5173
- body: dict
5174
- A dictionary containing the definition of the solution component to create.
5215
+ body: dict | TemplateRequestBody
5216
+ A dictionary containing the properties of the solution role to create.
5217
+
5175
5218
 
5176
5219
  Returns
5177
5220
  -------
@@ -5233,12 +5276,20 @@ class SolutionArchitect(Client):
5233
5276
  """
5234
5277
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
5235
5278
  f"solution-roles/from-template")
5279
+ if isinstance(body, TemplateRequestBody):
5280
+ validated_body = body
5236
5281
 
5237
- response = await self._async_make_request("POST", url, body_slimmer(body))
5282
+ elif isinstance(body, dict):
5283
+ validated_body = self._template_request_adapter.validate_python(body)
5238
5284
 
5239
- return response.json().get("guid", "Role not created")
5285
+ json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
5286
+ logger.info(json_body)
5287
+ resp = await self._async_make_request("POST", url, json_body, is_json=True)
5288
+ logger.info(f"Create Solution Role from template with GUID: {resp.json().get('guid')}")
5289
+ return resp.json().get("guid", NO_GUID_RETURNED)
5240
5290
 
5241
- def create_solution_role_from_template(self, body: dict) -> str:
5291
+ @dynamic_catch
5292
+ def create_solution_role_from_template(self, body: dict |TemplateRequestBody) -> str:
5242
5293
  """ Create a new solution component using an existing metadata element
5243
5294
  as a template. The template defines additional classifications and relationships that should be
5244
5295
  added to
@@ -5247,8 +5298,8 @@ class SolutionArchitect(Client):
5247
5298
 
5248
5299
  Parameters
5249
5300
  ----------
5250
- body: dict
5251
- A dictionary containing the definition of the solution component to create.
5301
+ body: dict | TemplateRequestBody
5302
+ A dictionary containing the properties of the solution role to create.
5252
5303
 
5253
5304
  Returns
5254
5305
  -------
@@ -5313,7 +5364,8 @@ class SolutionArchitect(Client):
5313
5364
  response = loop.run_until_complete(self._async_create_solution_role_from_template(body))
5314
5365
  return response
5315
5366
 
5316
- async def _async_update_solution_role(self, guid: str, body: dict, replace_all_properties: bool = False) -> None:
5367
+ @dynamic_catch
5368
+ async def _async_update_solution_role(self, guid: str, body: dict |UpdateElementRequestBody) -> None:
5317
5369
  """ Update the properties of a solution role. Async Version.
5318
5370
 
5319
5371
  Parameters
@@ -5368,24 +5420,22 @@ class SolutionArchitect(Client):
5368
5420
  }
5369
5421
 
5370
5422
  """
5371
- validate_guid(guid)
5372
- replace_all_properties_s = str(replace_all_properties).lower()
5423
+
5373
5424
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
5374
- f"solution-roles/{guid}/update?replaceAllProperties={replace_all_properties_s}")
5425
+ f"solution-roles/{guid}/update")
5375
5426
 
5376
- await self._async_make_request("POST", url, body_slimmer(body))
5427
+ await self._async_update_element_body_request(url, ["SolutionRoleProperties"], body)
5377
5428
 
5378
- def update_solution_role(self, guid: str, body: dict, replace_all_properties: bool = False) -> None:
5429
+ @dynamic_catch
5430
+ def update_solution_role(self, guid: str, body: dict | UpdateElementRequestBody) -> None:
5379
5431
  """ Update the properties of a solution role.
5380
5432
 
5381
5433
  Parameters
5382
5434
  ----------
5383
5435
  guid: str
5384
5436
  guid of the solution role to update.
5385
- body: dict
5386
- A dictionary containing the updates to the component.
5387
- replace_all_properties: bool, optional, default is False
5388
- Whether to replace all properties with those provided in the body or to merge with existing properties.
5437
+ body: dict | UpdateElementRequestBody
5438
+ A dictionary containing the updates to the role.
5389
5439
 
5390
5440
  Returns
5391
5441
  -------
@@ -5430,9 +5480,10 @@ class SolutionArchitect(Client):
5430
5480
  }
5431
5481
  """
5432
5482
  loop = asyncio.get_event_loop()
5433
- loop.run_until_complete(self._async_update_solution_role(guid, body, replace_all_properties))
5483
+ loop.run_until_complete(self._async_update_solution_role(guid, body))
5434
5484
 
5435
- async def _async_link_component_to_actor(self, role_guid: str, component_guid: str, body: dict) -> None:
5485
+ @dynamic_catch
5486
+ async def _async_link_component_to_actor(self, role_guid: str, component_guid: str, body: dict | NewRelationshipRequestBody) -> None:
5436
5487
  """ Attach a solution component to a solution role. Async Version.
5437
5488
 
5438
5489
  Parameters
@@ -5441,8 +5492,8 @@ class SolutionArchitect(Client):
5441
5492
  guid of the role to link.
5442
5493
  component_guid: str
5443
5494
  guid of the component to link.
5444
- body: dict
5445
- The body describing the link.
5495
+ body: dict | NewRelationshipRequestBody
5496
+ A dictionary containing the properties of the relationship to create.
5446
5497
 
5447
5498
  Returns
5448
5499
  -------
@@ -5462,7 +5513,7 @@ class SolutionArchitect(Client):
5462
5513
 
5463
5514
  Body structure:
5464
5515
  {
5465
- "class" : "RelationshipRequestBody",
5516
+ "class" : "NewRelationshipRequestBody",
5466
5517
  "properties": {
5467
5518
  "class": "SolutionComponentActorProperties",
5468
5519
  "role": "Add role here",
@@ -5477,14 +5528,15 @@ class SolutionArchitect(Client):
5477
5528
  "forDuplicateProcessing" : false
5478
5529
  }
5479
5530
  """
5480
- validate_guid(component_guid)
5481
- validate_guid(role_guid)
5531
+
5482
5532
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
5483
5533
  f"solution-roles/{role_guid}/solution-roles/{component_guid}/attach")
5484
5534
 
5485
- await self._async_make_request("POST", url, body_slimmer(body))
5535
+ await self._async_new_relationship_request(url, ["InformationSupplyChainLinkProperties"], body)
5536
+ logger.info(f"Linked Role to Component {role_guid} -> {component_guid}")
5486
5537
 
5487
- def link_component_to_actor(self, role_guid: str, component_guid: str, body: dict) -> None:
5538
+ @dynamic_catch
5539
+ def link_component_to_actor(self, role_guid: str, component_guid: str, body: dict | NewRelationshipRequestBody) -> None:
5488
5540
  """ Attach a solution component to a solution role.
5489
5541
 
5490
5542
  Parameters
@@ -5493,8 +5545,8 @@ class SolutionArchitect(Client):
5493
5545
  guid of the role to link.
5494
5546
  component_guid: str
5495
5547
  guid of the component to link.
5496
- body: dict
5497
- The body describing the link.
5548
+ body: dict | NewRelationshipRequestBody
5549
+ A dictionary containing the properties of the relationship to create.
5498
5550
 
5499
5551
  Returns
5500
5552
  -------
@@ -5514,7 +5566,7 @@ class SolutionArchitect(Client):
5514
5566
 
5515
5567
  Body structure:
5516
5568
  {
5517
- "class" : "RelationshipRequestBody",
5569
+ "class" : "NewRelationshipRequestBody",
5518
5570
  "properties": {
5519
5571
  "class": "SolutionComponentActorProperties",
5520
5572
  "role": "Add role here",
@@ -5533,7 +5585,8 @@ class SolutionArchitect(Client):
5533
5585
  loop = asyncio.get_event_loop()
5534
5586
  loop.run_until_complete(self._async_link_component_to_actor(role_guid, component_guid, body))
5535
5587
 
5536
- async def _async_detach_component_actor(self, role_guid: str, component_guid: str, body: dict = None) -> None:
5588
+ @dynamic_catch
5589
+ async def _async_detach_component_actor(self, role_guid: str, component_guid: str, body: dict | DeleteRequestBody = None) -> None:
5537
5590
  """ Detach a solution role from a solution component.
5538
5591
  Async Version.
5539
5592
 
@@ -5543,8 +5596,8 @@ class SolutionArchitect(Client):
5543
5596
  guid of the role to disconnect from.
5544
5597
  component_guid: str
5545
5598
  guid of the component to disconnect.
5546
- body: dict
5547
- The body describing the request.
5599
+ body: dict | DeleteRequestBody, optional
5600
+ A dictionary containing the properties of the relationship to create.
5548
5601
 
5549
5602
  Returns
5550
5603
  -------
@@ -5564,7 +5617,7 @@ class SolutionArchitect(Client):
5564
5617
 
5565
5618
  Body structure:
5566
5619
  {
5567
- "class": "MetadataSourceRequestBody",
5620
+ "class": "DeleteRequestBody",
5568
5621
  "externalSourceGUID": "add guid here",
5569
5622
  "externalSourceName": "add qualified name here",
5570
5623
  "effectiveTime": {{isotime}},
@@ -5573,14 +5626,16 @@ class SolutionArchitect(Client):
5573
5626
  }
5574
5627
 
5575
5628
  """
5576
- validate_guid(role_guid)
5577
- validate_guid(component_guid)
5629
+
5578
5630
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
5579
5631
  f"solution-components/{role_guid}/solution-component-actors/{component_guid}/detach")
5580
5632
 
5581
- await self._async_make_request("POST", url, body_slimmer(body))
5633
+ await self._async_delete_request(url, body)
5634
+ logger.info(
5635
+ f"Detached role from component {role_guid} -> {component_guid}")
5582
5636
 
5583
- def detach_component_actore(self, role_guid: str, component_guid: str, body: dict = None) -> None:
5637
+ @dynamic_catch
5638
+ def detach_component_actore(self, role_guid: str, component_guid: str, body: dict |DeleteRequestBody = None) -> None:
5584
5639
  """ Detach a solution role from a solution component.
5585
5640
 
5586
5641
  Parameters
@@ -5589,8 +5644,8 @@ class SolutionArchitect(Client):
5589
5644
  guid of the role to disconnect from.
5590
5645
  component_guid: str
5591
5646
  guid of the component to disconnect.
5592
- body: dict
5593
- The body describing the request.
5647
+ body: dict | DeleteRequestBody, optional
5648
+ A dictionary containing the properties of the relationship to create.
5594
5649
 
5595
5650
  Returns
5596
5651
  -------
@@ -5610,7 +5665,7 @@ class SolutionArchitect(Client):
5610
5665
 
5611
5666
  Body structure:
5612
5667
  {
5613
- "class": "MetadataSourceRequestBody",
5668
+ "class": "DeleteRequestBody",
5614
5669
  "externalSourceGUID": "add guid here",
5615
5670
  "externalSourceName": "add qualified name here",
5616
5671
  "effectiveTime": {{isotime}},
@@ -5621,15 +5676,15 @@ class SolutionArchitect(Client):
5621
5676
  loop = asyncio.get_event_loop()
5622
5677
  loop.run_until_complete(self._async_detach_component_actor(role_guid, component_guid, body))
5623
5678
 
5624
-
5625
- async def _async_delete_solution_role(self, guid: str, body: dict = None, cascade_delete: bool = False,) -> None:
5679
+ @dynamic_catch
5680
+ async def _async_delete_solution_role(self, guid: str, body: dict | DeleteRequestBody= None, cascade_delete: bool = False,) -> None:
5626
5681
  """Delete a solution role. Async Version.
5627
5682
 
5628
5683
  Parameters
5629
5684
  ----------
5630
5685
  guid: str
5631
5686
  guid of the role to delete.
5632
- body: dict, optional
5687
+ body: dict | DeleteRequestBody, optional
5633
5688
  A dictionary containing parameters for the deletion.
5634
5689
  cascade_delete: bool, optional, default: False
5635
5690
  Cascade the delete to dependent objects?
@@ -5653,7 +5708,7 @@ class SolutionArchitect(Client):
5653
5708
 
5654
5709
  Body structure:
5655
5710
  {
5656
- "class": "MetadataSourceRequestBody",
5711
+ "class": "DeleteRequestBody",
5657
5712
  "externalSourceGUID": "add guid here",
5658
5713
  "externalSourceName": "add qualified name here",
5659
5714
  "effectiveTime": {{isotime}},
@@ -5661,22 +5716,22 @@ class SolutionArchitect(Client):
5661
5716
  "forDuplicateProcessing": false
5662
5717
  }
5663
5718
  """
5664
- validate_guid(guid)
5665
- cascaded_s = str(cascade_delete).lower()
5666
5719
 
5667
5720
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/solution-architect/"
5668
- f"solution-roles/{guid}/delete?cascadedDelete={cascaded_s}")
5721
+ f"solution-roles/{guid}/delete")
5669
5722
 
5670
- await self._async_make_request("POST", url, body_slimmer(body))
5723
+ await self._async_delete_request(url, body, cascade_delete=cascade_delete)
5724
+ logger.info(f"Delete solution rule {guid} with cascade {cascade_delete}")
5671
5725
 
5672
- def delete_solution_role(self, guid: str, body: dict = None,cascade_delete: bool = False) -> None:
5726
+ @dynamic_catch
5727
+ def delete_solution_role(self, guid: str, body: dict | DeleteRequestBody = None,cascade_delete: bool = False) -> None:
5673
5728
  """Delete a solution role. Async Version.
5674
5729
 
5675
5730
  Parameters
5676
5731
  ----------
5677
5732
  guid: str
5678
5733
  guid of the role to delete.
5679
- body: dict, optional
5734
+ body: dict | DeleteRequestBody, optional
5680
5735
  A dictionary containing parameters for the deletion.
5681
5736
  cascade_delete: bool, optional, default: False
5682
5737
  Cascade the delete to dependent objects?
@@ -5699,7 +5754,7 @@ class SolutionArchitect(Client):
5699
5754
 
5700
5755
  Body structure:
5701
5756
  {
5702
- "class": "MetadataSourceRequestBody",
5757
+ "class": "DeleteRequestBody",
5703
5758
  "externalSourceGUID": "add guid here",
5704
5759
  "externalSourceName": "add qualified name here",
5705
5760
  "effectiveTime": {{isotime}},