Skip to content

Commit b43ba44

Browse files
authored
merge: Merge pull request #61 from DSD-DBS/test-parameters
feat: Implement testparameter endpoints
2 parents 0131540 + 3261fd5 commit b43ba44

21 files changed

+864
-4
lines changed

polarion_rest_api_client/client.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class DefaultFields:
2626
_testrecords: str = "@basic"
2727
_testruns: str = "@basic"
2828
_teststeps: str = "@basic"
29+
_testparameters: str = "@all"
2930

3031
@property
3132
def workitems(self) -> dict[str, str]:
@@ -65,7 +66,7 @@ def documents(self, value: str):
6566

6667
@property
6768
def testruns(self) -> dict[str, str]:
68-
"""Return the fields dict for document."""
69+
"""Return the fields dict for testruns."""
6970
return {"testruns": self._testruns}
7071

7172
@testruns.setter
@@ -74,7 +75,7 @@ def testruns(self, value: str):
7475

7576
@property
7677
def testrecords(self) -> dict[str, str]:
77-
"""Return the fields dict for document."""
78+
"""Return the fields dict for testrecords."""
7879
return {"testrecords": self._testrecords}
7980

8081
@testrecords.setter
@@ -83,13 +84,22 @@ def testrecords(self, value: str):
8384

8485
@property
8586
def teststeps(self) -> dict[str, str]:
86-
"""Return the fields dict for document."""
87+
"""Return the fields dict for teststeps."""
8788
return {"teststeps": self._teststeps}
8889

8990
@teststeps.setter
9091
def teststeps(self, value: str):
9192
self._teststeps = value
9293

94+
@property
95+
def testparameters(self) -> dict[str, str]:
96+
"""Return the fields dict for testparameters."""
97+
return {"testparameters": self._testparameters}
98+
99+
@testparameters.setter
100+
def testparameters(self, value: str):
101+
self._testparameters = value
102+
93103
@property
94104
def all_types(self) -> dict[str, str]:
95105
"""Return all fields dicts merged together."""
@@ -100,6 +110,7 @@ def all_types(self) -> dict[str, str]:
100110
| self.documents
101111
| self.testruns
102112
| self.testrecords
113+
| self.testparameters
103114
)
104115

105116

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
# Copyright DB InfraGO AG and contributors
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""Clients to handle test parameters."""
4+
5+
import itertools
6+
import typing as t
7+
8+
from polarion_rest_api_client import data_models as dm
9+
from polarion_rest_api_client.open_api_client import models as api_models
10+
from polarion_rest_api_client.open_api_client.api.test_records import (
11+
delete_test_record_test_parameter,
12+
get_test_record_test_parameters,
13+
post_test_record_test_parameters,
14+
)
15+
from polarion_rest_api_client.open_api_client.api.test_runs import (
16+
delete_test_run_test_parameters,
17+
get_test_run_test_parameters,
18+
post_test_run_test_parameters,
19+
)
20+
21+
from . import base_classes as bc
22+
23+
24+
class TestRunParameters(bc.ItemsClient[dm.TestRunParameter]):
25+
"""Client to handle TestParameters of a TestRun."""
26+
27+
def get(self, *args, **kwargs) -> dm.TestRunParameter:
28+
raise NotImplementedError
29+
30+
def get_multi( # type: ignore[override]
31+
self,
32+
test_run_id: str,
33+
*,
34+
page_size: int = 100,
35+
page_number: int = 1,
36+
fields: dict[str, str] | None = None,
37+
) -> tuple[list[dm.TestRunParameter], bool]:
38+
if fields is None:
39+
fields = self._client.default_fields.testparameters
40+
41+
sparse_fields = self._build_sparse_fields(fields)
42+
response = get_test_run_test_parameters.sync_detailed(
43+
self._project_id,
44+
test_run_id,
45+
client=self._client.client,
46+
fields=sparse_fields,
47+
pagenumber=page_number,
48+
pagesize=page_size,
49+
)
50+
51+
self._raise_on_error(response)
52+
return _parse_get_response(response.parsed, test_run_id)
53+
54+
def _split_into_batches(
55+
self, items: list[dm.TestRunParameter]
56+
) -> t.Generator[list[dm.TestRunParameter], None, None]:
57+
for _, group in itertools.groupby(
58+
sorted(
59+
items,
60+
key=lambda x: x.test_run_id,
61+
),
62+
lambda x: x.test_run_id,
63+
):
64+
yield list(group)
65+
66+
def _create(self, items: list[dm.TestRunParameter]):
67+
"""Call only with items with common test_run_id."""
68+
test_run_id = items[0].test_run_id
69+
body = _build_post_body(items)
70+
71+
response = post_test_run_test_parameters.sync_detailed(
72+
self._project_id,
73+
test_run_id,
74+
client=self._client.client,
75+
body=body,
76+
)
77+
self._raise_on_error(response)
78+
79+
assert (
80+
isinstance(
81+
response.parsed, api_models.TestparametersListPostResponse
82+
)
83+
and response.parsed.data
84+
)
85+
86+
def _delete(self, items: list[dm.TestRunParameter]):
87+
"""Call only with items with common test_run_id."""
88+
test_run_id = items[0].test_run_id
89+
body = api_models.TestparametersListDeleteRequest(
90+
data=[
91+
api_models.TestparametersListDeleteRequestDataItem(
92+
type_=api_models.TestparametersListDeleteRequestDataItemType.TESTPARAMETERS,
93+
id=f"{self._project_id}/{item.test_run_id}/{item.name}",
94+
)
95+
for item in items
96+
]
97+
)
98+
99+
response = delete_test_run_test_parameters.sync_detailed(
100+
self._project_id,
101+
test_run_id,
102+
client=self._client.client,
103+
body=body,
104+
)
105+
self._raise_on_error(response)
106+
107+
108+
class TestRecordParameters(bc.ItemsClient[dm.TestRecordParameter]):
109+
"""Clients to handle TestParameters of a TestRecord."""
110+
111+
def get(self, *args, **kwargs) -> dm.TestRecordParameter:
112+
raise NotImplementedError
113+
114+
def get_multi( # type: ignore[override]
115+
self,
116+
test_record: dm.TestRecord,
117+
*,
118+
page_size: int = 100,
119+
page_number: int = 1,
120+
fields: dict[str, str] | None = None,
121+
) -> tuple[list[dm.TestRecordParameter], bool]:
122+
if fields is None:
123+
fields = self._client.default_fields.testparameters
124+
125+
sparse_fields = self._build_sparse_fields(fields)
126+
response = get_test_record_test_parameters.sync_detailed(
127+
self._project_id,
128+
test_record.test_run_id,
129+
test_record.work_item_project_id,
130+
test_record.work_item_id,
131+
str(test_record.iteration),
132+
client=self._client.client,
133+
fields=sparse_fields,
134+
pagenumber=page_number,
135+
pagesize=page_size,
136+
)
137+
138+
self._raise_on_error(response)
139+
140+
return _parse_get_response(response.parsed, test_record)
141+
142+
def _split_into_batches(
143+
self, items: list[dm.TestRecordParameter]
144+
) -> t.Generator[list[dm.TestRecordParameter], None, None]:
145+
for _, group in itertools.groupby(
146+
sorted(
147+
items,
148+
key=lambda x: (
149+
f"{x.test_record.test_run_id}/{x.test_record.work_item_project_id}/{x.test_record.work_item_id}/{x.test_record.iteration}"
150+
),
151+
),
152+
lambda x: x.test_record,
153+
):
154+
yield list(group)
155+
156+
def _create(self, items: list[dm.TestRecordParameter]):
157+
"""Call only with items with common test_records."""
158+
test_record = items[0].test_record
159+
body = _build_post_body(items)
160+
161+
response = post_test_record_test_parameters.sync_detailed(
162+
self._project_id,
163+
test_record.test_run_id,
164+
test_record.work_item_project_id,
165+
test_record.work_item_id,
166+
str(test_record.iteration),
167+
client=self._client.client,
168+
body=body,
169+
)
170+
self._raise_on_error(response)
171+
172+
def _delete(self, items: list[dm.TestRecordParameter]):
173+
"""We expect only one TestRecordParameter for deletion."""
174+
assert len(items) == 1
175+
test_record = items[0].test_record
176+
177+
response = delete_test_record_test_parameter.sync_detailed(
178+
self._project_id,
179+
test_record.test_run_id,
180+
test_record.work_item_project_id,
181+
test_record.work_item_id,
182+
str(test_record.iteration),
183+
items[0].name,
184+
client=self._client.client,
185+
)
186+
self._raise_on_error(response)
187+
188+
def delete(
189+
self, items: dm.TestRecordParameter | list[dm.TestRecordParameter]
190+
):
191+
if not isinstance(items, list):
192+
items = [items]
193+
194+
for item in items:
195+
self._delete([item])
196+
197+
198+
@t.overload
199+
def _parse_get_response(
200+
parsed_response: (
201+
api_models.TestparametersListGetResponse | api_models.Errors | None
202+
),
203+
scope: dm.TestRecord,
204+
) -> tuple[list[dm.TestRecordParameter], bool]: ...
205+
206+
207+
@t.overload
208+
def _parse_get_response(
209+
parsed_response: (
210+
api_models.TestparametersListGetResponse | api_models.Errors | None
211+
),
212+
scope: str,
213+
) -> tuple[list[dm.TestRunParameter], bool]: ...
214+
215+
216+
def _parse_get_response(
217+
parsed_response: (
218+
api_models.TestparametersListGetResponse | api_models.Errors | None
219+
),
220+
scope: str | dm.TestRecord,
221+
) -> tuple[list[dm.TestRecordParameter] | list[dm.TestRunParameter], bool]:
222+
assert isinstance(
223+
parsed_response, api_models.TestparametersListGetResponse
224+
)
225+
next_page = isinstance(
226+
parsed_response.links, api_models.TestparametersListGetResponseLinks
227+
) and bool(parsed_response.links.next_)
228+
if isinstance(scope, str):
229+
return [
230+
dm.TestRunParameter(
231+
test_run_id=scope,
232+
name=data.attributes.name or "",
233+
value=data.attributes.value or "",
234+
)
235+
for data in parsed_response.data or []
236+
if data.attributes
237+
], next_page
238+
239+
return [
240+
dm.TestRecordParameter(
241+
test_record=scope,
242+
name=data.attributes.name or "",
243+
value=data.attributes.value or "",
244+
)
245+
for data in parsed_response.data or []
246+
if data.attributes
247+
], next_page
248+
249+
250+
def _build_post_body(
251+
items: list[dm.TestRunParameter] | list[dm.TestRecordParameter],
252+
) -> api_models.TestparametersListPostRequest:
253+
body = api_models.TestparametersListPostRequest(
254+
data=[
255+
api_models.TestparametersListPostRequestDataItem(
256+
type_=api_models.TestparametersListPostRequestDataItemType.TESTPARAMETERS,
257+
attributes=api_models.TestparametersListPostRequestDataItemAttributes(
258+
name=item.name, value=item.value
259+
),
260+
)
261+
for item in items
262+
]
263+
)
264+
return body

polarion_rest_api_client/clients/test_records.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Copyright DB InfraGO AG and contributors
22
# SPDX-License-Identifier: Apache-2.0
3+
from __future__ import annotations
34

45
import itertools
56
import typing as t
@@ -14,12 +15,24 @@
1415
)
1516

1617
from . import base_classes as bc
18+
from . import test_parameters
19+
20+
if t.TYPE_CHECKING:
21+
from polarion_rest_api_client import client as polarion_client
1722

1823

1924
class TestRecords(
2025
bc.SingleUpdatableItemsMixin[dm.TestRecord],
2126
bc.UpdatableItemsClient[dm.TestRecord],
2227
):
28+
def __init__(
29+
self, project_id: str, client: polarion_client.PolarionClient
30+
):
31+
super().__init__(project_id, client)
32+
self.parameters = test_parameters.TestRecordParameters(
33+
project_id, client
34+
)
35+
2336
def get(self, *args, **kwargs) -> dm.TestRecord:
2437
raise NotImplementedError
2538

polarion_rest_api_client/clients/test_runs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
)
1313

1414
from . import base_classes as bc
15-
from . import test_records
15+
from . import test_parameters, test_records
1616

1717
if t.TYPE_CHECKING:
1818
from polarion_rest_api_client import client as polarion_client
@@ -30,6 +30,7 @@ def __init__(
3030
):
3131
super().__init__(project_id, client)
3232
self.records = test_records.TestRecords(project_id, client)
33+
self.parameters = test_parameters.TestRunParameters(project_id, client)
3334

3435
def _update(self, to_update: list[dm.TestRun] | dm.TestRun):
3536
"""Create the given list of test runs."""

0 commit comments

Comments
 (0)