Skip to content

Commit 76edb5b

Browse files
committed
Improvements
1 parent a90d91f commit 76edb5b

File tree

4 files changed

+65
-139
lines changed

4 files changed

+65
-139
lines changed

polarion_rest_api_client/client.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import random
1111
import time
1212
import typing as t
13+
import urllib
1314

1415
from polarion_rest_api_client import base_client
1516
from polarion_rest_api_client import data_models as dm
@@ -652,8 +653,12 @@ def get_document(
652653
"""Return the document with the given document_name and space_id."""
653654

654655
if " " in space_id or " " in document_name:
655-
space_id = space_id.replace(" ", "%20")
656-
document_name = document_name.replace(" ", "%20")
656+
space_id = urllib.parse.quote(
657+
space_id, safe="/", encoding=None, errors=None
658+
)
659+
document_name = urllib.parse.quote(
660+
document_name, safe="/", encoding=None, errors=None
661+
)
657662
if fields is None:
658663
fields = self.default_fields.documents
659664

@@ -670,7 +675,7 @@ def get_document(
670675

671676
if not self._check_response(response, not retry) and retry:
672677
sleep_random_time()
673-
return self.get_work_items(
678+
return self.get_document(
674679
space_id, document_name, fields, include, revision, False
675680
)
676681

polarion_rest_api_client/data_models.py

Lines changed: 54 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -9,60 +9,26 @@
99
import typing as t
1010

1111

12-
class WorkItem:
13-
"""A data class containing all relevant data of a Polarion WorkItem."""
14-
12+
class BaseItem:
1513
id: str | None = None
16-
title: str | None = None
17-
description_type: str | None = None
18-
description: str | None = None
1914
type: str | None = None
2015
status: str | None = None
21-
additional_attributes: dict[str, t.Any] = {}
22-
linked_work_items: list[WorkItemLink] = []
23-
attachments: list[WorkItemAttachment] = []
2416
_checksum: str | None = None
2517

2618
def __init__(
2719
self,
2820
id: str | None = None,
29-
title: str | None = None,
30-
description_type: str | None = None,
31-
description: str | None = None,
3221
type: str | None = None,
3322
status: str | None = None,
34-
additional_attributes: dict[str, t.Any] | None = None,
35-
linked_work_items: list[WorkItemLink] | None = None,
36-
attachments: list[WorkItemAttachment] | None = None,
37-
**kwargs,
3823
):
3924
self.id = id
40-
self.title = title
41-
self.description_type = description_type
42-
self.description = description
4325
self.type = type
4426
self.status = status
45-
self.additional_attributes = (additional_attributes or {}) | kwargs
46-
self._checksum = self.additional_attributes.pop("checksum", None)
47-
self.linked_work_items = linked_work_items or []
48-
self.attachments = attachments or []
49-
50-
def __getattribute__(self, item: str) -> t.Any:
51-
"""Return all non WorkItem attributes from additional_properties."""
52-
if item.startswith("__") or item in dir(WorkItem):
53-
return super().__getattribute__(item)
54-
return self.additional_attributes.get(item)
55-
56-
def __setattr__(self, key: str, value: t.Any):
57-
"""Set all non WorkItem attributes in additional_properties."""
58-
if key in dir(WorkItem):
59-
super().__setattr__(key, value)
60-
else:
61-
self.additional_attributes[key] = value
27+
self._checksum = None
6228

6329
def __eq__(self, other: object) -> bool:
64-
"""Compare only WorkItem attributes."""
65-
if not isinstance(other, WorkItem):
30+
"""Compare only BaseItem attributes."""
31+
if not isinstance(other, BaseItem):
6632
return NotImplemented
6733
if self.get_current_checksum() is None:
6834
self.calculate_checksum()
@@ -71,48 +37,24 @@ def __eq__(self, other: object) -> bool:
7137

7238
return self.get_current_checksum() == other.get_current_checksum()
7339

74-
def to_dict(self) -> dict[str, t.Any]:
75-
"""Return the content of the WorkItem as dictionary."""
76-
sorted_links = sorted(
77-
self.linked_work_items,
78-
key=lambda x: f"{x.role}/{x.secondary_work_item_project}/{x.secondary_work_item_id}", # pylint: disable=line-too-long
79-
)
80-
81-
sorted_attachments = sorted(
82-
self.attachments, key=lambda x: x.file_name or ""
83-
)
84-
40+
def to_dict(self) -> dict[str, Any]:
41+
"""Return the content of the BaseItem as dictionary."""
8542
return {
8643
"id": self.id,
87-
"title": self.title,
88-
"description_type": self.description_type,
89-
"description": self.description,
9044
"type": self.type,
9145
"status": self.status,
92-
"additional_attributes": dict(
93-
sorted(self.additional_attributes.items())
94-
),
9546
"checksum": self._checksum,
96-
"linked_work_items": [
97-
dataclasses.asdict(lwi) for lwi in sorted_links
98-
],
99-
"attachments": [
100-
dataclasses.asdict(at) for at in sorted_attachments
101-
],
10247
}
10348

10449
def calculate_checksum(self) -> str:
105-
"""Calculate and return a checksum for this WorkItem.
50+
"""Calculate and return a checksum for this BaseItem.
10651
10752
In addition, the checksum will be written to self._checksum.
10853
"""
10954
data = self.to_dict()
11055
del data["checksum"]
11156
del data["id"]
11257

113-
for at in data["attachments"]:
114-
del at["id"]
115-
11658
data = dict(sorted(data.items()))
11759

11860
converted = json.dumps(data).encode("utf8")
@@ -124,6 +66,51 @@ def get_current_checksum(self) -> str | None:
12466
return self._checksum
12567

12668

69+
class WorkItem(BaseItem):
70+
"""A data class containing all relevant data of a Polarion WorkItem."""
71+
72+
title: str | None = None
73+
description_type: str | None = None
74+
description: str | None = None
75+
additional_attributes: dict[str, Any] = {}
76+
linked_work_items: list[WorkItemLink] = []
77+
attachments: list[WorkItemAttachment] = []
78+
79+
def __init__(
80+
self,
81+
id: str | None = None,
82+
title: str | None = None,
83+
description_type: str | None = None,
84+
description: str | None = None,
85+
type: str | None = None,
86+
status: str | None = None,
87+
additional_attributes: dict[str, Any] | None = None,
88+
linked_work_items: list[WorkItemLink] | None = None,
89+
attachments: list[WorkItemAttachment] | None = None,
90+
**kwargs,
91+
):
92+
super().__init__(id, type, status)
93+
self.title = title
94+
self.description_type = description_type
95+
self.description = description
96+
self.additional_attributes = (additional_attributes or {}) | kwargs
97+
self.linked_work_items = linked_work_items or []
98+
self.attachments = attachments or []
99+
100+
def __getattribute__(self, item: str) -> Any:
101+
"""Return all non WorkItem attributes from additional_properties."""
102+
if item.startswith("__") or item in dir(WorkItem):
103+
return super().__getattribute__(item)
104+
return self.additional_attributes.get(item)
105+
106+
def __setattr__(self, key: str, value: Any):
107+
"""Set all non WorkItem attributes in additional_properties."""
108+
if key in dir(WorkItem):
109+
super().__setattr__(key, value)
110+
else:
111+
self.additional_attributes[key] = value
112+
113+
127114
@dataclasses.dataclass
128115
class WorkItemLink:
129116
"""A link between multiple Polarion WorkItems.
@@ -153,16 +140,10 @@ class WorkItemAttachment:
153140
file_name: str | None = None
154141

155142

156-
class Document:
157-
"""A data class containing all relevant data of a Polarion WorkItem."""
158-
159-
id: str | None = None
143+
class Document(BaseItem):
160144
module_folder: str | None = None
161145
module_name: str | None = None
162-
type: str | None = None
163-
status: str | None = None
164146
home_page_content: dict | None = None
165-
_checksum: str | None = None
166147

167148
def __init__(
168149
self,
@@ -173,52 +154,7 @@ def __init__(
173154
status: str | None = None,
174155
home_page_content: dict | None = None,
175156
):
176-
self.id = id
157+
super().__init__(id, type, status)
177158
self.module_folder = module_folder
178159
self.module_name = module_name
179-
self.type = type
180-
self.status = status
181160
self.home_page_content = home_page_content
182-
self._checksum = None
183-
184-
def __eq__(self, other: object) -> bool:
185-
"""Compare only Document attributes."""
186-
if not isinstance(other, Document):
187-
return NotImplemented
188-
if self.get_current_checksum() is None:
189-
self.calculate_checksum()
190-
if other.get_current_checksum() is None:
191-
other.calculate_checksum()
192-
193-
return self.get_current_checksum() == other.get_current_checksum()
194-
195-
def to_dict(self) -> dict[str, t.Any]:
196-
"""Return the content of the Document as dictionary."""
197-
return {
198-
"id": self.id,
199-
"module_folder": self.module_folder,
200-
"module_name": self.module_name,
201-
"type": self.type,
202-
"status": self.status,
203-
"home_page_content": self.home_page_content,
204-
"checksum": self._checksum,
205-
}
206-
207-
def calculate_checksum(self) -> str:
208-
"""Calculate and return a checksum for this Document.
209-
210-
In addition, the checksum will be written to self._checksum.
211-
"""
212-
data = self.to_dict()
213-
del data["checksum"]
214-
del data["id"]
215-
216-
data = dict(sorted(data.items()))
217-
218-
converted = json.dumps(data).encode("utf8")
219-
self._checksum = hashlib.sha256(converted).hexdigest()
220-
return self._checksum
221-
222-
def get_current_checksum(self) -> str | None:
223-
"""Return the checksum currently set without calculation."""
224-
return self._checksum

tests/conftest.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,3 @@ def fixture_dummy_work_item_attachment():
6969
"text/plain",
7070
"test.json",
7171
)
72-
73-
74-
@pytest.fixture(name="document")
75-
def fixture_dummy_document():
76-
return polarion_api.Document(
77-
"MyProjectId/MySpaceId/MyDocumentName",
78-
"MySpaceId",
79-
"MyDocumentName",
80-
"standardSpecification",
81-
"open",
82-
{"type": "text/html", "value": "<h1>My text value</h1>"},
83-
)

tests/test_client_documents.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@
33

44
from __future__ import annotations
55

6-
import copy
76
import json
87

9-
import pytest
108
import pytest_httpx
11-
import pytest_mock as mock
129

1310
import polarion_rest_api_client as polarion_api
1411
from polarion_rest_api_client.open_api_client import models as api_models
@@ -22,9 +19,9 @@ def test_get_document_with_all_fields(
2219
with open(TEST_DOCUMENT_RESPONSE, encoding="utf8") as f:
2320
httpx_mock.add_response(json=json.load(f))
2421

25-
client.default_fields.documents = "@all"
26-
27-
document = client.get_document("MySpaceId", "MyDocumentName")
22+
document = client.get_document(
23+
"MySpaceId", "MyDocumentName", {"fields[documents]": "@all"}
24+
)
2825

2926
reqs = httpx_mock.get_requests()
3027
assert reqs[0].method == "GET"

0 commit comments

Comments
 (0)