Skip to content

Commit d7f9741

Browse files
authored
merge: Merge pull request #22 from DSD-DBS/get-single-work-item
feat: Implement get single work item and detect truncated relationships
2 parents 822cddb + 79337c7 commit d7f9741

File tree

9 files changed

+741
-68
lines changed

9 files changed

+741
-68
lines changed

polarion_rest_api_client/base_client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,15 @@ def get_work_items(
204204
"""
205205
raise NotImplementedError
206206

207+
@abc.abstractmethod
208+
def get_work_item(
209+
self,
210+
work_item_id: str,
211+
retry: bool = True,
212+
) -> WorkItemType | None:
213+
"""Return one specific work item with all fields."""
214+
raise NotImplementedError
215+
207216
def create_work_item(self, work_item: WorkItemType):
208217
"""Create a single given work item."""
209218
self.create_work_items([work_item])

polarion_rest_api_client/client.py

Lines changed: 108 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
)
3434
from polarion_rest_api_client.open_api_client.api.work_items import (
3535
delete_work_items,
36+
get_work_item,
3637
get_work_items,
3738
patch_work_item,
3839
post_work_items,
@@ -565,82 +566,121 @@ def get_work_items(
565566
)
566567
and work_items_response.data
567568
):
568-
for work_item in work_items_response.data:
569-
if not getattr(work_item.meta, "errors", []):
570-
assert work_item.attributes
571-
assert isinstance(work_item.id, str)
572-
work_item_id = work_item.id.split("/")[-1]
573-
574-
work_item_links = []
575-
work_item_attachments = []
576-
577-
if (
578-
work_item.relationships
579-
and work_item.relationships.linked_work_items
580-
and work_item.relationships.linked_work_items.data
581-
):
582-
for (
583-
link
584-
) in work_item.relationships.linked_work_items.data:
585-
work_item_links.append(
586-
self._parse_work_item_link(
587-
link.id,
588-
link.additional_properties.get(
589-
"suspect", False
590-
),
591-
work_item_id,
592-
)
593-
)
594-
595-
if (
596-
work_item.relationships
597-
and work_item.relationships.attachments
598-
and work_item.relationships.attachments.data
599-
):
600-
for (
601-
attachment
602-
) in work_item.relationships.attachments.data:
603-
assert attachment.id
604-
work_item_attachments.append(
605-
dm.WorkItemAttachment(
606-
work_item_id,
607-
attachment.id.split("/")[-1],
608-
None, # title isn't provided
609-
)
610-
)
569+
work_items = [
570+
self._generate_work_item(work_item)
571+
for work_item in work_items_response.data
572+
if not getattr(work_item.meta, "errors", [])
573+
]
611574

612-
work_items.append(
613-
self._work_item(
614-
work_item_id,
615-
unset_str_builder(work_item.attributes.title),
616-
(
617-
unset_str_builder(
618-
work_item.attributes.description.type
619-
)
620-
if work_item.attributes.description
621-
else None
622-
),
623-
(
624-
unset_str_builder(
625-
work_item.attributes.description.value
626-
)
627-
if work_item.attributes.description
628-
else None
629-
),
630-
unset_str_builder(work_item.attributes.type),
631-
unset_str_builder(work_item.attributes.status),
632-
work_item.attributes.additional_properties,
633-
work_item_links,
634-
work_item_attachments,
635-
)
636-
)
637575
next_page = isinstance(
638576
work_items_response.links,
639577
api_models.WorkitemsListGetResponseLinks,
640578
) and bool(work_items_response.links.next_)
641579

642580
return work_items, next_page
643581

582+
def get_work_item(
583+
self,
584+
work_item_id: str,
585+
retry: bool = True,
586+
) -> base_client.WorkItemType | None:
587+
"""Return one specific work item with all fields.
588+
589+
This also includes all linked work items and attachments. If
590+
there are to many of these to get them in one request, the
591+
truncated flags for linked_work_items and attachments will be
592+
set to True.
593+
"""
594+
response = get_work_item.sync_detailed(
595+
self.project_id,
596+
work_item_id,
597+
client=self.client,
598+
fields=_build_sparse_fields(
599+
{
600+
"workitems": "@all",
601+
"workitem_attachments": "@all",
602+
"linkedworkitems": "@all",
603+
}
604+
),
605+
)
606+
if not self._check_response(response, not retry) and retry:
607+
sleep_random_time()
608+
return self.get_work_item(work_item_id, False)
609+
610+
if response.parsed and isinstance(
611+
response.parsed.data, api_models.WorkitemsSingleGetResponseData
612+
):
613+
return self._generate_work_item(response.parsed.data)
614+
615+
return None
616+
617+
def _generate_work_item(
618+
self,
619+
work_item: api_models.WorkitemsListGetResponseDataItem
620+
| api_models.WorkitemsSingleGetResponseData,
621+
) -> base_client.WorkItemType:
622+
assert work_item.attributes
623+
assert isinstance(work_item.id, str)
624+
work_item_id = work_item.id.split("/")[-1]
625+
work_item_links = []
626+
work_item_attachments = []
627+
628+
# We set both truncated flags to True and will only set them to False,
629+
# if the corresponding fields were requested and returned completely
630+
links_truncated = True
631+
attachments_truncated = True
632+
if work_item.relationships:
633+
if links := work_item.relationships.linked_work_items:
634+
if not links.meta or links.meta.total_count is oa_types.UNSET:
635+
links_truncated = False
636+
637+
work_item_links = [
638+
self._parse_work_item_link(
639+
link.id,
640+
link.additional_properties.get("suspect", False),
641+
work_item_id,
642+
)
643+
for link in links.data or []
644+
]
645+
646+
if attachments := work_item.relationships.attachments:
647+
if (
648+
not attachments.meta
649+
or attachments.meta.total_count is oa_types.UNSET
650+
):
651+
attachments_truncated = False
652+
653+
work_item_attachments = [
654+
dm.WorkItemAttachment(
655+
work_item_id,
656+
attachment.id.split("/")[-1],
657+
None, # title isn't provided
658+
)
659+
for attachment in attachments.data or []
660+
if attachment.id
661+
]
662+
663+
desctype = None
664+
desc = None
665+
if work_item.attributes.description:
666+
desctype = unset_str_builder(work_item.attributes.description.type)
667+
desc = unset_str_builder(work_item.attributes.description.value)
668+
669+
work_item_obj = self._work_item(
670+
work_item_id,
671+
unset_str_builder(work_item.attributes.title),
672+
desctype,
673+
desc,
674+
unset_str_builder(work_item.attributes.type),
675+
unset_str_builder(work_item.attributes.status),
676+
work_item.attributes.additional_properties,
677+
work_item_links,
678+
work_item_attachments,
679+
links_truncated,
680+
attachments_truncated,
681+
)
682+
return work_item_obj
683+
644684
def get_document(
645685
self,
646686
space_id: str,

polarion_rest_api_client/data_models.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ class WorkItem(BaseItem):
6868
additional_attributes: dict[str, t.Any] = {}
6969
linked_work_items: list[WorkItemLink] = []
7070
attachments: list[WorkItemAttachment] = []
71+
linked_work_items_truncated: bool = False
72+
attachments_truncated: bool = False
7173

7274
def __init__(
7375
self,
@@ -80,6 +82,8 @@ def __init__(
8082
additional_attributes: dict[str, t.Any] | None = None,
8183
linked_work_items: list[WorkItemLink] | None = None,
8284
attachments: list[WorkItemAttachment] | None = None,
85+
linked_work_items_truncated: bool = False,
86+
attachments_truncated: bool = False,
8387
**kwargs,
8488
):
8589
super().__init__(id, type, status)
@@ -90,6 +94,8 @@ def __init__(
9094
self._checksum = self.additional_attributes.pop("checksum", None)
9195
self.linked_work_items = linked_work_items or []
9296
self.attachments = attachments or []
97+
self.linked_work_items_truncated = linked_work_items_truncated
98+
self.attachments_truncated = attachments_truncated
9399

94100
def __getattribute__(self, item: str) -> t.Any:
95101
"""Return all non WorkItem attributes from additional_properties."""

tests/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@
5959
TEST_RESPONSES / "workitems_next_page_error.json"
6060
)
6161
TEST_WI_NEXT_PAGE_RESPONSE = TEST_RESPONSES / "workitems_next_page.json"
62+
TEST_WI_SINGLE_RESPONSE = TEST_RESPONSES / "get_work_item.json"
63+
TEST_WI_NOT_TRUNCATED_RESPONSE = (
64+
TEST_RESPONSES / "get_work_item_not_truncated.json"
65+
)
6266
TEST_DOCUMENT_RESPONSE = TEST_RESPONSES / "get_document.json"
6367
TEST_ERROR_RESPONSE = TEST_RESPONSES / "error.json"
6468
TEST_PROJECT_RESPONSE_JSON = TEST_RESPONSES / "project.json"

0 commit comments

Comments
 (0)