Completed
Push — master ( be542d...29973a )
by Arma
08:49 queued 01:31
created

st2api.controllers.v1.RuleController   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 137
Duplicated Lines 0 %
Metric Value
wmc 13
dl 0
loc 137
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A st2api.controllers.v1.RuleViewController.name_pack_query_args() 0 3 1
A st2api.controllers.v1.RuleViewController.get_one() 0 5 1
A st2api.controllers.v1.RuleViewController.get_all() 0 5 1
B st2api.controllers.v1.RuleViewController._append_view_properties() 0 16 6
C st2api.controllers.v1.RuleViewController._get_referenced_models() 0 45 7
A st2api.controllers.v1.RuleViewController.ref_query_args() 0 2 1
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
import six
17
import jsonschema
18
import pecan
19
from pecan import abort
20
from mongoengine import ValidationError
21
22
from st2common import log as logging
23
from st2common.exceptions.apivalidation import ValueValidationException
24
from st2common.exceptions.triggers import TriggerDoesNotExistException
25
from st2api.controllers import resource
26
from st2api.controllers.v1.ruleviews import RuleViewController
27
from st2common.models.api.rule import RuleAPI
28
from st2common.models.api.base import jsexpose
29
from st2common.persistence.rule import Rule
30
from st2common.rbac.types import PermissionType
31
from st2common.rbac.decorators import request_user_has_permission
32
from st2common.rbac.decorators import request_user_has_resource_api_permission
33
from st2common.rbac.decorators import request_user_has_resource_db_permission
34
from st2common.rbac.utils import assert_request_user_has_rule_trigger_and_action_permission
35
from st2common.services.triggers import cleanup_trigger_db_for_rule, increment_trigger_ref_count
36
37
http_client = six.moves.http_client
38
39
LOG = logging.getLogger(__name__)
40
41
42
class RuleController(resource.ContentPackResourceController):
43
    """
44
        Implements the RESTful web endpoint that handles
45
        the lifecycle of Rules in the system.
46
    """
47
    views = RuleViewController()
48
49
    model = RuleAPI
50
    access = Rule
51
    supported_filters = {
52
        'name': 'name',
53
        'pack': 'pack',
54
        'action': 'action.ref',
55
        'trigger': 'trigger'
56
    }
57
58
    query_options = {
59
        'sort': ['pack', 'name']
60
    }
61
62
    include_reference = True
63
64
    @request_user_has_permission(permission_type=PermissionType.RULE_LIST)
65
    @jsexpose()
66
    def get_all(self, **kwargs):
67
        return super(RuleController, self)._get_all(**kwargs)
68
69
    @request_user_has_resource_db_permission(permission_type=PermissionType.RULE_VIEW)
70
    @jsexpose(arg_types=[str])
71
    def get_one(self, ref_or_id):
72
        return super(RuleController, self)._get_one(ref_or_id)
73
74
    @jsexpose(body_cls=RuleAPI, status_code=http_client.CREATED)
75
    @request_user_has_resource_api_permission(permission_type=PermissionType.RULE_CREATE)
76
    def post(self, rule):
77
        """
78
            Create a new rule.
79
80
            Handles requests:
81
                POST /rules/
82
        """
83
        try:
84
            rule_db = RuleAPI.to_model(rule)
85
            LOG.debug('/rules/ POST verified RuleAPI and formulated RuleDB=%s', rule_db)
86
87
            # Check referenced trigger and action permissions
88
            # Note: This needs to happen after "to_model" call since to_model performs some
89
            # validation (trigger exists, etc.)
90
            assert_request_user_has_rule_trigger_and_action_permission(request=pecan.request,
91
                                                                       rule_api=rule)
92
93
            rule_db = Rule.add_or_update(rule_db)
94
            # After the rule has been added modify the ref_count. This way a failure to add
95
            # the rule due to violated constraints will have no impact on ref_count.
96
            increment_trigger_ref_count(rule_api=rule)
97
        except (ValidationError, ValueError) as e:
98
            LOG.exception('Validation failed for rule data=%s.', rule)
99
            abort(http_client.BAD_REQUEST, str(e))
100
            return
101
        except (ValueValidationException, jsonschema.ValidationError) as e:
102
            LOG.exception('Validation failed for rule data=%s.', rule)
103
            abort(http_client.BAD_REQUEST, str(e))
104
            return
105
        except TriggerDoesNotExistException as e:
106
            msg = 'Trigger %s in rule does not exist in system' % rule.trigger['type']
107
            LOG.exception(msg)
108
            abort(http_client.BAD_REQUEST, msg)
109
            return
110
111
        extra = {'rule_db': rule_db}
112
        LOG.audit('Rule created. Rule.id=%s' % (rule_db.id), extra=extra)
113
        rule_api = RuleAPI.from_model(rule_db)
114
115
        return rule_api
116
117
    @request_user_has_resource_db_permission(permission_type=PermissionType.RULE_MODIFY)
118
    @jsexpose(arg_types=[str], body_cls=RuleAPI)
119
    def put(self, rule_ref_or_id, rule):
120
        rule_db = self._get_by_ref_or_id(rule_ref_or_id)
121
        LOG.debug('PUT /rules/ lookup with id=%s found object: %s', rule_ref_or_id, rule_db)
122
123
        try:
124
            if rule.id is not None and rule.id is not '' and rule.id != rule_ref_or_id:
125
                LOG.warning('Discarding mismatched id=%s found in payload and using uri_id=%s.',
126
                            rule.id, rule_ref_or_id)
127
            old_rule_db = rule_db
128
            rule_db = RuleAPI.to_model(rule)
129
130
            # Check referenced trigger and action permissions
131
            # Note: This needs to happen after "to_model" call since to_model performs some
132
            # validation (trigger exists, etc.)
133
            assert_request_user_has_rule_trigger_and_action_permission(request=pecan.request,
134
                                                                       rule_api=rule)
135
136
            rule_db.id = rule_ref_or_id
137
            rule_db = Rule.add_or_update(rule_db)
138
            # After the rule has been added modify the ref_count. This way a failure to add
139
            # the rule due to violated constraints will have no impact on ref_count.
140
            increment_trigger_ref_count(rule_api=rule)
141
        except (ValueValidationException, jsonschema.ValidationError, ValueError) as e:
142
            LOG.exception('Validation failed for rule data=%s', rule)
143
            abort(http_client.BAD_REQUEST, str(e))
144
            return
145
146
        # use old_rule_db for cleanup.
147
        cleanup_trigger_db_for_rule(old_rule_db)
148
149
        extra = {'old_rule_db': old_rule_db, 'new_rule_db': rule_db}
150
        LOG.audit('Rule updated. Rule.id=%s.' % (rule_db.id), extra=extra)
151
        rule_api = RuleAPI.from_model(rule_db)
152
153
        return rule_api
154
155
    @request_user_has_resource_db_permission(permission_type=PermissionType.RULE_DELETE)
156
    @jsexpose(arg_types=[str], status_code=http_client.NO_CONTENT)
157
    def delete(self, rule_ref_or_id):
158
        """
159
            Delete a rule.
160
161
            Handles requests:
162
                DELETE /rules/1
163
        """
164
        rule_db = self._get_by_ref_or_id(ref_or_id=rule_ref_or_id)
165
        LOG.debug('DELETE /rules/ lookup with id=%s found object: %s', rule_ref_or_id, rule_db)
166
        try:
167
            Rule.delete(rule_db)
168
        except Exception as e:
169
            LOG.exception('Database delete encountered exception during delete of id="%s".',
170
                          rule_ref_or_id)
171
            abort(http_client.INTERNAL_SERVER_ERROR, str(e))
172
            return
173
174
        # use old_rule_db for cleanup.
175
        cleanup_trigger_db_for_rule(rule_db)
176
177
        extra = {'rule_db': rule_db}
178
        LOG.audit('Rule deleted. Rule.id=%s.' % (rule_db.id), extra=extra)
179