pyegeria 5.4.0.25__py3-none-any.whl → 5.4.0.26__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.
@@ -8,33 +8,29 @@ Copyright Contributors to the ODPi Egeria project.
8
8
  """
9
9
 
10
10
  import asyncio
11
- import inspect
12
- from datetime import datetime
13
- from typing import Optional, Annotated, Literal, Union
14
11
 
15
12
  from loguru import logger
16
- from pydantic import ValidationError, Field, HttpUrl
17
13
 
18
- from pyegeria._exceptions_new import PyegeriaInvalidParameterException
19
- from pyegeria._globals import NO_ELEMENTS_FOUND, NO_GUID_RETURNED, NO_MEMBERS_FOUND
20
- from pyegeria._output_formats import select_output_format_set, get_output_format_type_match
14
+ from pyegeria._client_new import Client2
21
15
  from pyegeria.load_config import get_app_config
22
16
  from pyegeria.models import (SearchStringRequestBody, FilterRequestBody, GetRequestBody, NewElementRequestBody,
23
- ReferenceableProperties, InitialClassifications, TemplateRequestBody,
24
- UpdateElementRequestBody, UpdateStatusRequestBody, NewRelationshipRequestBody,
25
- DeleteRequestBody, UpdateRelationshipRequestBody, ResultsRequestBody,
26
- get_defined_field_values, PyegeriaModel)
17
+ TemplateRequestBody, UpdateElementRequestBody, DeleteRequestBody,
18
+ NewRelationshipRequestBody)
19
+ from pyegeria.utils import body_slimmer, dynamic_catch
20
+ from pyegeria._output_formats import select_output_format_set, get_output_format_type_match
27
21
  from pyegeria.output_formatter import (generate_output,
28
- _extract_referenceable_properties, populate_columns_from_properties,
22
+ _extract_referenceable_properties,
23
+ populate_columns_from_properties,
29
24
  get_required_relationships)
30
- from pyegeria._client_new import Client2
31
- from pyegeria.utils import body_slimmer, dynamic_catch
32
-
33
25
 
34
26
  app_settings = get_app_config()
35
27
  EGERIA_LOCAL_QUALIFIER = app_settings.User_Profile.egeria_local_qualifier
36
28
  from pyegeria._globals import NO_ELEMENTS_FOUND, NO_PROJECTS_FOUND
37
29
 
30
+ PROJECT_TYPES = ["Project", "Campaign", "StudyProject", "Task", "PersonalProject"]
31
+ PROJECT_PROPERTIES_LIST = ["ProjectProperties", "CampaignProperties", "StudyProjectProperties", "TaskProperties",
32
+ "PersonalProjectProperties"]
33
+
38
34
  class ProjectManager(Client2):
39
35
  """
40
36
  Create and manage projects. Projects may be organized in a hierarchy.
@@ -54,33 +50,111 @@ class ProjectManager(Client2):
54
50
  """
55
51
 
56
52
  def __init__(
57
- self,
58
- view_server: str,
59
- platform_url: str,
60
- user_id: str,
61
- user_pwd: str = None,
62
- token: str = None,
63
- ):
53
+ self,
54
+ view_server: str,
55
+ platform_url: str,
56
+ user_id: str,
57
+ user_pwd: str = None,
58
+ token: str = None,
59
+ ):
64
60
  self.view_server = view_server
65
61
  self.platform_url = platform_url
66
62
  self.user_id = user_id
67
63
  self.user_pwd = user_pwd
68
64
  self.project_command_base: str = (
69
- f"/api/open-metadata/project-manager/metadata-elements"
65
+ f"/api/open-metadata/project-manager/projects"
70
66
  )
71
- Client.__init__(self, view_server, platform_url, user_id, user_pwd, token)
67
+ Client2.__init__(self, view_server, platform_url, user_id, user_pwd, token)
72
68
 
69
+
70
+ def _extract_project_properties(self, element: dict, columns_struct: dict) -> dict:
71
+ """
72
+ Populate columns_struct values for a Project element using the standardized approach from collection_manager.
73
+ - Populate from element.properties
74
+ - Overlay header-derived values
75
+ - Derive classifications list
76
+ - Populate relationship-based columns generically
77
+ - Include mermaid graph if requested
78
+ """
79
+ col_data = populate_columns_from_properties(element, columns_struct)
80
+ columns_list = col_data.get("formats", {}).get("columns", [])
81
+
82
+ # Header-derived values
83
+ header_props = _extract_referenceable_properties(element)
84
+ for column in columns_list:
85
+ key = column.get('key')
86
+ if key in header_props:
87
+ column['value'] = header_props.get(key)
88
+ elif isinstance(key, str) and key.lower() == 'guid':
89
+ column['value'] = header_props.get('GUID')
90
+
91
+ # Classifications
92
+ cl_names = []
93
+ for c in element.get('elementHeader', {}).get('classifications', []) or []:
94
+ nm = c.get('classificationName')
95
+ if nm:
96
+ cl_names.append(nm)
97
+ if cl_names:
98
+ for column in columns_list:
99
+ if column.get('key') == 'classifications':
100
+ column['value'] = ", ".join(cl_names)
101
+ break
102
+
103
+ # Relationships
104
+ col_data = get_required_relationships(element, col_data)
105
+
106
+ # Mermaid
107
+ mermaid_val = element.get('mermaidGraph', "") or ""
108
+ for column in columns_list:
109
+ if column.get('key') == 'mermaid':
110
+ column['value'] = mermaid_val
111
+ break
112
+
113
+ return col_data
114
+
115
+ def _generate_project_output(self, elements: dict | list[dict], filter: str = None,
116
+ element_type_name: str | None = None,
117
+ output_format: str = 'DICT',
118
+ output_format_set: str | dict = None) -> str | list[dict]:
119
+ """
120
+ Generate output for projects using selectable output format sets.
121
+ """
122
+ entity_type = element_type_name or 'Project'
123
+
124
+ # Resolve output formats
125
+ if output_format_set:
126
+ if isinstance(output_format_set, str):
127
+ output_formats = select_output_format_set(output_format_set, output_format)
128
+ elif isinstance(output_format_set, dict):
129
+ output_formats = get_output_format_type_match(output_format_set, output_format)
130
+ elif element_type_name:
131
+ output_formats = select_output_format_set(element_type_name, output_format)
132
+ else:
133
+ output_formats = select_output_format_set(entity_type, output_format)
134
+ if output_formats is None:
135
+ output_formats = select_output_format_set('Default', output_format)
136
+
137
+ return generate_output(
138
+ elements,
139
+ filter,
140
+ entity_type,
141
+ output_format,
142
+ self._extract_project_properties,
143
+ None,
144
+ output_formats,
145
+ )
73
146
  #
74
147
  # Retrieving Projects= Information - https://egeria-project.org/concepts/project
75
148
  #
76
149
  async def _async_get_linked_projects(
77
- self,
78
- parent_guid: str,
79
- project_status: str = None,
80
- effective_time: str = None,
81
- start_from: int = 0,
82
- page_size: int = None,
83
- ) -> list | str:
150
+ self,
151
+ parent_guid: str,
152
+ filter_string: str = None,
153
+ classification_names: list[str] = None,
154
+ body: dict | FilterRequestBody = None,
155
+ start_from: int = 0, page_size: int = None,
156
+ output_format: str = "json", output_format_set: str | dict = None,
157
+ ) -> list | str:
84
158
  """Returns the list of projects that are linked off of the supplied element. Any relationship will do.
85
159
  The request body is optional, but if supplied acts as a filter on project status. Async version.
86
160
 
@@ -116,83 +190,52 @@ class ProjectManager(Client2):
116
190
 
117
191
  """
118
192
 
119
- if page_size is None:
120
- page_size = self.page_size
121
-
122
- body = {
123
- "filter": project_status,
124
- "effectiveTime": effective_time,
125
- }
126
- body_s = body_slimmer(body)
127
193
  url = (
128
194
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/"
129
- f"metadata-elements/{parent_guid}/projects?startFrom={start_from}&pageSize={page_size}"
195
+ f"metadata-elements/{parent_guid}/projects"
130
196
  )
131
197
 
132
- resp = await self._async_make_request("POST", url, body_s)
133
- return resp.json().get("elements", "No linked projects found")
198
+ response = await self._async_get_name_request(url, _type="Project",
199
+ _gen_output=self._generate_project_output,
200
+ filter_string=filter_string,
201
+ classification_names=classification_names,
202
+ start_from=start_from, page_size=page_size,
203
+ output_format=output_format, output_format_set=output_format_set,
204
+ body=body)
205
+
206
+ return response
134
207
 
135
208
  def get_linked_projects(
136
- self,
137
- parent_guid: str,
138
- project_status: str = None,
139
- effective_time: str = None,
140
- start_from: int = 0,
141
- page_size: int = None,
142
- ) -> list | str:
143
- """Returns the list of projects that are linked off of the supplied element. Any relationship will do.
144
- The request body is optional, but if supplied acts as a filter on project status.
209
+ self,
210
+ parent_guid: str,
211
+ filter_string: str = None,
212
+ classification_names: list[str] = None,
213
+ body: dict | FilterRequestBody = None,
214
+ start_from: int = 0, page_size: int = None,
215
+ output_format: str = "json", output_format_set: str | dict = None,
216
+ ) -> list | str:
217
+ loop = asyncio.get_event_loop()
218
+ resp = loop.run_until_complete(
219
+ self._async_get_linked_projects(parent_guid, filter_string, classification_names,
220
+ body, start_from,page_size,output_format, output_format_set)
221
+ )
145
222
 
146
- Parameters
147
- ----------
148
- parent_guid: str
149
- The identity of the parent to find linked projects from
150
- project_status: str, optional
151
- Optionally, filter results by project status.
152
- effective_time: str, optional
153
- Time at which to query for projects. Time format is "YYYY-MM-DDTHH:MM:SS" (ISO 8601).
223
+ return resp
154
224
 
155
- start_from: int, [default=0], optional
156
- When multiple pages of results are available, the page number to start from.
157
- page_size: int, [default=None]
158
- The number of items to return in a single page. If not specified, the default will be taken from
159
- the class instance.
160
- Returns
161
- -------
162
- List | str
163
225
 
164
- A list of projects linked off of the supplied element filtered by project status and effective time.
165
226
 
166
- Raises
167
- ------
168
227
 
169
- InvalidParameterException
170
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
171
- PropertyServerException
172
- Raised by the server when an issue arises in processing a valid request
173
- NotAuthorizedException
174
- The principle specified by the user_id does not have authorization for the requested action
175
228
 
176
- """
177
- loop = asyncio.get_event_loop()
178
- resp = loop.run_until_complete(
179
- self._async_get_linked_projects(
180
- parent_guid,
181
- project_status,
182
- effective_time,
183
- start_from,
184
- page_size,
185
- )
186
- )
187
- return resp
188
229
 
189
230
  async def _async_get_classified_projects(
190
- self,
191
- project_classification: str,
192
- effective_time: str = None,
193
- start_from: int = 0,
194
- page_size: int = None,
195
- ) -> list | str:
231
+ self,
232
+ project_classification: str,
233
+ classification_names: list[str] = None,
234
+ body: dict | FilterRequestBody = None,
235
+ start_from: int = 0, page_size: int = 0,
236
+ output_format: str = "json", output_format_set: str | dict = None,
237
+
238
+ ) -> list | str:
196
239
  """Returns the list of projects with a particular classification. The name of the classification is
197
240
  supplied in the request body. Examples of these classifications include StudyProject, PersonalProject,
198
241
  Campaign or Task. There is also GlossaryProject and GovernanceProject. Async version.
@@ -227,29 +270,31 @@ class ProjectManager(Client2):
227
270
 
228
271
  """
229
272
 
230
- if page_size is None:
231
- page_size = self.page_size
232
273
 
233
- body = {
234
- "filter": project_classification,
235
- "effectiveTime": effective_time,
236
- }
237
- body_s = body_slimmer(body)
238
274
  url = (
239
275
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/"
240
- f"projects/by-classifications?startFrom={start_from}&pageSize={page_size}"
276
+ f"projects/by-classifications"
241
277
  )
278
+ response = await self._async_get_name_request(url, _type="Project",
279
+ _gen_output=self._generate_project_output,
280
+ filter_string=project_classification,
281
+ classification_names=classification_names,
282
+ start_from=start_from, page_size=page_size,
283
+ output_format=output_format, output_format_set=output_format_set,
284
+ body=body)
285
+
286
+ return response
242
287
 
243
- resp = await self._async_make_request("POST", url, body_s)
244
- return resp.json()
245
288
 
246
289
  def get_classified_projects(
247
- self,
248
- project_classification: str,
249
- effective_time: str = None,
250
- start_from: int = 0,
251
- page_size: int = None,
252
- ) -> list | str:
290
+ self,
291
+ project_classification: str,
292
+ classification_names: list[str] = None,
293
+ body: dict | FilterRequestBody = None,
294
+ start_from: int = 0, page_size: int = 0,
295
+ output_format: str = "json", output_format_set: str | dict = None,
296
+
297
+ ) -> list | str:
253
298
  """Returns the list of projects with a particular classification. The name of the classification is
254
299
  supplied in the request body. Examples of these classifications include StudyProject, PersonalProject,
255
300
  Campaign or Task. There is also GlossaryProject and GovernanceProject.
@@ -287,21 +332,24 @@ class ProjectManager(Client2):
287
332
  resp = loop.run_until_complete(
288
333
  self._async_get_classified_projects(
289
334
  project_classification,
290
- effective_time,
291
- start_from,
335
+ classification_names,
336
+ body, start_from,
292
337
  page_size,
338
+ output_format,
339
+ output_format_set,
340
+ )
293
341
  )
294
- )
295
342
  return resp
296
343
 
297
344
  async def _async_get_project_team(
298
- self,
299
- project_guid: str,
300
- team_role: str = None,
301
- effective_time: str = None,
302
- start_from: int = 0,
303
- page_size: int = None,
304
- ) -> list | str:
345
+ self,
346
+ project_guid: str,
347
+ team_role: str = None,
348
+ classification_names: list[str] = None,
349
+ body: dict | FilterRequestBody = None,
350
+ start_from: int = 0, page_size: int = None,
351
+ output_format: str = "json", output_format_set: str | dict = None,
352
+ ) -> list | str:
305
353
  """Returns the list of actors that are linked off of the project. This includes the project managers.
306
354
  The optional request body allows a teamRole to be specified as a filter. To filter out the project managers,
307
355
  specify ProjectManagement as the team role. See https://egeria-project.org/concepts/project for details.
@@ -339,29 +387,30 @@ class ProjectManager(Client2):
339
387
  -----
340
388
  """
341
389
 
342
- if page_size is None:
343
- page_size = self.page_size
344
390
 
345
- body = {effective_time: effective_time, "filter": team_role}
346
- body_s = body_slimmer(body)
347
391
  url = (
348
392
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/"
349
- f"{project_guid}/team?startFrom={start_from}&pageSize={page_size}"
393
+ f"{project_guid}/team"
350
394
  )
395
+ response = await self._async_get_name_request(url, _type="Project",
396
+ _gen_output=self._generate_project_output,
397
+ filter_string=team_role,
398
+ classification_names=classification_names,
399
+ start_from=start_from, page_size=page_size,
400
+ output_format=output_format, output_format_set=output_format_set,
401
+ body=body)
351
402
 
352
- resp = await self._async_make_request("POST", url, body_s)
353
-
354
- result = resp.json().get("elements", NO_ELEMENTS_FOUND)
355
- return result
403
+ return response
356
404
 
357
405
  def get_project_team(
358
- self,
359
- project_guid: str,
360
- team_role: str = None,
361
- effective_time: str = None,
362
- start_from: int = 0,
363
- page_size: int = None,
364
- ) -> list | str:
406
+ self,
407
+ project_guid: str,
408
+ team_role: str = None,
409
+ classification_names: list[str] = None,
410
+ body: dict | FilterRequestBody = None,
411
+ start_from: int = 0, page_size: int = None,
412
+ output_format: str = "json", output_format_set: str | dict = None,
413
+ ) -> list | str:
365
414
  """Returns the list of actors that are linked off of the project. This includes the project managers.
366
415
  The optional request body allows a teamRole to be specified as a filter. To filter out the project managers,
367
416
  specify ProjectManagement as the team role. See https://egeria-project.org/concepts/project for details.
@@ -403,23 +452,27 @@ class ProjectManager(Client2):
403
452
  self._async_get_project_team(
404
453
  project_guid,
405
454
  team_role,
406
- effective_time,
407
- start_from,
455
+ classification_names,
456
+ body, start_from,
408
457
  page_size,
458
+ output_format,
459
+ output_format_set,
460
+ )
409
461
  )
410
- )
411
462
  return resp
412
463
 
464
+ @dynamic_catch
413
465
  async def _async_find_projects(
414
- self,
415
- search_string: str,
416
- effective_time: str = None,
417
- starts_with: bool = False,
418
- ends_with: bool = False,
419
- ignore_case: bool = False,
420
- start_from: int = 0,
421
- page_size: int = None,
422
- ) -> list | str:
466
+ self,
467
+ search_string: str, classification_names: list[str] = None, metadata_element_types: list[str] = None,
468
+ starts_with: bool = False,
469
+ ends_with: bool = False,
470
+ ignore_case: bool = False,
471
+ start_from: int = 0,
472
+ page_size: int = 0,
473
+ output_format: str = "json", output_format_set: str | dict = None,
474
+ body: dict | SearchStringRequestBody = None
475
+ ) -> list | str:
423
476
  """Returns the list of projects matching the search string.
424
477
  The search string is located in the request body and is interpreted as a plain string.
425
478
  The request parameters, startsWith, endsWith and ignoreCase can be used to allow a fuzzy search.
@@ -461,41 +514,36 @@ class ProjectManager(Client2):
461
514
 
462
515
  """
463
516
 
464
- if page_size is None:
465
- page_size = self.page_size
466
- starts_with_s = str(starts_with).lower()
467
- ends_with_s = str(ends_with).lower()
468
- ignore_case_s = str(ignore_case).lower()
469
-
470
- validate_search_string(search_string)
471
-
472
- if search_string == "*":
473
- search_string = None
474
-
475
- body = {
476
- "filter": search_string,
477
- "effective_time": effective_time,
478
- }
479
- body_s = body_slimmer(body)
480
517
  url = (
481
518
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/"
482
- f"by-search-string?startFrom={start_from}&pageSize={page_size}&startsWith={starts_with_s}&"
483
- f"endsWith={ends_with_s}&ignoreCase={ignore_case_s}"
519
+ f"by-search-string"
484
520
  )
485
521
 
486
- resp = await self._async_make_request("POST", url, body_s)
487
- return resp.json().get("elements", NO_ELEMENTS_FOUND)
522
+ response = await self._async_find_request(url, _type="Project",
523
+ _gen_output=self._generate_project_output,
524
+ search_string=search_string,
525
+ classification_names=classification_names,
526
+ metadata_element_types=metadata_element_types,
527
+ starts_with=starts_with, ends_with=ends_with, ignore_case=ignore_case,
528
+ start_from=start_from, page_size=page_size,
529
+ output_format=output_format, output_format_set=output_format_set,
530
+ body=body)
531
+
532
+ return response
488
533
 
534
+ @dynamic_catch
489
535
  def find_projects(
490
- self,
491
- search_string: str,
492
- effective_time: str = None,
493
- starts_with: bool = False,
494
- ends_with: bool = False,
495
- ignore_case: bool = False,
496
- start_from: int = 0,
497
- page_size: int = None,
498
- ) -> list | str:
536
+ self,
537
+ search_string: str, classification_names: list[str] = None, metadata_element_types: list[str] = None,
538
+ starts_with: bool = False,
539
+ ends_with: bool = False,
540
+ ignore_case: bool = False,
541
+ start_from: int = 0,
542
+ page_size: int = 0,
543
+ output_format: str = "json", output_format_set: str | dict = None,
544
+ body: dict | SearchStringRequestBody = None
545
+ ) -> list | str:
546
+
499
547
  """Returns the list of projects matching the search string.
500
548
  The search string is located in the request body and is interpreted as a plain string.
501
549
  The request parameters, startsWith, endsWith and ignoreCase can be used to allow a fuzzy search.
@@ -539,293 +587,288 @@ class ProjectManager(Client2):
539
587
  resp = loop.run_until_complete(
540
588
  self._async_find_projects(
541
589
  search_string,
542
- effective_time,
590
+ classification_names,
591
+ metadata_element_types,
543
592
  starts_with,
544
593
  ends_with,
545
594
  ignore_case,
546
595
  start_from,
547
596
  page_size,
597
+ output_format,
598
+ output_format_set,
599
+ body,
600
+ )
548
601
  )
549
- )
550
602
 
551
603
  return resp
552
604
 
605
+ @dynamic_catch
553
606
  async def _async_get_projects_by_name(
554
- self,
555
- name: str,
556
- effective_time: str = None,
557
- start_from: int = 0,
558
- page_size: int = None,
559
- ) -> list | str:
560
- """Returns the list of projects with a particular name. Async version.
561
-
562
- Parameters
563
- ----------
564
- name: str,
565
- name to use to find matching collections.
566
- effective_time: str, [default=None], optional
567
- Effective time of the query. If not specified will default to any time. ISO 8601 format.
568
-
569
- start_from: int, [default=0], optional
570
- When multiple pages of results are available, the page number to start from.
571
- page_size: int, [default=None]
572
- The number of items to return in a single page. If not specified, the default will be taken from
573
- the class instance.
574
- Returns
575
- -------
576
- List | str
577
-
578
- A list of collections match matching the name. Returns a string if none found.
579
-
580
- Raises
581
- ------
582
-
583
- InvalidParameterException
584
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
585
- PropertyServerException
586
- Raised by the server when an issue arises in processing a valid request
587
- NotAuthorizedException
588
- The principle specified by the user_id does not have authorization for the requested action
589
-
590
- """
591
-
592
- if page_size is None:
593
- page_size = self.page_size
594
-
595
- validate_search_string(name)
596
-
597
- body = {
598
- "filter": name,
599
- "effective_time": effective_time,
600
- }
601
- body_s = body_slimmer(body)
602
- url = (
603
- f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/"
604
- f"by-name?startFrom={start_from}&pageSize={page_size}"
605
- )
606
-
607
- resp = await self._async_make_request("POST", url, body_s)
608
- return resp.json().get("elements", NO_PROJECTS_FOUND)
609
-
607
+ self, filter_string: str = None, classification_names: list[str] = None,
608
+ body: dict | FilterRequestBody = None,
609
+ start_from: int = 0, page_size: int = 0,
610
+ output_format: str = 'JSON',
611
+ output_format_set: str | dict = None) -> list | str:
612
+ url = f"{self.project_command_base}/by-name"
613
+
614
+ response = await self._async_get_name_request(url, _type="Project",
615
+ _gen_output=self._generate_project_output,
616
+ filter_string=filter_string,
617
+ classification_names=classification_names,
618
+ start_from=start_from, page_size=page_size,
619
+ output_format=output_format, output_format_set=output_format_set,
620
+ body=body)
621
+
622
+ return response
623
+
624
+ @dynamic_catch
610
625
  def get_projects_by_name(
611
- self,
612
- name: str,
613
- effective_time: str = None,
614
- start_from: int = 0,
615
- page_size: int = None,
616
- ) -> list | str:
617
- """Returns the list of projects with a particular name.
618
-
619
- Parameters
620
- ----------
621
- name: str,
622
- name to use to find matching collections.
623
- effective_time: str, [default=None], optional
624
- Effective time of the query. If not specified will default to any time. ISO 8601 format.
626
+ self, filter_string: str = None, classification_names: list[str] = None,
627
+ body: dict | FilterRequestBody = None,
628
+ start_from: int = 0, page_size: int = 0,
629
+ output_format: str = 'JSON',
630
+ output_format_set: str | dict = None) -> list | str:
625
631
 
626
- start_from: int, [default=0], optional
627
- When multiple pages of results are available, the page number to start from.
628
- page_size: int, [default=None]
629
- The number of items to return in a single page. If not specified, the default will be taken from
630
- the class instance.
631
- Returns
632
- -------
633
- List | str
634
-
635
- A list of collections match matching the name. Returns a string if none found.
636
-
637
- Raises
638
- ------
639
-
640
- InvalidParameterException
641
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
642
- PropertyServerException
643
- Raised by the server when an issue arises in processing a valid request
644
- NotAuthorizedException
645
- The principle specified by the user_id does not have authorization for the requested action
646
-
647
- """
648
632
  loop = asyncio.get_event_loop()
649
633
  resp = loop.run_until_complete(
650
634
  self._async_get_projects_by_name(
651
- name, effective_time, start_from, page_size
635
+ filter_string,
636
+ classification_names,
637
+ body,
638
+ start_from,
639
+ page_size,
640
+ output_format,
641
+ output_format_set,
642
+ )
652
643
  )
653
- )
654
-
655
644
  return resp
656
645
 
657
- async def _async_get_project_by_guid(
658
- self,
659
- project_guid: str,
660
- effective_time: str = None,
661
- ) -> dict | str:
646
+ @dynamic_catch
647
+ async def _async_get_project_by_guid(self, project_guid: str, element_type: str = None,
648
+ body: dict | GetRequestBody = None,
649
+ output_format: str = 'JSON',
650
+ output_format_set: str | dict = None) -> dict | str:
662
651
  """Return the properties of a specific project. Async version.
663
652
 
664
- Parameters
665
- ----------
666
- project_guid: str,
667
- unique identifier of the project.
668
- effective_time: str, [default=None], optional
669
- Effective time of the query. If not specified will default to any time. Time in ISO8601 format is assumed.
670
-
671
-
672
- Returns
673
- -------
674
- dict | str
675
-
676
- A JSON dict representing the specified project. Returns a string if none found.
677
-
678
- Raises
679
- ------
680
-
681
- InvalidParameterException
682
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
683
- PropertyServerException
684
- Raised by the server when an issue arises in processing a valid request
685
- NotAuthorizedException
686
- The principle specified by the user_id does not have authorization for the requested action
653
+ Parameters
654
+ ----------
655
+ project_guid: str
656
+ Unique identifier of the project to retrieve.
657
+ element_type: str, optional, default = None
658
+ Metadata element type name to guide output formatting (defaults to "Project").
659
+ body: dict | GetRequestBody, optional, default = None
660
+ Full request body for advanced options (effective time, lineage, etc.).
661
+ output_format: str, optional, default = "JSON"
662
+ One of "DICT", "LIST", "MD", "FORM", "REPORT", "MERMAID" or "JSON".
663
+ output_format_set: str | dict, optional, default = None
664
+ The desired output columns/fields definition or a label referring to a predefined set.
665
+
666
+ Returns
667
+ -------
668
+ dict | str
669
+ JSON dict representing the specified project when output_format == "JSON";
670
+ otherwise a formatted string (or list) according to output_format.
671
+
672
+ Raises
673
+ ------
674
+ InvalidParameterException
675
+ If the client passes incorrect parameters on the request (bad URLs/values)
676
+ PropertyServerException
677
+ Raised by the server when an issue arises in processing a valid request
678
+ NotAuthorizedException
679
+ The requesting user is not authorized to issue this request
680
+
681
+ Notes
682
+ -----
683
+ Body sample:
684
+ {
685
+ "class": "GetRequestBody",
686
+ "asOfTime": "{{$isoTimestamp}}",
687
+ "effectiveTime": "{{$isoTimestamp}}",
688
+ "forLineage": false,
689
+ "forDuplicateProcessing": false
690
+ }
691
+ """
687
692
 
688
- """
693
+ url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/"
694
+ f"{project_guid}")
695
+ type = element_type if element_type else "Project"
689
696
 
690
- validate_guid(project_guid)
691
- body = {
692
- "effective_time": effective_time,
693
- }
694
- url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/{project_guid}"
697
+ response = await self._async_get_guid_request(url, _type=type,
698
+ _gen_output=self._generate_project_output,
699
+ output_format=output_format, output_format_set=output_format_set,
700
+ body=body)
695
701
 
696
- resp = await self._async_make_request("GET", url, body)
697
- return resp.json().get('element',NO_ELEMENTS_FOUND)
702
+ return response
698
703
 
699
- def get_project_by_guid(self, project_guid: str, effective_time: str = None) -> dict | str:
704
+ @dynamic_catch
705
+ def get_project_by_guid(self, project_guid: str, element_type: str = None,
706
+ body: dict | GetRequestBody = None,
707
+ output_format: str = 'JSON',
708
+ output_format_set: str | dict = None) -> dict | str:
700
709
  """Return the properties of a specific project.
701
710
 
702
- Parameters
703
- ----------
704
- project_guid: str,
705
- unique identifier of the project.
706
- effective_time: str, [default=None], optional
707
- Effective time of the query. If not specified will default to any time. Time in ISO8601 format is assumed.
708
-
709
-
710
- Returns
711
- -------
712
- dict | str
713
-
714
- A JSON dict representing the specified project. Returns a string if none found.
715
-
716
- Raises
717
- ------
718
-
719
- InvalidParameterException
720
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
721
- PropertyServerException
722
- Raised by the server when an issue arises in processing a valid request
723
- NotAuthorizedException
724
- The principle specified by the user_id does not have authorization for the requested action
725
-
726
- """
711
+ Parameters
712
+ ----------
713
+ project_guid: str
714
+ Unique identifier of the project to retrieve.
715
+ element_type: str, optional, default = None
716
+ Metadata element type name to guide output formatting (defaults to "Project").
717
+ body: dict | GetRequestBody, optional, default = None
718
+ Full request body for advanced options (effective time, lineage, etc.).
719
+ output_format: str, optional, default = "JSON"
720
+ One of "DICT", "LIST", "MD", "FORM", "REPORT", "MERMAID" or "JSON".
721
+ output_format_set: str | dict, optional, default = None
722
+ The desired output columns/fields definition or a label referring to a predefined set.
723
+
724
+ Returns
725
+ -------
726
+ dict | str
727
+ JSON dict representing the specified project when output_format == "JSON";
728
+ otherwise a formatted string (or list) according to output_format.
729
+
730
+ Raises
731
+ ------
732
+ InvalidParameterException
733
+ If the client passes incorrect parameters on the request (bad URLs/values)
734
+ PropertyServerException
735
+ Raised by the server when an issue arises in processing a valid request
736
+ NotAuthorizedException
737
+ The requesting user is not authorized to issue this request
738
+
739
+ Notes
740
+ -----
741
+ Body sample:
742
+ {
743
+ "class": "GetRequestBody",
744
+ "asOfTime": "{{$isoTimestamp}}",
745
+ "effectiveTime": "{{$isoTimestamp}}",
746
+ "forLineage": false,
747
+ "forDuplicateProcessing": false
748
+ }
749
+ """
727
750
  loop = asyncio.get_event_loop()
728
751
  resp = loop.run_until_complete(
729
- self._async_get_project_by_guid(project_guid, effective_time)
730
- )
752
+ self._async_get_project_by_guid(project_guid, element_type, body, output_format, output_format_set )
753
+ )
731
754
 
732
755
  return resp
733
756
 
757
+ @dynamic_catch
734
758
  async def _async_get_project_graph(
735
- self,
736
- project_guid: str,
737
- effective_time: str = None,
738
- ) -> dict | str:
739
- """Return the mermaid graph of a specific project. Async version.
759
+ self,
760
+ project_guid: str,
761
+ element_type: str = None,
762
+ body: dict | GetRequestBody = None,
763
+ output_format: str = 'JSON',
764
+ output_format_set: str | dict = None,
765
+ ) -> dict | str:
766
+ """Return the mermaid graph or formatted details of a specific project. Async version.
740
767
 
741
768
  Parameters
742
769
  ----------
743
- project_guid: str,
744
- unique identifier of the project.
745
- effective_time: str, [default=None], optional
746
- Effective time of the query. If not specified will default to any time. Time in ISO8601 format is assumed.
747
-
770
+ project_guid: str
771
+ Unique identifier of the project.
772
+ element_type: str, optional, default = None
773
+ Metadata element type name to guide output formatting (defaults to "Project").
774
+ body: dict | GetRequestBody, optional, default = None
775
+ Full request body for advanced options (effective time, lineage, etc.).
776
+ output_format: str, optional, default = "JSON"
777
+ One of "DICT", "LIST", "MD", "FORM", "REPORT", "MERMAID" or "JSON".
778
+ output_format_set: str | dict, optional, default = None
779
+ The desired output columns/fields definition or a label referring to a predefined set.
748
780
 
749
781
  Returns
750
782
  -------
751
- str
783
+ dict | str
784
+ JSON dict when output_format == "JSON"; otherwise a formatted string according to output_format
785
+ (for example, a Mermaid markdown string when output_format == "MERMAID").
752
786
 
753
- A mermaid markdown string representing the graph of the project.
754
787
  Raises
755
788
  ------
756
-
757
789
  InvalidParameterException
758
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
790
+ If the client passes incorrect parameters on the request (bad URLs/values)
759
791
  PropertyServerException
760
- Raised by the server when an issue arises in processing a valid request
792
+ Raised by the server when an issue arises in processing a valid request
761
793
  NotAuthorizedException
762
- The principle specified by the user_id does not have authorization for the requested action
794
+ The requesting user is not authorized to issue this request
763
795
 
764
796
  """
765
797
 
766
- validate_guid(project_guid)
767
- body = {
768
- "effective_time": effective_time,
769
- }
770
- url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/{project_guid}/graph"
771
798
 
772
- resp = await self._async_make_request("GET", url, body)
773
- return resp.json().get('element',NO_ELEMENTS_FOUND)
799
+ url = (f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/pr"
800
+ f"ojects/{project_guid}/graph")
801
+
802
+ response = await self._async_get_guid_request(url, _type=element_type or 'Project',
803
+ _gen_output=self._generate_project_output,
804
+ output_format=output_format, output_format_set=output_format_set,
805
+ body=body)
774
806
 
775
- def get_project_graph(self, project_guid: str, effective_time: str = None) -> dict | str:
776
- """Return the mermaid graph of a specific project.
807
+ return response
808
+
809
+ @dynamic_catch
810
+ def get_project_graph(
811
+ self,
812
+ project_guid: str,
813
+ element_type: str = None,
814
+ body: dict | GetRequestBody = None,
815
+ output_format: str = 'JSON',
816
+ output_format_set: str | dict = None,
817
+ ) -> dict | str:
818
+ """Return the mermaid graph or formatted details of a specific project.
777
819
 
778
820
  Parameters
779
821
  ----------
780
- project_guid: str,
781
- unique identifier of the project.
782
- effective_time: str, [default=None], optional
783
- Effective time of the query. If not specified will default to any time. Time in ISO8601 format is assumed.
784
-
822
+ project_guid: str
823
+ Unique identifier of the project.
824
+ element_type: str, optional, default = None
825
+ Metadata element type name to guide output formatting (defaults to "Project").
826
+ body: dict | GetRequestBody, optional, default = None
827
+ Full request body for advanced options (effective time, lineage, etc.).
828
+ output_format: str, optional, default = "JSON"
829
+ One of "DICT", "LIST", "MD", "FORM", "REPORT", "MERMAID" or "JSON".
830
+ output_format_set: str | dict, optional, default = None
831
+ The desired output columns/fields definition or a label referring to a predefined set.
785
832
 
786
833
  Returns
787
834
  -------
788
- str
835
+ dict | str
836
+ JSON dict when output_format == "JSON"; otherwise a formatted string according to output_format
837
+ (for example, a Mermaid markdown string when output_format == "MERMAID").
789
838
 
790
- A mermaid markdown string representing the graph of the project.
791
839
  Raises
792
840
  ------
793
-
794
841
  InvalidParameterException
795
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
842
+ If the client passes incorrect parameters on the request (bad URLs/values)
796
843
  PropertyServerException
797
- Raised by the server when an issue arises in processing a valid request
844
+ Raised by the server when an issue arises in processing a valid request
798
845
  NotAuthorizedException
799
- The principle specified by the user_id does not have authorization for the requested action
846
+ The requesting user is not authorized to issue this request
800
847
 
801
848
  """
802
849
  loop = asyncio.get_event_loop()
803
850
  resp = loop.run_until_complete(
804
- self._async_get_project_graph(project_guid, effective_time)
805
- )
851
+ self._async_get_project_graph(project_guid, element_type, body, output_format, output_format_set)
852
+ )
806
853
 
807
854
  return resp
808
855
 
809
-
810
856
  #
811
857
  # Create project methods
812
858
  #
813
- async def _async_create_project_w_body(
814
- self,
815
- body: dict,
816
- classification: str = None,
817
- ) -> str:
859
+ @dynamic_catch
860
+ async def _async_create_project(
861
+ self,
862
+ body: dict | NewElementRequestBody,
863
+ ) -> str:
818
864
  """Create project: https://egeria-project.org/concepts/project Async version.
819
865
 
820
866
  Parameters
821
867
  ----------.
822
868
  body: dict
823
- A dict representing the details of the project to create.
824
- classification: str, optional
825
- An optional project classification. See https://egeria-project.org/types/1/0130-Projects for values.
826
- view_server: str, optional, defaults to None
827
- The name of the server to configure. If not provided, the server name associated with the
828
- instance is used.
869
+ A dict representing the details of the project to create. To create different kinds of projects,
870
+ set the initial_classifications in the body to be, for instance, "PersonalProject" or "Campaign".
871
+
829
872
 
830
873
  Returns
831
874
  -------
@@ -844,11 +887,17 @@ class ProjectManager(Client2):
844
887
 
845
888
  Body structure like:
846
889
  {
890
+ "class": "NewElementRequestBody",
847
891
  "anchorGUID" : "anchor GUID, if set then isOwnAnchor=false",
848
892
  "isOwnAnchor" : False,
849
893
  "parentGUID" : "parent GUID, if set, set all parameters beginning 'parent'",
850
894
  "parentRelationshipTypeName" : "open metadata type name",
851
895
  "parentAtEnd1": True,
896
+ "initialClassifications" : {
897
+ "Folder" : {
898
+ "class": "FolderProperties"
899
+ }
900
+ },
852
901
  "projectProperties": {
853
902
  "class" : "ProjectProperties",
854
903
  "qualifiedName": "Must provide a unique name here",
@@ -865,37 +914,27 @@ class ProjectManager(Client2):
865
914
 
866
915
  """
867
916
 
868
- if classification is None:
869
- url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects"
870
- else:
871
- url = (
872
- f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects?"
873
- f"classificationName={classification}"
874
- )
875
- body_s = body_slimmer(body)
876
- resp = await self._async_make_request("POST", url, body_s)
877
- return resp.json().get("guid", "No GUID returned")
878
-
879
- def create_project_w_body(
880
- self,
881
- body: dict,
882
- classification: str = None,
883
- ) -> str:
917
+
918
+ url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects"
919
+
920
+ return await self._async_create_element_body_request(body, ["ProjectProperties"], body)
921
+
922
+ @dynamic_catch
923
+ def create_project(
924
+ self,
925
+ body: dict | NewElementRequestBody,
926
+ ) -> str:
884
927
  """Create project: https://egeria-project.org/concepts/project
885
928
 
886
929
  Parameters
887
- ----------.
888
- body: dict
889
- A dict representing the details of the project to create.
890
- classification: str, optional
891
- An optional project classification. See https://egeria-project.org/types/1/0130-Projects for values.
892
- view_server: str, optional, defaults to None
893
- The name of the server to configure. If not provided, the server name associated with the instance
894
- is used.
930
+ ----------
931
+ body: dict | NewElementRequestBody
932
+ A dict or NewElementRequestBody representing the details of the project to create.
895
933
 
896
934
  Returns
897
935
  -------
898
- str - the guid of the created collection
936
+ str
937
+ The GUID of the created project.
899
938
 
900
939
  Raises
901
940
  ------
@@ -932,344 +971,15 @@ class ProjectManager(Client2):
932
971
  """
933
972
  loop = asyncio.get_event_loop()
934
973
  resp = loop.run_until_complete(
935
- self._async_create_project_w_body(body, classification)
936
- )
937
- return resp
938
-
939
- async def _async_create_project(
940
- self,
941
- anchor_guid: str,
942
- parent_guid: str,
943
- parent_relationship_type_name: str,
944
- parent_at_end1: bool,
945
- display_name: str,
946
- description: str,
947
- classification_name: str = None,
948
- identifier: str = None,
949
- is_own_anchor: bool = False,
950
- project_status: str = None,
951
- project_phase: str = None,
952
- project_health: str = None,
953
- start_date: str = None,
954
- planned_end_date: str = None,
955
- ) -> str:
956
- """Create Project: https://egeria-project.org/concepts/project Async version.
957
-
958
- Parameters
959
- ----------
960
- classification_name: str, optional
961
- Type of project to create; "PersonalProject", "Campaign", etc. If not provided, project will not
962
- be classified.
963
- anchor_guid: str
964
- The unique identifier of the element that should be the anchor for the new element. Set to null if no
965
- anchor, or if this collection is to be its own anchor.
966
- parent_guid: str
967
- The optional unique identifier for an element that should be connected to the newly created element.
968
- If this property is specified, parentRelationshipTypeName must also be specified
969
- parent_relationship_type_name: str
970
- The name of the relationship, if any, that should be established between the new element and the parent
971
- element. Examples could be "ResourceList".
972
- parent_at_end1: bool
973
- Identifies which end any parent entity sits on the relationship.
974
- display_name: str
975
- The display name of the element. Will also be used as the basis of the qualified_name.
976
- description: str
977
- A description of the collection.
978
- identifier: str
979
- A project identifier.
980
- is_own_anchor: bool, optional, defaults to False
981
- Indicates if the collection should be classified as its own anchor or not.
982
- project_status: str, optional
983
- The project status
984
- project_phase: str, optional
985
- Project phase as defined in valid values
986
- project_health: str, optional
987
- Project health as defined in valid values
988
- start_date: str, optional, defaults to None
989
- Start date of the project in ISO 8601 string format.
990
- planned_end_date: str, optional, defaults to None
991
- Planned completion date in ISO 8601 string format.
992
-
993
-
994
- Returns
995
- -------
996
- str - the guid of the created project
997
-
998
- Raises
999
- ------
1000
- InvalidParameterException
1001
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1002
- PropertyServerException
1003
- Raised by the server when an issue arises in processing a valid request
1004
- NotAuthorizedException
1005
- The principle specified by the user_id does not have authorization for the requested action
1006
-
1007
- """
1008
-
1009
- if parent_guid is None:
1010
- is_own_anchor = False
1011
-
1012
- if classification_name is None:
1013
- url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects"
1014
- else:
1015
- url = (
1016
- f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects?"
1017
- f"classificationName={classification_name}"
1018
- )
1019
-
1020
- body = {
1021
- "anchorGUID": anchor_guid,
1022
- "isOwnAnchor": str(is_own_anchor).lower(),
1023
- "parentGUID": parent_guid,
1024
- "parentRelationshipTypeName": parent_relationship_type_name,
1025
- "parentAtEnd1": str(parent_at_end1).lower(),
1026
- "projectProperties": {
1027
- "class": "ProjectProperties",
1028
- "qualifiedName": f"{classification_name}-{display_name}-{time.asctime()}",
1029
- "identifier": identifier,
1030
- "name": display_name,
1031
- "description": description,
1032
- "projectStatus": project_status,
1033
- "projectPhase": project_phase,
1034
- "projectHealth": project_health,
1035
- "startDate": start_date,
1036
- "plannedEndDate": planned_end_date,
1037
- },
1038
- }
1039
- body_s = body_slimmer(body)
1040
- resp = await self._async_make_request("POST", url, body_s)
1041
- return resp.json().get("guid", "No GUID returned")
1042
-
1043
- def create_project(
1044
- self,
1045
- anchor_guid: str,
1046
- parent_guid: str,
1047
- parent_relationship_type_name: str,
1048
- parent_at_end1: bool,
1049
- display_name: str,
1050
- description: str,
1051
- classification_name: str,
1052
- identifier: str = None,
1053
- is_own_anchor: bool = False,
1054
- project_status: str = None,
1055
- project_phase: str = None,
1056
- project_health: str = None,
1057
- start_date: str = None,
1058
- planned_end_date: str = None,
1059
- ) -> str:
1060
- """Create Project: https://egeria-project.org/concepts/project
1061
-
1062
- Parameters
1063
- ----------
1064
- classification_name: str
1065
- Type of project to create; "PersonalProject", "Campaign", etc. If not provided, the project will not
1066
- have a project classification.
1067
- anchor_guid: str
1068
- The unique identifier of the element that should be the anchor for the new element. Set to null if no
1069
- anchor, or if this collection is to be its own anchor.
1070
- parent_guid: str
1071
- The optional unique identifier for an element that should be connected to the newly created element.
1072
- If this property is specified, parentRelationshipTypeName must also be specified
1073
- parent_relationship_type_name: str
1074
- The name of the relationship, if any, that should be established between the new element and the parent
1075
- element. Examples could be "ResourceList".
1076
- parent_at_end1: bool
1077
- Identifies which end any parent entity sits on the relationship.
1078
- display_name: str
1079
- The display name of the element. Will also be used as the basis of the qualified_name.
1080
- description: str
1081
- A description of the collection.
1082
- identifier: str
1083
- A project identifier.
1084
- is_own_anchor: bool, optional, defaults to False
1085
- Indicates if the collection should be classified as its own anchor or not.
1086
- project_status: str, optional
1087
- The project status
1088
- project_phase: str, optional
1089
- Project phase as defined in valid values
1090
- project_health: str, optional
1091
- Project health as defined in valid values
1092
- start_date: str, optional, defaults to None
1093
- Start date of the project in ISO 8601 string format.
1094
- planned_end_date: str, optional, defaults to None
1095
- Planned completion date in ISO 8601 string format.
1096
-
1097
-
1098
- Returns
1099
- -------
1100
- str - the guid of the created project
1101
-
1102
- Raises
1103
- ------
1104
- InvalidParameterException
1105
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1106
- PropertyServerException
1107
- Raised by the server when an issue arises in processing a valid request
1108
- NotAuthorizedException
1109
- The principle specified by the user_id does not have authorization for the requested action
1110
-
1111
- """
1112
- loop = asyncio.get_event_loop()
1113
- resp = loop.run_until_complete(
1114
- self._async_create_project(
1115
- anchor_guid,
1116
- parent_guid,
1117
- parent_relationship_type_name,
1118
- parent_at_end1,
1119
- display_name,
1120
- description,
1121
- classification_name,
1122
- identifier,
1123
- is_own_anchor,
1124
- project_status,
1125
- project_phase,
1126
- project_health,
1127
- start_date,
1128
- planned_end_date,
1129
- )
1130
- )
1131
- return resp
1132
-
1133
- async def _async_create_project_task(
1134
- self,
1135
- project_guid: str,
1136
- display_name: str,
1137
- identifier: str = None,
1138
- description: str = None,
1139
- project_status: str = None,
1140
- project_phase: str = None,
1141
- project_health: str = None,
1142
- start_date: str = None,
1143
- planned_end_date: str = None,
1144
- ) -> str:
1145
- """Create a new project with the Task classification and link it to a project. Async version.
1146
-
1147
- Parameters
1148
- ----------
1149
- project_guid: str
1150
- The unique identifier of the project to create the task for (the parent).
1151
- display_name: str
1152
- The display name of the element. Will also be used as the basis of the qualified_name.
1153
- identifier: str
1154
- A project identifier.
1155
- description: str
1156
- A description of the collection.
1157
- project_status: str, optional, defaults to "OTHER"
1158
- The project status
1159
- project_phase: str, optional
1160
- Project phase as defined in valid values
1161
- project_health: str, optional
1162
- Project health as defined in valid values
1163
- start_date: str, optional, defaults to None
1164
- Start date of the project in ISO 8601 string format.
1165
- planned_end_date: str, optional, defaults to None
1166
- Planned completion date in ISO 8601 string format.
1167
-
1168
- Returns
1169
- -------
1170
- str - the guid of the created project task
1171
-
1172
- Raises
1173
- ------
1174
- InvalidParameterException
1175
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1176
- PropertyServerException
1177
- Raised by the server when an issue arises in processing a valid request
1178
- NotAuthorizedException
1179
- The principle specified by the user_id does not have authorization for the requested action
1180
-
1181
- """
1182
-
1183
- url = (
1184
- f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/"
1185
- f"{project_guid}/task"
1186
- )
1187
-
1188
- body = {
1189
- "class": "ProjectProperties",
1190
- "qualifiedName": f"task-{display_name}-{time.asctime()}",
1191
- "identifier": identifier,
1192
- "name": display_name,
1193
- "description": description,
1194
- "projectStatus": project_status,
1195
- "projectPhase": project_phase,
1196
- "projectHealth": project_health,
1197
- "startDate": start_date,
1198
- "plannedEndDate": planned_end_date,
1199
- }
1200
- body_s = body_slimmer(body)
1201
- resp = await self._async_make_request("POST", url, body_s)
1202
- return resp.json().get("guid", "No GUID Returned")
1203
-
1204
- def create_project_task(
1205
- self,
1206
- project_guid: str,
1207
- display_name: str,
1208
- identifier: str = None,
1209
- description: str = None,
1210
- project_status: str = None,
1211
- project_phase: str = None,
1212
- project_health: str = None,
1213
- start_date: str = None,
1214
- planned_end_date: str = None,
1215
- ) -> str:
1216
- """Create a new project with the Task classification and link it to a project.
1217
-
1218
- Parameters
1219
- ----------
1220
- project_guid: str
1221
- The unique identifier of the project to create the task for.The parent.
1222
- display_name: str
1223
- The display name of the element. Will also be used as the basis of the qualified_name.
1224
- identifier: str
1225
- A project identifier.
1226
- description: str
1227
- A description of the collection.
1228
- project_status: str, optional, defaults to "OTHER"
1229
- The project status
1230
- project_phase: str, optional
1231
- Project phase as defined in valid values
1232
- project_health: str, optional
1233
- Project health as defined in valid values
1234
- start_date: str, optional, defaults to None
1235
- Start date of the project in ISO 8601 string format.
1236
- planned_end_date: str, optional, defaults to None
1237
- Planned completion date in ISO 8601 string format.
1238
-
1239
- Returns
1240
- -------
1241
- str - the guid of the created project task
1242
-
1243
- Raises
1244
- ------
1245
- InvalidParameterException
1246
- If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1247
- PropertyServerException
1248
- Raised by the server when an issue arises in processing a valid request
1249
- NotAuthorizedException
1250
- The principle specified by the user_id does not have authorization for the requested action
1251
-
1252
- """
1253
- loop = asyncio.get_event_loop()
1254
- resp = loop.run_until_complete(
1255
- self._async_create_project_task(
1256
- project_guid,
1257
- display_name,
1258
- identifier,
1259
- description,
1260
- project_status,
1261
- project_phase,
1262
- project_health,
1263
- start_date,
1264
- planned_end_date,
974
+ self._async_create_project(body)
1265
975
  )
1266
- )
1267
976
  return resp
1268
977
 
978
+ @dynamic_catch
1269
979
  async def _async_create_project_from_template(
1270
- self,
1271
- body: dict,
1272
- ) -> str:
980
+ self,
981
+ body: dict | TemplateRequestBody,
982
+ ) -> str:
1273
983
  """Create a new metadata element to represent a project using an existing metadata element as a template.
1274
984
  The template defines additional classifications and relationships that should be added to the new project.
1275
985
  Async version.
@@ -1326,14 +1036,13 @@ class ProjectManager(Client2):
1326
1036
  """
1327
1037
 
1328
1038
  url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/from-template"
1329
- body_s = body_slimmer(body)
1330
- resp = await self._async_make_request("POST", url, body_s)
1331
- return resp.json().get("guid", "No GUID Returned")
1039
+ return await self._async_create_element_from_template(url, body)
1332
1040
 
1041
+ @dynamic_catch
1333
1042
  def create_project_from_template(
1334
- self,
1335
- body: dict,
1336
- ) -> str:
1043
+ self,
1044
+ body: dict,
1045
+ ) -> str:
1337
1046
  """Create a new metadata element to represent a project using an existing metadata element as a template.
1338
1047
  The template defines additional classifications and relationships that should be added to the new project.
1339
1048
 
@@ -1389,50 +1098,28 @@ class ProjectManager(Client2):
1389
1098
  resp = loop.run_until_complete(self._async_create_project_from_template(body))
1390
1099
  return resp
1391
1100
 
1101
+
1102
+ #
1103
+ #
1104
+ #
1105
+
1392
1106
  async def _async_update_project(
1393
- self,
1394
- project_guid: str,
1395
- qualified_name: str = None,
1396
- identifier: str = None,
1397
- display_name: str = None,
1398
- description: str = None,
1399
- project_status: str = None,
1400
- project_phase: str = None,
1401
- project_health: str = None,
1402
- start_date: str = None,
1403
- planned_end_date: str = None,
1404
- replace_all_props: bool = False,
1405
- ) -> None:
1406
- """Update the properties of a project. Async Version.
1107
+ self,
1108
+ project_guid: str,
1109
+ body: dict | UpdateElementRequestBody,
1110
+ ) -> None:
1111
+ """Update the properties of a project. Async version.
1407
1112
 
1408
1113
  Parameters
1409
1114
  ----------
1410
1115
  project_guid: str
1411
- Unique identifier for the project.
1412
- qualified_name: str, optional, defaults to None
1413
- The unique identifier of the project.
1414
- identifier: str
1415
- A project identifier.
1416
- display_name: str
1417
- The display name of the element. Will also be used as the basis of the qualified_name.
1418
- description: str
1419
- A description of the collection.
1420
- project_status: str, optional
1421
- The project status
1422
- project_phase: str, optional
1423
- Project phase as defined in valid values
1424
- project_health: str, optional
1425
- Project health as defined in valid values
1426
- start_date: str, optional, defaults to None
1427
- Start date of the project in ISO 8601 string format.
1428
- planned_end_date: str, optional, defaults to None
1429
- Planned completion date in ISO 8601 string format.
1430
- replace_all_props: bool, optional, defaults to False
1431
- If True, then all the properties of the project will be replaced with the specified properties.
1116
+ Unique identifier of the project to update.
1117
+ body: dict | UpdateElementRequestBody
1118
+ The update payload specifying properties to change.
1432
1119
 
1433
1120
  Returns
1434
1121
  -------
1435
- str - the guid of the created project task
1122
+ None
1436
1123
 
1437
1124
  Raises
1438
1125
  ------
@@ -1444,72 +1131,28 @@ class ProjectManager(Client2):
1444
1131
  The principle specified by the user_id does not have authorization for the requested action
1445
1132
  """
1446
1133
 
1447
- replace_all_props_s = str(replace_all_props).lower()
1448
1134
  url = (
1449
- f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/{project_guid}/"
1450
- f"update?replaceAllProperties={replace_all_props_s}"
1135
+ f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/{project_guid}/update"
1451
1136
  )
1452
1137
 
1453
- body = {
1454
- "class": "ProjectProperties",
1455
- "qualifiedName": qualified_name,
1456
- "identifier": identifier,
1457
- "name": display_name,
1458
- "description": description,
1459
- "projectStatus": project_status,
1460
- "projectPhase": project_phase,
1461
- "projectHealth": project_health,
1462
- "startDate": start_date,
1463
- "plannedEndDate": planned_end_date,
1464
- }
1465
- body_s = body_slimmer(body)
1466
- await self._async_make_request("POST", url, body_s)
1467
- return
1138
+ await self._async_update_element_body_request(url, PROJECT_PROPERTIES_LIST, body)
1468
1139
 
1469
1140
  def update_project(
1470
- self,
1471
- project_guid: str,
1472
- qualified_name: str = None,
1473
- identifier: str = None,
1474
- display_name: str = None,
1475
- description: str = None,
1476
- project_status: str = None,
1477
- project_phase: str = None,
1478
- project_health: str = None,
1479
- start_date: str = None,
1480
- planned_end_date: str = None,
1481
- replace_all_props: bool = False,
1482
- ) -> None:
1141
+ self,
1142
+ project_guid: str,
1143
+ body: dict | UpdateElementRequestBody ) -> None:
1483
1144
  """Update the properties of a project.
1484
1145
 
1485
1146
  Parameters
1486
1147
  ----------
1487
1148
  project_guid: str
1488
- Unique identifier for the project.
1489
- qualified_name: str, optional, defaults to None
1490
- The unique identifier of the project.
1491
- identifier: str
1492
- A project identifier.
1493
- display_name: str
1494
- The display name of the element. Will also be used as the basis of the qualified_name.
1495
- description: str
1496
- A description of the collection.
1497
- project_status: str, optional
1498
- The project status
1499
- project_phase: str, optional
1500
- Project phase as defined in valid values
1501
- project_health: str, optional
1502
- Project health as defined in valid values
1503
- start_date: str, optional, defaults to None
1504
- Start date of the project in ISO 8601 string format.
1505
- planned_end_date: str, optional, defaults to None
1506
- Planned completion date in ISO 8601 string format.
1507
- replace_all_props: bool, optional, defaults to False
1508
- If True, then all the properties of the project will be replaced with the specified properties.
1149
+ Unique identifier of the project to update.
1150
+ body: dict | UpdateElementRequestBody
1151
+ The update payload specifying properties to change.
1509
1152
 
1510
1153
  Returns
1511
1154
  -------
1512
- str - the guid of the created project task
1155
+ None
1513
1156
 
1514
1157
  Raises
1515
1158
  ------
@@ -1524,36 +1167,30 @@ class ProjectManager(Client2):
1524
1167
  loop.run_until_complete(
1525
1168
  self._async_update_project(
1526
1169
  project_guid,
1527
- qualified_name,
1528
- identifier,
1529
- display_name,
1530
- description,
1531
- project_status,
1532
- project_phase,
1533
- project_health,
1534
- start_date,
1535
- planned_end_date,
1536
- replace_all_props,
1170
+ body
1171
+ )
1537
1172
  )
1538
- )
1539
1173
  return
1540
1174
 
1541
1175
  async def _async_delete_project(
1542
- self,
1543
- project_guid: str, cascade: bool = False
1544
- ) -> None:
1545
- """Delete a project. It is detected from all parent elements. Async version
1176
+ self,
1177
+ project_guid: str, cascade_delete: bool = False,
1178
+ body: dict | DeleteRequestBody = None
1179
+ ) -> None:
1180
+ """Delete a project. Async version.
1546
1181
 
1547
1182
  Parameters
1548
1183
  ----------
1549
1184
  project_guid: str
1550
- The guid of the project to update.
1551
- cascade: bool, optional, defaults to False
1552
- If true, then all anchored elements will be deleted.
1185
+ The GUID of the project to delete.
1186
+ cascade_delete: bool, optional, default = False
1187
+ If True, then all anchored elements will be deleted.
1188
+ body: dict | DeleteRequestBody, optional, default = None
1189
+ Request body for additional options.
1553
1190
 
1554
1191
  Returns
1555
1192
  -------
1556
- Nothing
1193
+ None
1557
1194
 
1558
1195
  Raises
1559
1196
  ------
@@ -1565,36 +1202,33 @@ class ProjectManager(Client2):
1565
1202
  The principle specified by the user_id does not have authorization for the requested action
1566
1203
 
1567
1204
  """
1568
- cascade_s = str(cascade).lower()
1205
+
1569
1206
  url = (
1570
1207
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/"
1571
- f"{project_guid}/delete?cascadedDelete={cascade_s}"
1208
+ f"{project_guid}/delete"
1572
1209
  )
1573
-
1574
- body = {"class": "NullRequestBody"}
1575
-
1576
- await self._async_make_request("POST", url, body)
1577
- return
1210
+ await self._async_delete_request(url, body, cascade_delete)
1211
+ logger.info(f"Deleted project {project_guid} with cascade {cascade_delete}")
1578
1212
 
1579
1213
  def delete_project(
1580
- self,
1581
- project_guid: str, cascade: bool = False
1582
- ) -> None:
1583
- """Delete a project. It is detected from all parent elements.
1214
+ self,
1215
+ project_guid: str, cascade_delete: bool = False,
1216
+ body: dict |DeleteRequestBody = None
1217
+ ) -> None:
1218
+ """Delete a project.
1584
1219
 
1585
1220
  Parameters
1586
1221
  ----------
1587
1222
  project_guid: str
1588
- The guid of the collection to update.
1589
- cascade: bool, optional, defaults to False
1590
- If true, then all anchored elements will be deleted.
1591
-
1223
+ The GUID of the project to delete.
1224
+ cascade_delete: bool, optional, default = False
1225
+ If True, then all anchored elements will be deleted.
1226
+ body: dict | DeleteRequestBody, optional, default = None
1227
+ Request body for additional options.
1592
1228
 
1593
- cascade: bool, optional, defaults to False
1594
- If true, then all anchored elements will be deleted.
1595
1229
  Returns
1596
1230
  -------
1597
- Nothing
1231
+ None
1598
1232
 
1599
1233
  Raises
1600
1234
  ------
@@ -1608,32 +1242,25 @@ class ProjectManager(Client2):
1608
1242
 
1609
1243
  """
1610
1244
  loop = asyncio.get_event_loop()
1611
- loop.run_until_complete(self._async_delete_project(project_guid, cascade))
1612
- return
1245
+ loop.run_until_complete(self._async_delete_project(project_guid, cascade_delete, body))
1246
+
1613
1247
 
1614
1248
  async def _async_add_to_project_team(
1615
- self,
1616
- project_guid: str,
1617
- actor_guid: str,
1618
- team_role: str = None,
1619
- effective_from: str = None,
1620
- effective_to: str = None,
1621
- ) -> None:
1622
- """Add an actor to a project. The request body is optional. If supplied, it contains the name of the role that
1623
- the actor plays in the project. Async version.
1249
+ self,
1250
+ project_guid: str,
1251
+ actor_guid: str,
1252
+ body: dict | NewRelationshipRequestBody = None,
1253
+ ) -> None:
1254
+ """Add an actor to a project. Async version.
1624
1255
 
1625
1256
  Parameters
1626
1257
  ----------
1627
1258
  project_guid: str
1628
- identity of the project to update.
1259
+ Identity of the project to update.
1629
1260
  actor_guid: str
1630
- identity of the actor to add.
1631
- team_role: str, optional, defaults to None
1632
- Name of the role the actor plays in the project.
1633
- effective_from: str, optional, defaults to None
1634
- Date at which the actor becomes active in the project. Date format is ISO 8601 string format.
1635
- effective_to: str, optional, defaults to None
1636
- Date at which the actor is no longer active in the project. Date format is ISO 8601 string format.
1261
+ Identity of the actor to add.
1262
+ body: dict | NewRelationshipRequestBody, optional, default = None
1263
+ Optional relationship properties (for example, role name, effective times).
1637
1264
 
1638
1265
  Returns
1639
1266
  -------
@@ -1655,42 +1282,25 @@ class ProjectManager(Client2):
1655
1282
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/{project_guid}/"
1656
1283
  f"members/{actor_guid}/attach"
1657
1284
  )
1658
- body = {
1659
- "class": "ProjectTeamProperties",
1660
- "teamRole": team_role,
1661
- "effectiveFrom": effective_from,
1662
- "effectiveTo": effective_to,
1663
- }
1664
- body_s = body_slimmer(body)
1665
- if body_s is None:
1666
- await self._async_make_request("POST", url)
1667
- else:
1668
- await self._async_make_request("POST", url, body_s)
1669
- return
1285
+ await self._async_new_relationship_request(url, "AssignmentScopeProperties", body)
1286
+ logger.info(f"Added actor {actor_guid} to project {project_guid}")
1670
1287
 
1671
1288
  def add_to_project_team(
1672
- self,
1673
- project_guid: str,
1674
- actor_guid: str,
1675
- team_role: str = None,
1676
- effective_from: str = None,
1677
- effective_to: str = None,
1678
- ) -> None:
1679
- """Add an actor to a project. The request body is optional. If supplied, it contains the name of the role that
1680
- the actor plays in the project.
1289
+ self,
1290
+ project_guid: str,
1291
+ actor_guid: str,
1292
+ body: dict | NewRelationshipRequestBody = None,
1293
+ ) -> None:
1294
+ """Add an actor to a project.
1681
1295
 
1682
1296
  Parameters
1683
1297
  ----------
1684
1298
  project_guid: str
1685
- identity of the project to update.
1299
+ Identity of the project to update.
1686
1300
  actor_guid: str
1687
- identity of the actor to add.
1688
- team_role: str, optional, defaults to None
1689
- Name of the role the actor plays in the project.
1690
- effective_from: str, optional, defaults to None
1691
- Date at which the actor becomes active in the project. Date format is ISO 8601 string format.
1692
- effective_to: str, optional, defaults to None
1693
- Date at which the actor is no longer active in the project. Date format is ISO 8601 string format.
1301
+ Identity of the actor to add.
1302
+ body: dict | NewRelationshipRequestBody, optional, default = None
1303
+ Optional relationship properties (for example, role name, effective times).
1694
1304
 
1695
1305
  Returns
1696
1306
  -------
@@ -1712,26 +1322,30 @@ class ProjectManager(Client2):
1712
1322
  self._async_add_to_project_team(
1713
1323
  project_guid,
1714
1324
  actor_guid,
1715
- team_role,
1716
- effective_from,
1717
- effective_to,
1325
+ body
1326
+ )
1718
1327
  )
1719
- )
1720
- return
1328
+
1721
1329
 
1722
1330
  async def _async_remove_from_project_team(
1723
- self,
1724
- project_guid: str,
1725
- actor_guid: str,
1726
- ) -> None:
1331
+ self,
1332
+ project_guid: str,
1333
+ actor_guid: str,
1334
+ body: dict | DeleteRequestBody = None,
1335
+ cascade_delete: bool = False,
1336
+ ) -> None:
1727
1337
  """Remove an actor from a project. Async version.
1728
1338
 
1729
1339
  Parameters
1730
1340
  ----------
1731
1341
  project_guid: str
1732
- identity of the project to remove members from.
1342
+ Identity of the project to remove members from.
1733
1343
  actor_guid: str
1734
- identity of the actor to remove.
1344
+ Identity of the actor to remove.
1345
+ body: dict | DeleteRequestBody, optional, default = None
1346
+ Optional relationship properties.
1347
+ cascade_delete: bool, optional, default = False
1348
+ If True, deletes related anchored elements as applicable.
1735
1349
 
1736
1350
  Returns
1737
1351
  -------
@@ -1753,24 +1367,29 @@ class ProjectManager(Client2):
1753
1367
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/{project_guid}/"
1754
1368
  f"members/{actor_guid}/detach"
1755
1369
  )
1370
+ await self._async_delete_request(url, body, cascade_delete)
1371
+ logger.info(f"Removed actor {actor_guid} from project {project_guid}")
1756
1372
 
1757
- body = {"class": "NullRequestBody"}
1758
- await self._async_make_request("POST", url, body)
1759
- return
1760
1373
 
1761
1374
  def remove_from_project_team(
1762
- self,
1763
- project_guid: str,
1764
- actor_guid: str,
1765
- ) -> None:
1375
+ self,
1376
+ project_guid: str,
1377
+ actor_guid: str,
1378
+ body: dict | DeleteRequestBody = None,
1379
+ cascade_delete: bool = False,
1380
+ ) -> None:
1766
1381
  """Remove an actor from a project.
1767
1382
 
1768
1383
  Parameters
1769
1384
  ----------
1770
1385
  project_guid: str
1771
- identity of the project.
1386
+ Identity of the project.
1772
1387
  actor_guid: str
1773
- identity of the actor to remove.
1388
+ Identity of the actor to remove.
1389
+ body: dict | DeleteRequestBody, optional, default = None
1390
+ Optional relationship properties.
1391
+ cascade_delete: bool, optional, default = False
1392
+ If True, deletes related anchored elements as applicable.
1774
1393
 
1775
1394
  Returns
1776
1395
  -------
@@ -1789,24 +1408,102 @@ class ProjectManager(Client2):
1789
1408
  """
1790
1409
  loop = asyncio.get_event_loop()
1791
1410
  loop.run_until_complete(
1792
- self._async_remove_from_project_team(project_guid, actor_guid)
1411
+ self._async_remove_from_project_team(project_guid, actor_guid, body, cascade_delete)
1412
+ )
1413
+
1414
+
1415
+ async def _async_setup_project_dependency(
1416
+ self,
1417
+ project_guid: str,
1418
+ depends_on_project_guid: str,
1419
+ body: dict | NewRelationshipRequestBody = None,
1420
+ ) -> None:
1421
+ """Create a project dependency link from one project to another. Async version.
1422
+
1423
+ Parameters
1424
+ ----------
1425
+ project_guid: str
1426
+ Identity of the project that has the dependency.
1427
+ depends_on_project_guid: str
1428
+ GUID of the project that the first project depends on.
1429
+
1430
+
1431
+ Returns
1432
+ -------
1433
+ None
1434
+
1435
+ Raises
1436
+ ------
1437
+
1438
+ InvalidParameterException
1439
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1440
+ PropertyServerException
1441
+ Raised by the server when an issue arises in processing a valid request
1442
+ NotAuthorizedException
1443
+ The principle specified by the user_id does not have authorization for the requested action
1444
+
1445
+ """
1446
+
1447
+ url = (
1448
+ f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/{project_guid}/"
1449
+ f"project-dependencies/{depends_on_project_guid}/attach"
1793
1450
  )
1451
+ await self._async_new_relationship_request(url, "ProjectDependencyProperties", body)
1452
+ logger.info(f"Added dependency between project {project_guid} depending on project {depends_on_project_guid}")
1453
+
1454
+
1455
+ def setup_project_dependency(
1456
+ self,
1457
+ project_guid: str,
1458
+ depends_on_project_guid: str,
1459
+ body: dict | NewRelationshipRequestBody = None,
1460
+ ) -> None:
1461
+ """Create a project dependency link from one project to another.
1462
+
1463
+ Parameters
1464
+ ----------
1465
+ project_guid: str
1466
+ Identity of the project that has the dependency.
1467
+ depends_on_project_guid: str
1468
+ GUID of the project that the first project depends on.
1469
+
1470
+
1471
+ Returns
1472
+ -------
1473
+ None
1474
+
1475
+ Raises
1476
+ ------
1477
+
1478
+ InvalidParameterException
1479
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1480
+ PropertyServerException
1481
+ Raised by the server when an issue arises in processing a valid request
1482
+ NotAuthorizedException
1483
+ The principle specified by the user_id does not have authorization for the requested action
1484
+
1485
+ """
1486
+ loop = asyncio.get_event_loop()
1487
+ loop.run_until_complete(
1488
+ self._async_setup_project_dependency( project_guid, depends_on_project_guid, body)
1489
+ )
1794
1490
  return
1795
1491
 
1796
- async def _async_setup_project_management_role(
1797
- self,
1798
- project_guid: str,
1799
- project_role_guid: str,
1800
- ) -> None:
1801
- """Create a ProjectManagement relationship between a project and a person role to show that anyone appointed to
1802
- the role is a member of the project. Async version.
1492
+ async def _async_clear_project_dependency(
1493
+ self,
1494
+ project_guid: str,
1495
+ depends_on_project_guid: str,
1496
+ body: dict | DeleteRequestBody = None,
1497
+ cascade_delete: bool = False
1498
+ ) -> None:
1499
+ """Remove a project dependency link between two projects. Async version.
1803
1500
 
1804
1501
  Parameters
1805
1502
  ----------
1806
1503
  project_guid: str
1807
- identity of the project.
1808
- project_role_guid: str
1809
- guid of the role to assign to the project.
1504
+ Identity of the project that previously had the dependency.
1505
+ depends_on_project_guid: str
1506
+ GUID of the project that was depended on.
1810
1507
 
1811
1508
 
1812
1509
  Returns
@@ -1827,27 +1524,27 @@ class ProjectManager(Client2):
1827
1524
 
1828
1525
  url = (
1829
1526
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/{project_guid}/"
1830
- f"project-management-roles/{project_role_guid}/attach"
1527
+ f"project-dependencies/{depends_on_project_guid}/detach"
1831
1528
  )
1529
+ await self._async_delete_request(url, body, cascade_delete)
1530
+ logger.info(f"Removed project dependency between project {project_guid} and project {depends_on_project_guid}")
1832
1531
 
1833
- body = {"class": "NullRequestBody"}
1834
- await self._async_make_request("POST", url, body)
1835
- return
1836
1532
 
1837
- def setup_project_management_role(
1838
- self,
1839
- project_guid: str,
1840
- project_role_guid: str,
1841
- ) -> None:
1842
- """Create a ProjectManagement relationship between a project and a person role to show that anyone appointed to
1843
- the role is a member of the project. Async version.
1533
+ def clear_project_dependency(
1534
+ self,
1535
+ project_guid: str,
1536
+ depends_on_project_guid: str,
1537
+ body: dict | DeleteRequestBody = None,
1538
+ cascade_delete: bool = False
1539
+ ) -> None:
1540
+ """Clear a project dependency link between two projects.
1844
1541
 
1845
1542
  Parameters
1846
1543
  ----------
1847
1544
  project_guid: str
1848
- identity of the project.
1849
- project_role_guid: str
1850
- guid of the role to assign to the project.
1545
+ Identity of the project that previously had the dependency.
1546
+ depends_on_project_guid: str
1547
+ GUID of the project that was depended on.
1851
1548
 
1852
1549
 
1853
1550
  Returns
@@ -1867,23 +1564,25 @@ class ProjectManager(Client2):
1867
1564
  """
1868
1565
  loop = asyncio.get_event_loop()
1869
1566
  loop.run_until_complete(
1870
- self._async_setup_project_management_role(project_guid, project_role_guid)
1871
- )
1567
+ self._async_clear_project_dependency(project_guid, depends_on_project_guid,
1568
+ body, cascade_delete)
1569
+ )
1872
1570
  return
1873
1571
 
1874
- async def _async_clear_project_management_role(
1875
- self,
1876
- project_guid: str,
1877
- project_role_guid: str,
1878
- ) -> None:
1879
- """Remove a ProjectManagement relationship between a project and a person role. Async version.
1572
+ async def _async_setup_project_hierarchy(
1573
+ self,
1574
+ project_guid: str,
1575
+ managed_project_guid: str,
1576
+ body: dict | NewRelationshipRequestBody = None,
1577
+ ) -> None:
1578
+ """Create a project hierarchy link from a project to a managed (child) project. Async version.
1880
1579
 
1881
1580
  Parameters
1882
1581
  ----------
1883
1582
  project_guid: str
1884
- identity of the project.
1885
- project_role_guid: str
1886
- guid of the role to assign to the project.
1583
+ Identity of the parent (managing) project.
1584
+ managed_project_guid: str
1585
+ GUID of the managed (child) project.
1887
1586
 
1888
1587
 
1889
1588
  Returns
@@ -1904,26 +1603,26 @@ class ProjectManager(Client2):
1904
1603
 
1905
1604
  url = (
1906
1605
  f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/{project_guid}/"
1907
- f"project-management-roles/{project_role_guid}/detach"
1606
+ f"project-hierarchies/{managed_project_guid}/attach"
1908
1607
  )
1608
+ await self._async_new_relationship_request(url, "ProjectHierarchyProperties", body)
1609
+ logger.info(f"Added hierarchy between project {project_guid} abd project {managed_project_guid}")
1909
1610
 
1910
- body = {"class": "NullRequestBody"}
1911
- await self._async_make_request("POST", url, body)
1912
- return
1913
1611
 
1914
- def clear_project_management_role(
1915
- self,
1916
- project_guid: str,
1917
- project_role_guid: str,
1918
- ) -> None:
1919
- """Clear a ProjectManagement relationship between a project and a person role.
1612
+ def setup_project_hierarchy(
1613
+ self,
1614
+ project_guid: str,
1615
+ managed_project_guid: str,
1616
+ body: dict | NewRelationshipRequestBody = None,
1617
+ ) -> None:
1618
+ """Create a project hierarchy link from a project to a managed (child) project.
1920
1619
 
1921
1620
  Parameters
1922
1621
  ----------
1923
1622
  project_guid: str
1924
- identity of the project.
1925
- project_role_guid: str
1926
- guid of the role to assign to the project.
1623
+ Identity of the parent (managing) project.
1624
+ managed_project_guid: str
1625
+ GUID of the managed (child) project.
1927
1626
 
1928
1627
 
1929
1628
  Returns
@@ -1943,10 +1642,102 @@ class ProjectManager(Client2):
1943
1642
  """
1944
1643
  loop = asyncio.get_event_loop()
1945
1644
  loop.run_until_complete(
1946
- self._async_clear_project_management_role(project_guid, project_role_guid)
1645
+ self._async_setup_project_dependency( project_guid, managed_project_guid, body)
1646
+ )
1647
+ return
1648
+
1649
+ async def _async_clear_project_hierarchy(
1650
+ self,
1651
+ project_guid: str,
1652
+ managed_project_guid: str,
1653
+ body: dict | DeleteRequestBody = None,
1654
+ cascade_delete: bool = False
1655
+ ) -> None:
1656
+ """Remove a project hierarchy link between two projects. Async version.
1657
+
1658
+ Parameters
1659
+ ----------
1660
+ project_guid: str
1661
+ Identity of the parent (managing) project.
1662
+ managed_project_guid: str
1663
+ GUID of the managed (child) project.
1664
+
1665
+
1666
+ Returns
1667
+ -------
1668
+ None
1669
+
1670
+ Raises
1671
+ ------
1672
+
1673
+ InvalidParameterException
1674
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1675
+ PropertyServerException
1676
+ Raised by the server when an issue arises in processing a valid request
1677
+ NotAuthorizedException
1678
+ The principle specified by the user_id does not have authorization for the requested action
1679
+
1680
+ """
1681
+
1682
+ url = (
1683
+ f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/project-manager/projects/{project_guid}/"
1684
+ f"project-hierarchies/{managed_project_guid}/detach"
1947
1685
  )
1686
+ await self._async_delete_request(url, body, cascade_delete)
1687
+ logger.info(f"Removed project hierarchy link between project {project_guid} and project {managed_project_guid}")
1688
+
1689
+
1690
+ def clear_project_hierarchy(
1691
+ self,
1692
+ project_guid: str,
1693
+ managed_project_guid: str,
1694
+ body: dict | DeleteRequestBody = None,
1695
+ cascade_delete: bool = False
1696
+ ) -> None:
1697
+ """Clear a project hierarchy link between two projects.
1698
+
1699
+ Parameters
1700
+ ----------
1701
+ project_guid: str
1702
+ Identity of the parent (managing) project.
1703
+ managed_project_guid: str
1704
+ GUID of the managed (child) project.
1705
+
1706
+
1707
+ Returns
1708
+ -------
1709
+ None
1710
+
1711
+ Raises
1712
+ ------
1713
+
1714
+ InvalidParameterException
1715
+ If the client passes incorrect parameters on the request - such as bad URLs or invalid values
1716
+ PropertyServerException
1717
+ Raised by the server when an issue arises in processing a valid request
1718
+ NotAuthorizedException
1719
+ The principle specified by the user_id does not have authorization for the requested action
1720
+
1721
+ """
1722
+ loop = asyncio.get_event_loop()
1723
+ loop.run_until_complete(
1724
+ self._async_clear_project_hierarchy(project_guid, managed_project_guid,
1725
+ body, cascade_delete)
1726
+ )
1948
1727
  return
1949
1728
 
1950
1729
 
1951
1730
  if __name__ == "__main__":
1952
1731
  print("Main-Project Manager")
1732
+
1733
+
1734
+ # Automatically apply @dynamic_catch to all methods of ProjectManager (excluding dunder methods)
1735
+ try:
1736
+ for _name, _attr in list(ProjectManager.__dict__.items()):
1737
+ if callable(_attr) and not _name.startswith("__"):
1738
+ # Avoid double-wrapping if already decorated
1739
+ if not getattr(_attr, "__wrapped__", None):
1740
+ setattr(ProjectManager, _name, dynamic_catch(_attr))
1741
+ except Exception as _e:
1742
+ # Be resilient; if anything goes wrong, leave methods as-is
1743
+ logger.debug(f"dynamic_catch auto-wrap skipped due to: {_e}")