Passed
Push — master ( 85e1bb...09f4b0 )
by Alexander
02:22
created

tcms.xmlrpc.api.testcase.update()   B

Complexity

Conditions 8

Size

Total Lines 37
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 37
rs 7.3333
c 0
b 0
f 0
cc 8
nop 3
1
# -*- coding: utf-8 -*-
2
3
from django.forms import EmailField, ValidationError
4
5
from modernrpc.core import rpc_method, REQUEST_KEY
6
7
from tcms.core.utils import form_errors_to_list
8
from tcms.management.models import Tag
9
from tcms.management.models import Component
10
from tcms.testcases.models import TestCase
11
from tcms.xmlrpc.forms import UpdateCaseForm, NewCaseForm
12
13
from tcms.xmlrpc.decorators import permissions_required
14
15
16
__all__ = (
17
    'create',
18
    'update',
19
    'filter',
20
    'remove',
21
22
    'add_component',
23
    'get_components',
24
    'remove_component',
25
26
    'add_notification_cc',
27
    'get_notification_cc',
28
    'remove_notification_cc',
29
30
    'add_tag',
31
    'remove_tag',
32
)
33
34
35
@permissions_required('testcases.add_testcasecomponent')
36
@rpc_method(name='TestCase.add_component')
37
def add_component(case_id, component):
38
    """
39
    .. function:: XML-RPC TestCase.add_component(case_id, component_id)
40
41
        Add component to the selected test case.
42
43
        :param case_id: PK of TestCase to modify
44
        :type case_id: int
45
        :param component: Name of Component to add
46
        :type component_id: str
47
        :return: None
48
        :raises: PermissionDenied if missing the *testcases.add_testcasecomponent*
49
                 permission
50
        :raises: DoesNotExist if missing test case or component that match the
51
                 specified PKs
52
    """
53
    TestCase.objects.get(pk=case_id).add_component(
54
        Component.objects.get(name=component)
55
    )
56
57
58
@rpc_method(name='TestCase.get_components')
59
def get_components(case_id):
60
    """
61
    .. function:: XML-RPC TestCase.get_components(case_id)
62
63
        Get the list of components attached to this case.
64
65
        :param case_id: PK if TestCase
66
        :type case_id: int
67
        :return: Serialized list of :class:`tcms.management.models.Component` objects
68
        :rtype: list(dict)
69
        :raises: TestCase.DoesNotExist if missing test case matching PK
70
    """
71
    test_case = TestCase.objects.get(case_id=case_id)
72
73
    component_ids = test_case.component.values_list('id', flat=True)
74
    query = {'id__in': component_ids}
75
    return Component.to_xmlrpc(query)
76
77
78
@permissions_required('testcases.delete_testcasecomponent')
79
@rpc_method(name='TestCase.remove_component')
80
def remove_component(case_id, component_id):
81
    """
82
    .. function:: XML-RPC TestCase.remove_component(case_id, component_id)
83
84
        Remove selected component from the selected test case.
85
86
        :param case_id: PK of TestCase to modify
87
        :type case_id: int
88
        :param component_id: PK of Component to remove
89
        :type component_id: int
90
        :return: None
91
        :raises: PermissionDenied if missing the *testcases.delete_testcasecomponent*
92
                 permission
93
        :raises: DoesNotExist if missing test case or component that match the
94
                 specified PKs
95
    """
96
    TestCase.objects.get(pk=case_id).remove_component(
97
        Component.objects.get(pk=component_id)
98
    )
99
100
101
def _validate_cc_list(cc_list):
102
    """
103
        Validate each email address given in argument. Called by
104
        notification RPC methods.
105
106
        :param cc_list: List of email addresses
107
        :type cc_list: list
108
        :return: None
109
        :raises: TypeError or ValidationError if addresses are not valid.
110
    """
111
112
    if not isinstance(cc_list, list):
113
        raise TypeError('cc_list should be a list object.')
114
115
    field = EmailField(required=True)
116
    invalid_emails = []
117
118
    for item in cc_list:
119
        try:
120
            field.clean(item)
121
        except ValidationError:
122
            invalid_emails.append(item)
123
124
    if invalid_emails:
125
        raise ValidationError(
126
            field.error_messages['invalid'] % {
127
                'value': ', '.join(invalid_emails)})
128
129
130
@permissions_required('testcases.change_testcase')
131
@rpc_method(name='TestCase.add_notification_cc')
132
def add_notification_cc(case_id, cc_list):
133
    """
134
    .. function:: XML-RPC TestCase.add_notification_cc(case_id, cc_list)
135
136
        Add email addresses to the notification list of specified TestCase
137
138
        :param case_id: PK of TestCase to be modified
139
        :param case_id: int
140
        :param cc_list: List of email addresses
141
        :type cc_list: list(str)
142
        :return: None
143
        :raises: TypeError or ValidationError if email validation fails
144
        :raises: PermissionDenied if missing *testcases.change_testcase* permission
145
        :raises: TestCase.DoesNotExist if object with case_id doesn't exist
146
    """
147
148
    _validate_cc_list(cc_list)
149
150
    test_case = TestCase.objects.get(pk=case_id)
151
    test_case.emailing.add_cc(cc_list)
152
153
154
@permissions_required('testcases.change_testcase')
155
@rpc_method(name='TestCase.remove_notification_cc')
156
def remove_notification_cc(case_id, cc_list):
157
    """
158
    .. function:: XML-RPC TestCase.remove_notification_cc(case_id, cc_list)
159
160
        Remove email addresses from the notification list of specified TestCase
161
162
        :param case_id: PK of TestCase to modify
163
        :type case_id: int
164
        :param cc_list: List of email addresses
165
        :type cc_list: list(str)
166
        :return: None
167
        :raises: TypeError or ValidationError if email validation fails
168
        :raises: PermissionDenied if missing *testcases.change_testcase* permission
169
        :raises: TestCase.DoesNotExist if object with case_id doesn't exist
170
    """
171
172
    _validate_cc_list(cc_list)
173
174
    TestCase.objects.get(pk=case_id).emailing.remove_cc(cc_list)
175
176
177
@rpc_method(name='TestCase.get_notification_cc')
178
def get_notification_cc(case_id):
179
    """
180
    .. function:: XML-RPC TestCase.get_notification_cc(case_id)
181
182
        Return notification list for specified TestCase
183
184
        :param case_id: PK of TestCase
185
        :type case_id: int
186
        :return: List of email addresses
187
        :rtype: list(str)
188
        :raises: TestCase.DoesNotExist if object with case_id doesn't exist
189
    """
190
    return TestCase.objects.get(pk=case_id).get_cc_list()
191
192
193
@permissions_required('testcases.add_testcasetag')
194
@rpc_method(name='TestCase.add_tag')
195
def add_tag(case_id, tag, **kwargs):
196
    """
197
    .. function:: XML-RPC TestCase.add_tag(case_id, tag)
198
199
        Add one tag to the specified test case.
200
201
        :param case_id: PK of TestCase to modify
202
        :type case_id: int
203
        :param tag: Tag name to add
204
        :type tag: str
205
        :return: None
206
        :raises: PermissionDenied if missing *testcases.add_testcasetag* permission
207
        :raises: TestCase.DoesNotExist if object specified by PK doesn't exist
208
        :raises: Tag.DoesNotExist if missing *management.add_tag* permission and *tag*
209
                 doesn't exist in the database!
210
    """
211
    request = kwargs.get(REQUEST_KEY)
212
    tag, _ = Tag.get_or_create(request.user, tag)
213
    TestCase.objects.get(pk=case_id).add_tag(tag)
214
215
216
@permissions_required('testcases.delete_testcasetag')
217
@rpc_method(name='TestCase.remove_tag')
218
def remove_tag(case_id, tag):
219
    """
220
    .. function:: XML-RPC TestCase.remove_tag(case_id, tag)
221
222
        Remove tag from a test case.
223
224
        :param case_id: PK of TestCase to modify
225
        :type case_id: int
226
        :param tag: Tag name to remove
227
        :type tag: str
228
        :return: None
229
        :raises: PermissionDenied if missing *testcases.delete_testcasetag* permission
230
        :raises: DoesNotExist if objects specified don't exist
231
    """
232
    TestCase.objects.get(pk=case_id).remove_tag(
233
        Tag.objects.get(name=tag)
234
    )
235
236
237
@permissions_required('testcases.add_testcase')
238
@rpc_method(name='TestCase.create')
239
def create(values, **kwargs):
240
    """
241
    .. function:: XML-RPC TestCase.create(values)
242
243
        Create a new TestCase object and store it in the database.
244
245
        :param values: Field values for :class:`tcms.testcases.models.TestCase`
246
        :type values: dict
247
        :return: Serialized :class:`tcms.testcases.models.TestCase` object
248
        :rtype: dict
249
        :raises: PermissionDenied if missing *testcases.add_testcase* permission
250
251
        Minimal test case parameters::
252
253
            >>> values = {
254
                'category': 135,
255
                'product': 61,
256
            'summary': 'Testing XML-RPC',
257
            'priority': 1,
258
            }
259
            >>> TestCase.create(values)
260
    """
261
    request = kwargs.get(REQUEST_KEY)
262
263
    if not (values.get('category') or values.get('summary')):
264
        raise ValueError()
265
266
    form = NewCaseForm(values)
267
    form.populate(values.get('product'))
268
269
    if form.is_valid():
270
        # Create the case
271
        test_case = TestCase.create(author=request.user, values=form.cleaned_data)
272
    else:
273
        # Print the errors if the form is not passed validation.
274
        raise ValueError(form_errors_to_list(form))
275
276
    result = test_case.serialize()
277
278
    return result
279
280
281
@rpc_method(name='TestCase.filter')
282
def filter(query):  # pylint: disable=redefined-builtin
283
    """
284
    .. function:: XML-RPC TestCase.filter(query)
285
286
        Perform a search and return the resulting list of test cases
287
        augmented with their latest ``text``.
288
289
        :param query: Field lookups for :class:`tcms.testcases.models.TestCase`
290
        :type query: dict
291
        :return: Serialized list of :class:`tcms.testcases.models.TestCase` objects.
292
        :rtype: list(dict)
293
    """
294
    results = []
295
    for case in TestCase.objects.filter(**query).distinct():
296
        serialized_case = case.serialize()
297
        results.append(serialized_case)
298
299
    return results
300
301
302
@permissions_required('testcases.change_testcase')
303
@rpc_method(name='TestCase.update')
304
def update(case_id, values, **kwargs):
305
    """
306
    .. function:: XML-RPC TestCase.update(case_id, values)
307
308
        Update the fields of the selected test case.
309
310
        :param case_id: PK of TestCase to be modified
311
        :type case_id: int
312
        :param values: Field values for :class:`tcms.testcases.models.TestCase`.
313
        :type values: dict
314
        :return: Serialized :class:`tcms.testcases.models.TestCase` object
315
        :rtype: dict
316
        :raises: TestCase.DoesNotExist if object specified by PK doesn't exist
317
        :raises: PermissionDenied if missing *testcases.change_testcase* permission
318
    """
319
    form = UpdateCaseForm(values)
320
321
    if values.get('category') and not values.get('product'):
322
        raise ValueError('Product ID is required for category')
323
324
    if values.get('product'):
325
        form.populate(product_id=values['product'])
326
327
    if form.is_valid():
328
        test_case = TestCase.objects.get(pk=case_id)
329
        for key in values.keys():
330
            # only modify attributes that were passed via parameters
331
            # skip attributes which are Many-to-Many relations
332
            if key not in ['component', 'tag'] and hasattr(test_case, key):
333
                setattr(test_case, key, form.cleaned_data[key])
334
        test_case.save()
335
    else:
336
        raise ValueError(form_errors_to_list(form))
337
338
    return test_case.serialize()
339
340
341
@permissions_required('testcases.delete_testcase')
342
@rpc_method(name='TestCase.remove')
343
def remove(query):
344
    """
345
    .. function:: XML-RPC TestCase.remove(query)
346
347
        Remove TestCase object(s).
348
349
        :param query: Field lookups for :class:`tcms.testcases.models.TestCase`
350
        :type query: dict
351
        :return: None
352
        :raises: PermissionDenied if missing the *testcases.delete_testcase* permission
353
354
        Example - removing bug from TestCase::
355
356
            >>> TestCase.remove({
357
                'pk__in': [1, 2, 3, 4],
358
            })
359
    """
360
    return TestCase.objects.filter(**query).delete()
361