1 | # -*- coding: utf-8 -*- |
||
2 | |||
3 | from datetime import timedelta |
||
4 | |||
5 | from django.db.models.functions import Coalesce |
||
6 | from django.forms import EmailField, ValidationError |
||
7 | from django.forms.models import model_to_dict |
||
8 | from modernrpc.core import REQUEST_KEY, rpc_method |
||
9 | |||
10 | from tcms.core import helpers |
||
11 | from tcms.core.utils import form_errors_to_list |
||
12 | from tcms.management.models import Component, Tag |
||
13 | from tcms.rpc import utils |
||
14 | from tcms.rpc.api.forms.testcase import NewForm, UpdateForm |
||
15 | from tcms.rpc.decorators import permissions_required |
||
16 | from tcms.testcases.models import Property, TestCase, TestCasePlan |
||
17 | |||
18 | __all__ = ( |
||
19 | "create", |
||
20 | "update", |
||
21 | "filter", |
||
22 | "history", |
||
23 | "sortkeys", |
||
24 | "remove", |
||
25 | "add_comment", |
||
26 | "remove_comment", |
||
27 | "add_component", |
||
28 | "comments", |
||
29 | "remove_component", |
||
30 | "add_notification_cc", |
||
31 | "get_notification_cc", |
||
32 | "remove_notification_cc", |
||
33 | "add_tag", |
||
34 | "remove_tag", |
||
35 | "add_attachment", |
||
36 | "list_attachments", |
||
37 | "properties", |
||
38 | "remove_property", |
||
39 | "add_property", |
||
40 | ) |
||
41 | |||
42 | |||
43 | @permissions_required("testcases.add_testcasecomponent") |
||
44 | @rpc_method(name="TestCase.add_component") |
||
45 | def add_component(case_id, component): |
||
46 | """ |
||
47 | .. function:: RPC TestCase.add_component(case_id, component) |
||
48 | |||
49 | Add component to the selected test case. |
||
50 | |||
51 | :param case_id: PK of TestCase to modify |
||
52 | :type case_id: int |
||
53 | :param component: Name of Component to add |
||
54 | :type component: str |
||
55 | :return: Serialized :class:`tcms.management.models.Component` object |
||
56 | :rtype: dict |
||
57 | :raises PermissionDenied: if missing the *testcases.add_testcasecomponent* |
||
58 | permission |
||
59 | :raises DoesNotExist: if missing test case or component that match the |
||
60 | specified PKs |
||
61 | """ |
||
62 | case = TestCase.objects.get(pk=case_id) |
||
63 | component_obj = Component.objects.get(name=component, product=case.category.product) |
||
64 | case.add_component(component_obj) |
||
65 | return model_to_dict(component_obj) |
||
66 | |||
67 | |||
68 | @permissions_required("testcases.delete_testcasecomponent") |
||
69 | @rpc_method(name="TestCase.remove_component") |
||
70 | def remove_component(case_id, component_id): |
||
71 | """ |
||
72 | .. function:: RPC TestCase.remove_component(case_id, component_id) |
||
73 | |||
74 | Remove selected component from the selected test case. |
||
75 | |||
76 | :param case_id: PK of TestCase to modify |
||
77 | :type case_id: int |
||
78 | :param component_id: PK of Component to remove |
||
79 | :type component_id: int |
||
80 | :raises PermissionDenied: if missing the *testcases.delete_testcasecomponent* |
||
81 | permission |
||
82 | :raises DoesNotExist: if missing test case or component that match the |
||
83 | specified PKs |
||
84 | """ |
||
85 | TestCase.objects.get(pk=case_id).remove_component( |
||
86 | Component.objects.get(pk=component_id) |
||
87 | ) |
||
88 | |||
89 | |||
90 | def _validate_cc_list(cc_list): |
||
91 | """ |
||
92 | Validate each email address given in argument. Called by |
||
93 | notification RPC methods. |
||
94 | |||
95 | :param cc_list: List of email addresses |
||
96 | :type cc_list: list |
||
97 | :raises TypeError or ValidationError: if addresses are not valid. |
||
98 | """ |
||
99 | |||
100 | if not isinstance(cc_list, list): |
||
101 | raise TypeError("cc_list should be a list object.") |
||
102 | |||
103 | field = EmailField( |
||
104 | required=True, |
||
105 | error_messages={"invalid": "Following email address(es) are invalid: %s"}, |
||
106 | ) |
||
107 | invalid_emails = [] |
||
108 | |||
109 | for item in cc_list: |
||
110 | try: |
||
111 | field.clean(item) |
||
112 | except ValidationError: |
||
113 | invalid_emails.append(item) |
||
114 | |||
115 | if invalid_emails: |
||
116 | raise ValidationError( |
||
117 | field.error_messages["invalid"] % ", ".join(invalid_emails) |
||
118 | ) |
||
119 | |||
120 | |||
121 | @permissions_required("testcases.change_testcase") |
||
122 | @rpc_method(name="TestCase.add_notification_cc") |
||
123 | def add_notification_cc(case_id, cc_list): |
||
124 | """ |
||
125 | .. function:: RPC TestCase.add_notification_cc(case_id, cc_list) |
||
126 | |||
127 | Add email addresses to the notification list of specified TestCase |
||
128 | |||
129 | :param case_id: PK of TestCase to be modified |
||
130 | :type case_id: int |
||
131 | :param cc_list: List of email addresses |
||
132 | :type cc_list: list(str) |
||
133 | :raises TypeError or ValidationError: if email validation fails |
||
134 | :raises PermissionDenied: if missing *testcases.change_testcase* permission |
||
135 | :raises TestCase.DoesNotExist: if object with case_id doesn't exist |
||
136 | """ |
||
137 | |||
138 | _validate_cc_list(cc_list) |
||
139 | |||
140 | test_case = TestCase.objects.get(pk=case_id) |
||
141 | test_case.emailing.add_cc(cc_list) |
||
142 | |||
143 | |||
144 | @permissions_required("testcases.change_testcase") |
||
145 | @rpc_method(name="TestCase.remove_notification_cc") |
||
146 | def remove_notification_cc(case_id, cc_list): |
||
147 | """ |
||
148 | .. function:: RPC TestCase.remove_notification_cc(case_id, cc_list) |
||
149 | |||
150 | Remove email addresses from the notification list of specified TestCase |
||
151 | |||
152 | :param case_id: PK of TestCase to modify |
||
153 | :type case_id: int |
||
154 | :param cc_list: List of email addresses |
||
155 | :type cc_list: list(str) |
||
156 | :raises TypeError or ValidationError: if email validation fails |
||
157 | :raises PermissionDenied: if missing *testcases.change_testcase* permission |
||
158 | :raises TestCase.DoesNotExist: if object with case_id doesn't exist |
||
159 | """ |
||
160 | |||
161 | _validate_cc_list(cc_list) |
||
162 | |||
163 | TestCase.objects.get(pk=case_id).emailing.remove_cc(cc_list) |
||
164 | |||
165 | |||
166 | @permissions_required("testcases.view_testcase") |
||
167 | @rpc_method(name="TestCase.get_notification_cc") |
||
168 | def get_notification_cc(case_id): |
||
169 | """ |
||
170 | .. function:: RPC TestCase.get_notification_cc(case_id) |
||
171 | |||
172 | Return notification list for specified TestCase |
||
173 | |||
174 | :param case_id: PK of TestCase |
||
175 | :type case_id: int |
||
176 | :return: List of email addresses |
||
177 | :rtype: list(str) |
||
178 | :raises TestCase.DoesNotExist: if object with case_id doesn't exist |
||
179 | """ |
||
180 | return TestCase.objects.get(pk=case_id).emailing.get_cc_list() |
||
181 | |||
182 | |||
183 | @permissions_required("testcases.add_testcasetag") |
||
184 | @rpc_method(name="TestCase.add_tag") |
||
185 | def add_tag(case_id, tag, **kwargs): |
||
186 | """ |
||
187 | .. function:: RPC TestCase.add_tag(case_id, tag) |
||
188 | |||
189 | Add one tag to the specified test case. |
||
190 | |||
191 | :param case_id: PK of TestCase to modify |
||
192 | :type case_id: int |
||
193 | :param tag: Tag name to add |
||
194 | :type tag: str |
||
195 | :param \\**kwargs: Dict providing access to the current request, protocol, |
||
196 | entry point name and handler instance from the rpc method |
||
197 | :raises PermissionDenied: if missing *testcases.add_testcasetag* permission |
||
198 | :raises TestCase.DoesNotExist: if object specified by PK doesn't exist |
||
199 | :raises Tag.DoesNotExist: if missing *management.add_tag* permission and *tag* |
||
200 | doesn't exist in the database! |
||
201 | """ |
||
202 | request = kwargs.get(REQUEST_KEY) |
||
203 | tag, _ = Tag.get_or_create(request.user, tag) |
||
204 | TestCase.objects.get(pk=case_id).add_tag(tag) |
||
205 | |||
206 | |||
207 | @permissions_required("testcases.delete_testcasetag") |
||
208 | @rpc_method(name="TestCase.remove_tag") |
||
209 | def remove_tag(case_id, tag): |
||
210 | """ |
||
211 | .. function:: RPC TestCase.remove_tag(case_id, tag) |
||
212 | |||
213 | Remove tag from a test case. |
||
214 | |||
215 | :param case_id: PK of TestCase to modify |
||
216 | :type case_id: int |
||
217 | :param tag: Tag name to remove |
||
218 | :type tag: str |
||
219 | :raises PermissionDenied: if missing *testcases.delete_testcasetag* permission |
||
220 | :raises DoesNotExist: if objects specified don't exist |
||
221 | """ |
||
222 | TestCase.objects.get(pk=case_id).remove_tag(Tag.objects.get(name=tag)) |
||
223 | |||
224 | |||
225 | @permissions_required("testcases.add_testcase") |
||
226 | @rpc_method(name="TestCase.create") |
||
227 | def create(values, **kwargs): |
||
228 | """ |
||
229 | .. function:: RPC TestCase.create(values) |
||
230 | |||
231 | Create a new TestCase object and store it in the database. |
||
232 | |||
233 | :param values: Field values for :class:`tcms.testcases.models.TestCase` |
||
234 | :type values: dict |
||
235 | :param \\**kwargs: Dict providing access to the current request, protocol, |
||
236 | entry point name and handler instance from the rpc method |
||
237 | :return: Serialized :class:`tcms.testcases.models.TestCase` object |
||
238 | :rtype: dict |
||
239 | :raises ValueError: if form is not valid |
||
240 | :raises PermissionDenied: if missing *testcases.add_testcase* permission |
||
241 | |||
242 | Minimal test case parameters:: |
||
243 | |||
244 | >>> values = { |
||
245 | 'category': 135, |
||
246 | 'product': 61, |
||
247 | 'summary': 'Testing XML-RPC', |
||
248 | 'priority': 1, |
||
249 | } |
||
250 | >>> TestCase.create(values) |
||
251 | """ |
||
252 | request = kwargs.get(REQUEST_KEY) |
||
253 | |||
254 | if not (values.get("author") or values.get("author_id")): |
||
255 | values["author"] = request.user.pk |
||
256 | |||
257 | form = NewForm(values) |
||
258 | |||
259 | if form.is_valid(): |
||
260 | test_case = form.save() |
||
261 | result = model_to_dict(test_case, exclude=["component", "plan", "tag"]) |
||
262 | # b/c date is added in the DB layer and model_to_dict() doesn't return it |
||
263 | result["create_date"] = test_case.create_date |
||
264 | result["setup_duration"] = str(result["setup_duration"]) |
||
265 | result["testing_duration"] = str(result["testing_duration"]) |
||
266 | return result |
||
267 | |||
268 | raise ValueError(form_errors_to_list(form)) |
||
269 | |||
270 | |||
271 | @permissions_required("testcases.view_testcase") |
||
272 | @rpc_method(name="TestCase.filter") |
||
273 | def filter(query=None): # pylint: disable=redefined-builtin |
||
274 | """ |
||
275 | .. function:: RPC TestCase.filter(query) |
||
276 | |||
277 | Perform a search and return the resulting list of test cases |
||
278 | augmented with their latest ``text``. |
||
279 | |||
280 | :param query: Field lookups for :class:`tcms.testcases.models.TestCase` |
||
281 | :type query: dict |
||
282 | :return: Serialized list of :class:`tcms.testcases.models.TestCase` objects. |
||
283 | :rtype: list(dict) |
||
284 | """ |
||
285 | if query is None: |
||
286 | query = {} |
||
287 | |||
288 | qs = ( |
||
289 | TestCase.objects.annotate( |
||
290 | expected_duration=Coalesce("setup_duration", timedelta(0)) |
||
291 | + Coalesce("testing_duration", timedelta(0)) |
||
292 | ) |
||
293 | .filter(**query) |
||
294 | .values( |
||
295 | "id", |
||
296 | "create_date", |
||
297 | "is_automated", |
||
298 | "script", |
||
299 | "arguments", |
||
300 | "extra_link", |
||
301 | "summary", |
||
302 | "requirement", |
||
303 | "notes", |
||
304 | "text", |
||
305 | "case_status", |
||
306 | "case_status__name", |
||
307 | "category", |
||
308 | "category__name", |
||
309 | "priority", |
||
310 | "priority__value", |
||
311 | "author", |
||
312 | "author__username", |
||
313 | "default_tester", |
||
314 | "default_tester__username", |
||
315 | "reviewer", |
||
316 | "reviewer__username", |
||
317 | "setup_duration", |
||
318 | "testing_duration", |
||
319 | "expected_duration", |
||
320 | ) |
||
321 | .distinct() |
||
322 | ) |
||
323 | |||
324 | return list(qs) |
||
325 | |||
326 | |||
327 | @permissions_required("testcases.view_testcase") |
||
328 | @rpc_method(name="TestCase.history") |
||
329 | def history(case_id, query=None): |
||
330 | """ |
||
331 | .. function:: RPC TestCase.history(case_id, query) |
||
332 | |||
333 | Return the history for a specified test case. |
||
334 | |||
335 | :param case_id: TestCase PK |
||
336 | :type case_id: int |
||
337 | :param query: Field lookups for :class:`tcms.testcases.models.TestCase` |
||
338 | :type query: dict |
||
339 | :return: Serialized list of HistoricalTestCase objects. |
||
340 | :rtype: list(dict) |
||
341 | """ |
||
342 | if query is None: |
||
343 | query = {} |
||
344 | |||
345 | return list(TestCase.objects.get(pk=case_id).history.filter(**query).values()) |
||
346 | |||
347 | |||
348 | @permissions_required("testcases.view_testcase") |
||
349 | @rpc_method(name="TestCase.sortkeys") |
||
350 | def sortkeys(query=None): |
||
351 | """ |
||
352 | .. function:: RPC TestCase.sortkeys(query) |
||
353 | |||
354 | Return information about TestCase position inside TestPlan. |
||
355 | |||
356 | For example `TestCase.sortkeys({'plan': 3})` |
||
357 | |||
358 | :param query: Field lookups for :class:`tcms.testcases.models.TestCasePlan` |
||
359 | :type query: dict |
||
360 | :return: Dictionary of (case_id, sortkey) pairs! |
||
361 | :rtype: dict(case_id, sortkey) |
||
362 | """ |
||
363 | if query is None: |
||
364 | query = {} |
||
365 | |||
366 | result = {} |
||
367 | for record in TestCasePlan.objects.filter(**query): |
||
368 | # NOTE: convert to str() otherwise we get: |
||
369 | # Unable to serialize result as valid XML: dictionary key must be string |
||
370 | result[str(record.case_id)] = record.sortkey |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
Loading history...
|
|||
371 | |||
372 | return result |
||
373 | |||
374 | |||
375 | @permissions_required("testcases.change_testcase") |
||
376 | @rpc_method(name="TestCase.update") |
||
377 | def update(case_id, values): |
||
378 | """ |
||
379 | .. function:: RPC TestCase.update(case_id, values) |
||
380 | |||
381 | Update the fields of the selected test case. |
||
382 | |||
383 | :param case_id: PK of TestCase to be modified |
||
384 | :type case_id: int |
||
385 | :param values: Field values for :class:`tcms.testcases.models.TestCase`. |
||
386 | :type values: dict |
||
387 | :return: Serialized :class:`tcms.testcases.models.TestCase` object |
||
388 | :rtype: dict |
||
389 | :raises ValueError: if form is not valid |
||
390 | :raises TestCase.DoesNotExist: if object specified by PK doesn't exist |
||
391 | :raises PermissionDenied: if missing *testcases.change_testcase* permission |
||
392 | """ |
||
393 | test_case = TestCase.objects.get(pk=case_id) |
||
394 | form = UpdateForm(values, instance=test_case) |
||
395 | |||
396 | if form.is_valid(): |
||
397 | test_case = form.save() |
||
398 | result = model_to_dict(test_case, exclude=["component", "plan", "tag"]) |
||
399 | # b/c date may be None and model_to_dict() doesn't return it |
||
400 | result["create_date"] = test_case.create_date |
||
401 | |||
402 | # additional information |
||
403 | result["case_status__name"] = test_case.case_status.name |
||
404 | result["category__name"] = test_case.category.name |
||
405 | result["priority__value"] = test_case.priority.value |
||
406 | result["author__username"] = ( |
||
407 | test_case.author.username if test_case.author else None |
||
408 | ) |
||
409 | result["default_tester__username"] = ( |
||
410 | test_case.default_tester.username if test_case.default_tester else None |
||
411 | ) |
||
412 | result["reviewer__username"] = ( |
||
413 | test_case.reviewer.username if test_case.reviewer else None |
||
414 | ) |
||
415 | result["setup_duration"] = str(result["setup_duration"]) |
||
416 | result["testing_duration"] = str(result["testing_duration"]) |
||
417 | |||
418 | return result |
||
419 | |||
420 | raise ValueError(form_errors_to_list(form)) |
||
421 | |||
422 | |||
423 | @permissions_required("testcases.delete_testcase") |
||
424 | @rpc_method(name="TestCase.remove") |
||
425 | def remove(query): |
||
426 | """ |
||
427 | .. function:: RPC TestCase.remove(query) |
||
428 | |||
429 | Remove TestCase object(s). |
||
430 | |||
431 | :param query: Field lookups for :class:`tcms.testcases.models.TestCase` |
||
432 | :type query: dict |
||
433 | :raises PermissionDenied: if missing the *testcases.delete_testcase* permission |
||
434 | :return: The number of objects deleted and a dictionary with the |
||
435 | number of deletions per object type. |
||
436 | :rtype: int, dict |
||
437 | |||
438 | Example - removing bug from TestCase:: |
||
439 | |||
440 | >>> TestCase.remove({ |
||
441 | 'pk__in': [1, 2, 3, 4], |
||
442 | }) |
||
443 | """ |
||
444 | return TestCase.objects.filter(**query).delete() |
||
445 | |||
446 | |||
447 | @permissions_required("attachments.view_attachment") |
||
448 | @rpc_method(name="TestCase.list_attachments") |
||
449 | def list_attachments(case_id, **kwargs): |
||
450 | """ |
||
451 | .. function:: RPC TestCase.list_attachments(case_id) |
||
452 | |||
453 | List attachments for the given TestCase. |
||
454 | |||
455 | :param case_id: PK of TestCase to inspect |
||
456 | :type case_id: int |
||
457 | :param \\**kwargs: Dict providing access to the current request, protocol, |
||
458 | entry point name and handler instance from the rpc method |
||
459 | :return: A list containing information and download URLs for attachements |
||
460 | :rtype: list |
||
461 | :raises TestCase.DoesNotExist: if object specified by PK is missing |
||
462 | """ |
||
463 | case = TestCase.objects.get(pk=case_id) |
||
464 | request = kwargs.get(REQUEST_KEY) |
||
465 | return utils.get_attachments_for(request, case) |
||
466 | |||
467 | |||
468 | @permissions_required("attachments.add_attachment") |
||
469 | @rpc_method(name="TestCase.add_attachment") |
||
470 | def add_attachment(case_id, filename, b64content, **kwargs): |
||
471 | """ |
||
472 | .. function:: RPC TestCase.add_attachment(case_id, filename, b64content) |
||
473 | |||
474 | Add attachment to the given TestCase. |
||
475 | |||
476 | :param case_id: PK of TestCase |
||
477 | :type case_id: int |
||
478 | :param filename: File name of attachment, e.g. 'logs.txt' |
||
479 | :type filename: str |
||
480 | :param b64content: Base64 encoded content |
||
481 | :type b64content: str |
||
482 | :param \\**kwargs: Dict providing access to the current request, protocol, |
||
483 | entry point name and handler instance from the rpc method |
||
484 | """ |
||
485 | utils.add_attachment( |
||
486 | case_id, |
||
487 | "testcases.TestCase", |
||
488 | kwargs.get(REQUEST_KEY).user, |
||
489 | filename, |
||
490 | b64content, |
||
491 | ) |
||
492 | |||
493 | |||
494 | @permissions_required("django_comments.add_comment") |
||
495 | @rpc_method(name="TestCase.add_comment") |
||
496 | def add_comment(case_id, comment, **kwargs): |
||
497 | """ |
||
498 | .. function:: TestCase.add_comment(case_id, comment) |
||
499 | |||
500 | Add comment to selected test case. |
||
501 | |||
502 | :param case_id: PK of a TestCase object |
||
503 | :type case_id: int |
||
504 | :param comment: The text to add as a comment |
||
505 | :type comment: str |
||
506 | :param \\**kwargs: Dict providing access to the current request, protocol, |
||
507 | entry point name and handler instance from the rpc method |
||
508 | :return: Serialized :class:`django_comments.models.Comment` object |
||
509 | :rtype: dict |
||
510 | :raises PermissionDenied: if missing *django_comments.add_comment* permission |
||
511 | :raises TestCase.DoesNotExist: if object specified by PK is missing |
||
512 | |||
513 | .. important:: |
||
514 | |||
515 | In webUI comments are only shown **only** during test case review! |
||
516 | """ |
||
517 | case = TestCase.objects.get(pk=case_id) |
||
518 | created = helpers.comments.add_comment( |
||
519 | [case], comment, kwargs.get(REQUEST_KEY).user |
||
520 | ) |
||
521 | # we always create only one comment |
||
522 | return model_to_dict(created[0]) |
||
523 | |||
524 | |||
525 | @permissions_required("django_comments.delete_comment") |
||
526 | @rpc_method(name="TestCase.remove_comment") |
||
527 | def remove_comment(case_id, comment_id=None): |
||
528 | """ |
||
529 | .. function:: TestCase.remove_comment(case_id, comment_id) |
||
530 | |||
531 | Remove all or specified comment(s) from selected test case. |
||
532 | |||
533 | :param case_id: PK of a TestCase object |
||
534 | :type case_id: int |
||
535 | :param comment_id: PK of a Comment object or None |
||
536 | :type comment_id: int |
||
537 | :raises PermissionDenied: if missing *django_comments.delete_comment* permission |
||
538 | :raises TestCase.DoesNotExist: if object specified by PK is missing |
||
539 | """ |
||
540 | case = TestCase.objects.get(pk=case_id) |
||
541 | to_be_deleted = helpers.comments.get_comments(case) |
||
542 | if comment_id: |
||
543 | to_be_deleted = to_be_deleted.filter(pk=comment_id) |
||
544 | |||
545 | to_be_deleted.delete() |
||
546 | |||
547 | |||
548 | @permissions_required("django_comments.view_comment") |
||
549 | @rpc_method(name="TestCase.comments") |
||
550 | def comments(case_id): |
||
551 | """ |
||
552 | .. function:: TestCase.comments(case_id) |
||
553 | |||
554 | Return all comment(s) for the specified test case. |
||
555 | |||
556 | :param case_id: PK of a TestCase object |
||
557 | :type case_id: int |
||
558 | :return: Serialized list of :class:`django_comments.models.Comment` objects |
||
559 | :rtype: list |
||
560 | :raises PermissionDenied: if missing *django_comments.view_comment* permission |
||
561 | :raises TestCase.DoesNotExist: if object specified by PK is missing |
||
562 | """ |
||
563 | case = TestCase.objects.get(pk=case_id) |
||
564 | result = [] |
||
565 | for comment in helpers.comments.get_comments(case): |
||
566 | result.append(model_to_dict(comment)) |
||
567 | |||
568 | return result |
||
569 | |||
570 | |||
571 | View Code Duplication | @permissions_required("testcases.view_property") |
|
0 ignored issues
–
show
|
|||
572 | @rpc_method(name="TestCase.properties") |
||
573 | def properties(query=None): |
||
574 | """ |
||
575 | .. function:: TestCase.properties(query) |
||
576 | |||
577 | Return all properties for the specified test case(s). |
||
578 | |||
579 | :param query: Field lookups for :class:`tcms.testcases.models.Property` |
||
580 | :type query: dict |
||
581 | :return: Serialized list of :class:`tcms.testcases.models.Property` objects. |
||
582 | :rtype: list(dict) |
||
583 | :raises PermissionDenied: if missing *testcases.view_property* permission |
||
584 | """ |
||
585 | if query is None: |
||
586 | query = {} |
||
587 | |||
588 | return list( |
||
589 | Property.objects.filter(**query) |
||
590 | .values( |
||
591 | "id", |
||
592 | "case", |
||
593 | "name", |
||
594 | "value", |
||
595 | ) |
||
596 | .order_by("case", "name", "value") |
||
597 | .distinct() |
||
598 | ) |
||
599 | |||
600 | |||
601 | @permissions_required("testcases.delete_property") |
||
602 | @rpc_method(name="TestCase.remove_property") |
||
603 | def remove_property(query): |
||
604 | """ |
||
605 | .. function:: TestCase.remove_property(query) |
||
606 | |||
607 | Remove selected properties. |
||
608 | |||
609 | :param query: Field lookups for :class:`tcms.testcases.models.Property` |
||
610 | :type query: dict |
||
611 | :raises PermissionDenied: if missing *testcases.delete_property* permission |
||
612 | """ |
||
613 | Property.objects.filter(**query).delete() |
||
614 | |||
615 | |||
616 | @permissions_required("testcases.add_property") |
||
617 | @rpc_method(name="TestCase.add_property") |
||
618 | def add_property(case_id, name, value): |
||
619 | """ |
||
620 | .. function:: TestCase.add_property(case_id, name, value) |
||
621 | |||
622 | Add property to test case! Duplicates are skipped without errors. |
||
623 | |||
624 | :param case_id: Primary key for :class:`tcms.testcases.models.TestCase` |
||
625 | :type case_id: int |
||
626 | :param name: Name of the property |
||
627 | :type name: str |
||
628 | :param value: Value of the property |
||
629 | :type value: str |
||
630 | :return: Serialized :class:`tcms.testcases.models.Property` object. |
||
631 | :rtype: dict |
||
632 | :raises PermissionDenied: if missing *testcases.add_property* permission |
||
633 | """ |
||
634 | prop, _ = Property.objects.get_or_create(case_id=case_id, name=name, value=value) |
||
635 | return model_to_dict(prop) |
||
636 |