Skip to content

Commit 8cbcc8c

Browse files
authored
Merge pull request #69 from Mephody/v5_support
Report Portal version 5 support.
2 parents 715dfc7 + 36cfd30 commit 8cbcc8c

File tree

4 files changed

+76
-412
lines changed

4 files changed

+76
-412
lines changed

reportportal_client/__init__.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,9 @@
1313
# limitations under the License.
1414

1515
from .service import ReportPortalService
16-
from .service_async import ReportPortalServiceAsync
17-
1816

1917
__all__ = (
2018
ReportPortalService,
21-
ReportPortalServiceAsync,
2219
)
2320

2421
POST_LOGBATCH_RETRY_COUNT = 10

reportportal_client/errors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,5 @@ class EntryCreatedError(ResponseError):
3131
class OperationCompletionError(ResponseError):
3232
"""Represents error in case of operation failure.
3333
34-
No 'msg' in the json response.
34+
No 'message' in the json response.
3535
"""

reportportal_client/service.py

Lines changed: 75 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import uuid
2020
import logging
2121

22+
import six
2223
from requests.adapters import HTTPAdapter
2324

2425
from .errors import ResponseError, EntryCreatedError, OperationCompletionError
@@ -27,6 +28,19 @@
2728
logger.addHandler(logging.NullHandler())
2829

2930

31+
def _dict_to_payload(dictionary):
32+
def _str(value):
33+
if isinstance(value, six.text_type):
34+
# Don't try to encode 'unicode' in Python 2.
35+
return value
36+
return str(value)
37+
38+
return [
39+
{"key": key, "value": _str(value)}
40+
for key, value in dictionary.items()
41+
]
42+
43+
3044
def _get_id(response):
3145
try:
3246
return _get_data(response)["id"]
@@ -37,10 +51,10 @@ def _get_id(response):
3751

3852
def _get_msg(response):
3953
try:
40-
return _get_data(response)["msg"]
54+
return _get_data(response)
4155
except KeyError:
4256
raise OperationCompletionError(
43-
"No 'msg' in response: {0}".format(response.text))
57+
"No 'message' in response: {0}".format(response.text))
4458

4559

4660
def _get_data(response):
@@ -75,12 +89,10 @@ def _get_json(response):
7589
def _get_messages(data):
7690
error_messages = []
7791
for ret in data.get("responses", [data]):
78-
if "message" in ret:
79-
if "error_code" in ret:
80-
error_messages.append(
81-
"{0}: {1}".format(ret["error_code"], ret["message"]))
82-
else:
83-
error_messages.append(ret["message"])
92+
if "errorCode" in ret:
93+
error_messages.append(
94+
"{0}: {1}".format(ret["errorCode"], ret.get("message"))
95+
)
8496

8597
return error_messages
8698

@@ -104,172 +116,153 @@ def uri_join(*uri_parts):
104116
class ReportPortalService(object):
105117
"""Service class with report portal event callbacks."""
106118

107-
def __init__(self, endpoint, project, token, api_base="api/v1",
119+
def __init__(self, endpoint, project, token,
108120
is_skipped_an_issue=True, verify_ssl=True, retries=None):
109121
"""Init the service class.
110122
111123
Args:
112124
endpoint: endpoint of report portal service.
113125
project: project name to use for launch names.
114126
token: authorization token.
115-
api_base: defaults to api/v1, can be changed to other version.
116127
is_skipped_an_issue: option to mark skipped tests as not
117128
'To Investigate' items on Server side.
118129
verify_ssl: option to not verify ssl certificates
119130
"""
120131
super(ReportPortalService, self).__init__()
121132
self.endpoint = endpoint
122-
self.api_base = api_base
123133
self.project = project
124134
self.token = token
125135
self.is_skipped_an_issue = is_skipped_an_issue
126-
self.base_url = uri_join(self.endpoint,
127-
self.api_base,
128-
self.project)
136+
self.base_url_v1 = uri_join(self.endpoint, "api/v1", self.project)
137+
self.base_url_v2 = uri_join(self.endpoint, "api/v2", self.project)
129138

130139
self.session = requests.Session()
131140
if retries:
132141
self.session.mount('https://', HTTPAdapter(max_retries=retries))
133142
self.session.mount('http://', HTTPAdapter(max_retries=retries))
134143
self.session.headers["Authorization"] = "bearer {0}".format(self.token)
135-
self.stack = [None]
136144
self.launch_id = None
137145
self.verify_ssl = verify_ssl
138146

139-
def terminate(self):
147+
def terminate(self, *args, **kwargs):
140148
pass
141149

142-
def start_launch(self, name, start_time, description=None, tags=None,
150+
def start_launch(self, name, start_time, description=None, attributes=None,
143151
mode=None):
152+
if attributes is not None:
153+
attributes = _dict_to_payload(attributes)
144154
data = {
145155
"name": name,
146156
"description": description,
147-
"tags": tags,
148-
"start_time": start_time,
157+
"attributes": attributes,
158+
"startTime": start_time,
149159
"mode": mode
150160
}
151-
url = uri_join(self.base_url, "launch")
161+
url = uri_join(self.base_url_v2, "launch")
152162
r = self.session.post(url=url, json=data, verify=self.verify_ssl)
153163
self.launch_id = _get_id(r)
154-
self.stack.append(None)
155-
logger.debug("start_launch - Stack: %s", self.stack)
164+
logger.debug("start_launch - ID: %s", self.launch_id)
156165
return self.launch_id
157166

158-
def _finalize_launch(self, end_time, action, status):
167+
def finish_launch(self, end_time, status=None):
168+
"""
169+
status can be (PASSED, FAILED, STOPPED, SKIPPED, RESETED, CANCELLED)
170+
"""
159171
data = {
160-
"end_time": end_time,
172+
"endTime": end_time,
161173
"status": status
162174
}
163-
url = uri_join(self.base_url, "launch", self.launch_id, action)
175+
url = uri_join(self.base_url_v1, "launch", self.launch_id, "finish")
164176
r = self.session.put(url=url, json=data, verify=self.verify_ssl)
165-
self.stack.pop()
166-
logger.debug("%s_launch - Stack: %s", action, self.stack)
177+
logger.debug("finish_launch - ID: %s", self.launch_id)
167178
return _get_msg(r)
168179

169-
def finish_launch(self, end_time, status=None):
170-
return self._finalize_launch(end_time=end_time, action="finish",
171-
status=status)
172-
173-
def stop_launch(self, end_time, status=None):
174-
return self._finalize_launch(end_time=end_time, action="stop",
175-
status=status)
176-
177180
def start_test_item(self, name, start_time, item_type, description=None,
178-
tags=None, parameters=None):
181+
attributes=None, parameters=None, parent_item_id=None):
179182
"""
180183
item_type can be (SUITE, STORY, TEST, SCENARIO, STEP, BEFORE_CLASS,
181184
BEFORE_GROUPS, BEFORE_METHOD, BEFORE_SUITE, BEFORE_TEST, AFTER_CLASS,
182185
AFTER_GROUPS, AFTER_METHOD, AFTER_SUITE, AFTER_TEST)
183186
184-
parameters should be a dictionary with the following format:
187+
attributes and parameters should be a dictionary
188+
with the following format:
185189
{
186190
"<key1>": "<value1>",
187191
"<key2>": "<value2>",
188192
...
189193
}
190194
"""
195+
if attributes is not None:
196+
attributes = _dict_to_payload(attributes)
191197
if parameters is not None:
192-
parameters = [{"key": key, "value": str(value)}
193-
for key, value in parameters.items()]
198+
parameters = _dict_to_payload(parameters)
194199

195200
data = {
196201
"name": name,
197202
"description": description,
198-
"tags": tags,
199-
"start_time": start_time,
200-
"launch_id": self.launch_id,
203+
"attributes": attributes,
204+
"startTime": start_time,
205+
"launchUuid": self.launch_id,
201206
"type": item_type,
202207
"parameters": parameters,
203208
}
204-
parent_item_id = self.stack[-1]
205209
if parent_item_id is not None:
206-
url = uri_join(self.base_url, "item", parent_item_id)
210+
url = uri_join(self.base_url_v2, "item", parent_item_id)
207211
else:
208-
url = uri_join(self.base_url, "item")
212+
url = uri_join(self.base_url_v2, "item")
209213
r = self.session.post(url=url, json=data, verify=self.verify_ssl)
210214

211215
item_id = _get_id(r)
212-
self.stack.append(item_id)
213-
logger.debug("start_test_item - Stack: %s", self.stack)
216+
logger.debug("start_test_item - ID: %s", item_id)
214217
return item_id
215218

216-
def update_test_item(self, description=None, tags=None):
217-
"""Update test item.
218-
219-
:param str description: test item description
220-
:param list tags: test item tags
221-
"""
222-
data = {
223-
"description": description,
224-
"tags": tags,
225-
}
226-
227-
item_id = self.stack[-1]
228-
url = uri_join(self.base_url, "item", item_id, "update")
229-
r = self.session.put(url=url, json=data, verify=self.verify_ssl)
230-
logger.debug("update_test_item - Stack: %s", self.stack)
231-
return _get_msg(r)
232-
233-
def finish_test_item(self, end_time, status, issue=None):
219+
def finish_test_item(self, item_id, end_time, status,
220+
issue=None, attributes=None):
234221
# check if skipped test should not be marked as "TO INVESTIGATE"
235222
if issue is None and status == "SKIPPED" \
236223
and not self.is_skipped_an_issue:
237224
issue = {"issue_type": "NOT_ISSUE"}
238225

226+
if attributes is not None:
227+
attributes = _dict_to_payload(attributes)
228+
239229
data = {
240-
"end_time": end_time,
230+
"endTime": end_time,
241231
"status": status,
242232
"issue": issue,
233+
"launchUuid": self.launch_id,
234+
"attributes": attributes
243235
}
244-
item_id = self.stack.pop()
245-
url = uri_join(self.base_url, "item", item_id)
236+
url = uri_join(self.base_url_v2, "item", item_id)
246237
r = self.session.put(url=url, json=data, verify=self.verify_ssl)
247-
logger.debug("finish_test_item - Stack: %s", self.stack)
238+
logger.debug("finish_test_item - ID: %s", item_id)
248239
return _get_msg(r)
249240

250241
def get_project_settings(self):
251-
url = uri_join(self.base_url, "settings")
242+
url = uri_join(self.base_url_v1, "settings")
252243
r = self.session.get(url=url, json={}, verify=self.verify_ssl)
253-
logger.debug("settings - Stack: %s", self.stack)
244+
logger.debug("settings")
254245
return _get_json(r)
255246

256-
def log(self, time, message, level=None, attachment=None):
247+
def log(self, time, message, level=None, attachment=None, item_id=None):
257248
data = {
258-
"item_id": self.stack[-1] or self.launch_id,
249+
"launchUuid": self.launch_id,
259250
"time": time,
260251
"message": message,
261252
"level": level,
262253
}
254+
if item_id:
255+
data["itemUuid"] = item_id
263256
if attachment:
264257
data["attachment"] = attachment
265-
return self.log_batch([data])
258+
return self.log_batch([data], item_id=item_id)
266259
else:
267-
url = uri_join(self.base_url, "log")
260+
url = uri_join(self.base_url_v2, "log")
268261
r = self.session.post(url=url, json=data, verify=self.verify_ssl)
269-
logger.debug("log - Stack: %s", self.stack)
262+
logger.debug("log - ID: %s", item_id)
270263
return _get_id(r)
271264

272-
def log_batch(self, log_data):
265+
def log_batch(self, log_data, item_id=None):
273266
"""Logs batch of messages with attachment.
274267
275268
Args:
@@ -283,11 +276,13 @@ def log_batch(self, log_data):
283276
284277
"""
285278

286-
url = uri_join(self.base_url, "log")
279+
url = uri_join(self.base_url_v2, "log")
287280

288281
attachments = []
289282
for log_item in log_data:
290-
log_item["item_id"] = self.stack[-1]
283+
if item_id:
284+
log_item["itemUuid"] = item_id
285+
log_item["launchUuid"] = self.launch_id
291286
attachment = log_item.get("attachment", None)
292287

293288
if "attachment" in log_item:
@@ -328,7 +323,7 @@ def log_batch(self, log_data):
328323
raise
329324
break
330325

331-
logger.debug("log_batch - Stack: %s", self.stack)
326+
logger.debug("log_batch - ID: %s", item_id)
332327
logger.debug("log_batch response: %s", r.text)
333328

334329
return _get_data(r)

0 commit comments

Comments
 (0)