Test Failed
Push — master ( e380d0...f5671d )
by W
02:58
created

st2api/st2api/controllers/v1/rule_views.py (1 issue)

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
18
from mongoengine.queryset import Q
19
20
from st2common import log as logging
21
from st2api.controllers.resource import BaseResourceIsolationControllerMixin
22
from st2api.controllers.resource import ContentPackResourceController
23
from st2common.models.api.rule import RuleViewAPI
24
from st2common.models.system.common import ResourceReference
25
from st2common.persistence.action import Action
26
from st2common.persistence.rule import Rule
27
from st2common.persistence.trigger import TriggerType, Trigger
28
from st2common.rbac.types import PermissionType
29
30
http_client = six.moves.http_client
31
32
LOG = logging.getLogger(__name__)
33
34
35
__all__ = ['RuleViewController']
36
37
38
class RuleViewController(BaseResourceIsolationControllerMixin, ContentPackResourceController):
39
    """
40
    Add some extras to a Rule object to make it easier for UI to render a rule. The additions
41
    do not necessarily belong in the Rule itself but are still valuable augmentations.
42
43
    :Example:
44
        {
45
            "action": {
46
                "description": "Action that executes an arbitrary Linux command on the localhost.",
47
                "parameters": {
48
                    "cmd": "echo \"{{trigger.executed_at}}\""
49
                },
50
                "ref": "core.local"
51
            },
52
            "criteria": {},
53
            "description": "Sample rule using an Interval Timer.",
54
            "enabled": false,
55
            "id": "55ea221832ed35759cf3b312",
56
            "name": "sample.with_timer",
57
            "pack": "examples",
58
            "ref": "examples.sample.with_timer",
59
            "tags": [],
60
            "trigger": {
61
                "description": "Triggers on specified intervals. e.g. every 30s, 1week etc.",
62
                "parameters": {
63
                    "delta": 5,
64
                    "unit": "seconds"
65
                },
66
                "ref": "core.4ad65602-6fb4-4c89-b0f2-b990d7b68bad",
67
                "type": "core.st2.IntervalTimer"
68
            },
69
            "uid": "rule:examples:sample.with_timer"
70
        }
71
72
    The `description` fields in action and trigger are augmented properties.
73
    """
74
75
    model = RuleViewAPI
76
    access = Rule
77
    supported_filters = {
78
        'name': 'name',
79
        'pack': 'pack',
80
        'user': 'context.user'
81
    }
82
83
    query_options = {
84
        'sort': ['pack', 'name']
85
    }
86
87
    include_reference = True
88
89
    def get_all(self, sort=None, offset=0, limit=None, requester_user=None, **raw_filters):
90
        rules = super(RuleViewController, self)._get_all(sort=sort,
91
                                                         offset=offset,
92
                                                         limit=limit,
93
                                                         raw_filters=raw_filters,
94
                                                         requester_user=requester_user)
95
        result = self._append_view_properties(rules.json)
96
        rules.json = result
97
        return rules
98
99
    def get_one(self, ref_or_id, requester_user):
100
        rule = self._get_one(ref_or_id, permission_type=PermissionType.RULE_VIEW,
101
                             requester_user=requester_user)
102
        result = self._append_view_properties([rule.json])[0]
103
        rule.json = result
104
        return rule
105
106
    def _append_view_properties(self, rules):
107
        action_by_refs, trigger_by_refs, trigger_type_by_refs = self._get_referenced_models(rules)
108
109
        for rule in rules:
110
            action_db = action_by_refs.get(rule['action']['ref'], None)
111
            rule['action']['description'] = action_db.description if action_db else ''
112
113
            rule['trigger']['description'] = ''
114
115
            trigger_db = trigger_by_refs.get(rule['trigger']['ref'], None)
116
            if trigger_db:
117
                rule['trigger']['description'] = trigger_db.description
118
119
            # If description is not found in trigger get description from triggertype
120
            if not rule['trigger']['description']:
121
                trigger_type_db = trigger_type_by_refs.get(rule['trigger']['type'], None)
122
                if trigger_type_db:
123
                    rule['trigger']['description'] = trigger_type_db.description
124
125
        return rules
126
127
    def _get_referenced_models(self, rules):
128
        """
129
        Reduces the number of queries to be made to the DB by creating sets of Actions, Triggers
130
        and TriggerTypes.
131
        """
132
        action_refs = set()
133
        trigger_refs = set()
134
        trigger_type_refs = set()
135
136
        for rule in rules:
137
            action_refs.add(rule['action']['ref'])
138
            trigger_refs.add(rule['trigger']['ref'])
139
            trigger_type_refs.add(rule['trigger']['type'])
140
141
        action_by_refs = {}
142
        trigger_by_refs = {}
143
        trigger_type_by_refs = {}
144
145
        # The functions that will return args that can used to query.
146
        def ref_query_args(ref):
147
            return {'ref': ref}
148
149
        def name_pack_query_args(ref):
150
            resource_ref = ResourceReference.from_string_reference(ref=ref)
151
            return {'name': resource_ref.name, 'pack': resource_ref.pack}
152
153
        action_dbs = self._get_entities(model_persistence=Action,
154
                                        refs=action_refs,
155
                                        query_args=ref_query_args)
156
        for action_db in action_dbs:
157
            action_by_refs[action_db.ref] = action_db
158
159
        trigger_dbs = self._get_entities(model_persistence=Trigger,
160
                                         refs=trigger_refs,
161
                                         query_args=name_pack_query_args)
162
        for trigger_db in trigger_dbs:
163
            trigger_by_refs[trigger_db.get_reference().ref] = trigger_db
164
165
        trigger_type_dbs = self._get_entities(model_persistence=TriggerType,
166
                                              refs=trigger_type_refs,
167
                                              query_args=name_pack_query_args)
168
        for trigger_type_db in trigger_type_dbs:
169
            trigger_type_by_refs[trigger_type_db.get_reference().ref] = trigger_type_db
170
171
        return (action_by_refs, trigger_by_refs, trigger_type_by_refs)
172
173
    def _get_entities(self, model_persistence, refs, query_args):
174
        """
175
        Returns all the entities for the supplied refs. model_persistence is the persistence
176
        object that will be used to get to the correct query method and the query_args function
177
        to return the ref specific query argument.
178
179
        This is such a weirdly specific method that it is likely better only in this context.
180
        """
181
        q = None
182
        for ref in refs:
183
            if not q:
184
                q = Q(**query_args(ref))
185
            else:
186
                q |= Q(**query_args(ref))
187
        if q:
188
            return model_persistence._get_impl().model.objects(q)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _get_impl was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
189
        return []
190
191
192
rule_view_controller = RuleViewController()
193