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.
Files changed (121) hide show
  1. commands/cat/.DS_Store +0 -0
  2. commands/cat/.env +8 -0
  3. commands/cat/debug_log.log +0 -0
  4. commands/cat/list_collections.py +15 -6
  5. commands/cat/list_format_set.py +90 -85
  6. commands/cat/logs/pyegeria.log +136 -0
  7. commands/cli/debug_log.log +0 -0
  8. commands/ops/logs/pyegeria.log +0 -0
  9. md_processing/.DS_Store +0 -0
  10. md_processing/dr-egeria-outbox/Collections-2025-08-12-13-30-37.md +163 -0
  11. md_processing/dr-egeria-outbox/Collections-2025-08-12-13-35-58.md +474 -0
  12. md_processing/dr_egeria_inbox/Derive-Dr-Gov-Defs.md +8 -0
  13. md_processing/dr_egeria_inbox/Dr.Egeria Templates.md +873 -0
  14. md_processing/dr_egeria_inbox/arch_test.md +57 -0
  15. md_processing/dr_egeria_inbox/archive/dr_egeria_intro.md +254 -0
  16. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_more_terms.md +696 -0
  17. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part1.md +254 -0
  18. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part2.md +298 -0
  19. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part3.md +608 -0
  20. md_processing/dr_egeria_inbox/archive/dr_egeria_intro_part4.md +94 -0
  21. md_processing/dr_egeria_inbox/archive/freddie_intro.md +284 -0
  22. md_processing/dr_egeria_inbox/archive/freddie_intro_orig.md +275 -0
  23. md_processing/dr_egeria_inbox/archive/test-term.md +110 -0
  24. md_processing/dr_egeria_inbox/cat_test.md +100 -0
  25. md_processing/dr_egeria_inbox/collections.md +39 -0
  26. md_processing/dr_egeria_inbox/data_designer_debug.log +6 -0
  27. md_processing/dr_egeria_inbox/data_designer_out.md +60 -0
  28. md_processing/dr_egeria_inbox/data_designer_search_test.md +11 -0
  29. md_processing/dr_egeria_inbox/data_field.md +54 -0
  30. md_processing/dr_egeria_inbox/data_spec.md +77 -0
  31. md_processing/dr_egeria_inbox/data_spec_test.md +2406 -0
  32. md_processing/dr_egeria_inbox/data_test.md +179 -0
  33. md_processing/dr_egeria_inbox/data_test2.md +429 -0
  34. md_processing/dr_egeria_inbox/data_test3.md +462 -0
  35. md_processing/dr_egeria_inbox/dr_egeria_data_designer_1.md +124 -0
  36. md_processing/dr_egeria_inbox/dr_egeria_intro_categories.md +168 -0
  37. md_processing/dr_egeria_inbox/dr_egeria_intro_part1.md +280 -0
  38. md_processing/dr_egeria_inbox/dr_egeria_intro_part2.md +313 -0
  39. md_processing/dr_egeria_inbox/dr_egeria_intro_part3.md +1073 -0
  40. md_processing/dr_egeria_inbox/dr_egeria_isc1.md +44 -0
  41. md_processing/dr_egeria_inbox/generated_help_report.md +9 -0
  42. md_processing/dr_egeria_inbox/glossary_list.md +5 -0
  43. md_processing/dr_egeria_inbox/glossary_search_test.md +40 -0
  44. md_processing/dr_egeria_inbox/glossary_test1.md +324 -0
  45. md_processing/dr_egeria_inbox/gov_def.md +424 -0
  46. md_processing/dr_egeria_inbox/gov_def2.md +447 -0
  47. md_processing/dr_egeria_inbox/product.md +50 -0
  48. md_processing/dr_egeria_inbox/rel.md +8 -0
  49. md_processing/dr_egeria_inbox/sb.md +119 -0
  50. md_processing/dr_egeria_inbox/solution-components.md +136 -0
  51. md_processing/dr_egeria_inbox/solution_blueprints.md +118 -0
  52. md_processing/dr_egeria_inbox/synonym_test.md +42 -0
  53. md_processing/dr_egeria_inbox/t2.md +268 -0
  54. md_processing/dr_egeria_outbox/.obsidian/app.json +1 -0
  55. md_processing/dr_egeria_outbox/.obsidian/appearance.json +1 -0
  56. md_processing/dr_egeria_outbox/.obsidian/community-plugins.json +6 -0
  57. md_processing/dr_egeria_outbox/.obsidian/core-plugins.json +31 -0
  58. md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/data.json +10 -0
  59. md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/main.js +4459 -0
  60. md_processing/dr_egeria_outbox/.obsidian/plugins/calendar/manifest.json +10 -0
  61. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/data.json +3 -0
  62. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/main.js +153 -0
  63. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/manifest.json +11 -0
  64. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-kanban/styles.css +1 -0
  65. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/main.js +500 -0
  66. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/manifest.json +12 -0
  67. md_processing/dr_egeria_outbox/.obsidian/plugins/obsidian-tasks-plugin/styles.css +1 -0
  68. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/main.js +37 -0
  69. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/manifest.json +11 -0
  70. md_processing/dr_egeria_outbox/.obsidian/plugins/templater-obsidian/styles.css +220 -0
  71. md_processing/dr_egeria_outbox/.obsidian/types.json +28 -0
  72. md_processing/dr_egeria_outbox/.obsidian/workspace.json +220 -0
  73. md_processing/dr_egeria_outbox/Untitled.canvas +1 -0
  74. md_processing/dr_egeria_outbox/friday/processed-2025-07-18 15:00-product.md +62 -0
  75. md_processing/dr_egeria_outbox/friday/processed-2025-07-18 15:13-product.md +62 -0
  76. md_processing/dr_egeria_outbox/friday/processed-2025-07-20 13:23-product.md +47 -0
  77. md_processing/dr_egeria_outbox/friday/processed-2025-08-01 11:55-data_test3.md +503 -0
  78. md_processing/dr_egeria_outbox/monday/processed-2025-07-14 12:38-data_designer_out.md +663 -0
  79. md_processing/dr_egeria_outbox/monday/processed-2025-07-21 10:52-generated_help_report.md +2744 -0
  80. md_processing/dr_egeria_outbox/monday/processed-2025-07-21 18:38-collections.md +62 -0
  81. md_processing/dr_egeria_outbox/monday/processed-2025-08-01 11:34-gov_def.md +444 -0
  82. md_processing/dr_egeria_outbox/processed-2025-08-03 16:05-glossary_list.md +37 -0
  83. md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 14:55-product.md +77 -0
  84. md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 15:05-product.md +75 -0
  85. md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 15:11-product.md +74 -0
  86. md_processing/dr_egeria_outbox/sunday/processed-2025-07-20 20:40-collections.md +49 -0
  87. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 15:00-Derive-Dr-Gov-Defs.md +719 -0
  88. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:13-Derive-Dr-Gov-Defs.md +41 -0
  89. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:14-Derive-Dr-Gov-Defs.md +33 -0
  90. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 20:50-Derive-Dr-Gov-Defs.md +192 -0
  91. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 22:08-gov_def2.md +486 -0
  92. md_processing/dr_egeria_outbox/thursday/processed-2025-07-17 22:10-gov_def2.md +486 -0
  93. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 08:53-gov_def2.md +486 -0
  94. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 08:54-gov_def2.md +486 -0
  95. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:03-gov_def2.md +486 -0
  96. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:06-gov_def2.md +486 -0
  97. md_processing/dr_egeria_outbox/thursday/processed-2025-07-18 09:10-gov_def2.md +486 -0
  98. md_processing/dr_egeria_outbox/tuesday/processed-2025-07-16 19:15-gov_def2.md +527 -0
  99. md_processing/dr_egeria_outbox/tuesday/processed-2025-07-17 12:08-gov_def2.md +527 -0
  100. md_processing/dr_egeria_outbox/tuesday/processed-2025-07-17 14:27-gov_def2.md +485 -0
  101. md_processing/md_processing_utils/debug_log.log +0 -0
  102. md_processing/md_processing_utils/solution_architect_log.log +0 -0
  103. pyegeria/.DS_Store +0 -0
  104. pyegeria/__init__.py +2 -2
  105. pyegeria/_client_new.py +392 -98
  106. pyegeria/_exceptions_new.py +16 -13
  107. pyegeria/_output_format_models.py +22 -17
  108. pyegeria/_output_formats.py +107 -34
  109. pyegeria/collection_manager.py +703 -1429
  110. pyegeria/collection_manager_omvs.py +48 -19
  111. pyegeria/egeria_cat_client.py +1 -1
  112. pyegeria/egeria_client.py +6 -0
  113. pyegeria/egeria_tech_client.py +6 -1
  114. pyegeria/governance_officer.py +2515 -0
  115. pyegeria/models.py +23 -6
  116. pyegeria/output_formatter.py +298 -79
  117. {pyegeria-5.4.0.20.dist-info → pyegeria-5.4.0.23.dist-info}/METADATA +1 -1
  118. {pyegeria-5.4.0.20.dist-info → pyegeria-5.4.0.23.dist-info}/RECORD +121 -19
  119. {pyegeria-5.4.0.20.dist-info → pyegeria-5.4.0.23.dist-info}/LICENSE +0 -0
  120. {pyegeria-5.4.0.20.dist-info → pyegeria-5.4.0.23.dist-info}/WHEEL +0 -0
  121. {pyegeria-5.4.0.20.dist-info → pyegeria-5.4.0.23.dist-info}/entry_points.txt +0 -0
@@ -9,34 +9,35 @@ Copyright Contributors to the ODPi Egeria project.
9
9
  import asyncio
10
10
  import inspect
11
11
  from datetime import datetime
12
- from typing import Dict, Union, List, Optional, Annotated, Literal
12
+ from typing import Optional, Annotated, Literal, Union
13
13
 
14
- import pydantic_core
15
- # from jsonschema import ValidationError
16
14
  from loguru import logger
17
- from pydantic import BaseModel, ValidationError, TypeAdapter, Field
15
+ from pydantic import ValidationError, Field, HttpUrl
18
16
 
19
- from pyegeria._exceptions_new import PyegeriaInvalidParameterException, PyegeriaException
20
- from pyegeria._output_formats import select_output_format_set, get_output_format_type_match
21
- from pyegeria._client import Client
17
+ from pyegeria._exceptions_new import PyegeriaInvalidParameterException
22
18
  from pyegeria._globals import NO_ELEMENTS_FOUND, NO_GUID_RETURNED, NO_MEMBERS_FOUND
23
- from pyegeria._validators import validate_guid, validate_search_string
24
- from pyegeria.output_formatter import (generate_output,
25
- _extract_referenceable_properties)
26
- from pyegeria.utils import body_slimmer, dynamic_catch
19
+ from pyegeria._output_formats import select_output_format_set, get_output_format_type_match
20
+ from pyegeria.load_config import get_app_config
27
21
  from pyegeria.models import (SearchStringRequestBody, FilterRequestBody, GetRequestBody, NewElementRequestBody,
28
22
  ReferenceableProperties, InitialClassifications, TemplateRequestBody,
29
23
  UpdateElementRequestBody, UpdateStatusRequestBody, NewRelationshipRequestBody,
30
- DeleteRequestBody)
31
- from pyegeria.load_config import get_app_config
24
+ DeleteRequestBody, UpdateRelationshipRequestBody, ResultsRequestBody,
25
+ get_defined_field_values, PyegeriaModel)
26
+ from pyegeria.output_formatter import (generate_output,
27
+ _extract_referenceable_properties, populate_columns_from_properties,
28
+ get_required_relationships)
29
+ from pyegeria.utils import body_slimmer, dynamic_catch
30
+
32
31
 
33
32
  app_settings = get_app_config()
34
33
  EGERIA_LOCAL_QUALIFIER = app_settings.User_Profile.egeria_local_qualifier
35
34
 
36
- COLLECTION_PROPERTIES_LIST = ["CollectionPropertie", "DataDictionaryProperties",
35
+ COLLECTION_PROPERTIES_LIST = ["CollectionProperties", "DataDictionaryProperties",
37
36
  "DataSpecProperties", "DigitalProductProperties",
38
37
  "AgreementProperties"]
39
38
 
39
+ AGREEMENT_PROPERTIES_LIST = ["AgreementProperties", "DigitalSubscriptionProperties",]
40
+
40
41
 
41
42
  def query_seperator(current_string):
42
43
  if current_string == "":
@@ -62,28 +63,39 @@ class CollectionProperties(ReferenceableProperties):
62
63
  class_: Annotated[Literal["CollectionProperties"], Field(alias="class")]
63
64
 
64
65
 
65
- class DataSpecProperties(ReferenceableProperties):
66
+ class DataSpecProperties(CollectionProperties):
66
67
  class_: Annotated[Literal["DataSpecProperties"], Field(alias="class")]
67
68
 
68
69
 
69
- class DataDictionaryProperties(ReferenceableProperties):
70
+ class DataDictionaryProperties(CollectionProperties):
70
71
  class_: Annotated[Literal["DataDictionaryProperties"], Field(alias="class")]
71
72
 
72
73
 
73
- class AgreementProperties(ReferenceableProperties):
74
+ class AgreementProperties(CollectionProperties):
74
75
  class_: Annotated[Literal["AgreementProperties"], Field(alias="class")]
75
76
  identifier: str | None = None
76
77
  user_defined_status: str | None = None
77
78
 
79
+ class DigitalSubscriptionProperties(AgreementProperties):
80
+ class_: Annotated[Literal["DigitalSubscriptionProperties"], Field(alias="class")]
81
+ support_level: str | None = None
82
+ service_levels: dict | None = None
78
83
 
79
- class DigitalProductProperties(ReferenceableProperties):
84
+
85
+ class DigitalProductProperties(CollectionProperties):
86
+ class_: Annotated[Literal["DigitalProductProperties"], Field(alias="class")]
87
+ user_defined_status: str | None = None
80
88
  product_name: str | None = None
89
+ identifier: str | None = None
90
+ introduction_date: datetime | None = None
81
91
  maturity: str | None = None
82
92
  service_life: str | None = None
83
- introduction_date: datetime | None = None
84
93
  next_version_date: datetime | None = None
85
94
  withdrawal_date: datetime | None = None
86
- current_version: str | None = None
95
+
96
+ class Collections(PyegeriaModel):
97
+ collection: Union[CollectionProperties, DigitalSubscriptionProperties, DigitalProductProperties, AgreementProperties,
98
+ DataSpecProperties, DataDictionaryProperties] = Field(desciminator="class_")
87
99
 
88
100
 
89
101
  class CollectionManager(Client2):
@@ -128,7 +140,8 @@ class CollectionManager(Client2):
128
140
 
129
141
  @dynamic_catch
130
142
  async def _async_get_attached_collections(self, parent_guid: str, start_from: int = 0, page_size: int = 0,
131
- body: dict = None, output_format: str = "JSON",
143
+ category: str = None, classification_names: list[str]= None,
144
+ body: dict | FilterRequestBody = None, output_format: str = "JSON",
132
145
  output_format_set: str | dict = None) -> list | str:
133
146
  """Returns the list of collections that are linked off of the supplied element using the ResourceList
134
147
  relationship. Async version.
@@ -181,12 +194,10 @@ class CollectionManager(Client2):
181
194
  }
182
195
 
183
196
  """
184
- if body is None:
185
- body = {}
186
197
 
187
198
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/collection-manager/"
188
- f"metadata-elements/{parent_guid}/collections?startFrom={start_from}&pageSize={page_size}")
189
-
199
+ f"metadata-elements/{parent_guid}/collections")
200
+ return await self._async_get_name_request(url, body, output_format, output_format_set)
190
201
  response = await self._async_make_request("POST", url, body_slimmer(body))
191
202
  elements = response.json().get("elements", NO_ELEMENTS_FOUND)
192
203
  if type(elements) is str:
@@ -260,164 +271,26 @@ class CollectionManager(Client2):
260
271
 
261
272
 
262
273
  @dynamic_catch
263
- async def _async_find_collections_w_body(self, body: dict | SearchStringRequestBody,
264
- output_format: str = 'JSON',
265
- output_format_set: str | dict = None) -> list | str:
266
- """ Returns the list of collections matching the search string filtered by the optional classification.
267
- The search string is located in the request body and is interpreted as a plain string. The full
268
- body allows complete control including status, asOfTime and effectiveTime.
269
- The request parameters, startsWith, endsWith and ignoreCase can be used to allow a fuzzy search.
270
-
271
- Parameters
272
- ----------
273
- body: dict
274
- Details of the search request - see the notes below for details.
275
- output_format: str, default = "JSON"
276
- - one of "MD", "LIST", "FORM", "REPORT", "DICT", "MERMAID" or "JSON"
277
- output_format_set: str | dict , optional, default = None
278
- - The desired output columns/field options.
279
- Returns
280
- -------
281
- List | str
282
-
283
- A list of collections match matching the search string. Returns a string if none found.
284
-
285
- Raises
286
- ------
287
-
288
- InvalidParameterException
289
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
290
- PropertyServerException
291
- Raised by the server when an issue arises in processing a valid request
292
- NotAuthorizedException
293
- The principle specified by the user_id does not have authorization for the requested action
294
-
295
- Notes
296
- -----
297
-
298
- {
299
- "class" : "SearchStringRequestBody",
300
- "searchString" : "Data Product Development Journey",
301
- "includeOnlyClassifiedElements" : ["RootCollection"],
302
- "startsWith" : false,
303
- "endsWith" : false,
304
- "ignoreCase" : true,
305
- "startFrom" : 0,
306
- "pageSize": 0,
307
- "asOfTime" : "{{$isoTimestamp}}",
308
- "effectiveTime" : "{{$isoTimestamp}}",
309
- "forLineage" : false,
310
- "forDuplicateProcessing" : false,
311
- "limitResultsByStatus" : ["ACTIVE"],
312
- "sequencingOrder" : "PROPERTY_ASCENDING",
313
- "sequencingProperty" : "qualifiedName"
314
- }
315
- """
316
- if isinstance(body, SearchStringRequestBody):
317
- validated_body = body
318
- elif isinstance(body, dict):
319
- try:
320
- validated_body = self._search_string_request_adapter.validate_python(body)
321
- except ValidationError as e:
322
- context: dict = {
323
- 'class name': __class__.__name__, 'caller method': inspect.currentframe().f_back.f_code.co_name
324
- }
325
- raise PyegeriaInvalidParameterException(context = context, Exception = e)
326
- else:
327
- raise TypeError("Invalid parameter type")
328
-
329
- val_json = validated_body.model_dump_json()
330
-
331
- url = f"{self.collection_command_root}/by-search-string"
332
- classification_name = validated_body.include_only_classified_elements
333
-
334
- response = await self._async_make_request("POST", url, val_json)
335
- elements = response.json().get("elements", NO_ELEMENTS_FOUND)
336
- if type(elements) is str:
337
- logger.info(NO_ELEMENTS_FOUND)
338
- return NO_ELEMENTS_FOUND
339
-
340
- if output_format != 'JSON': # return a simplified markdown representation
341
- # logger.info(f"Found elements, output format: {output_format} and output_format_set: {output_format_set}")
342
- return self._generate_collection_output(elements, None, classification_name,
343
- output_format, output_format_set)
344
- return elements
345
-
346
- @dynamic_catch
347
- def find_collections_w_body(self, body: dict | SearchStringRequestBody, output_format: str = 'JSON',
348
- output_format_set: str | dict = None) -> list | str:
349
- """ Returns the list of collections matching the search string filtered by the optional classification.
350
- The search string is located in the request body and is interpreted as a plain string. The full
351
- body allows complete control including status, asOfTime and effectiveTime.
352
- The request parameters, startsWith, endsWith and ignoreCase can be used to allow a fuzzy search.
353
-
354
- Parameters
355
- ----------
356
- body: dict
357
- Details of the search request - see the notes below for details.
358
- output_format: str, default = "JSON"
359
- - one of "MD", "LIST", "FORM", "REPORT", "DICT", "MERMAID" or "JSON"
360
- output_format_set: str | dict , optional, default = None
361
- - The desired output columns/field options.
362
- Returns
363
- -------
364
- List | str
365
-
366
- A list of collections match matching the search string. Returns a string if none found.
367
-
368
- Raises
369
- ------
370
-
371
- InvalidParameterException
372
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
373
- PropertyServerException
374
- Raised by the server when an issue arises in processing a valid request
375
- NotAuthorizedException
376
- The principle specified by the user_id does not have authorization for the requested action
377
-
378
- Notes
379
- -----
380
-
381
- {
382
- "class" : "SearchStringRequestBody",
383
- "searchString" : "Data Product Development Journey",
384
- "includeOnlyClassifiedElements" : ["RootCollection"],
385
- "startsWith" : false,
386
- "endsWith" : false,
387
- "ignoreCase" : true,
388
- "startFrom" : 0,
389
- "pageSize": 0,
390
- "asOfTime" : "{{$isoTimestamp}}",
391
- "effectiveTime" : "{{$isoTimestamp}}",
392
- "forLineage" : false,
393
- "forDuplicateProcessing" : false,
394
- "limitResultsByStatus" : ["ACTIVE"],
395
- "sequencingOrder" : "PROPERTY_ASCENDING",
396
- "sequencingProperty" : "qualifiedName"
397
- }
398
- """
399
- return asyncio.get_event_loop().run_until_complete(
400
- self._async_find_collections_w_body(body, output_format, output_format_set))
401
-
402
-
403
- @dynamic_catch
404
- async def _async_find_collections(self, search_string: str = '*', classification_name: str = None,
274
+ async def _async_find_collections(self, search_string: str = "*", classification_names: list[str] = None,
275
+ metadata_element_types: list[str] = None,
405
276
  starts_with: bool = True, ends_with: bool = False, ignore_case: bool = False,
406
277
  start_from: int = 0, page_size: int = 0, output_format: str = 'JSON',
407
- output_format_set: str | dict = None) -> list | str:
278
+ output_format_set: str | dict = None,
279
+ body: dict | SearchStringRequestBody = None) -> list | str:
408
280
  """ Returns the list of collections matching the search string filtered by the optional classification.
409
- The search string is located in the request body and is interpreted as a plain string. The full
410
- body allows complete control including status, asOfTime and effectiveTime.
411
- The request parameters, startsWith, endsWith and ignoreCase can be used to allow a fuzzy search.
281
+ This method can either be used with a body, allowing full control, or with the individual parameters.
282
+ If the body is provided it will be used and the search_string will be ignored.
412
283
 
413
284
  Parameters
414
285
  ----------
415
286
  search_string: str
416
287
  Search string to match against - None or '*' indicate match against all collections (may be filtered by
417
288
  classification).
418
- classification_name: str, optional, default=None
419
- A classification name to filter on - for example, DataSpec for data specifications. If none,
289
+ classification_names: list[str], optional, default=None
290
+ A list of classification names to filter on - for example, ["DataSpec"], for data specifications. If none,
420
291
  then all classifications are returned.
292
+ metadata_element_types: list[str], optional, default=None
293
+ A list of metadata element types to filter on - for example, ["DataSpec"], for data specifications. If none,
421
294
  starts_with : bool, [default=False], optional
422
295
  Starts with the supplied string.
423
296
  ends_with : bool, [default=False], optional
@@ -431,58 +304,62 @@ class CollectionManager(Client2):
431
304
  the class instance.
432
305
  output_format: str, default = "JSON"
433
306
  - one of "MD", "LIST", "FORM", "REPORT", "DICT", "MERMAID" or "JSON"
434
- output_format_set: str | dict , optional, default = None
307
+ output_format_set: str | dict , optional, default = None
435
308
  - The desired output columns/fields to include.
309
+ body: dict | SearchStringRequestBody, optional, default = None
310
+ - if provided, the search parameters in the body will supercede other attributes, such as "search_string"
311
+
436
312
  Returns
437
313
  -------
438
314
  List | str
439
315
 
440
- A list of collections match matching the search string. Returns a string if none found.
316
+ Output depends on the output format specified.
441
317
 
442
318
  Raises
443
319
  ------
444
320
 
445
- InvalidParameterException
446
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
447
- PropertyServerException
448
- Raised by the server when an issue arises in processing a valid request
321
+ ValidationError
322
+ If the client passes incorrect parameters on the request that don't conform to the data model.
323
+ PyegeriaException
324
+ Issues raised in communicating or server side processing.
449
325
  NotAuthorizedException
450
326
  The principle specified by the user_id does not have authorization for the requested action
451
327
 
452
328
  """
453
- search_string = search_string if search_string != "*" else None
454
-
455
- body = SearchStringRequestBody(
456
- class_="SearchStringRequestBody",
457
- starts_with=starts_with,
458
- ends_with=ends_with,
459
- ignore_case=ignore_case,
460
- include_only_classified_elements = [classification_name],
461
- search_string=search_string,
462
- start_from=start_from,
463
- page_size=page_size
464
- )
465
-
466
- resp = await self._async_find_collections_w_body(body, output_format, output_format_set)
467
- return resp
329
+ url = str(HttpUrl(f"{self.collection_command_root}/by-search-string"))
330
+ response = await self._async_find_request(url, _type="Collection",
331
+ _gen_output=self._generate_collection_output,
332
+ search_string = search_string, classification_names = classification_names,
333
+ metadata_element_types = metadata_element_types,
334
+ starts_with = starts_with, ends_with = ends_with, ignore_case = ignore_case,
335
+ start_from = start_from, page_size = page_size,
336
+ output_format=output_format, output_format_set=output_format_set,
337
+ body=body)
338
+
339
+ return response
468
340
 
469
341
  @dynamic_catch
470
- def find_collections(self, search_string: str = '*', classification_name: str = None, starts_with: bool = True,
342
+ def find_collections(self, search_string: str = '*', classification_names: str = None,
343
+ metadata_element_types: list[str] = None, starts_with: bool = True,
471
344
  ends_with: bool = False, ignore_case: bool = False,
472
345
  start_from: int = 0, page_size: int = 0, output_format: str = 'JSON',
473
- output_format_set: str | dict = None) -> list | str:
346
+ output_format_set: str | dict = None,
347
+ body: dict | SearchStringRequestBody = None) -> list | str:
474
348
  """ Returns the list of collections matching the search string filtered by the optional classification.
475
- The search string is located in the request body and is interpreted as a plain string. The full
476
- body allows complete control including status, asOfTime and effectiveTime.
477
- The request parameters, startsWith, endsWith and ignoreCase can be used to allow a fuzzy search.
349
+ This method can either be used with a body, allowing full control, or with the individual parameters.
350
+ If the body is provided it will be used and the search_string will be ignored.
478
351
 
479
352
  Parameters
480
353
  ----------
481
354
  search_string: str
482
- String to match against - None or '*' indicate match against all collections (may be filtered
483
- classification_name: str, optional, default=None
484
- A classification name to filter on - for example, DataSpec for data specifications. If none,
355
+ Search string to match against - None or '*' indicate match against all collections (may be filtered by
356
+ classification).
357
+ classification_names: list[str], optional, default=None
358
+ A list of classification names to filter on - for example, ["DataSpec"], for data specifications. If none,
485
359
  then all classifications are returned.
360
+ metadata_element_types: list[str], optional, default=None
361
+ A list of metadata element types to filter on - for example, ["DataSpec"], for data specifications. If none,
362
+ then all metadata element types are returned.
486
363
  starts_with : bool, [default=False], optional
487
364
  Starts with the supplied string.
488
365
  ends_with : bool, [default=False], optional
@@ -496,32 +373,41 @@ class CollectionManager(Client2):
496
373
  the class instance.
497
374
  output_format: str, default = "JSON"
498
375
  - one of "MD", "LIST", "FORM", "REPORT", "DICT", "MERMAID" or "JSON"
499
- output_format_set: str | dict , optional, default = None
376
+ output_format_set: str | dict , optional, default = None
500
377
  - The desired output columns/fields to include.
378
+ body: dict | SearchStringRequestBody, optional, default = None
379
+ - if provided, the search parameters in the body will supercede other attributes, such as "search_string"
380
+
501
381
  Returns
502
382
  -------
503
383
  List | str
504
384
 
505
- A list of collections match matching the search string. Returns a string if none found.
385
+ Output depends on the output format specified.
506
386
 
507
387
  Raises
508
388
  ------
509
389
 
510
- InvalidParameterException
511
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
512
- PropertyServerException
513
- Raised by the server when an issue arises in processing a valid request
390
+ ValidationError
391
+ If the client passes incorrect parameters on the request that don't conform to the data model.
392
+ PyegeriaException
393
+ Issues raised in communicating or server side processing.
514
394
  NotAuthorizedException
515
395
  The principle specified by the user_id does not have authorization for the requested action
516
396
 
397
+ Args:
398
+ classification_names ():
399
+ metadata_element_types ():
400
+
517
401
  """
518
402
  return asyncio.get_event_loop().run_until_complete(
519
- self._async_find_collections(search_string, classification_name, starts_with, ends_with, ignore_case,
520
- start_from, page_size, output_format, output_format_set))
403
+ self._async_find_collections(search_string, classification_names, metadata_element_types,
404
+ starts_with, ends_with, ignore_case,
405
+ start_from, page_size, output_format,
406
+ output_format_set, body))
521
407
 
522
408
 
523
409
  @dynamic_catch
524
- async def _async_get_collections_by_name(self, name: str = None, classification_name: str = None,
410
+ async def _async_get_collections_by_name(self, filter_string: str = None, classification_names: list[str] = None,
525
411
  body: dict | FilterRequestBody = None,
526
412
  start_from: int = 0, page_size: int = 0,
527
413
  output_format: str = 'JSON',
@@ -532,7 +418,7 @@ class CollectionManager(Client2):
532
418
  ----------
533
419
  name: str,
534
420
  name to use to find matching collections.
535
- classification_name: str, optional, default = None
421
+ classification_names: list[str], optional, default = None
536
422
  type of collection to filter by - e.g., DataDict, Folder, Root
537
423
  body: dict, optional, default = None
538
424
  Provides, a full request body. If specified, the body supercedes the name parameter.
@@ -543,7 +429,7 @@ class CollectionManager(Client2):
543
429
  the class instance.
544
430
  output_format: str, default = "JSON"
545
431
  - one of "DICT", "MERMAID" or "JSON"
546
- output_format_set: dict , optional, default = None
432
+ output_format_set: dict , optional, default = None
547
433
  The desired output columns/fields to include.
548
434
 
549
435
  Returns
@@ -562,47 +448,20 @@ class CollectionManager(Client2):
562
448
  NotAuthorizedException
563
449
  The principle specified by the user_id does not have authorization for the requested action
564
450
  """
451
+ url = str(HttpUrl(f"{self.collection_command_root}/by-name"))
452
+ response = await self._async_get_name_request(url, _type="Collection",
453
+ _gen_output=self._generate_collection_output,
454
+ filter_string = filter_string, classification_names = classification_names,
455
+ start_from = start_from, page_size = page_size,
456
+ output_format=output_format, output_format_set=output_format_set,
457
+ body=body)
565
458
 
566
- if body is None:
567
- validated_body = FilterRequestBody(
568
- class_ = "FilterRequestBody",
569
- filter = name,
570
- start_from = start_from,
571
- page_size = page_size,
572
- include_only_classified_elements= [classification_name] if classification_name else None
573
- )
574
- elif isinstance(body, FilterRequestBody):
575
- validated_body = body
576
- elif isinstance(body, dict):
577
- try:
578
- validated_body = self._filter_request_adapter.validate_python(body)
579
- except ValidationError as e:
580
- context: dict = {
581
- 'class name': __class__.__name__, 'caller method': inspect.currentframe().f_back.f_code.co_name
582
- }
583
- raise PyegeriaInvalidParameterException(context=context, Exception=e)
584
- else:
585
- raise TypeError("Invalid parameter type")
586
-
587
- url = f"{self.collection_command_root}/by-name"
588
- classification_name = validated_body.include_only_classified_elements
459
+ return response
589
460
 
590
- response = await self._async_make_request("POST", url, validated_body.model_dump_json())
591
- elements = response.json().get("elements", NO_ELEMENTS_FOUND)
592
- if type(elements) is str:
593
- logger.info(NO_ELEMENTS_FOUND)
594
- return NO_ELEMENTS_FOUND
595
461
 
596
- if output_format != 'JSON': # return a simplified markdown representation
597
- logger.info(f"Found elements, output format: {output_format}, output_format_set: {output_format_set}")
598
- return self._generate_collection_output(elements, filter, classification_name,
599
- output_format, output_format_set)
600
- return elements
601
-
602
-
603
- def get_collections_by_name(self, name: str = None, classification_name: str = None,
462
+ def get_collections_by_name(self, name: str = None, classification_names: list[str] = None,
604
463
  body: dict | FilterRequestBody = None,
605
- start_from: int = 0, page_size: int = None, output_format: str = 'JSON',
464
+ start_from: int = 0, page_size: int = 0, output_format: str = 'JSON',
606
465
  output_format_set: str | dict = None) -> list | str:
607
466
  """Returns the list of collections matching the search string. Async version.
608
467
  The search string is located in the request body and is interpreted as a plain string.
@@ -612,7 +471,7 @@ class CollectionManager(Client2):
612
471
  ----------
613
472
  name: str,
614
473
  name to use to find matching collections.
615
- classification_name: str, optional, default = None
474
+ classification_names: list[str], optional, default = None
616
475
  type of collection to filter by - e.g., DataDict, Folder, Root
617
476
  body: dict, optional, default = None
618
477
  Provides, a full request body. If specified, the body supercedes the name parameter.
@@ -638,24 +497,24 @@ class CollectionManager(Client2):
638
497
 
639
498
  """
640
499
  return asyncio.get_event_loop().run_until_complete(
641
- self._async_get_collections_by_name(name, classification_name, body, start_from, page_size,
500
+ self._async_get_collections_by_name(name, classification_names, body, start_from, page_size,
642
501
  output_format, output_format_set))
643
502
 
644
503
 
645
504
  @dynamic_catch
646
- async def _async_get_collections_by_type(self, collection_type: str, classification_name: str = None,
505
+ async def _async_get_collections_by_category(self, category: str, classification_names: list[str] = None,
647
506
  body: dict | FilterRequestBody = None, start_from: int = 0, page_size: int = 0,
648
507
  output_format: str = 'JSON',
649
508
  output_format_set: str | dict = None) -> list | str:
650
- """Returns the list of collections with a particular collectionType. This is an optional text field in the
509
+ """Returns the list of collections with a particular category. This is an optional text field in the
651
510
  collection element.
652
511
 
653
512
  Parameters
654
513
  ----------
655
- collection_type: str
656
- collection_type to use to find matching collections.
657
- classification_name: str, optional
658
- An optional filter on the search, e.g., DataSpec
514
+ category: str
515
+ category to use to find matching collections.
516
+ classification_names: str, optional
517
+ An optional filter on the search, e.g., DataSpec
659
518
  body: dict, optional, default = None
660
519
  Provides, a full request body. If specified, the body filter parameter supercedes the collection_type
661
520
  parameter.
@@ -692,49 +551,23 @@ class CollectionManager(Client2):
692
551
  "limitResultsByStatus": ["ACTIVE"],
693
552
  "sequencingOrder": "PROPERTY_ASCENDING",
694
553
  "sequencingProperty": "qualifiedName",
695
- "filter": "Add collection type here"
554
+ "filter": "Add category here"
696
555
  }
697
556
 
698
557
  """
699
- if body is None:
700
- validated_body = FilterRequestBody(
701
- class_ = "FilterRequestBody",
702
- filter = collection_type,
703
- start_from = start_from,
704
- page_size = page_size,
705
- include_only_classified_elements= [classification_name] if classification_name else None
706
- )
707
- elif isinstance(body, FilterRequestBody):
708
- validated_body = body
709
- elif isinstance(body, dict):
710
- try:
711
- validated_body = self._filter_request_adapter.validate_python(body)
712
- except ValidationError as e:
713
- context: dict = {
714
- 'class name': __class__.__name__, 'caller method': inspect.currentframe().f_back.f_code.co_name
715
- }
716
- raise PyegeriaInvalidParameterException(context=context, Exception=e)
717
- else:
718
- raise TypeError("Invalid parameter type")
719
-
720
- classification_name = validated_body.include_only_classified_elements
558
+ url = str(HttpUrl(f"{self.collection_command_root}/by-collection-category"))
559
+ response = await self._async_get_name_request(url, _type="Collection",
560
+ _gen_output=self._generate_collection_output,
561
+ filter_string = category, classification_names = classification_names,
562
+ start_from = start_from, page_size = page_size,
563
+ output_format=output_format, output_format_set=output_format_set,
564
+ body=body)
721
565
 
722
- url = f"{self.collection_command_root}/by-collection-category"
566
+ return response
723
567
 
724
- response = await self._async_make_request("POST", url, validated_body.model_dump_json())
725
- elements = response.json().get("elements", NO_ELEMENTS_FOUND)
726
- if type(elements) is str:
727
- logger.info(NO_ELEMENTS_FOUND)
728
- return NO_ELEMENTS_FOUND
729
-
730
- if output_format != 'JSON': # return a simplified markdown representation
731
- logger.info(f"Found elements, output format: {output_format}, output_format_set: {output_format_set}")
732
- return self._generate_collection_output(elements, filter, classification_name,
733
- output_format, output_format_set)
734
- return elements
735
568
 
736
569
 
737
- def get_collections_by_type(self, collection_type: str, classification_name: str = None,
570
+ def get_collections_by_category(self, category: str, classification_names: list[str] = None,
738
571
  body: dict | FilterRequestBody = None,
739
572
  start_from: int = 0, page_size: int = 0, output_format: str = 'JSON',
740
573
  output_format_set: str | dict = None) -> list | str:
@@ -743,9 +576,9 @@ class CollectionManager(Client2):
743
576
 
744
577
  Parameters
745
578
  ----------
746
- collection_type: str
579
+ category: str
747
580
  category to use to find matching collections.
748
- classification_name: str, optional
581
+ classification_names: list[str], optional
749
582
  An optional filter on the search, e.g., DataSpec
750
583
  body: dict, optional, default = None
751
584
  Provides, a full request body. If specified, the body filter parameter supersedes the category
@@ -757,14 +590,14 @@ class CollectionManager(Client2):
757
590
  the class instance.
758
591
  output_format: str, default = "JSON"
759
592
  - one of "DICT", "MERMAID" or "JSON"
760
- output_format_set: str | dict , optional, default = None
593
+ output_format_set: str | dict, optional, default = None
761
594
  The desired output columns/fields to include.
762
595
 
763
596
  Returns
764
597
  -------
765
598
  List | str
766
599
 
767
- A list of collections of the specified collection type. Returns a string if none found.
600
+ A list of collections filtered by the specified category. Output based on specified output format.
768
601
 
769
602
  Raises
770
603
  ------
@@ -789,13 +622,13 @@ class CollectionManager(Client2):
789
622
  "limitResultsByStatus": ["ACTIVE"],
790
623
  "sequencingOrder": "PROPERTY_ASCENDING",
791
624
  "sequencingProperty": "qualifiedName",
792
- "filter": "Add collection type here"
625
+ "filter": "Add category here"
793
626
  }
794
627
 
795
628
  """
796
629
 
797
630
  return asyncio.get_event_loop().run_until_complete(
798
- self._async_get_collections_by_type(collection_type, classification_name, body, start_from, page_size,
631
+ self._async_get_collections_by_category(category, classification_names, body, start_from, page_size,
799
632
  output_format, output_format_set))
800
633
 
801
634
 
@@ -810,13 +643,13 @@ class CollectionManager(Client2):
810
643
  ----------
811
644
  collection_guid: str,
812
645
  unique identifier of the collection.
813
- collection_type: str, default = None, optional
814
- type of collection - Data Dictionary, Data Spec, Data Product, etc.
815
- body: dict, optional, default = None
646
+ element_type: str, default = None, optional
647
+ type of collection - Collection, DataSpec, Agreement, etc.
648
+ body: dict | GetRequestBody, optional, default = None
816
649
  full request body.
817
650
  output_format: str, default = "JSON"
818
651
  - one of "DICT", "MERMAID" or "JSON"
819
- output_format_set: str | dict , optional, default = None
652
+ output_format_set: str | dict, optional, default = None
820
653
  The desired output columns/fields to include.
821
654
 
822
655
  Returns
@@ -847,43 +680,20 @@ class CollectionManager(Client2):
847
680
  }
848
681
  """
849
682
 
850
- if body is None:
851
- validated_body = GetRequestBody(
852
- class_ = "GetRequestBody",
853
- metadata_element_type_name=element_type
854
- )
855
- elif isinstance(body, FilterRequestBody):
856
- validated_body = body
857
- elif isinstance(body, dict):
858
- try:
859
- validated_body = self._get_request_adapter.validate_python(body)
860
- except ValidationError as e:
861
- context: dict = {
862
- 'class name': __class__.__name__, 'caller method': inspect.currentframe().f_back.f_code.co_name
863
- }
864
- raise PyegeriaInvalidParameterException(context=context, Exception=e)
865
- else:
866
- raise TypeError("Invalid parameter type")
867
-
868
- url = f"{self.collection_command_root}/{collection_guid}"
683
+ url = str(HttpUrl(f"{self.collection_command_root}/{collection_guid}"))
684
+ type = element_type if element_type else "Collection"
869
685
 
686
+ response = await self._async_get_guid_request(url, _type=type,
687
+ _gen_output=self._generate_collection_output,
688
+ output_format=output_format, output_format_set=output_format_set,
689
+ body=body)
870
690
 
871
- response = await self._async_make_request("POST", url,
872
- validated_body.model_dump_json())
691
+ return response
873
692
 
874
- elements = response.json().get("element", NO_ELEMENTS_FOUND)
875
- if type(elements) is str:
876
- logger.info(NO_ELEMENTS_FOUND)
877
- return NO_ELEMENTS_FOUND
878
693
 
879
- if output_format != 'JSON': # return a simplified markdown representation
880
- logger.info(f"Found elements, output format: {output_format}, output_format_set: {output_format_set}")
881
- return self._generate_collection_output(elements, None, element_type,
882
- output_format, output_format_set)
883
- return elements
884
694
 
885
695
 
886
- def get_collection_by_guid(self, collection_guid: str, element_type: str = None, body: dict = None,
696
+ def get_collection_by_guid(self, collection_guid: str, element_type: str = None, body: dict | GetRequestBody= None,
887
697
  output_format: str = 'JSON', output_format_set: str | dict = None) -> dict | str:
888
698
  """ Return the properties of a specific collection. Async version.
889
699
 
@@ -891,13 +701,13 @@ class CollectionManager(Client2):
891
701
  ----------
892
702
  collection_guid: str,
893
703
  unique identifier of the collection.
894
- collection_type: str, default = None, optional
895
- type of collection - Data Dictionary, Data Spec, Data Product, etc.
896
- body: dict, optional, default = None
704
+ element_type: str, default = None, optional
705
+ type of element - Collection, DataSpec, Agreement, etc.
706
+ body: dict | GetRequestBody, optional, default = None
897
707
  full request body.
898
708
  output_format: str, default = "JSON"
899
709
  - one of "DICT", "MERMAID" or "JSON"
900
- output_format_set: dict , optional, default = None
710
+ output_format_set: dict , optional, default = None
901
711
  The desired output columns/fields to include.
902
712
 
903
713
 
@@ -935,7 +745,7 @@ class CollectionManager(Client2):
935
745
 
936
746
  @dynamic_catch
937
747
  async def _async_get_collection_members(self, collection_guid: str = None, collection_name: str = None,
938
- collection_qname: str = None, body: dict = None, start_from: int = 0,
748
+ collection_qname: str = None, body: dict | ResultsRequestBody = None, start_from: int = 0,
939
749
  page_size: int = 0, output_format: str = "JSON",
940
750
  output_format_set: str | dict = None) -> list | str:
941
751
  """Return a list of elements that are a member of a collection. Async version.
@@ -994,27 +804,18 @@ class CollectionManager(Client2):
994
804
  """
995
805
 
996
806
  if collection_guid is None:
997
- collection_guid = self.__get_guid__(collection_guid, collection_name, "name",
807
+ collection_guid = self.__get_guid__(collection_guid, collection_name, "displayName",
998
808
  collection_qname, None, )
999
809
 
1000
- url = (f"{self.collection_command_root}/{collection_guid}/"
1001
- f"members?startFrom={start_from}&pageSize={page_size}")
810
+ url = str(HttpUrl(f"{self.collection_command_root}/{collection_guid}/members"))
1002
811
 
1003
- if body:
1004
- response = await self._async_make_request("POST", url, body_slimmer(body))
1005
- else:
1006
- response = await self._async_make_request("POST", url)
1007
812
 
1008
- elements = response.json().get("elements", NO_MEMBERS_FOUND)
1009
- if type(elements) is str:
1010
- logger.trace(f"No elements found for collection {collection_guid}")
1011
- return NO_MEMBERS_FOUND
813
+ response = await self._async_get_results_body_request(url, _type="Collection",
814
+ _gen_output=self._generate_collection_output,
815
+ output_format=output_format, output_format_set=output_format_set,
816
+ body=body)
1012
817
 
1013
- if output_format != 'JSON': # return a simplified markdown representation
1014
- logger.debug(f"Found elements, output format: {output_format}, output_format_set: {output_format_set}")
1015
- return self._generate_collection_output(elements, None, None,
1016
- output_format, output_format_set)
1017
- return elements
818
+ return response
1018
819
 
1019
820
 
1020
821
  def get_collection_members(self, collection_guid: str = None, collection_name: str = None,
@@ -1083,19 +884,17 @@ class CollectionManager(Client2):
1083
884
 
1084
885
 
1085
886
  @dynamic_catch
1086
- async def _async_get_collection_graph(self, collection_guid: str, body: dict = None, start_from: int = 0,
887
+ async def _async_get_collection_hierarchy(self, collection_guid: str, body: dict | ResultsRequestBody = None, start_from: int = 0,
1087
888
  page_size: int = 0, output_format: str = "JSON",
1088
889
  output_format_set: str | dict = None) -> list | str:
1089
- """ Return a graph of elements that are the nested members of a collection along
1090
- with elements immediately connected to the starting collection. The result
1091
- includes a mermaid graph of the returned elements. Async version.
890
+ """ Return a hierarchy of nested collections. Request body is optional Async version.
1092
891
 
1093
892
  Parameters
1094
893
  ----------
1095
894
  collection_guid: str,
1096
895
  identity of the collection to return members for. If none, collection_name or
1097
896
  collection_qname are used.
1098
- body: dict, optional, default = None
897
+ body: dict | ResultsRequestBody, optional, default = None
1099
898
  Providing the body allows full control of the request and replaces filter parameters.
1100
899
  start_from: int, [default=0], optional
1101
900
  When multiple pages of results are available, the page number to start from.
@@ -1104,14 +903,14 @@ class CollectionManager(Client2):
1104
903
  the class instance.
1105
904
  output_format: str, default = "JSON"
1106
905
  - one of "MD", "LIST", "FORM", "REPORT", "DICT", "MERMAID" or "JSON"
1107
- output_format_set: dict , optional, default = None
906
+ output_format_set: dict , optional, default = None
1108
907
  The desired output columns/fields to include.
1109
908
 
1110
909
  Returns
1111
910
  -------
1112
911
  List | str
1113
912
 
1114
- A graph anchored in the collection.
913
+ Results based on the output format.
1115
914
 
1116
915
  Raises
1117
916
  ------
@@ -1126,37 +925,29 @@ class CollectionManager(Client2):
1126
925
  Notes:
1127
926
  -----
1128
927
  Body sample:
1129
- {
1130
- "class": "ResultsRequestBody",
1131
- "effectiveTime": "{{$isoTimestamp}}",
1132
- "limitResultsByStatus": ["ACTIVE"],
1133
- "asOfTime": "{{$isoTimestamp}}",
1134
- "sequencingOrder": "CREATION_DATE_RECENT",
1135
- "sequencingProperty": ""
1136
- }
928
+ {
929
+ "class": "ResultsRequestBody",
930
+ "startFrom": 0,
931
+ "pageSize": 0,
932
+ "effectiveTime": "{{$isoTimestamp}}",
933
+ "limitResultsByStatus": ["ACTIVE"],
934
+ "asOfTime": "{{$isoTimestamp}}",
935
+ "sequencingOrder": "CREATION_DATE_RECENT",
936
+ "sequencingProperty": ""
937
+ }
1137
938
  """
1138
939
 
1139
- url = (f"{self.collection_command_root}/{collection_guid}/"
1140
- f"graph?startFrom={start_from}&pageSize={page_size}")
1141
-
1142
- if body:
1143
- response = await self._async_make_request("POST", url, body_slimmer(body))
1144
- else:
1145
- response = await self._async_make_request("POST", url)
1146
-
1147
- elements = response.json().get("graph", NO_ELEMENTS_FOUND)
1148
- if type(elements) is str:
1149
- logger.info(NO_ELEMENTS_FOUND)
1150
- return NO_ELEMENTS_FOUND
1151
-
1152
- if output_format != 'JSON': # return a simplified markdown representation
1153
- logger.info(f"Found elements, output format: {output_format}, output_format_set: {output_format_set}")
1154
- return self._generate_collection_output(elements, None, None,
1155
- output_format, output_format_set)
1156
- return elements
940
+ url = str(HttpUrl(f"{self.collection_command_root}/{collection_guid}/hierarchy"))
941
+ response = await self._async_get_results_body_request(url, _type="Collection",
942
+ _gen_output=self._generate_collection_output,
943
+ start_from=start_from, page_size=page_size,
944
+ output_format=output_format,
945
+ output_format_set=output_format_set,
946
+ body=body)
1157
947
 
948
+ return response
1158
949
 
1159
- def get_collection_graph(self, collection_guid: str = None, body: dict = None, start_from: int = 0,
950
+ def get_collection_hierarchy(self, collection_guid: str = None, body: dict| ResultsRequestBody = None, start_from: int = 0,
1160
951
  page_size: int = 0, output_format: str = "JSON",
1161
952
  output_format_set: str | dict = None) -> list | str:
1162
953
  """ Return a graph of elements that are the nested members of a collection along
@@ -1209,168 +1000,14 @@ class CollectionManager(Client2):
1209
1000
  }
1210
1001
  """
1211
1002
  return asyncio.get_event_loop().run_until_complete(
1212
- self._async_get_collection_graph(collection_guid, body, start_from, page_size,
1003
+ self._async_get_collection_hierarchy(collection_guid, body, start_from, page_size,
1213
1004
  output_format, output_format_set))
1214
1005
 
1215
1006
 
1216
- @dynamic_catch
1217
- async def _async_get_collection_graph_w_body(self, collection_guid: str, body: dict = None, start_from: int = 0,
1218
- page_size: int = None, output_format: str = "JSON",
1219
- output_format_set: str | dict = None) -> list | str:
1220
- """ Return a graph of elements that are the nested members of a collection along
1221
- with elements immediately connected to the starting collection. The result
1222
- includes a mermaid graph of the returned elements. Async version.
1223
-
1224
- Parameters
1225
- ----------
1226
- collection_guid: str,
1227
- identity of the collection to return members for.
1228
- body: dict
1229
- A dictionary containing the body of the request. See Note.
1230
- start_from: int, [default=0], optional
1231
- When multiple pages of results are available, the page number to start from.
1232
- page_size: int, [default=None]
1233
- The number of items to return in a single page. If not specified, the default will be taken from
1234
- the class instance.
1235
- output_format: str, default = "JSON"
1236
- - one of "MD", "LIST", "FORM", "REPORT", "DICT", "MERMAID" or "JSON"
1237
- output_format_set: dict , optional, default = None
1238
-
1239
- Returns
1240
- -------
1241
- List | str
1242
-
1243
- A list of collection members in the collection.
1244
-
1245
- Raises
1246
- ------
1247
- InvalidParameterException
1248
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1249
- PropertyServerException
1250
- Raised by the server when an issue arises in processing a valid request
1251
- NotAuthorizedException
1252
- The principle specified by the user_id does not have authorization for the requested action
1253
-
1254
- Note
1255
- ____
1256
- {
1257
- "class": "ResultsRequestBody",
1258
- "effectiveTime": "{{$isoTimestamp}}",
1259
- "limitResultsByStatus": ["ACTIVE"],
1260
- "asOfTime": "{{$isoTimestamp}}",
1261
- "sequencingOrder": "CREATION_DATE_RECENT",
1262
- "sequencingProperty": ""
1263
- }
1264
-
1265
- """
1266
-
1267
- if page_size is None:
1268
- page_size = self.page_size
1269
-
1270
- url = (f"{self.collection_command_root}/{collection_guid}/"
1271
- f"graph?startFrom={start_from}&pageSize={page_size}")
1272
-
1273
- response = await self._async_make_request("GET", url, body_slimmer(body))
1274
- elements = response.json().get("elements", NO_ELEMENTS_FOUND)
1275
- if type(elements) is str:
1276
- logger.info(NO_ELEMENTS_FOUND)
1277
- return NO_ELEMENTS_FOUND
1278
-
1279
- if output_format != 'JSON': # return a simplified markdown representation
1280
- logger.info(f"Found elements, output format: {output_format}, output_format_set: {output_format_set}")
1281
- return self._generate_collection_output(elements, None, None,
1282
- output_format, output_format_set)
1283
- return elements
1284
-
1285
-
1286
- def get_collection_graph_w_body(self, collection_guid: str, body: dict = None, start_from: int = 0,
1287
- page_size: int = None, output_format: str = "JSON",
1288
- output_format_set: str | dict = None) -> list | str:
1289
- """ Return a graph of elements that are the nested members of a collection along
1290
- with elements immediately connected to the starting collection. The result
1291
- includes a mermaid graph of the returned elements.
1292
-
1293
- Parameters
1294
- ----------
1295
- collection_guid: str,
1296
- identity of the collection to return members for.
1297
- body: dict
1298
- A dictionary containing the body of the request. See Note.
1299
- start_from: int, [default=0], optional
1300
- When multiple pages of results are available, the page number to start from.
1301
- page_size: int, [default=None]
1302
- The number of items to return in a single page. If not specified, the default will be taken from
1303
- the class instance.
1304
- output_format: str, default = "JSON"
1305
- - one of "MD", "LIST", "FORM", "REPORT", "DICT", "MERMAID" or "JSON"
1306
- output_format_set: str | dict , optional, default = None
1307
- The desired output columns/fields to include.
1308
-
1309
- Returns
1310
- -------
1311
- List | str
1312
-
1313
- A list of collection members in the collection.
1314
-
1315
- Raises
1316
- ------
1317
- InvalidParameterException
1318
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1319
- PropertyServerException
1320
- Raised by the server when an issue arises in processing a valid request
1321
- NotAuthorizedException
1322
- The principle specified by the user_id does not have authorization for the requested action
1323
-
1324
- Note
1325
- ____
1326
- {
1327
- "class": "ResultsRequestBody",
1328
- "effectiveTime": "{{$isoTimestamp}}",
1329
- "limitResultsByStatus": ["ACTIVE"],
1330
- "asOfTime": "{{$isoTimestamp}}",
1331
- "sequencingOrder": "CREATION_DATE_RECENT",
1332
- "sequencingProperty": ""
1333
- }
1334
-
1335
- """
1336
-
1337
- return asyncio.get_event_loop().run_until_complete(
1338
- self._async_get_collection_graph_w_body(collection_guid, body, start_from,
1339
- page_size, output_format, output_format_set))
1340
-
1341
- #
1342
- # Create collection methods
1343
- #
1344
-
1345
- ###
1346
- # =====================================================================================================================
1347
- # Create Collections: https://egeria-project.org/concepts/collection
1348
- # These requests use the following parameters:
1349
- #
1350
- # anchorGUID - the unique identifier of the element that should be the anchor for the new element. Set to
1351
- # null if
1352
- # no anchor,
1353
- # or if this collection is to be its own anchor.
1354
- #
1355
- # isOwnAnchor -this element should be classified as its own anchor or not. The default is false.
1356
- #
1357
- # parentGUID - the optional unique identifier for an element that should be connected to the newly
1358
- # created element.
1359
- # If this property is specified, parentRelationshipTypeName must also be specified
1360
- #
1361
- # parentRelationshipTypeName - the name of the relationship, if any, that should be established between
1362
- # the new
1363
- # element and the parent element.
1364
- # Examples could be "ResourceList" or "DigitalServiceProduct".
1365
- #
1366
- # parentAtEnd1 -identifies which end any parent entity sits on the relationship.
1367
- #
1368
-
1369
-
1370
1007
  @dynamic_catch
1371
1008
  async def _async_create_collection(self, display_name: str = None, description: str = None,
1372
- category: str = None, classification_name: str = None,
1373
- body: dict | NewElementRequestBody = None) -> str:
1009
+ category: str = None, initial_classifications: list[str] = None,
1010
+ body: dict | NewElementRequestBody = None) -> str:
1374
1011
  """ Create a new generic collection. If the body is not present, the display_name, description, category
1375
1012
  and classification will be used to create a simple, self-anchored collection.
1376
1013
  Collections: https://egeria-project.org/concepts/collection
@@ -1385,8 +1022,8 @@ class CollectionManager(Client2):
1385
1022
  The description of the collection.
1386
1023
  category: str, optional
1387
1024
  An optional user-assigned category for the collection.
1388
- classification: str, optional
1389
- An initial classification for the collection. This can be used to distinguish, for instance, Folders
1025
+ initial_classifications: list[str], optional
1026
+ An initial list of classifications for the collection. This can be used to distinguish, for instance, Folders
1390
1027
  from Root Collections.
1391
1028
 
1392
1029
  body: dict | NewElementRequestBody, optional
@@ -1473,30 +1110,19 @@ class CollectionManager(Client2):
1473
1110
  }
1474
1111
 
1475
1112
  """
1476
-
1477
-
1478
- if isinstance(body, NewElementRequestBody):
1479
- validated_body = body
1480
- elif isinstance(body, dict):
1481
- try:
1482
- validated_body = self._new_element_request_adapter.validate_python(body)
1483
- except ValidationError as e:
1484
- logger.error(f"Validation error: {e}")
1485
- raise ValidationError(e)
1486
- elif display_name is not None:
1487
- pre = classification_name if classification_name is not None else "Collection "
1113
+ if body:
1114
+ validated_body = self.validate_new_element_request(body,"CollectionProperties")
1115
+ elif (body is None) and (display_name is not None):
1116
+ pre = initial_classifications[0] if initial_classifications is not None else "Collection"
1488
1117
  qualified_name = self.__create_qualified_name__(pre, display_name, EGERIA_LOCAL_QUALIFIER)
1489
- print(f"\n\tDisplayName was {display_name}, classification {classification_name}\n")
1490
- if classification_name:
1491
- initial_classifications_data = {"class": "ClassificationProperties"}
1492
- initial_classification_dict = {
1493
- classification_name: InitialClassifications.model_validate(initial_classifications_data)
1494
- }
1118
+ if initial_classifications:
1119
+ initial_classifications_dict = {}
1120
+ for c in initial_classifications:
1121
+ initial_classifications_dict = initial_classifications_dict | {c : {"class": "ClassificationProperties"}}
1122
+
1495
1123
  else:
1496
- initial_classification_dict = None
1497
- initial_classification_dict = {
1498
- classification_name: InitialClassifications.model_validate(initial_classifications_data)
1499
- }
1124
+ initial_classifications_dict = None
1125
+
1500
1126
  collection_properties = CollectionProperties( class_ = "CollectionProperties",
1501
1127
  qualified_name = qualified_name,
1502
1128
  display_name = display_name,
@@ -1506,12 +1132,12 @@ class CollectionManager(Client2):
1506
1132
  body = {
1507
1133
  "class" :"NewElementRequestBody",
1508
1134
  "isOwnAnchor": True,
1509
- "initialClassifications": initial_classification_dict,
1135
+ "initialClassifications": initial_classifications_dict,
1510
1136
  "properties": collection_properties.model_dump()
1511
1137
  }
1512
1138
  validated_body = NewElementRequestBody.model_validate(body)
1513
1139
  else:
1514
- raise TypeError("Invalid parameter type")
1140
+ raise PyegeriaInvalidParameterException(additional_info={"reason": "Invalid input parameters"})
1515
1141
 
1516
1142
  url = f"{self.collection_command_root}"
1517
1143
  json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
@@ -1522,7 +1148,7 @@ class CollectionManager(Client2):
1522
1148
 
1523
1149
 
1524
1150
  def create_collection(self, display_name: str = None, description: str = None,
1525
- category: str = None, classification_name: str = None,
1151
+ category: str = None, initial_classifications: list[str] = None,
1526
1152
  body: dict | NewElementRequestBody = None) -> str:
1527
1153
  """ Create a new generic collection. If the body is not present, the display_name, description, category
1528
1154
  and classification will be used to create a simple, self-anchored collection.
@@ -1537,7 +1163,7 @@ class CollectionManager(Client2):
1537
1163
  The description of the collection.
1538
1164
  category: str, optional
1539
1165
  An optional user-assigned category for the collection.
1540
- classification: str, optional
1166
+ initial_classifications: str, optional
1541
1167
  An initial classification for the collection. This can be used to distinguish, for instance, Folders
1542
1168
  from Root Collections.
1543
1169
 
@@ -1629,7 +1255,7 @@ class CollectionManager(Client2):
1629
1255
 
1630
1256
  return asyncio.get_event_loop().run_until_complete(
1631
1257
  self._async_create_collection(display_name, description,category,
1632
- classification_name, body))
1258
+ initial_classifications, body))
1633
1259
 
1634
1260
 
1635
1261
  @dynamic_catch
@@ -1668,11 +1294,9 @@ class CollectionManager(Client2):
1668
1294
 
1669
1295
  return asyncio.get_event_loop().run_until_complete(
1670
1296
  self._async_create_collection(display_name, description, category,
1671
- "RootCollection", body))
1297
+ ["RootCollection"], body))
1672
1298
 
1673
- #######
1674
1299
 
1675
- return resp
1676
1300
  @dynamic_catch
1677
1301
  def create_folder_collection(self, display_name: str = None, description: str = None,
1678
1302
  category: str = None, body: dict | NewElementRequestBody = None) -> str:
@@ -1709,7 +1333,7 @@ class CollectionManager(Client2):
1709
1333
 
1710
1334
  return asyncio.get_event_loop().run_until_complete(
1711
1335
  self._async_create_collection(display_name, description, category,
1712
- "Folder", body))
1336
+ ["Folder"], body))
1713
1337
 
1714
1338
 
1715
1339
 
@@ -1750,14 +1374,10 @@ class CollectionManager(Client2):
1750
1374
 
1751
1375
  return asyncio.get_event_loop().run_until_complete(
1752
1376
  self._async_create_collection(display_name, description, category,
1753
- "ReferenceList", body))
1754
-
1755
-
1756
-
1377
+ ["ReferenceList"], body))
1757
1378
 
1758
- return resp
1759
1379
  @dynamic_catch
1760
- def create_reference_list_collection(self, display_name: str = None, description: str = None,
1380
+ def create_context_event_collection(self, display_name: str = None, description: str = None,
1761
1381
  category: str = None, body: dict | NewElementRequestBody = None) -> str:
1762
1382
  """ Create a new collection with the RootCollection classification. Used to identify the top of a
1763
1383
  collection hierarchy.
@@ -1792,7 +1412,7 @@ class CollectionManager(Client2):
1792
1412
 
1793
1413
  return asyncio.get_event_loop().run_until_complete(
1794
1414
  self._async_create_collection(display_name, description, category,
1795
- "ReferenceList", body))
1415
+ ["ContextEvent"], body))
1796
1416
 
1797
1417
  @dynamic_catch
1798
1418
  async def _async_create_data_spec_collection(self, display_name: str = None, description: str = None,
@@ -1900,51 +1520,35 @@ class CollectionManager(Client2):
1900
1520
  }
1901
1521
 
1902
1522
  """
1523
+ validated_body = self.validate_new_element_request(body,"DataSpecProperties")
1903
1524
 
1904
- try:
1905
- if isinstance(body, NewElementRequestBody):
1906
- if body.properties.class_ == "DataSpecProperties":
1907
- validated_body = body
1908
- else:
1909
- raise PyegeriaInvalidParameterException(additional_info =
1910
- {"reason" : "unexpected property class name"})
1911
-
1912
- elif isinstance(body, dict):
1913
- if body.get("properties", {}).get("class", "") == "DataSpecProperties":
1914
- validated_body = self._new_element_request_adapter.validate_python(body)
1915
- else:
1916
- raise PyegeriaInvalidParameterException(additional_info =
1917
- {"reason" : "unexpected property class name"})
1918
-
1919
- elif display_name is not None:
1920
- qualified_name = self.__create_qualified_name__("DataSpec", display_name, EGERIA_LOCAL_QUALIFIER)
1921
- print(f"\n\tDisplayName was {display_name}, classification {classification_name}\n")
1922
- initial_classifications_data = {"class" : "ClassificationProperties"}
1923
- if classification_name:
1924
- initial_classification_dict = {
1925
- classification_name: InitialClassifications.model_validate(initial_classifications_data)
1926
- }
1927
- else:
1928
- initial_classification_dict = None
1929
- collection_properties = DataSpecProperties( class_ = "DataSpecProperties",
1930
- qualified_name = qualified_name,
1931
- display_name = display_name,
1932
- description = description,
1933
- category = category
1934
- )
1935
- body = {
1936
- "class" :"NewElementRequestBody",
1937
- "isOwnAnchor": True,
1938
- "initialClassifications": initial_classification_dict,
1939
- "properties": collection_properties.model_dump()
1940
- }
1941
- validated_body = NewElementRequestBody.model_validate(body)
1525
+ if validated_body is None and display_name is not None:
1526
+ qualified_name = self.__create_qualified_name__("DataSpec", display_name, EGERIA_LOCAL_QUALIFIER)
1527
+ print(f"\n\tDisplayName was {display_name}, classification {classification_name}\n")
1528
+ initial_classifications_data = {"class" : "ClassificationProperties"}
1529
+ if classification_name:
1530
+ initial_classification_dict = {
1531
+ classification_name: InitialClassifications.model_validate(initial_classifications_data)
1532
+ }
1942
1533
  else:
1943
- raise TypeError("Invalid parameter type")
1534
+ initial_classification_dict = None
1535
+ collection_properties = DataSpecProperties( class_ = "DataSpecProperties",
1536
+ qualified_name = qualified_name,
1537
+ display_name = display_name,
1538
+ description = description,
1539
+ category = category
1540
+ )
1541
+ body = {
1542
+ "class" :"NewElementRequestBody",
1543
+ "isOwnAnchor": True,
1544
+ "initialClassifications": initial_classification_dict,
1545
+ "properties": collection_properties.model_dump()
1546
+ }
1547
+ validated_body = NewElementRequestBody.model_validate(body)
1548
+ else:
1549
+ raise PyegeriaInvalidParameterException(additional_info={"reason": "Invalid input parameters"})
1550
+
1944
1551
 
1945
- except ValidationError as e:
1946
- logger.error(f"Validation error: {e}")
1947
- raise ValidationError(e)
1948
1552
 
1949
1553
 
1950
1554
  url = f"{self.collection_command_root}"
@@ -1971,7 +1575,7 @@ class CollectionManager(Client2):
1971
1575
  The description of the collection.
1972
1576
  category: str, optional
1973
1577
  An optional user-assigned category for the collection.
1974
- classification: str, optional
1578
+ classification_name: str, optional
1975
1579
  An initial classification for the collection. This can be used to distinguish, for instance, Folders
1976
1580
  from Root Collections.
1977
1581
 
@@ -2173,49 +1777,33 @@ class CollectionManager(Client2):
2173
1777
 
2174
1778
  """
2175
1779
 
2176
- try:
2177
- if isinstance(body, NewElementRequestBody):
2178
- if body.properties.class_ == "DataDictionaryProperties":
2179
- validated_body = body
2180
- else:
2181
- raise PyegeriaInvalidParameterException(additional_info =
2182
- {"reason" : "unexpected property class name"})
2183
-
2184
- elif isinstance(body, dict):
2185
- if body.get("properties", {}).get("class", "") == "DataDictionaryProperties":
2186
- validated_body = self._new_element_request_adapter.validate_python(body)
2187
- else:
2188
- raise PyegeriaInvalidParameterException(additional_info =
2189
- {"reason" : "unexpected property class name"})
2190
-
2191
- elif display_name is not None:
2192
- qualified_name = self.__create_qualified_name__("DataDictionary", display_name, EGERIA_LOCAL_QUALIFIER)
2193
- initial_classifications_data = {"class" : "ClassificationProperties"}
2194
- if classification_name:
2195
- initial_classification_dict = {
2196
- classification_name: InitialClassifications.model_validate(initial_classifications_data)
2197
- }
2198
- else:
2199
- initial_classification_dict = None
2200
- collection_properties = DataDictionaryProperties( class_ = "DataDictionaryProperties",
2201
- qualified_name = qualified_name,
2202
- display_name = display_name,
2203
- description = description,
2204
- category = category
2205
- )
2206
- body = {
2207
- "class" :"NewElementRequestBody",
2208
- "isOwnAnchor": True,
2209
- "initialClassifications": initial_classification_dict,
2210
- "properties": collection_properties.model_dump()
2211
- }
2212
- validated_body = NewElementRequestBody.model_validate(body)
1780
+ validated_body = self.validate_new_element_request(body,"DataDictionaryProperties")
1781
+
1782
+ if validated_body is None and display_name is not None:
1783
+ qualified_name = self.__create_qualified_name__("DataDictionary", display_name, EGERIA_LOCAL_QUALIFIER)
1784
+ initial_classifications_data = {"class" : "ClassificationProperties"}
1785
+ if classification_name:
1786
+ initial_classification_dict = {
1787
+ classification_name: InitialClassifications.model_validate(initial_classifications_data)
1788
+ }
2213
1789
  else:
2214
- raise TypeError("Invalid parameter type")
1790
+ initial_classification_dict = None
1791
+ collection_properties = DataDictionaryProperties( class_ = "DataDictionaryProperties",
1792
+ qualified_name = qualified_name,
1793
+ display_name = display_name,
1794
+ description = description,
1795
+ category = category
1796
+ )
1797
+ body = {
1798
+ "class" :"NewElementRequestBody",
1799
+ "isOwnAnchor": True,
1800
+ "initialClassifications": initial_classification_dict,
1801
+ "properties": collection_properties.model_dump()
1802
+ }
1803
+ validated_body = NewElementRequestBody.model_validate(body)
2215
1804
 
2216
- except ValidationError as e:
2217
- logger.error(f"Validation error: {e}")
2218
- raise ValidationError(e)
1805
+ if validated_body is None:
1806
+ raise PyegeriaInvalidParameterException(additional_info={"reason": "Invalid input parameters"})
2219
1807
 
2220
1808
 
2221
1809
  url = f"{self.collection_command_root}"
@@ -2480,8 +2068,7 @@ class CollectionManager(Client2):
2480
2068
 
2481
2069
 
2482
2070
  @dynamic_catch
2483
- async def _async_update_collection(self, collection_guid: str, body: dict | NewElementRequestBody,
2484
- merge_update: bool = True) -> None:
2071
+ async def _async_update_collection(self, collection_guid: str, body: dict | UpdateElementRequestBody) -> None:
2485
2072
  """ Update the properties of a collection. Use the correct properties object (CollectionProperties,
2486
2073
  DigitalProductProperties, AgreementProperties, etc), that is appropriate for your element.
2487
2074
  Collections: https://egeria-project.org/concepts/collection
@@ -2492,7 +2079,7 @@ class CollectionManager(Client2):
2492
2079
  collection_guid: str
2493
2080
  The guid of the collection to update.
2494
2081
 
2495
- body: dict | NewElementRequestBody, optional
2082
+ body: dict | UpdateElementRequestBody, optional
2496
2083
  A dict or NewElementRequestBody representing the details of the collection to create. If supplied, this
2497
2084
  information will be used to create the collection and the other attributes will be ignored. The body is
2498
2085
  validated before being used.
@@ -2534,44 +2121,18 @@ class CollectionManager(Client2):
2534
2121
  }
2535
2122
  """
2536
2123
 
2537
- try:
2538
- if isinstance(body, NewElementRequestBody):
2539
- if body.properties.class_ in COLLECTION_PROPERTIES_LIST:
2540
- validated_body = body
2541
- else:
2542
- raise PyegeriaInvalidParameterException(additional_info =
2543
- {"reason" : "unexpected property class name"})
2544
-
2545
- elif isinstance(body, dict):
2546
- if body.get("properties", {}).get("class", "") in COLLECTION_PROPERTIES_LIST:
2547
- validated_body = self._new_element_request_adapter.validate_python(body)
2548
- else:
2549
- raise PyegeriaInvalidParameterException(additional_info =
2550
- {"reason" : "unexpected property class name"})
2551
- else:
2552
- raise TypeError("Invalid parameter type")
2553
-
2554
- except ValidationError as e:
2555
- logger.error(f"Validation error: {e}")
2556
- raise ValidationError(e)
2557
-
2558
- merge_update_s = str(merge_update).lower()
2559
-
2560
- url = (f"{self.collection_command_root}/{collection_guid}/update?"
2561
- f"replaceAllProperties={merge_update_s}")
2124
+ # try:
2562
2125
 
2563
- json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
2126
+ url = (f"{self.collection_command_root}/{collection_guid}/update")
2127
+ await self._async_update_element_body_request(url, COLLECTION_PROPERTIES_LIST,body)
2564
2128
 
2565
- await self._async_make_request("POST", url, json_body)
2566
2129
 
2567
- logger.info(f"Successfully updated {collection_guid} with {json_body}")
2568
2130
 
2569
2131
 
2570
2132
 
2571
2133
 
2572
2134
  @dynamic_catch
2573
- def update_collection(self, collection_guid: str, body: dict | NewElementRequestBody,
2574
- merge_update: bool = True) -> None:
2135
+ def update_collection(self, collection_guid: str, body: dict | NewElementRequestBody) -> None:
2575
2136
  """ Update the properties of a collection. Use the correct properties object (CollectionProperties,
2576
2137
  DigitalProductProperties, AgreementProperties, etc), that is appropriate for your element.
2577
2138
  Collections: https://egeria-project.org/concepts/collection
@@ -2624,7 +2185,7 @@ class CollectionManager(Client2):
2624
2185
  """
2625
2186
 
2626
2187
  return asyncio.get_event_loop().run_until_complete(
2627
- self._async_update_collection(collection_guid, body, merge_update))
2188
+ self._async_update_collection(collection_guid, body))
2628
2189
 
2629
2190
 
2630
2191
  @dynamic_catch
@@ -2695,32 +2256,9 @@ class CollectionManager(Client2):
2695
2256
  OTHER. If using OTHER, set the userDefinedStatus with the status value you want. If not specified, will
2696
2257
  default to ACTIVE.
2697
2258
  """
2698
-
2699
- if isinstance(body, NewElementRequestBody):
2700
- if body.properties.class_ == "DigitalProductProperties":
2701
- validated_body = body
2702
- else:
2703
- raise PyegeriaInvalidParameterException(additional_info=
2704
- {"reason": "unexpected property class name"})
2705
-
2706
- elif isinstance(body, dict):
2707
- if body.get("properties", {}).get("class", "") == "DigitalProductProperties":
2708
- validated_body = self._new_element_request_adapter.validate_python(body)
2709
- else:
2710
- raise PyegeriaInvalidParameterException(additional_info=
2711
- {"reason": "unexpected property class name"})
2712
-
2713
- else:
2714
- raise TypeError("Invalid parameter type")
2715
-
2716
-
2717
-
2718
2259
  url = f"{self.collection_command_root}"
2719
- json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
2720
- logger.info(json_body)
2721
- resp = await self._async_make_request("POST", url, json_body, is_json=True)
2722
- logger.info(f"Create collection with GUID: {resp.json().get('guid')}")
2723
- return resp.json().get("guid", NO_GUID_RETURNED)
2260
+ return await self._async_create_element_body_request(url, "DigitalProductProperties", body)
2261
+
2724
2262
 
2725
2263
  @dynamic_catch
2726
2264
  def create_digital_product(self, body: dict | NewElementRequestBody = None) -> str:
@@ -2795,8 +2333,7 @@ class CollectionManager(Client2):
2795
2333
 
2796
2334
 
2797
2335
  @dynamic_catch
2798
- async def _async_update_digital_product(self, collection_guid: str, body: dict | UpdateElementRequestBody,
2799
- merge_update: bool = True) -> None:
2336
+ async def _async_update_digital_product(self, collection_guid: str, body: dict | UpdateElementRequestBody) -> None:
2800
2337
  """ Update the properties of a digital product.
2801
2338
  Collections: https://egeria-project.org/concepts/collection
2802
2339
 
@@ -2860,37 +2397,8 @@ class CollectionManager(Client2):
2860
2397
  }
2861
2398
  """
2862
2399
 
2863
- try:
2864
- if isinstance(body, NewElementRequestBody):
2865
- if body.properties.class_ == "DigitalProductProperties":
2866
- validated_body = body
2867
- else:
2868
- raise PyegeriaInvalidParameterException(additional_info =
2869
- {"reason" : "unexpected property class name"})
2870
-
2871
- elif isinstance(body, dict):
2872
- if body.get("properties", {}).get("class", "") == "DigitalProductProperties":
2873
- validated_body = self._update_element_request_adapter.validate_python(body)
2874
- else:
2875
- raise PyegeriaInvalidParameterException(additional_info =
2876
- {"reason" : "unexpected property class name"})
2877
- else:
2878
- raise TypeError("Invalid parameter type")
2879
-
2880
- except ValidationError as e:
2881
- logger.error(f"Validation error: {e}")
2882
- raise ValidationError(e)
2883
-
2884
- merge_update_s = str(merge_update).lower()
2885
-
2886
- url = (f"{self.collection_command_root}/{collection_guid}/update?"
2887
- f"replaceAllProperties={merge_update_s}")
2888
-
2889
- json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
2890
-
2891
- await self._async_make_request("POST", url, json_body)
2892
-
2893
- logger.info(f"Successfully updated {collection_guid} with {json_body}")
2400
+ url = (f"{self.collection_command_root}/{collection_guid}/update")
2401
+ await self._async_update_element_request_body(url, "DigitalProductProperties", body )
2894
2402
 
2895
2403
 
2896
2404
  @dynamic_catch
@@ -2963,15 +2471,19 @@ class CollectionManager(Client2):
2963
2471
 
2964
2472
 
2965
2473
  @dynamic_catch
2966
- async def _async_update_collection_status(self, collection_guid: str, body: dict | UpdateStatusRequestBody):
2474
+ async def _async_update_collection_status(self, collection_guid: str, status: str = None,
2475
+ body: dict | UpdateStatusRequestBody = None):
2967
2476
  """Update the status of a collection. Async version.
2968
2477
 
2969
2478
  Parameters
2970
2479
  ----------
2971
2480
  collection_guid: str
2972
2481
  The guid of the collection to update.
2973
- body: dict | UpdateStatusRequestBody
2974
- A structure representing the details of the collection to create.
2482
+ status: str, optional
2483
+ The new lifecycle status for the collection. Ignored, if the body is provided.
2484
+ body: dict | UpdateStatusRequestBody, optional
2485
+ A structure representing the details of the collection to create. If supplied, these details
2486
+ supersede the status parameter provided.
2975
2487
 
2976
2488
  Returns
2977
2489
  -------
@@ -2999,31 +2511,24 @@ class CollectionManager(Client2):
2999
2511
  "forDuplicateProcessing": false
3000
2512
  }
3001
2513
  """
3002
- try:
3003
- if isinstance(body, UpdateStatusRequestBody):
3004
- validated_body = body
3005
- elif isinstance(body, dict):
3006
- validated_body = self._update_status_request_adapter.validate_python(body)
3007
-
3008
- except ValidationError as e:
3009
- logger.error(f"Validation error: {e}")
3010
- raise ValidationError(e)
3011
2514
 
3012
2515
  url = f"{self.collection_command_root}/{collection_guid}/update-status"
3013
- json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
3014
- await self._async_make_request("POST", url, json_body)
3015
- logger.info(f"Successfully updated {collection_guid} with {json_body}")
2516
+ await self._async_update_status_request(url, status, body)
3016
2517
 
3017
2518
  @dynamic_catch
3018
- def update_collection_status(self, collection_guid: str, body: dict | UpdateStatusRequestBody):
2519
+ def update_collection_status(self, collection_guid: str, status: str = None,
2520
+ body: dict | UpdateStatusRequestBody = None):
3019
2521
  """Update the status of a DigitalProduct collection.
3020
2522
 
3021
2523
  Parameters
3022
2524
  ----------
3023
2525
  collection_guid: str
3024
2526
  The guid of the collection to update.
3025
- body: dict | UpdateStatusRequestBody
3026
- A structure representing the details of the collection to create.
2527
+ status: str, optional
2528
+ The new lifecycle status for the digital product. Ignored, if the body is provided.
2529
+ body: dict | UpdateStatusRequestBody, optional
2530
+ A structure representing the details of the collection to create. If supplied, these details
2531
+ supersede the status parameter provided.
3027
2532
 
3028
2533
  Returns
3029
2534
  -------
@@ -3052,19 +2557,23 @@ class CollectionManager(Client2):
3052
2557
  }
3053
2558
  """
3054
2559
  loop = asyncio.get_event_loop()
3055
- loop.run_until_complete(self._async_update_collection_status(collection_guid, body))
2560
+ loop.run_until_complete(self._async_update_collection_status(collection_guid, status, body))
3056
2561
 
3057
2562
 
3058
2563
  @dynamic_catch
3059
- def update_digital_product_status(self, digital_product_guid: str, body: dict | UpdateStatusRequestBody):
2564
+ def update_digital_product_status(self, digital_product_guid: str, status: str = None,
2565
+ body: dict | UpdateStatusRequestBody = None):
3060
2566
  """Update the status of a DigitalProduct collection.
3061
2567
 
3062
2568
  Parameters
3063
2569
  ----------
3064
2570
  digital_product_guid: str
3065
- The guid of the digital product collection to update.
3066
- body: dict | UpdateStatusRequestBody
3067
- A structure representing the details of the collection to create.
2571
+ The guid of the collection to update.
2572
+ status: str, optional
2573
+ The new lifecycle status for the digital product. Ignored, if the body is provided.
2574
+ body: dict | UpdateStatusRequestBody, optional
2575
+ A structure representing the details of the collection to create. If supplied, these details
2576
+ supersede the status parameter provided.
3068
2577
 
3069
2578
  Returns
3070
2579
  -------
@@ -3093,7 +2602,7 @@ class CollectionManager(Client2):
3093
2602
  }
3094
2603
  """
3095
2604
  loop = asyncio.get_event_loop()
3096
- loop.run_until_complete(self._async_update_collection_status(digital_product_guid, body))
2605
+ loop.run_until_complete(self._async_update_collection_status(digital_product_guid, status, body))
3097
2606
 
3098
2607
 
3099
2608
  @dynamic_catch
@@ -3144,28 +2653,11 @@ class CollectionManager(Client2):
3144
2653
  }
3145
2654
  }
3146
2655
  """
3147
- try:
3148
- if isinstance(body, NewRelationshipRequestBody):
3149
- validated_body = body
3150
- elif isinstance(body, dict):
3151
- validated_body = self._new_relationship_request_adapter.validate_python(body)
3152
- else:
3153
- validated_body = None
3154
-
3155
- except ValidationError as e:
3156
- logger.error(f"Validation error: {e}")
3157
- raise ValidationError(e)
3158
-
3159
-
3160
2656
  url = (
3161
2657
  f"{self.platform_url}/servers/"
3162
2658
  f"{self.view_server}/api/open-metadata/collection-manager/collections/digital-products/"
3163
2659
  f"{upstream_digital_prod_guid}/product-dependencies/{downstream_digital_prod_guid}/attach")
3164
- if validated_body:
3165
- json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
3166
- await self._async_make_request("POST", url, json_body)
3167
- else:
3168
- await self._async_make_request("POST", url)
2660
+ await self._async_new_relationship_request(url, "InformationSupplyChainLinkProperties", body)
3169
2661
  logger.info(f"Linked {upstream_digital_prod_guid} -> {downstream_digital_prod_guid}")
3170
2662
 
3171
2663
 
@@ -3264,30 +2756,13 @@ class CollectionManager(Client2):
3264
2756
  }
3265
2757
 
3266
2758
  """
3267
- try:
3268
- if isinstance(body, DeleteRequestBody):
3269
- validated_body = body
3270
- elif isinstance(body, dict):
3271
- validated_body = self._delete_request_adapter.validate_python(body)
3272
- else:
3273
- validated_body = None
3274
-
3275
- except ValidationError as e:
3276
- logger.error(f"Validation error: {e}")
3277
- raise ValidationError(e)
3278
-
3279
2759
 
3280
2760
  url = (
3281
2761
  f"{self.platform_url}/servers/"
3282
2762
  f"{self.view_server}/api/open-metadata/collection-manager/collections/digital-products/"
3283
2763
  f"{upstream_digital_prod_guid}/product-dependencies/{downstream_digital_prod_guid}/detach")
3284
-
3285
- if validated_body:
3286
- json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
3287
- await self._async_make_request("POST", url, json_body)
3288
- else:
3289
- await self._async_make_request("POST", url)
3290
- logger.info(f"Detched digital product dependency {upstream_digital_prod_guid} -> {downstream_digital_prod_guid}")
2764
+ await self._async_delete_request(url, body)
2765
+ logger.info(f"Detached digital product dependency {upstream_digital_prod_guid} -> {downstream_digital_prod_guid}")
3291
2766
 
3292
2767
 
3293
2768
  def detach_digital_product_dependency(self, upstream_digital_prod_guid: str, downstream_digital_prod_guid: str,
@@ -3383,30 +2858,12 @@ class CollectionManager(Client2):
3383
2858
  }
3384
2859
 
3385
2860
  """
3386
- try:
3387
- if isinstance(body, NewRelationshipRequestBody):
3388
- validated_body = body
3389
- elif isinstance(body, dict):
3390
- validated_body = self._new_relationship_request_adapter.validate_python(body)
3391
- else:
3392
- validated_body = None
3393
- raise PyegeriaInvalidParameterException(additional_info=
3394
- {"reason": "unexpected body type"})
3395
- except ValidationError as e:
3396
- logger.error(f"Validation error: {e}")
3397
- raise ValidationError(e)
3398
-
3399
2861
  url = (
3400
2862
  f"{self.platform_url}/servers/"
3401
2863
  f"{self.view_server}/api/open-metadata/collection-manager/collections/digital"
3402
2864
  f"-products/"
3403
2865
  f"{digital_prod_guid}/product-managers/{digital_prod_manager_guid}/attach")
3404
-
3405
- if validated_body:
3406
- json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
3407
- await self._async_make_request("POST", url, json_body)
3408
- else:
3409
- await self._async_make_request("POST", url)
2866
+ await self._async_new_relationship_request(url, "AssignmentScopeProperties",body)
3410
2867
  logger.info(f"Attached digital product manager {digital_prod_guid} -> {digital_prod_manager_guid}")
3411
2868
 
3412
2869
 
@@ -3500,31 +2957,12 @@ class CollectionManager(Client2):
3500
2957
  }
3501
2958
 
3502
2959
  """
3503
- try:
3504
- if isinstance(body, DeleteRequestBody):
3505
- validated_body = body
3506
- elif isinstance(body, dict):
3507
- validated_body = self._delete_request_adapter.validate_python(body)
3508
- else:
3509
- validated_body = None
3510
- raise PyegeriaInvalidParameterException(additional_info=
3511
- {"reason": "unexpected body type"})
3512
- except ValidationError as e:
3513
- logger.error(f"Validation error: {e}")
3514
- raise ValidationError(e)
3515
-
3516
-
3517
2960
  url = (
3518
2961
  f"{self.platform_url}/servers/"
3519
2962
  f"{self.view_server}/api/open-metadata/collection-manager/collections/digital-products/"
3520
2963
  f"{digital_prod_guid}/product-dependencies/{digital_prod_manager_guid}/detach")
3521
-
3522
- if validated_body:
3523
- json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
3524
- await self._async_make_request("POST", url, json_body)
3525
- else:
3526
- await self._async_make_request("POST", url)
3527
- logger.info(f"Detched digital product manager {digital_prod_guid} -> {digital_prod_manager_guid}")
2964
+ await self._async_delete_request(url, body)
2965
+ logger.info(f"Detached digital product manager {digital_prod_guid} -> {digital_prod_manager_guid}")
3528
2966
 
3529
2967
  @dynamic_catch
3530
2968
  def detach_product_manager(self, digital_prod_guid: str, digital_prod_manager_guid: str,
@@ -3569,174 +3007,21 @@ class CollectionManager(Client2):
3569
3007
  """
3570
3008
  loop = asyncio.get_event_loop()
3571
3009
  loop.run_until_complete(
3572
- self._async_detach_product_manager(digital_prod_guid, digital_prod_manager_guid,
3573
- body))
3574
-
3575
-
3576
- #
3577
-
3578
- @dynamic_catch
3579
- async def _async_create_agreement(self, body: dict | NewElementRequestBody) -> str:
3580
- """ Create a new collection that represents an agreement.
3581
- Async version.
3582
-
3583
- Parameters
3584
- ----------
3585
- body: dict | NewElementRequestBody
3586
- A structure representing the details of the agreement to create.
3587
-
3588
- Returns
3589
- -------
3590
- str - the guid of the created collection
3591
-
3592
- Raises
3593
- ------
3594
- PyegeriaException
3595
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
3596
- ValidationError
3597
- Raised by the pydantic validator if the body does not conform to the NewElementRequestBody.
3598
- NotAuthorizedException
3599
- The principle specified by the user_id does not have authorization for the requested action
3600
-
3601
- Notes
3602
- -----
3603
- Note: the three dates: introductionDate, nextVersionDate and withdrawDate must
3604
- be valid dates if specified, otherwise you will get a 400 error response.
3605
-
3606
- JSON Structure looks like:
3607
- {
3608
- "class" : "NewElementRequestBody",
3609
- "isOwnAnchor" : true,
3610
- "anchorScopeGUID" : "optional GUID of search scope",
3611
- "parentGUID" : "xxx",
3612
- "parentRelationshipTypeName" : "CollectionMembership",
3613
- "parentAtEnd1": true,
3614
- "properties": {
3615
- "class" : "AgreementProperties",
3616
- "qualifiedName": "Agreement::Add agreement name here",
3617
- "name" : "display name",
3618
- "description" : "Add description of the agreement here",
3619
- "identifier" : "Add agreement identifier here",
3620
- "additionalProperties": {
3621
- "property1Name" : "property1Value",
3622
- "property2Name" : "property2Value"
3623
- }
3624
- },
3625
- "externalSourceGUID": "add guid here",
3626
- "externalSourceName": "add qualified name here",
3627
- "effectiveTime" : "{{$isoTimestamp}}",
3628
- "forLineage" : false,
3629
- "forDuplicateProcessing" : false,
3630
- "initialStatus" : "ACTIVE"
3631
- }
3632
-
3633
- The valid values for initialStatus are: DRAFT, PREPARED, PROPOSED, APPROVED, REJECTED, APPROVED_CONCEPT,
3634
- UNDER_DEVELOPMENT, DEVELOPMENT_COMPLETE, APPROVED_FOR_DEPLOYMENT, ACTIVE, DISABLED, DEPRECATED,
3635
- OTHER. If using OTHER, set the userDefinedStatus with the status value you want. If not specified, will
3636
- default to ACTIVE.
3637
- """
3638
- try:
3639
- if isinstance(body, NewElementRequestBody):
3640
- if body.properties.class_ == "AgreementProperties":
3641
- validated_body = body
3642
- else:
3643
- raise PyegeriaInvalidParameterException(additional_info=
3644
- {"reason": "unexpected property class name"})
3645
-
3646
- elif isinstance(body, dict):
3647
- if body.get("properties", {}).get("class", "") == "AgreementProperties":
3648
- validated_body = self._new_element_request_adapter.validate_python(body)
3649
- else:
3650
- raise PyegeriaInvalidParameterException(additional_info=
3651
- {"reason": "unexpected property class name"})
3652
-
3653
- else:
3654
- raise TypeError("Invalid parameter type")
3655
-
3656
- except ValidationError as e:
3657
- logger.error(f"Validation error: {e}")
3658
- raise ValidationError(e)
3659
-
3660
- url = f"{self.collection_command_root}"
3661
- json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
3662
- logger.info(json_body)
3663
- resp = await self._async_make_request("POST", url, json_body, is_json=True)
3664
- logger.info(f"Create collection with GUID: {resp.json().get('guid')}")
3665
- return resp.json().get("guid", NO_GUID_RETURNED)
3666
-
3667
- @dynamic_catch
3668
- def create_agreement(self, body: dict | NewElementRequestBody = None) -> str:
3669
- """ Create a new collection that represents an agreement..
3670
-
3671
- Parameters
3672
- ----------
3673
- body: dict | NewElementRequestBody
3674
- A structure representing the details of the agreement to create.
3675
-
3676
- Returns
3677
- -------
3678
- str - the guid of the created collection
3679
-
3680
- Raises
3681
- ------
3682
- PyegeriaException
3683
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
3684
- ValidationError
3685
- Raised by the pydantic validator if the body does not conform to the NewElementRequestBody.
3686
- NotAuthorizedException
3687
- The principle specified by the user_id does not have authorization for the requested action
3688
-
3689
- Notes
3690
- -----
3691
- Note: the three dates: introductionDate, nextVersionDate and withdrawDate must
3692
- be valid dates if specified, otherwise you will get a 400 error response.
3693
-
3694
- JSON Structure looks like:
3695
- {
3696
- "class" : "NewElementRequestBody",
3697
- "isOwnAnchor" : true,
3698
- "anchorScopeGUID" : "optional GUID of search scope",
3699
- "parentGUID" : "xxx",
3700
- "parentRelationshipTypeName" : "CollectionMembership",
3701
- "parentAtEnd1": true,
3702
- "properties": {
3703
- "class" : "AgreementProperties",
3704
- "qualifiedName": "Agreement::Add agreement name here",
3705
- "name" : "display name",
3706
- "description" : "Add description of the agreement here",
3707
- "identifier" : "Add agreement identifier here",
3708
- "additionalProperties": {
3709
- "property1Name" : "property1Value",
3710
- "property2Name" : "property2Value"
3711
- }
3712
- },
3713
- "externalSourceGUID": "add guid here",
3714
- "externalSourceName": "add qualified name here",
3715
- "effectiveTime" : "{{$isoTimestamp}}",
3716
- "forLineage" : false,
3717
- "forDuplicateProcessing" : false,
3718
- "initialStatus" : "ACTIVE"
3719
- }
3720
-
3721
- The valid values for initialStatus are: DRAFT, PREPARED, PROPOSED, APPROVED, REJECTED, APPROVED_CONCEPT,
3722
- UNDER_DEVELOPMENT, DEVELOPMENT_COMPLETE, APPROVED_FOR_DEPLOYMENT, ACTIVE, DISABLED, DEPRECATED,
3723
- OTHER. If using OTHER, set the userDefinedStatus with the status value you want. If not specified, will
3724
- default to ACTIVE.
3725
- """
3010
+ self._async_detach_product_manager(digital_prod_guid, digital_prod_manager_guid,
3011
+ body))
3726
3012
 
3727
- return asyncio.get_event_loop().run_until_complete(
3728
- self._async_create_agreement(body))
3729
3013
 
3014
+ #
3730
3015
 
3731
3016
  @dynamic_catch
3732
- async def _async_create_data_sharing_agreement(self, body: dict | NewElementRequestBody) -> str:
3733
- """ Create a new collection that represents a data sharing agreement.
3017
+ async def _async_create_agreement(self, body: dict | NewElementRequestBody) -> str:
3018
+ """ Create a new collection that represents an agreement.
3734
3019
  Async version.
3735
3020
 
3736
3021
  Parameters
3737
3022
  ----------
3738
3023
  body: dict | NewElementRequestBody
3739
- A structure representing the details of the data sharing agreement to create.
3024
+ A structure representing the details of the agreement to create.
3740
3025
 
3741
3026
  Returns
3742
3027
  -------
@@ -3769,19 +3054,18 @@ class CollectionManager(Client2):
3769
3054
  "qualifiedName": "Agreement::Add agreement name here",
3770
3055
  "name" : "display name",
3771
3056
  "description" : "Add description of the agreement here",
3772
- "userDefinedStatus" : "NEW",
3773
3057
  "identifier" : "Add agreement identifier here",
3774
3058
  "additionalProperties": {
3775
3059
  "property1Name" : "property1Value",
3776
3060
  "property2Name" : "property2Value"
3777
3061
  }
3778
3062
  },
3779
- "initialStatus" : "ACTIVE",
3780
3063
  "externalSourceGUID": "add guid here",
3781
3064
  "externalSourceName": "add qualified name here",
3782
3065
  "effectiveTime" : "{{$isoTimestamp}}",
3783
3066
  "forLineage" : false,
3784
- "forDuplicateProcessing" : false
3067
+ "forDuplicateProcessing" : false,
3068
+ "initialStatus" : "ACTIVE"
3785
3069
  }
3786
3070
 
3787
3071
  The valid values for initialStatus are: DRAFT, PREPARED, PROPOSED, APPROVED, REJECTED, APPROVED_CONCEPT,
@@ -3789,43 +3073,17 @@ class CollectionManager(Client2):
3789
3073
  OTHER. If using OTHER, set the userDefinedStatus with the status value you want. If not specified, will
3790
3074
  default to ACTIVE.
3791
3075
  """
3792
- try:
3793
- if isinstance(body, NewElementRequestBody):
3794
- if body.properties.class_ == "AgreementProperties":
3795
- validated_body = body
3796
- else:
3797
- raise PyegeriaInvalidParameterException(additional_info=
3798
- {"reason": "unexpected property class name"})
3799
-
3800
- elif isinstance(body, dict):
3801
- if body.get("properties", {}).get("class", "") == "AgreementProperties":
3802
- validated_body = self._new_element_request_adapter.validate_python(body)
3803
- else:
3804
- raise PyegeriaInvalidParameterException(additional_info=
3805
- {"reason": "unexpected property class name"})
3806
-
3807
- else:
3808
- raise TypeError("Invalid parameter type")
3809
-
3810
- except ValidationError as e:
3811
- logger.error(f"Validation error: {e}")
3812
- raise ValidationError(e)
3813
-
3814
- url = f"{self.collection_command_root}/data-sharing-agreement"
3815
- json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
3816
- logger.info(json_body)
3817
- resp = await self._async_make_request("POST", url, json_body, is_json=True)
3818
- logger.info(f"Create collection with GUID: {resp.json().get('guid')}")
3819
- return resp.json().get("guid", NO_GUID_RETURNED)
3076
+ url = f"{self.collection_command_root}"
3077
+ return await self._async_create_element_body_request(url, "AgreementProperties", body)
3820
3078
 
3821
3079
  @dynamic_catch
3822
- def create_data_sharing_agreement(self, body: dict | NewElementRequestBody = None) -> str:
3823
- """ Create a new collection that represents a digital product.
3080
+ def create_agreement(self, body: dict | NewElementRequestBody = None) -> str:
3081
+ """ Create a new collection that represents an agreement..
3824
3082
 
3825
3083
  Parameters
3826
3084
  ----------
3827
3085
  body: dict | NewElementRequestBody
3828
- A structure representing the details of the digital product to create.
3086
+ A structure representing the details of the agreement to create.
3829
3087
 
3830
3088
  Returns
3831
3089
  -------
@@ -3854,19 +3112,11 @@ class CollectionManager(Client2):
3854
3112
  "parentRelationshipTypeName" : "CollectionMembership",
3855
3113
  "parentAtEnd1": true,
3856
3114
  "properties": {
3857
- "class" : "DigitalProductProperties",
3858
- "qualifiedName": "DigitalProduct::Add product name here",
3859
- "name" : "Product contents",
3860
- "description" : "Add description of product and its expected usage here",
3861
- "identifier" : "Add product identifier here",
3862
- "productName" : "Add product name here",
3863
- "category" : "Periodic Delta",
3864
- "maturity" : "Add valid value here",
3865
- "serviceLife" : "Add the estimated lifetime of the product",
3866
- "introductionDate" : "date",
3867
- "nextVersionDate": "date",
3868
- "withdrawDate": "date",
3869
- "currentVersion": "V0.1",
3115
+ "class" : "AgreementProperties",
3116
+ "qualifiedName": "Agreement::Add agreement name here",
3117
+ "name" : "display name",
3118
+ "description" : "Add description of the agreement here",
3119
+ "identifier" : "Add agreement identifier here",
3870
3120
  "additionalProperties": {
3871
3121
  "property1Name" : "property1Value",
3872
3122
  "property2Name" : "property2Value"
@@ -3887,12 +3137,148 @@ class CollectionManager(Client2):
3887
3137
  """
3888
3138
 
3889
3139
  return asyncio.get_event_loop().run_until_complete(
3890
- self._async_create_data_sharing_agreement(body))
3140
+ self._async_create_agreement(body))
3891
3141
 
3142
+ #
3143
+ # @dynamic_catch
3144
+ # async def _async_create_data_sharing_agreement(self, body: dict | NewElementRequestBody) -> str:
3145
+ # """ Create a new collection that represents a data sharing agreement.
3146
+ # Async version.
3147
+ #
3148
+ # Parameters
3149
+ # ----------
3150
+ # body: dict | NewElementRequestBody
3151
+ # A structure representing the details of the data sharing agreement to create.
3152
+ #
3153
+ # Returns
3154
+ # -------
3155
+ # str - the guid of the created collection
3156
+ #
3157
+ # Raises
3158
+ # ------
3159
+ # PyegeriaException
3160
+ # If the client passes incorrect parameters on the request - such as bad URLs or invalid values
3161
+ # ValidationError
3162
+ # Raised by the pydantic validator if the body does not conform to the NewElementRequestBody.
3163
+ # NotAuthorizedException
3164
+ # The principle specified by the user_id does not have authorization for the requested action
3165
+ #
3166
+ # Notes
3167
+ # -----
3168
+ # Note: the three dates: introductionDate, nextVersionDate and withdrawDate must
3169
+ # be valid dates if specified, otherwise you will get a 400 error response.
3170
+ #
3171
+ # JSON Structure looks like:
3172
+ # {
3173
+ # "class" : "NewElementRequestBody",
3174
+ # "isOwnAnchor" : true,
3175
+ # "anchorScopeGUID" : "optional GUID of search scope",
3176
+ # "parentGUID" : "xxx",
3177
+ # "parentRelationshipTypeName" : "CollectionMembership",
3178
+ # "parentAtEnd1": true,
3179
+ # "properties": {
3180
+ # "class" : "AgreementProperties",
3181
+ # "qualifiedName": "Agreement::Add agreement name here",
3182
+ # "name" : "display name",
3183
+ # "description" : "Add description of the agreement here",
3184
+ # "userDefinedStatus" : "NEW",
3185
+ # "identifier" : "Add agreement identifier here",
3186
+ # "additionalProperties": {
3187
+ # "property1Name" : "property1Value",
3188
+ # "property2Name" : "property2Value"
3189
+ # }
3190
+ # },
3191
+ # "initialStatus" : "ACTIVE",
3192
+ # "externalSourceGUID": "add guid here",
3193
+ # "externalSourceName": "add qualified name here",
3194
+ # "effectiveTime" : "{{$isoTimestamp}}",
3195
+ # "forLineage" : false,
3196
+ # "forDuplicateProcessing" : false
3197
+ # }
3198
+ #
3199
+ # The valid values for initialStatus are: DRAFT, PREPARED, PROPOSED, APPROVED, REJECTED, APPROVED_CONCEPT,
3200
+ # UNDER_DEVELOPMENT, DEVELOPMENT_COMPLETE, APPROVED_FOR_DEPLOYMENT, ACTIVE, DISABLED, DEPRECATED,
3201
+ # OTHER. If using OTHER, set the userDefinedStatus with the status value you want. If not specified, will
3202
+ # default to ACTIVE.
3203
+ # """
3204
+ #
3205
+ # url = f"{self.collection_command_root}/data-sharing-agreement"
3206
+ # return await self._async_create_element_body_request(url, "AgreementProperties", body)
3207
+ #
3208
+ # @dynamic_catch
3209
+ # def create_data_sharing_agreement(self, body: dict | NewElementRequestBody = None) -> str:
3210
+ # """ Create a new collection that represents a digital product.
3211
+ #
3212
+ # Parameters
3213
+ # ----------
3214
+ # body: dict | NewElementRequestBody
3215
+ # A structure representing the details of the digital product to create.
3216
+ #
3217
+ # Returns
3218
+ # -------
3219
+ # str - the guid of the created collection
3220
+ #
3221
+ # Raises
3222
+ # ------
3223
+ # PyegeriaException
3224
+ # If the client passes incorrect parameters on the request - such as bad URLs or invalid values
3225
+ # ValidationError
3226
+ # Raised by the pydantic validator if the body does not conform to the NewElementRequestBody.
3227
+ # NotAuthorizedException
3228
+ # The principle specified by the user_id does not have authorization for the requested action
3229
+ #
3230
+ # Notes
3231
+ # -----
3232
+ # Note: the three dates: introductionDate, nextVersionDate and withdrawDate must
3233
+ # be valid dates if specified, otherwise you will get a 400 error response.
3234
+ #
3235
+ # JSON Structure looks like:
3236
+ # {
3237
+ # "class" : "NewElementRequestBody",
3238
+ # "isOwnAnchor" : true,
3239
+ # "anchorScopeGUID" : "optional GUID of search scope",
3240
+ # "parentGUID" : "xxx",
3241
+ # "parentRelationshipTypeName" : "CollectionMembership",
3242
+ # "parentAtEnd1": true,
3243
+ # "properties": {
3244
+ # "class" : "DigitalProductProperties",
3245
+ # "qualifiedName": "DigitalProduct::Add product name here",
3246
+ # "name" : "Product contents",
3247
+ # "description" : "Add description of product and its expected usage here",
3248
+ # "identifier" : "Add product identifier here",
3249
+ # "productName" : "Add product name here",
3250
+ # "category" : "Periodic Delta",
3251
+ # "maturity" : "Add valid value here",
3252
+ # "serviceLife" : "Add the estimated lifetime of the product",
3253
+ # "introductionDate" : "date",
3254
+ # "nextVersionDate": "date",
3255
+ # "withdrawDate": "date",
3256
+ # "currentVersion": "V0.1",
3257
+ # "additionalProperties": {
3258
+ # "property1Name" : "property1Value",
3259
+ # "property2Name" : "property2Value"
3260
+ # }
3261
+ # },
3262
+ # "externalSourceGUID": "add guid here",
3263
+ # "externalSourceName": "add qualified name here",
3264
+ # "effectiveTime" : "{{$isoTimestamp}}",
3265
+ # "forLineage" : false,
3266
+ # "forDuplicateProcessing" : false,
3267
+ # "initialStatus" : "ACTIVE"
3268
+ # }
3269
+ #
3270
+ # The valid values for initialStatus are: DRAFT, PREPARED, PROPOSED, APPROVED, REJECTED, APPROVED_CONCEPT,
3271
+ # UNDER_DEVELOPMENT, DEVELOPMENT_COMPLETE, APPROVED_FOR_DEPLOYMENT, ACTIVE, DISABLED, DEPRECATED,
3272
+ # OTHER. If using OTHER, set the userDefinedStatus with the status value you want. If not specified, will
3273
+ # default to ACTIVE.
3274
+ # """
3275
+ #
3276
+ # return asyncio.get_event_loop().run_until_complete(
3277
+ # self._async_create_data_sharing_agreement(body))
3278
+ #
3892
3279
 
3893
3280
  @dynamic_catch
3894
- async def _async_update_agreement(self, agreement_guid: str, body: dict | UpdateElementRequestBody,
3895
- merge_update: bool = True) -> None:
3281
+ async def _async_update_agreement(self, agreement_guid: str, body: dict | UpdateElementRequestBody) -> None:
3896
3282
  """ Update the properties of an agreement.
3897
3283
  Collections: https://egeria-project.org/concepts/collection
3898
3284
 
@@ -3945,43 +3331,11 @@ class CollectionManager(Client2):
3945
3331
  "forDuplicateProcessing" : false
3946
3332
  }
3947
3333
  """
3948
-
3949
- try:
3950
- if isinstance(body, NewElementRequestBody):
3951
- if body.properties.class_ == "AgreementProperties":
3952
- validated_body = body
3953
- else:
3954
- raise PyegeriaInvalidParameterException(additional_info =
3955
- {"reason" : "unexpected property class name"})
3956
-
3957
- elif isinstance(body, dict):
3958
- if body.get("properties", {}).get("class", "") == "AgreementProperties":
3959
- validated_body = self._update_element_request_adapter.validate_python(body)
3960
- else:
3961
- raise PyegeriaInvalidParameterException(additional_info =
3962
- {"reason" : "unexpected property class name"})
3963
- else:
3964
- raise TypeError("Invalid parameter type")
3965
-
3966
- except ValidationError as e:
3967
- logger.error(f"Validation error: {e}")
3968
- raise ValidationError(e)
3969
-
3970
- merge_update_s = str(merge_update).lower()
3971
-
3972
- url = (f"{self.collection_command_root}/{agreement_guid}/update?"
3973
- f"replaceAllProperties={merge_update_s}")
3974
-
3975
- json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
3976
-
3977
- await self._async_make_request("POST", url, json_body)
3978
-
3979
- logger.info(f"Successfully updated {agreement_guid} with {json_body}")
3980
-
3334
+ url = (f"{self.collection_command_root}/{agreement_guid}/update")
3335
+ await self._async_update_element_body_request(url, AGREEMENT_PROPERTIES_LIST,body)
3981
3336
 
3982
3337
  @dynamic_catch
3983
- def update_agreement(self, agreement_guid: str, body: dict | UpdateElementRequestBody,
3984
- merge_update: bool = True) -> None:
3338
+ def update_agreement(self, agreement_guid: str, body: dict | UpdateElementRequestBody) -> None:
3985
3339
  """ Update the properties of an agreement.
3986
3340
  Collections: https://egeria-project.org/concepts/collection
3987
3341
 
@@ -4035,68 +3389,20 @@ class CollectionManager(Client2):
4035
3389
  """
4036
3390
 
4037
3391
  return asyncio.get_event_loop().run_until_complete(
4038
- self._async_update_agreement(agreement_guid, body, merge_update))
4039
-
4040
-
4041
- @dynamic_catch
4042
- async def _async_update_agreement_status(self, agreement_guid: str, body: dict | UpdateStatusRequestBody):
4043
- """Update the status of an agreement. Async version.
4044
-
4045
- Parameters
4046
- ----------
4047
- agreement_guid: str
4048
- The guid of the collection to update.
4049
- body: dict | UpdateStatusRequestBody
4050
- A structure representing the details of the collection to create.
4051
-
4052
- Returns
4053
- -------
4054
- Nothing
4055
-
4056
- Raises
4057
- ------
4058
- InvalidParameterException
4059
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
4060
- PropertyServerException
4061
- Raised by the server when an issue arises in processing a valid request
4062
- NotAuthorizedException
4063
- The principle specified by the user_id does not have authorization for the requested action
3392
+ self._async_update_agreement(agreement_guid, body))
4064
3393
 
4065
- Notes
4066
- -----
4067
- JSON Structure looks like:
4068
- {
4069
- "class": "UpdateStatusRequestBody",
4070
- "status": "APPROVED",
4071
- "externalSourceGUID": "add guid here",
4072
- "externalSourceName": "add qualified name here",
4073
- "effectiveTime": "{{$isoTimestamp}}",
4074
- "forLineage": false,
4075
- "forDuplicateProcessing": false
4076
- }
4077
- """
4078
- try:
4079
- if isinstance(body, UpdateStatusRequestBody):
4080
- validated_body = body
4081
- elif isinstance(body, dict):
4082
- validated_body = self._update_status_request_adapter.validate_python(body)
4083
-
4084
- except ValidationError as e:
4085
- logger.error(f"Validation error: {e}")
4086
- raise ValidationError(e)
4087
3394
 
4088
- url = f"{self.collection_command_root}/{agreement_guid}/update-status"
4089
- json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
4090
- await self._async_make_request("POST", url, json_body)
4091
- logger.info(f"Successfully updated {agreement_guid} with {json_body}")
4092
3395
 
4093
3396
  @dynamic_catch
4094
- def update_agreement_status(self, agreement_guid: str, body: dict | UpdateStatusRequestBody):
3397
+ def update_agreement_status(self, agreement_guid: str, status: str = None,
3398
+ body: dict | UpdateStatusRequestBody = None):
4095
3399
  """Update the status of an agreement.
4096
3400
  Parameters
4097
3401
  ----------
4098
3402
  agreement_guid: str
4099
3403
  The guid of the collection to update.
3404
+ status: str, optional
3405
+ The new lifecycle status for the collection. Ignored, if the body is provided.
4100
3406
  body: dict | UpdateStatusRequestBody
4101
3407
  A structure representing the details of the collection to create.
4102
3408
 
@@ -4127,7 +3433,7 @@ class CollectionManager(Client2):
4127
3433
  }
4128
3434
  """
4129
3435
  loop = asyncio.get_event_loop()
4130
- loop.run_until_complete(self._async_update_collection_status(agreement_guid, body))
3436
+ loop.run_until_complete(self._async_update_collection_status(agreement_guid, status,body))
4131
3437
 
4132
3438
 
4133
3439
 
@@ -4180,37 +3486,11 @@ class CollectionManager(Client2):
4180
3486
  }
4181
3487
 
4182
3488
  """
4183
- try:
4184
- if isinstance(body, NewRelationshipRequestBody):
4185
- if body.properties.class_ == "AgreementActorProperties":
4186
- validated_body = body
4187
- else:
4188
- raise PyegeriaInvalidParameterException(additional_info=
4189
- {"reason": "unexpected property class name"})
4190
-
4191
- elif isinstance(body, dict):
4192
- if body.get("properties", {}).get("class", "") == "AgreementActorProperties":
4193
- validated_body = self._new_relationship_request_adapter.validate_python(body)
4194
- else:
4195
- raise PyegeriaInvalidParameterException(additional_info=
4196
- {"reason": "unexpected property class name"})
4197
- else:
4198
- validated_body = None
4199
-
4200
- except ValidationError as e:
4201
- logger.error(f"Validation error: {e}")
4202
- raise ValidationError(e)
4203
-
4204
3489
  url = (
4205
3490
  f"{self.platform_url}/servers/"
4206
3491
  f"{self.view_server}/api/open-metadata/collection-manager/collections/agreements/"
4207
3492
  f"{agreement_guid}/agreement-actors/{actor_guid}/attach")
4208
-
4209
- if validated_body:
4210
- json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
4211
- await self._async_make_request("POST", url, json_body)
4212
- else:
4213
- await self._async_make_request("POST", url)
3493
+ await self._async_new_relationship_request(url, "AgreementActorProperties",body)
4214
3494
  logger.info(f"Attached digital product manager {agreement_guid} -> {actor_guid}")
4215
3495
 
4216
3496
 
@@ -4309,28 +3589,11 @@ class CollectionManager(Client2):
4309
3589
 
4310
3590
  """
4311
3591
 
4312
- try:
4313
- if isinstance(body, DeleteRequestBody):
4314
- validated_body = body
4315
- elif isinstance(body, dict):
4316
- validated_body = self._delete_request_adapter.validate_python(body)
4317
- else:
4318
- validated_body = None
4319
-
4320
- except ValidationError as e:
4321
- logger.error(f"Validation error: {e}")
4322
- raise ValidationError(e)
4323
-
4324
3592
  url = (
4325
3593
  f"{self.platform_url}/servers/"
4326
3594
  f"{self.view_server}/api/open-metadata/collection-manager/collections/agreements/"
4327
3595
  f"{agreement_guid}/agreement-actors/{actor_guid}/detach")
4328
-
4329
- if validated_body:
4330
- json_body = validated_body.model_dump_json(indent=2, exclude_none=True)
4331
- await self._async_make_request("POST", url, json_body)
4332
- else:
4333
- await self._async_make_request("POST", url)
3596
+ self._async_delete_request(url, body)
4334
3597
  logger.info(f"Detached digital product manager {agreement_guid} -> {actor_guid}")
4335
3598
 
4336
3599
 
@@ -4447,29 +3710,11 @@ class CollectionManager(Client2):
4447
3710
  }
4448
3711
 
4449
3712
  """
4450
- pass
4451
- # if isinstance(body,NewRelationshipRequestBody ):
4452
- # validated_body = body
4453
- # elif isinstance(body, dict):
4454
- # try:
4455
- # validated_body = (**body)
4456
- # except ValidationError as e:
4457
- # logger.error(f"Validation error: {e}")
4458
- # raise PyegeriaInvalidParameterException(f"Invalid body: {e}")
4459
- # elif body is None:
4460
- # validated_body = (
4461
- #
4462
- # )
4463
- # # else:
4464
- # raise PyegeriaInvalidParameterException(f"Invalid body type: {type(body)}")
4465
- # url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/collection-manager/collections/"
4466
- # f"agreements/{agreement_guid}/agreement-items/{agreement_item_guid}/attach")
4467
- #
4468
- # if body:
4469
- # await self._async_make_request("POST", url, body)
4470
- # else:
4471
- # await self._async_make_request("POST", url)
4472
- # logger.info(f"Attached agreement item {agreement_item_guid} to {agreement_guid}")
3713
+ url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/collection-manager/collections/"
3714
+ f"agreements/{agreement_guid}/agreement-items/{agreement_item_guid}/attach")
3715
+
3716
+ await self._async_new_relationship_request(url, "AgreementItemProperties", body)
3717
+ logger.info(f"Attached agreement item {agreement_item_guid} to {agreement_guid}")
4473
3718
 
4474
3719
 
4475
3720
  def link_agreement_item(self, agreement_guid: str, agreement_item_guid: str, body: dict = None) -> None:
@@ -4537,7 +3782,7 @@ class CollectionManager(Client2):
4537
3782
 
4538
3783
  @dynamic_catch
4539
3784
  async def _async_detach_agreement_item(self, agreement_guid: str, agreement_item_guid: str,
4540
- body: dict = None) -> None:
3785
+ body: dict | DeleteRequestBody = None) -> None:
4541
3786
  """Detach an agreement item from an agreement. Request body is optional. Async version.
4542
3787
 
4543
3788
  Parameters
@@ -4546,8 +3791,8 @@ class CollectionManager(Client2):
4546
3791
  The guid of the agreement to link.
4547
3792
  agreement_item_guid: str
4548
3793
  The guid of the element to attach.
4549
- body: dict, optional, default = None
4550
- A dict representing the details of the relationship.
3794
+ body: dict | DeleteRequestBody, optional, default = None
3795
+ A structure representing the details of the relationship.
4551
3796
 
4552
3797
  Returns
4553
3798
  -------
@@ -4565,25 +3810,20 @@ class CollectionManager(Client2):
4565
3810
  Notes
4566
3811
  _____
4567
3812
  {
4568
- "class" : "MetadataSourceRequestBody",
3813
+ "class": "DeleteRequestBody",
4569
3814
  "externalSourceGUID": "add guid here",
4570
3815
  "externalSourceName": "add qualified name here",
4571
- "effectiveTime" : "{{$isoTimestamp}}",
4572
- "forLineage" : false,
4573
- "forDuplicateProcessing" : false
3816
+ "effectiveTime": "{{$isoTimestamp}}",
3817
+ "forLineage": false,
3818
+ "forDuplicateProcessing": false
4574
3819
  }
4575
3820
 
4576
3821
  """
4577
-
4578
3822
  url = (
4579
3823
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/collection-manager/collections"
4580
3824
  f"/agreements"
4581
3825
  f"{agreement_guid}/agreement-items/{agreement_item_guid}/detach")
4582
-
4583
- if body:
4584
- await self._async_make_request("POST", url, body)
4585
- else:
4586
- await self._async_make_request("POST", url)
3826
+ await self._async_delete_request(url, body)
4587
3827
  logger.info(f"Detached agreement item {agreement_item_guid} from {agreement_guid}")
4588
3828
 
4589
3829
 
@@ -4629,7 +3869,8 @@ class CollectionManager(Client2):
4629
3869
 
4630
3870
 
4631
3871
  @dynamic_catch
4632
- async def _async_link_contract(self, agreement_guid: str, external_ref_guid: str, body: dict = None) -> None:
3872
+ async def _async_link_contract(self, agreement_guid: str, external_ref_guid: str,
3873
+ body: dict | NewRelationshipRequestBody = None) -> None:
4633
3874
  """ Attach an agreement to an external reference element that describes the location of the contract
4634
3875
  documents.
4635
3876
  Request body is optional. Async version.
@@ -4640,8 +3881,8 @@ class CollectionManager(Client2):
4640
3881
  The guid of the agreement to update.
4641
3882
  external_ref_guid: str
4642
3883
  The guid of the external reference to attach.
4643
- body: dict, optional, default = None
4644
- A dict representing the details of the relationship.
3884
+ body: dict | NewRelationshipRequestBody, optional, default = None
3885
+ A structure representing the details of the relationship.
4645
3886
 
4646
3887
  Returns
4647
3888
  -------
@@ -4678,17 +3919,12 @@ class CollectionManager(Client2):
4678
3919
  }
4679
3920
 
4680
3921
  """
4681
-
4682
3922
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/collection-manager/collections/"
4683
3923
  f"agreements/{agreement_guid}/contract-links/{external_ref_guid}/attach")
3924
+ await self._async_new_relationship_request(url, "ContractLinkProperties", body)
3925
+ logger.info(f"Attached agreement {agreement_guid} to contract {external_ref_guid}")
4684
3926
 
4685
- if body:
4686
- await self._async_make_request("POST", url, body)
4687
- else:
4688
- await self._async_make_request("POST", url)
4689
- logger.info(f"Attached agreemenbt {agreement_guid} to contract {external_ref_guid}")
4690
-
4691
-
3927
+ @dynamic_catch
4692
3928
  def link_contract(self, agreement_guid: str, external_ref_guid: str, body: dict = None) -> None:
4693
3929
  """ Attach an agreement to an external reference element that describes the location of the contract
4694
3930
  documents.
@@ -4783,16 +4019,11 @@ class CollectionManager(Client2):
4783
4019
 
4784
4020
  url = (
4785
4021
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/collection-manager/collections"
4786
- f"/agreements"
4787
- f"{agreement_guid}/contract-links/{external_ref_guid}/detach")
4788
-
4789
- if body:
4790
- await self._async_make_request("POST", url, body)
4791
- else:
4792
- await self._async_make_request("POST", url)
4022
+ f"/agreements/{agreement_guid}/contract-links/{external_ref_guid}/detach")
4023
+ await self._async_delete_request(url, body)
4793
4024
  logger.info(f"Detached contract: {external_ref_guid} from {agreement_guid}")
4794
4025
 
4795
-
4026
+ @dynamic_catch
4796
4027
  def detach_contract(self, agreement_guid: str, external_ref_guid: str, body: dict = None) -> None:
4797
4028
  """Detach an external reference to a contract, from an agreement. Request body is optional.
4798
4029
 
@@ -4839,13 +4070,13 @@ class CollectionManager(Client2):
4839
4070
 
4840
4071
 
4841
4072
  @dynamic_catch
4842
- async def _async_create_digital_subscription(self, body: dict) -> str:
4073
+ async def _async_create_digital_subscription(self, body: dict | NewElementRequestBody) -> str:
4843
4074
  """Create a new collection that represents a type of agreement called a digital_subscription. Async version.
4844
4075
 
4845
4076
  Parameters
4846
4077
  ----------
4847
- body: dict
4848
- A dict representing the details of the collection to create.
4078
+ body | NewElementRequewstBody: dict
4079
+ A structure representing the details of the collection to create.
4849
4080
 
4850
4081
  Returns
4851
4082
  -------
@@ -4937,13 +4168,8 @@ class CollectionManager(Client2):
4937
4168
  "forDuplicateProcessing" : false
4938
4169
  }
4939
4170
  """
4940
-
4941
4171
  url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/collection-manager/collections"
4942
-
4943
- resp = await self._async_make_request("POST", url, body_slimmer(body))
4944
- guid = resp.json().get('guid', NO_GUID_RETURNED)
4945
- logger.info(f"Create collection with GUID: {guid}")
4946
- return guid
4172
+ return await self._async_create_element_body_request(url, "DigitalSubscriptionProperties", body)
4947
4173
 
4948
4174
 
4949
4175
  def create_digital_subscription(self, body: dict) -> str:
@@ -5019,19 +4245,16 @@ class CollectionManager(Client2):
5019
4245
 
5020
4246
 
5021
4247
  @dynamic_catch
5022
- async def _async_update_digital_subscription(self, digital_subscription_guid: str, body: dict,
5023
- replace_all_props: bool = False, ):
4248
+ async def _async_update_digital_subscription(self, digital_subscription_guid: str,
4249
+ body: dict | UpdateElementRequestBody) -> None:
5024
4250
  """Update the properties of the digital_subscription collection. Async version.
5025
4251
 
5026
4252
  Parameters
5027
4253
  ----------
5028
4254
  digital_subscription_guid: str
5029
4255
  The guid of the digital_subscription to update.
5030
- body: dict
5031
- A dict representing the details of the collection to create.
5032
- replace_all_props: bool, optional, defaults to False
5033
- Whether to replace all properties in the collection.
5034
-
4256
+ body: dict | UpdateElementRequestBody
4257
+ A structure representing the details of the collection to create.
5035
4258
 
5036
4259
  Returns
5037
4260
  -------
@@ -5075,17 +4298,13 @@ class CollectionManager(Client2):
5075
4298
  "forDuplicateProcessing" : false
5076
4299
  }
5077
4300
  """
5078
-
5079
- replace_all_props_s = str(replace_all_props).lower()
5080
4301
  url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/collection-manager/collections/"
5081
- f"{digital_subscription_guid}/update?replaceAllProperties={replace_all_props_s}")
5082
-
5083
- await self._async_make_request("POST", url, body)
4302
+ f"{digital_subscription_guid}/update")
4303
+ await self._async_update_element_body_request(url,["DigitalSubscriptionProperties"], body )
5084
4304
  logger.info(f"Updated digital subscription {digital_subscription_guid}")
5085
4305
 
5086
-
5087
- def update_digital_subscription(self, digital_subscription_guid: str, body: dict,
5088
- replace_all_props: bool = False, ):
4306
+ @dynamic_catch
4307
+ def update_digital_subscription(self, digital_subscription_guid: str, body: dict,):
5089
4308
  """Update the properties of the DigitalProduct classification attached to a collection.
5090
4309
 
5091
4310
  Parameters
@@ -5138,19 +4357,22 @@ class CollectionManager(Client2):
5138
4357
  """
5139
4358
  loop = asyncio.get_event_loop()
5140
4359
  loop.run_until_complete(
5141
- self._async_update_digital_subscription(digital_subscription_guid, body, replace_all_props))
4360
+ self._async_update_digital_subscription(digital_subscription_guid, body))
5142
4361
 
5143
4362
 
5144
4363
  @dynamic_catch
5145
- async def _async_update_digital_subscription_status(self, digital_subscription_guid: str, body: dict):
5146
- """Update the status of an digital_subscription collection. Async version.
4364
+ async def _async_update_digital_subscription_status(self, digital_subscription_guid: str, status: str = None,
4365
+ body: dict | UpdateStatusRequestBody = None)-> None:
4366
+ """Update the status of a digital_subscription collection. Async version.
5147
4367
 
5148
4368
  Parameters
5149
4369
  ----------
5150
4370
  digital_subscription_guid: str
5151
4371
  The guid of the digital product collection to update.
5152
- body: dict
5153
- A dict representing the details of the collection to create.
4372
+ status: str, optional
4373
+ The new status of the digital_subscription collection. Will be used only if body is not provided.
4374
+ body: dict | UpdateStatusRequestBody, optional, defaults to None
4375
+ A structure representing the details of the collection to create.
5154
4376
 
5155
4377
  Returns
5156
4378
  -------
@@ -5168,27 +4390,26 @@ class CollectionManager(Client2):
5168
4390
  Notes
5169
4391
  -----
5170
4392
  JSON Structure looks like:
5171
- {
5172
- "class": "AgreementStatusRequestBody",
5173
- "status": "APPROVED",
5174
- "externalSourceGUID": "add guid here",
5175
- "externalSourceName": "add qualified name here",
5176
- "effectiveTime": "{{$isoTimestamp}}",
5177
- "forLineage": false,
5178
- "forDuplicateProcessing": false
5179
- }
4393
+ {
4394
+ "class": "UpdateStatusRequestBody",
4395
+ "status": "APPROVED",
4396
+ "externalSourceGUID": "add guid here",
4397
+ "externalSourceName": "add qualified name here",
4398
+ "effectiveTime": "{{$isoTimestamp}}",
4399
+ "forLineage": false,
4400
+ "forDuplicateProcessing": false
4401
+ }
5180
4402
  """
5181
-
5182
4403
  url = (
5183
4404
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/collection-manager/collections"
5184
4405
  f"/agreements/"
5185
4406
  f"{digital_subscription_guid}/update-status")
5186
-
5187
- await self._async_make_request("POST", url, body)
4407
+ await self._async_update_status_request(url, status, body)
5188
4408
  logger.info(f"Updated status for DigitalProduct {digital_subscription_guid}")
5189
4409
 
5190
-
5191
- def update_digital_subscription_status(self, digital_subscription_guid: str, body: dict):
4410
+ @dynamic_catch
4411
+ def update_digital_subscription_status(self, digital_subscription_guid: str,
4412
+ body: dict | UpdateStatusRequestBody = None,):
5192
4413
  """Update the status of an digital_subscription collection. Async version.
5193
4414
 
5194
4415
  Parameters
@@ -5230,7 +4451,8 @@ class CollectionManager(Client2):
5230
4451
 
5231
4452
 
5232
4453
  @dynamic_catch
5233
- async def _async_link_subscriber(self, subscriber_guid: str, subscription_guid: str, body: dict = None):
4454
+ async def _async_link_subscriber(self, subscriber_guid: str, subscription_guid: str,
4455
+ body: dict | NewRelationshipRequestBody = None)-> None:
5234
4456
  """ Attach a subscriber to a subscription. The subscriber is of type 'Referenceable' to allow digital
5235
4457
  products, team or business capabilities to be the subscriber. The subscription is an element of type
5236
4458
  DigitalSubscription.
@@ -5242,8 +4464,8 @@ class CollectionManager(Client2):
5242
4464
  The unique identifier of the subscriber.
5243
4465
  subscription_guid: str
5244
4466
  The identifier of the subscription.
5245
- body: dict, optional, default = None
5246
- A dict representing the details of the relationship.
4467
+ body: dict | NewRelationshipRequestBody, optional, default = None
4468
+ A structure representing the details of the relationship.
5247
4469
 
5248
4470
  Returns
5249
4471
  -------
@@ -5262,7 +4484,7 @@ class CollectionManager(Client2):
5262
4484
  -----
5263
4485
  JSON Structure looks like:
5264
4486
  {
5265
- "class" : "RelationshipRequestBody",
4487
+ "class" : "NewRelationshipRequestBody",
5266
4488
  "externalSourceGUID": "add guid here",
5267
4489
  "externalSourceName": "add qualified name here",
5268
4490
  "effectiveTime" : "{{$isoTimestamp}}",
@@ -5276,19 +4498,16 @@ class CollectionManager(Client2):
5276
4498
  }
5277
4499
  }
5278
4500
  """
5279
-
5280
4501
  url = (
5281
4502
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/collection-manager/collections"
5282
4503
  f"/subscribers/"
5283
4504
  f"{subscriber_guid}/subscriptions/{subscription_guid}/attach")
5284
- if body:
5285
- await self._async_make_request("POST", url, body)
5286
- else:
5287
- await self._async_make_request("POST", url)
4505
+ await self._async_new_relationship_request(url, "DigitalSubscriberProperties", body)
5288
4506
  logger.info(f"Linking subscriber {subscriber_guid} to subscription {subscription_guid}")
5289
4507
 
5290
-
5291
- def link_subscriber(self, subscriber_guid: str, subscription_guid: str, body: dict = None):
4508
+ @dynamic_catch
4509
+ def link_subscriber(self, subscriber_guid: str, subscription_guid: str,
4510
+ body: dict | NewRelationshipRequestBody = None):
5292
4511
  """ Attach a subscriber to a subscription. The subscriber is of type 'Referenceable' to allow digital
5293
4512
  products, team or business capabilities to be the subscriber. The subscription is an element of type
5294
4513
  DigitalSubscription.
@@ -5339,7 +4558,8 @@ class CollectionManager(Client2):
5339
4558
 
5340
4559
 
5341
4560
  @dynamic_catch
5342
- async def _async_detach_subscriber(self, subscriber_guid: str, subscription_guid: str, body: dict = None):
4561
+ async def _async_detach_subscriber(self, subscriber_guid: str, subscription_guid: str,
4562
+ body: dict | DeleteRequestBody = None) -> None:
5343
4563
  """ Detach a subscriber from a subscription Request body is optional. Async version.
5344
4564
 
5345
4565
  Parameters
@@ -5348,8 +4568,8 @@ class CollectionManager(Client2):
5348
4568
  The unique identifier of the subscriber.
5349
4569
  subscription_guid: str
5350
4570
  The unique identifier of the subscription.
5351
- body: dict, optional, default = None
5352
- A dict representing the details of the relationship.
4571
+ body: dict | DeleteRequestBody, optional, default = None
4572
+ A structure representing the details of the relationship.
5353
4573
 
5354
4574
  Returns
5355
4575
  -------
@@ -5376,19 +4596,15 @@ class CollectionManager(Client2):
5376
4596
  "forDuplicateProcessing": false
5377
4597
  }
5378
4598
  """
5379
-
5380
4599
  url = (
5381
4600
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/collection-manager/collections"
5382
4601
  f"/agreements/"
5383
4602
  f"{subscriber_guid}/agreement-actors/{subscription_guid}/detach")
5384
- if body:
5385
- await self._async_make_request("POST", url, body)
5386
- else:
5387
- await self._async_make_request("POST", url)
4603
+ await self._async_delete_request(url, body)
5388
4604
  logger.info(f"Detached subscriber {subscriber_guid} from subscription {subscription_guid}")
5389
4605
 
5390
4606
 
5391
- def detach_subscriber(self, subscriber_guid: str, subscription_guid: str, body: dict = None):
4607
+ def detach_subscriber(self, subscriber_guid: str, subscription_guid: str, body: dict | DeleteRequestBody= None):
5392
4608
  """ Detach a subscriber from a subscription. Request body is optional.
5393
4609
 
5394
4610
  Parameters
@@ -5434,8 +4650,8 @@ class CollectionManager(Client2):
5434
4650
 
5435
4651
 
5436
4652
  @dynamic_catch
5437
- async def _async_attach_collection(self, parent_guid: str, collection_guid: str, body: dict = None,
5438
- make_anchor: bool = False):
4653
+ async def _async_attach_collection(self, parent_guid: str, collection_guid: str,
4654
+ body: dict | NewRelationshipRequestBody= None):
5439
4655
  """ Connect an existing collection to an element using the ResourceList relationship (0019).
5440
4656
  Async version.
5441
4657
 
@@ -5445,10 +4661,8 @@ class CollectionManager(Client2):
5445
4661
  The unique identifier of the parent to attach to.
5446
4662
  collection_guid: str
5447
4663
  The identifier of the collection being attached.
5448
- body: dict, optional, default = None
5449
- A dict representing the details of the relationship.
5450
- make_anchor: bool, optional, default = False
5451
- Indicates if the collection should be anchored to the element.
4664
+ body: dict | NewRelationshipRequestBody, optional, default = None
4665
+ A structure representing the details of the relationship.
5452
4666
 
5453
4667
  Returns
5454
4668
  -------
@@ -5468,7 +4682,7 @@ class CollectionManager(Client2):
5468
4682
  JSON Structure looks like:
5469
4683
 
5470
4684
  {
5471
- "class" : "RelationshipRequestBody",
4685
+ "class" : "NewRelationshipRequestBody",
5472
4686
  "properties": {
5473
4687
  "class": "ResourceListProperties",
5474
4688
  "resourceUse": "Add valid value here",
@@ -5491,16 +4705,13 @@ class CollectionManager(Client2):
5491
4705
  url = (
5492
4706
  f"{self.platform_url}/servers/"
5493
4707
  f"{self.view_server}/api/open-metadata/collection-manager/metadata-elements/"
5494
- f"{parent_guid}/collections/{collection_guid}/attach?makeAchor={make_anchor}")
5495
- if body:
5496
- await self._async_make_request("POST", url, body)
5497
- else:
5498
- await self._async_make_request("POST", url)
4708
+ f"{parent_guid}/collections/{collection_guid}/attach")
4709
+ await self._async_new_relationship_request(url, "ResourceListProperties", body)
5499
4710
  logger.info(f"Attached {collection_guid} to {parent_guid}")
5500
4711
 
5501
-
5502
- def attach_collection(self, parent_guid: str, collection_guid: str, body: dict = None,
5503
- make_anchor: bool = False):
4712
+ @dynamic_catch
4713
+ def attach_collection(self, parent_guid: str, collection_guid: str,
4714
+ body: dict | NewRelationshipRequestBody = None):
5504
4715
  """ Connect an existing collection to an element using the ResourceList relationship (0019).
5505
4716
 
5506
4717
  Parameters
@@ -5551,11 +4762,12 @@ class CollectionManager(Client2):
5551
4762
  }
5552
4763
  """
5553
4764
  loop = asyncio.get_event_loop()
5554
- loop.run_until_complete(self._async_attach_collection(parent_guid, collection_guid, body, make_anchor))
4765
+ loop.run_until_complete(self._async_attach_collection(parent_guid, collection_guid, body))
5555
4766
 
5556
4767
 
5557
4768
  @dynamic_catch
5558
- async def _async_detach_collection(self, parent_guid: str, collection_guid: str, body: dict = None):
4769
+ async def _async_detach_collection(self, parent_guid: str, collection_guid: str,
4770
+ body: dict | DeleteRequestBody = None):
5559
4771
  """ Detach an existing collection from an element. If the collection is anchored to the element,
5560
4772
  it is delete.
5561
4773
  Async version.
@@ -5566,8 +4778,8 @@ class CollectionManager(Client2):
5566
4778
  The unique identifier of the parent to detach from.
5567
4779
  collection_guid: str
5568
4780
  The identifier of the collection being detached.
5569
- body: dict, optional, default = None
5570
- A dict representing the details of the relationship.
4781
+ body: dict | DeleteRequestBody, optional, default = None
4782
+ A structure representing the details of the relationship.
5571
4783
 
5572
4784
 
5573
4785
  Returns
@@ -5587,27 +4799,25 @@ class CollectionManager(Client2):
5587
4799
  -----
5588
4800
  JSON Structure looks like:
5589
4801
  {
5590
- "class": "MetadataSourceRequestBody",
4802
+ "class": "DeleteRequestBody",
5591
4803
  "externalSourceGUID": "add guid here",
5592
4804
  "externalSourceName": "add qualified name here",
5593
4805
  "effectiveTime": "{{$isoTimestamp}}",
5594
4806
  "forLineage": false,
5595
4807
  "forDuplicateProcessing": false
5596
4808
  }
5597
- """
5598
4809
 
4810
+ """
5599
4811
  url = (
5600
4812
  f"{self.platform_url}/servers/"
5601
4813
  f"{self.view_server}/api/open-metadata/collection-manager/metadata-elements/"
5602
4814
  f"{parent_guid}/collections/{collection_guid}/detach")
5603
- if body:
5604
- await self._async_make_request("POST", url, body)
5605
- else:
5606
- await self._async_make_request("POST", url)
4815
+ await self._async_delete_request(url, body)
5607
4816
  logger.info(f"Detached collection {collection_guid} from {parent_guid}")
5608
4817
 
5609
4818
 
5610
- def detach_collection(self, parent_guid: str, collection_guid: str, body: dict = None):
4819
+ def detach_collection(self, parent_guid: str, collection_guid: str,
4820
+ body: dict | DeleteRequestBody = None):
5611
4821
  """ Detach an existing collection from an element. If the collection is anchored to the element,
5612
4822
  it is delete.
5613
4823
 
@@ -5650,7 +4860,7 @@ class CollectionManager(Client2):
5650
4860
 
5651
4861
 
5652
4862
  @dynamic_catch
5653
- async def _async_delete_collection(self, collection_guid: str, body: dict = None,
4863
+ async def _async_delete_collection(self, collection_guid: str, body: dict | DeleteRequestBody= None,
5654
4864
  cascade: bool = False) -> None:
5655
4865
  """Delete a collection. It is detected from all parent elements. If members are anchored to the collection
5656
4866
  then they are also deleted. Async version
@@ -5659,13 +4869,13 @@ class CollectionManager(Client2):
5659
4869
  Parameters
5660
4870
  ----------
5661
4871
  collection_guid: str
5662
- The guid of the collection to update.
4872
+ The guid of the collection to delete.
5663
4873
 
5664
4874
  cascade: bool, optional, defaults to True
5665
4875
  If true, a cascade delete is performed.
5666
4876
 
5667
- body: dict, optional, default = None
5668
- A dict representing the details of the relationship.
4877
+ body: dict | DeleteRequestBody, optional, default = None
4878
+ A structure representing the details of the relationship.
5669
4879
 
5670
4880
  Returns
5671
4881
  -------
@@ -5684,38 +4894,35 @@ class CollectionManager(Client2):
5684
4894
  _____
5685
4895
  JSON Structure looks like:
5686
4896
  {
5687
- "class" : "MetadataSourceRequestBody",
4897
+ "class": "DeleteRequestBody",
5688
4898
  "externalSourceGUID": "add guid here",
5689
4899
  "externalSourceName": "add qualified name here",
5690
- "effectiveTime" : "{{$isoTimestamp}}",
5691
- "forLineage" : false,
5692
- "forDuplicateProcessing" : false
4900
+ "effectiveTime": "{{$isoTimestamp}}",
4901
+ "forLineage": false,
4902
+ "forDuplicateProcessing": false
5693
4903
  }
5694
4904
 
5695
4905
 
5696
4906
  """
5697
- cascade_s = str(cascade).lower()
5698
- url = f"{self.collection_command_root}/{collection_guid}/delete?cascadedDelete={cascade_s}"
5699
- if body is None:
5700
- body = {"class": "NullRequestBody"}
5701
-
5702
- await self._async_make_request("POST", url, body)
4907
+ url = f"{self.collection_command_root}/{collection_guid}/delete"
4908
+ await self._async_delete_request(url, body, cascade)
5703
4909
  logger.info(f"Deleted collection {collection_guid} with cascade {cascade}")
5704
4910
 
5705
4911
 
5706
- def delete_collection(self, collection_guid: str, body: dict = None, cascade: bool = False) -> None:
5707
- """Delete a collection. It is detected from all parent elements. If members are anchored to the collection
4912
+ def delete_collection(self, collection_guid: str, body: dict | DeleteRequestBody = None,
4913
+ cascade: bool = False) -> None:
4914
+ """Delete a collection. It is deleted from all parent elements. If members are anchored to the collection
5708
4915
  then they are also deleted.
5709
4916
 
5710
4917
  Parameters
5711
4918
  ----------
5712
4919
  collection_guid: str
5713
- The guid of the collection to update.
4920
+ The guid of the collection to delete.
5714
4921
 
5715
4922
  cascade: bool, optional, defaults to True
5716
4923
  If true, a cascade delete is performed.
5717
4924
 
5718
- body: dict, optional, default = None
4925
+ body: dict DeleteRequestBodyt, optional, default = None
5719
4926
  A dict representing the details of the relationship.
5720
4927
 
5721
4928
  Returns
@@ -5734,14 +4941,7 @@ class CollectionManager(Client2):
5734
4941
  Notes
5735
4942
  _____
5736
4943
  JSON Structure looks like:
5737
- {
5738
- "class" : "MetadataSourceRequestBody",
5739
- "externalSourceGUID": "add guid here",
5740
- "externalSourceName": "add qualified name here",
5741
- "effectiveTime" : "{{$isoTimestamp}}",
5742
- "forLineage" : false,
5743
- "forDuplicateProcessing" : false
5744
- }
4944
+
5745
4945
 
5746
4946
 
5747
4947
  """
@@ -5750,7 +4950,8 @@ class CollectionManager(Client2):
5750
4950
 
5751
4951
 
5752
4952
  @dynamic_catch
5753
- async def _async_add_to_collection(self, collection_guid: str, element_guid: str, body: dict = None, ) -> None:
4953
+ async def _async_add_to_collection(self, collection_guid: str, element_guid: str,
4954
+ body: dict | NewRelationshipRequestBody = None, ) -> None:
5754
4955
  """Add an element to a collection. The request body is optional. Async version.
5755
4956
 
5756
4957
  Parameters
@@ -5759,12 +4960,9 @@ class CollectionManager(Client2):
5759
4960
  identity of the collection to return members for.
5760
4961
  element_guid: str
5761
4962
  Effective time of the query. If not specified will default to any time.
5762
- body: dict, optional, defaults to None
4963
+ body: dict NewRelationshipRequestBody, optional, defaults to None
5763
4964
  The body of the request to add to the collection. See notes.
5764
4965
 
5765
- The name of the server to use.
5766
-
5767
-
5768
4966
  Returns
5769
4967
  -------
5770
4968
  None
@@ -5782,7 +4980,7 @@ class CollectionManager(Client2):
5782
4980
  Notes
5783
4981
  -----
5784
4982
  Example body:
5785
- { "class": "RelationshipRequestBody",
4983
+ { "class": "NewRelationshipRequestBody",
5786
4984
  "properties" : {
5787
4985
  "class" : "CollectionMembershipProperties",
5788
4986
  "membershipRationale": "xxx",
@@ -5809,12 +5007,12 @@ class CollectionManager(Client2):
5809
5007
 
5810
5008
  url = (f"{self.collection_command_root}/{collection_guid}/members/"
5811
5009
  f"{element_guid}/attach")
5812
- body_s = body_slimmer(body)
5813
- await self._async_make_request("POST", url, body_s)
5010
+ await self._async_new_relationship_request(url, "CollectionMembershipProperties", body)
5814
5011
  logger.info(f"Added {element_guid} to {collection_guid}")
5815
5012
 
5816
5013
 
5817
- def add_to_collection(self, collection_guid: str, element_guid: str, body: dict = None, ) -> None:
5014
+ def add_to_collection(self, collection_guid: str, element_guid: str,
5015
+ body: dict | NewRelationshipRequestBody= None, ) -> None:
5818
5016
  """Add an element to a collection. The request body is optional.
5819
5017
 
5820
5018
  Parameters
@@ -5875,8 +5073,8 @@ class CollectionManager(Client2):
5875
5073
 
5876
5074
 
5877
5075
  @dynamic_catch
5878
- async def _async_update_collection_membership(self, collection_guid: str, element_guid: str, body: dict = None,
5879
- replace_all_props: bool = False, ) -> None:
5076
+ async def _async_update_collection_membership_prop(self, collection_guid: str, element_guid: str, body: dict = None,
5077
+ ) -> None:
5880
5078
  """Update an element's membership to a collection. Async version.
5881
5079
 
5882
5080
  Parameters
@@ -5934,16 +5132,15 @@ class CollectionManager(Client2):
5934
5132
  }
5935
5133
  """
5936
5134
 
5937
- replace_all_props_s = str(replace_all_props).lower()
5938
5135
  url = (f"{self.collection_command_root}/{collection_guid}/members/"
5939
- f"{element_guid}/update?replaceAllProperties={replace_all_props_s}")
5940
- body_s = body_slimmer(body)
5941
- await self._async_make_request("POST", url, body_s)
5136
+ f"{element_guid}/update")
5137
+ await self._async_update_relationship_request(url, "CollectionMembershipProperties", body )
5942
5138
  logger.info(f"Updated membership for collection {collection_guid}")
5943
5139
 
5944
5140
 
5945
- def update_collection_membership(self, collection_guid: str, element_guid: str, body: dict = None,
5946
- replace_all_props: bool = False, ) -> None:
5141
+ def update_collection_membership_prop(self, collection_guid: str, element_guid: str,
5142
+ body: dict | UpdateRelationshipRequestBody= None,
5143
+ ) -> None:
5947
5144
  """Update an element's membership to a collection.
5948
5145
 
5949
5146
  Parameters
@@ -6002,12 +5199,12 @@ class CollectionManager(Client2):
6002
5199
  """
6003
5200
  loop = asyncio.get_event_loop()
6004
5201
  loop.run_until_complete(
6005
- self._async_update_collection_membership(collection_guid, element_guid, body, replace_all_props))
5202
+ self._async_update_collection_membership(collection_guid, element_guid, body))
6006
5203
 
6007
5204
 
6008
5205
  @dynamic_catch
6009
5206
  async def _async_remove_from_collection(self, collection_guid: str, element_guid: str,
6010
- body: dict = None) -> None:
5207
+ body: dict | DeleteRequestBody = None) -> None:
6011
5208
  """Remove an element from a collection. Async version.
6012
5209
 
6013
5210
  Parameters
@@ -6016,7 +5213,7 @@ class CollectionManager(Client2):
6016
5213
  identity of the collection to return members for.
6017
5214
  element_guid: str
6018
5215
  Effective time of the query. If not specified will default to any time.
6019
- body: dict, optional, defaults to None
5216
+ body: dict | DeleteRequestBody, optional, defaults to None
6020
5217
  The body of the request to add to the collection. See notes.
6021
5218
 
6022
5219
  Returns
@@ -6036,7 +5233,7 @@ class CollectionManager(Client2):
6036
5233
  Notes
6037
5234
  -----
6038
5235
  {
6039
- "class" : "MetadataSourceRequestBody",
5236
+ "class" : "DeleteRequestBody",
6040
5237
  "externalSourceGUID": "add guid here",
6041
5238
  "externalSourceName": "add qualified name here",
6042
5239
  "effectiveTime" : "{{$isoTimestamp}}",
@@ -6048,13 +5245,12 @@ class CollectionManager(Client2):
6048
5245
 
6049
5246
  url = (f"{self.collection_command_root}/{collection_guid}/members/"
6050
5247
  f"{element_guid}/detach")
6051
- if body is None:
6052
- body = {"class": "NullRequestBody"}
6053
- await self._async_make_request("POST", url, body)
5248
+ await self._async_delete_collection(url, body)
6054
5249
  logger.info(f"Removed member {element_guid} from collection {collection_guid}")
6055
5250
 
6056
5251
 
6057
- def remove_from_collection(self, collection_guid: str, element_guid: str, body: dict = None) -> None:
5252
+ def remove_from_collection(self, collection_guid: str, element_guid: str,
5253
+ body: dict | DeleteRequestBody= None) -> None:
6058
5254
  """Remove an element from a collection. Async version.
6059
5255
 
6060
5256
  Parameters
@@ -6182,46 +5378,113 @@ class CollectionManager(Client2):
6182
5378
  self._async_get_member_list(collection_guid, collection_name, collection_qname))
6183
5379
  return resp
6184
5380
 
6185
-
6186
- def _extract_collection_properties(self, element: dict) -> dict:
5381
+ def _extract_digital_product_properties(self, element: dict, guid: str, output_format: str) -> dict:
5382
+ props = element["properties"]
5383
+ digital_prod = DigitalProductProperties.model_validate(props)
5384
+ added_props = get_defined_field_values(digital_prod)
5385
+
5386
+ used_by_digital_products = element.get("usedByDigitalProducts", "")
5387
+ if isinstance(used_by_digital_products, (list | dict)):
5388
+ used_by_digital_products_list = ""
5389
+ for prod in used_by_digital_products:
5390
+ used_by_digital_products_list += f"{prod["relatedElement"]["properties"]["qualifiedName"]}, "
5391
+ added_props["used_by_digital_products"] = used_by_digital_products_list[:-2]
5392
+
5393
+ uses_digital_products = element.get("usesDigitalProducts", "")
5394
+ if isinstance(uses_digital_products, (list | dict)):
5395
+ uses_digital_products_list = ""
5396
+ for prod in uses_digital_products:
5397
+ uses_digital_products_list += f"{prod["relatedElement"]["properties"]["qualifiedName"]}, "
5398
+ added_props["uses_digital_products"] = uses_digital_products_list[:-2]
5399
+
5400
+ return added_props
5401
+
5402
+ def _extract_agreement_properties(self, element: dict, guid: str, output_format: str) -> dict:
5403
+ props = element["properties"]
5404
+ # agreement = Collections.model_validate(props)
5405
+ # added_props = get_defined_field_values(agreement)
5406
+
5407
+ added_props = {}
5408
+ agreement_items = element.get("agreementItems", "")
5409
+ if isinstance(agreement_items, (list | dict)):
5410
+ agreement_items_list = ""
5411
+ for item in agreement_items:
5412
+ agreement_items_list += f"{item["relatedElement"]["properties"]["qualifiedName"]}, "
5413
+ added_props["agreementItems"] = agreement_items_list[:-2]
5414
+
5415
+ return added_props
5416
+
5417
+ def _extract_collection_properties(self, element: dict, columns_struct: dict) -> dict:
6187
5418
  """
6188
- Extract common properties from a collection element.
5419
+ Extract common properties from a collection element and populate into the provided columns_struct.
6189
5420
 
6190
5421
  Args:
6191
5422
  element (dict): The collection element
5423
+ columns_struct (dict): The columns structure to populate
6192
5424
 
6193
5425
  Returns:
6194
- dict: Dictionary of extracted properties
5426
+ dict: columns_struct with column 'value' fields populated
6195
5427
  """
6196
-
6197
- props = _extract_referenceable_properties(element)
5428
+ # First, populate from element.properties using the utility
5429
+ col_data = populate_columns_from_properties(element, columns_struct)
5430
+
5431
+ columns_list = col_data.get("formats", {}).get("columns", [])
5432
+
5433
+ # Populate header-derived values
5434
+ header_props = _extract_referenceable_properties(element)
5435
+ for column in columns_list:
5436
+ key = column.get('key')
5437
+ if key in header_props:
5438
+ column['value'] = header_props.get(key)
5439
+ elif isinstance(key, str) and key.lower() == 'guid':
5440
+ column['value'] = header_props.get('GUID')
5441
+
5442
+ # Derived/computed fields
5443
+ # collectionCategories are classifications
6198
5444
  classification_names = ""
6199
- # classifications = element['elementHeader'].get("classifications", [])
6200
- for classification in props['classifications']:
5445
+ classifications = element.get('elementHeader', {}).get("collectionCategories", [])
5446
+ for classification in classifications:
6201
5447
  classification_names += f"{classification['classificationName']}, "
6202
- props["classifications"] = classification_names[:-2] # why?
5448
+ if classification_names:
5449
+ for column in columns_list:
5450
+ if column.get('key') == 'classifications':
5451
+ column['value'] = classification_names[:-2]
5452
+ break
5453
+
5454
+ # Populate requested relationship-based columns generically from top-level keys
5455
+ col_data = get_required_relationships(element, col_data)
6203
5456
 
6204
- props['mermaid'] = element.get('mermaidGraph', "") or ""
5457
+ # Subject area classification
5458
+ subject_area = element.get('elementHeader', {}).get("subjectArea", "") or ""
5459
+ subj_val = ""
5460
+ if isinstance(subject_area, dict):
5461
+ subj_val = subject_area.get("classificationProperties", {}).get("subjectAreaName", "")
5462
+ for column in columns_list:
5463
+ if column.get('key') == 'subject_area':
5464
+ column['value'] = subj_val
5465
+ break
6205
5466
 
6206
- member_names = ""
6207
- members = self.get_member_list(collection_guid=props["GUID"])
6208
- if isinstance(members, list):
6209
- for member in members:
6210
- member_names += f"{member['qualifiedName']}, "
6211
- props['members'] = member_names[:-2]
6212
- logger.trace(f"Extracted properties: {props}")
6213
- return props
5467
+ # Mermaid graph
5468
+ mermaid_val = element.get('mermaidGraph', "") or ""
5469
+ for column in columns_list:
5470
+ if column.get('key') == 'mermaid':
5471
+ column['value'] = mermaid_val
5472
+ break
5473
+
5474
+ logger.trace(f"Extracted/Populated columns: {col_data}")
5475
+
5476
+ return col_data
6214
5477
 
6215
5478
 
6216
5479
  def _generate_collection_output(self, elements: dict|list[dict], filter: Optional[str],
6217
- classification_name: Optional[str], output_format: str = "DICT",
5480
+ element_type_name: Optional[str], output_format: str = "DICT",
6218
5481
  output_format_set: dict | str = None) -> str| list[dict]:
6219
5482
  """ Generate output for collections in the specified format.
6220
5483
 
6221
5484
  Args:
6222
5485
  elements (Union[Dict, List[Dict]]): Dictionary or list of dictionaries containing data field elements
6223
5486
  filter (Optional[str]): The search string used to find the elements
6224
- classification_name (Optional[str]): The type of collection
5487
+ element_type_name (Optional[str]): The type of collection
6225
5488
  output_format (str): The desired output format (MD, FORM, REPORT, LIST, DICT, MERMAID, HTML)
6226
5489
  output_format_set (Optional[dict], optional): List of dictionaries containing column data. Defaults
6227
5490
  to None.
@@ -6229,22 +5492,33 @@ class CollectionManager(Client2):
6229
5492
  Returns:
6230
5493
  Union[str, List[Dict]]: Formatted output as a string or list of dictionaries
6231
5494
  """
6232
- if classification_name is None:
5495
+ if element_type_name is None:
6233
5496
  entity_type = "Collections"
6234
5497
  else:
6235
- entity_type = classification_name
5498
+ entity_type = element_type_name
6236
5499
  # First see if the user has specified an output_format_set - either a label or a dict
5500
+ get_additional_props_func = None
6237
5501
  if output_format_set:
6238
5502
  if isinstance(output_format_set, str):
6239
5503
  output_formats = select_output_format_set(output_format_set, output_format)
6240
- if isinstance(output_format_set, dict):
5504
+ elif isinstance(output_format_set, dict):
6241
5505
  output_formats = get_output_format_type_match(output_format_set, output_format)
6242
- # If no output_format was set, then use the classification_name to lookup the output format
6243
- elif classification_name:
6244
- output_formats = select_output_format_set(classification_name, output_format)
5506
+
5507
+ # If no output_format was set, then use the element_type_name to lookup the output format
5508
+ elif element_type_name:
5509
+ output_formats = select_output_format_set(element_type_name, output_format)
6245
5510
  else:
6246
5511
  # fallback to collections or entity type
6247
5512
  output_formats = select_output_format_set(entity_type,output_format)
5513
+ if output_formats is None:
5514
+ output_formats = select_output_format_set("Default", output_format)
5515
+
5516
+ if output_formats:
5517
+ get_additional_props_name = output_formats.get("get_additional_props", {}).get("function", None)
5518
+ if isinstance(get_additional_props_name, str):
5519
+ class_name, method_name = get_additional_props_name.split(".")
5520
+ if hasattr(self, method_name):
5521
+ get_additional_props_func = getattr(self, method_name)
6248
5522
 
6249
5523
  logger.trace(f"Executing generate_collection_output for {entity_type}: {output_formats}")
6250
5524
  return generate_output(
@@ -6253,7 +5527,7 @@ class CollectionManager(Client2):
6253
5527
  entity_type,
6254
5528
  output_format,
6255
5529
  self._extract_collection_properties,
6256
- None,
5530
+ get_additional_props_func,
6257
5531
  output_formats,
6258
5532
  )
6259
5533