GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — develop-v1.3.1 ( 969842...8fa207 )
by
unknown
05:56
created

ResourceController._get_one()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 1
dl 0
loc 3
rs 10
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
# pylint: disable=no-member
17
18
import abc
19
import copy
20
21
from mongoengine import ValidationError
22
import pecan
23
from pecan import rest
24
import six
25
from six.moves import http_client
26
27
from st2common.models.api.base import jsexpose
28
from st2common import log as logging
29
from st2common.models.system.common import InvalidResourceReferenceError
30
from st2common.models.system.common import ResourceReference
31
from st2common.exceptions.db import StackStormDBObjectNotFoundError
32
33
LOG = logging.getLogger(__name__)
34
35
RESERVED_QUERY_PARAMS = {
36
    'id': 'id',
37
    'name': 'name',
38
    'sort': 'order_by'
39
}
40
41
42
@six.add_metaclass(abc.ABCMeta)
43
class ResourceController(rest.RestController):
44
    model = abc.abstractproperty
45
    access = abc.abstractproperty
46
    supported_filters = abc.abstractproperty
47
48
    # Default kwargs passed to "APIClass.from_model" method
49
    from_model_kwargs = {}
50
51
    # Maximum value of limit which can be specified by user
52
    max_limit = 100
53
54
    query_options = {
55
        'sort': []
56
    }
57
58
    # A list of optional transformation functions for user provided filter values
59
    filter_transform_functions = {}
60
61
    # Method responsible for retrieving an instance of the corresponding model DB object
62
    get_one_db_method = None
63
64
    def __init__(self):
65
        self.supported_filters = copy.deepcopy(self.__class__.supported_filters)
66
        self.supported_filters.update(RESERVED_QUERY_PARAMS)
67
        self.get_one_db_method = self._get_by_name_or_id
68
69
    @jsexpose()
70
    def get_all(self, **kwargs):
71
        return self._get_all(**kwargs)
72
73
    @jsexpose(arg_types=[str])
74
    def get_one(self, id):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
75
        return self._get_one_by_id(id=id)
76
77
    def _get_all(self, exclude_fields=None, sort=None, offset=0, limit=None, query_options=None,
78
                 **kwargs):
79
        """
80
        :param exclude_fields: A list of object fields to exclude.
81
        :type exclude_fields: ``list``
82
        """
83
        kwargs = copy.deepcopy(kwargs)
84
85
        exclude_fields = exclude_fields or []
86
        query_options = query_options if query_options else self.query_options
87
88
        # TODO: Why do we use comma delimited string, user can just specify
89
        # multiple values using ?sort=foo&sort=bar and we get a list back
90
        sort = sort.split(',') if sort else []
91
92
        db_sort_values = []
93
        for sort_key in sort:
94
            if sort_key.startswith('-'):
95
                direction = '-'
96
                sort_key = sort_key[1:]
97
            elif sort_key.startswith('+'):
98
                direction = '+'
99
                sort_key = sort_key[1:]
100
            else:
101
                direction = ''
102
103
            if sort_key not in self.supported_filters:
104
                # Skip unsupported sort key
105
                continue
106
107
            sort_value = direction + self.supported_filters[sort_key]
108
            db_sort_values.append(sort_value)
109
110
        default_sort_values = copy.copy(query_options.get('sort'))
111
        kwargs['sort'] = db_sort_values if db_sort_values else default_sort_values
112
113
        # TODO: To protect us from DoS, we need to make max_limit mandatory
114
        offset = int(offset)
115
116
        if limit and int(limit) > self.max_limit:
117
            limit = self.max_limit
118
        eop = offset + int(limit) if limit else None
119
120
        filters = {}
121
122
        for k, v in six.iteritems(self.supported_filters):
123
            filter_value = kwargs.get(k, None)
124
125
            if not filter_value:
126
                continue
127
128
            value_transform_function = self.filter_transform_functions.get(k, None)
129
            value_transform_function = value_transform_function or (lambda value: value)
130
            filter_value = value_transform_function(value=filter_value)
131
132
            filters['__'.join(v.split('.'))] = filter_value
133
134
        extra = {
135
            'filters': filters,
136
            'sort': sort,
137
            'offset': offset,
138
            'limit': limit
139
        }
140
        LOG.info('GET all %s with filters=%s' % (pecan.request.path, filters), extra=extra)
141
142
        instances = self.access.query(exclude_fields=exclude_fields, **filters)
143
        if limit == 1:
144
            # Perform the filtering on the DB side
145
            instances = instances.limit(limit)
146
147
        if limit:
148
            pecan.response.headers['X-Limit'] = str(limit)
149
        pecan.response.headers['X-Total-Count'] = str(instances.count())
150
151
        from_model_kwargs = self._get_from_model_kwargs_for_request(request=pecan.request)
152
153
        result = []
154
        for instance in instances[offset:eop]:
155
            item = self.model.from_model(instance, **from_model_kwargs)
156
            result.append(item)
157
158
        return result
159
160
    def _get_one(self, id, exclude_fields=None):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
161
        # Note: This is here for backward compatibility reasons
162
        return self._get_one_by_id(id=id, exclude_fields=exclude_fields)
163
164
    def _get_one_by_id(self, id, exclude_fields=None):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
165
        """
166
        :param exclude_fields: A list of object fields to exclude.
167
        :type exclude_fields: ``list``
168
        """
169
170
        LOG.info('GET %s with id=%s', pecan.request.path, id)
171
172
        instance = self._get_by_id(resource_id=id, exclude_fields=exclude_fields)
173
174
        if not instance:
175
            msg = 'Unable to identify resource with id "%s".' % id
176
            pecan.abort(http_client.NOT_FOUND, msg)
177
178
        from_model_kwargs = self._get_from_model_kwargs_for_request(request=pecan.request)
179
        result = self.model.from_model(instance, **from_model_kwargs)
180
        LOG.debug('GET %s with id=%s, client_result=%s', pecan.request.path, id, result)
181
182
        return result
183
184
    def _get_one_by_name_or_id(self, name_or_id, exclude_fields=None):
185
        """
186
        :param exclude_fields: A list of object fields to exclude.
187
        :type exclude_fields: ``list``
188
        """
189
190
        LOG.info('GET %s with name_or_id=%s', pecan.request.path, name_or_id)
191
192
        instance = self._get_by_name_or_id(name_or_id=name_or_id, exclude_fields=exclude_fields)
193
194
        if not instance:
195
            msg = 'Unable to identify resource with name_or_id "%s".' % (name_or_id)
196
            pecan.abort(http_client.NOT_FOUND, msg)
197
198
        from_model_kwargs = self._get_from_model_kwargs_for_request(request=pecan.request)
199
        result = self.model.from_model(instance, **from_model_kwargs)
200
        LOG.debug('GET %s with name_or_id=%s, client_result=%s', pecan.request.path, id, result)
201
202
        return result
203
204
    def _get_by_id(self, resource_id, exclude_fields=None):
205
        try:
206
            resource_db = self.access.get(id=resource_id, exclude_fields=exclude_fields)
207
        except ValidationError:
208
            resource_db = None
209
210
        return resource_db
211
212
    def _get_by_name(self, resource_name, exclude_fields=None):
213
        try:
214
            resource_db = self.access.get(name=resource_name, exclude_fields=exclude_fields)
215
        except Exception:
216
            resource_db = None
217
218
        return resource_db
219
220
    def _get_by_name_or_id(self, name_or_id, exclude_fields=None):
221
        """
222
        Retrieve resource object by an id of a name.
223
        """
224
        resource_db = self._get_by_id(resource_id=name_or_id, exclude_fields=exclude_fields)
225
226
        if not resource_db:
227
            # Try name
228
            resource_db = self._get_by_name(resource_name=name_or_id, exclude_fields=exclude_fields)
229
230
        return resource_db
231
232
    def _get_from_model_kwargs_for_request(self, request):
233
        """
234
        Retrieve kwargs which are passed to "LiveActionAPI.model" method.
235
236
        :param request: Pecan request object.
237
238
        :rtype: ``dict``
239
        """
240
        return self.from_model_kwargs
241
242
243
class ContentPackResourceController(ResourceController):
244
    include_reference = False
245
246
    def __init__(self):
247
        super(ContentPackResourceController, self).__init__()
248
        self.get_one_db_method = self._get_by_ref_or_id
249
250
    @jsexpose(arg_types=[str])
251
    def get_one(self, ref_or_id):
252
        return self._get_one(ref_or_id)
253
254
    @jsexpose()
255
    def get_all(self, **kwargs):
256
        return self._get_all(**kwargs)
257
258
    def _get_one(self, ref_or_id, exclude_fields=None):
259
        LOG.info('GET %s with ref_or_id=%s', pecan.request.path, ref_or_id)
260
261
        try:
262
            instance = self._get_by_ref_or_id(ref_or_id=ref_or_id, exclude_fields=exclude_fields)
263
        except Exception as e:
264
            LOG.exception(e.message)
265
            pecan.abort(http_client.NOT_FOUND, e.message)
266
            return
267
268
        from_model_kwargs = self._get_from_model_kwargs_for_request(request=pecan.request)
269
        result = self.model.from_model(instance, **from_model_kwargs)
270
        if result and self.include_reference:
271
            pack = getattr(result, 'pack', None)
272
            name = getattr(result, 'name', None)
273
            result.ref = ResourceReference(pack=pack, name=name).ref
274
275
        LOG.debug('GET %s with ref_or_id=%s, client_result=%s',
276
                  pecan.request.path, ref_or_id, result)
277
278
        return result
279
280
    def _get_all(self, **kwargs):
281
        result = super(ContentPackResourceController, self)._get_all(**kwargs)
282
283
        if self.include_reference:
284
            for item in result:
285
                pack = getattr(item, 'pack', None)
286
                name = getattr(item, 'name', None)
287
                item.ref = ResourceReference(pack=pack, name=name).ref
288
289
        return result
290
291
    def _get_by_ref_or_id(self, ref_or_id, exclude_fields=None):
292
        """
293
        Retrieve resource object by an id of a reference.
294
295
        Note: This method throws StackStormDBObjectNotFoundError exception if the object is not
296
        found in the database.
297
        """
298
299
        if ResourceReference.is_resource_reference(ref_or_id):
300
            # references always contain a dot and id's can't contain it
301
            is_reference = True
302
        else:
303
            is_reference = False
304
305
        if is_reference:
306
            resource_db = self._get_by_ref(resource_ref=ref_or_id, exclude_fields=exclude_fields)
307
        else:
308
            resource_db = self._get_by_id(resource_id=ref_or_id, exclude_fields=exclude_fields)
309
310
        if not resource_db:
311
            msg = 'Resource with a reference or id "%s" not found' % (ref_or_id)
312
            raise StackStormDBObjectNotFoundError(msg)
313
314
        return resource_db
315
316
    def _get_by_ref(self, resource_ref, exclude_fields=None):
317
        try:
318
            ref = ResourceReference.from_string_reference(ref=resource_ref)
319
        except Exception:
320
            return None
321
322
        resource_db = self.access.query(name=ref.name, pack=ref.pack,
323
                                        exclude_fields=exclude_fields).first()
324
        return resource_db
325
326
    def _get_filters(self, **kwargs):
327
        filters = copy.deepcopy(kwargs)
328
        ref = filters.get('ref', None)
329
330
        if ref:
331
            try:
332
                ref_obj = ResourceReference.from_string_reference(ref=ref)
333
            except InvalidResourceReferenceError:
334
                raise
335
336
            filters['name'] = ref_obj.name
337
            filters['pack'] = ref_obj.pack
338
            del filters['ref']
339
340
        return filters
341