Passed
Pull Request — master (#3804)
by Lakshmi
05:58
created

RuleAPI.to_model()   A

Complexity

Conditions 2

Size

Total Lines 52

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
c 0
b 0
f 0
dl 0
loc 52
rs 9.4929

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
from __future__ import absolute_import
17
import copy
18
19
import six
20
21
from st2common import log as logging
22
from st2common.constants import keyvalue as kv_constants
23
from st2common.constants.pack import DEFAULT_PACK_NAME
24
from st2common.models.api.base import BaseAPI
25
from st2common.models.api.base import APIUIDMixin
26
from st2common.models.api.tag import TagsHelper
27
from st2common.models.db.rule import RuleDB, RuleTypeDB, RuleTypeSpecDB, ActionExecutionSpecDB
28
from st2common.models.system.common import ResourceReference
29
from st2common.persistence.trigger import Trigger
30
from st2common.services import keyvalues as kv_service
31
import st2common.services.triggers as TriggerService
32
from st2common.util import reference
33
import st2common.util.jinja as jinja_utils
34
import st2common.validators.api.reactor as validator
35
36
LOG = logging.getLogger(__name__)
37
38
39
class RuleTypeSpec(BaseAPI):
40
    schema = {
41
        'type': 'object',
42
        'properties': {
43
            'ref': {
44
                'type': 'string',
45
                'required': True
46
            },
47
            'parameters': {
48
                'type': 'object'
49
            }
50
        },
51
        'additionalProperties': False
52
    }
53
54
55
class ActionSpec(BaseAPI):
56
    schema = {
57
        'type': 'object',
58
        'properties': {
59
            'ref': {
60
                'type': 'string',
61
                'required': True
62
            },
63
            'parameters': {
64
                'type': 'object'
65
            }
66
        },
67
        'additionalProperties': False
68
    }
69
70
71
REQUIRED_ATTR_SCHEMAS = {
72
    'action': copy.deepcopy(ActionSpec.schema)
73
}
74
75
for k, v in six.iteritems(REQUIRED_ATTR_SCHEMAS):
76
    v.update({'required': True})
77
78
79
class RuleTypeAPI(BaseAPI):
80
    model = RuleTypeDB
81
    schema = {
82
        'title': 'RuleType',
83
        'description': 'A specific type of rule.',
84
        'type': 'object',
85
        'properties': {
86
            'id': {
87
                'description': 'The unique identifier for the action runner.',
88
                'type': 'string',
89
                'default': None
90
            },
91
            'name': {
92
                'description': 'The name of the action runner.',
93
                'type': 'string',
94
                'required': True
95
            },
96
            'description': {
97
                'description': 'The description of the action runner.',
98
                'type': 'string'
99
            },
100
            'enabled': {
101
                'type': 'boolean',
102
                'default': True
103
            },
104
            'parameters': {
105
                'type': 'object'
106
            }
107
        },
108
        'additionalProperties': False
109
    }
110
111
    @classmethod
112
    def to_model(cls, rule_type):
113
        name = getattr(rule_type, 'name', None)
114
        description = getattr(rule_type, 'description', None)
115
        enabled = getattr(rule_type, 'enabled', False)
116
        parameters = getattr(rule_type, 'parameters', {})
117
118
        return cls.model(name=name, description=description, enabled=enabled,
119
                         parameters=parameters)
120
121
122
class RuleAPI(BaseAPI, APIUIDMixin):
123
    """
124
    Attribute:
125
        trigger_type: Trigger that trips this rule. Of the form {'id':'1234', 'name':'trigger-1'}.
126
        Only 1 of the id or name is required and if both are specified name is ignored.
127
        criteria: Criteria used to further restrict the trigger that applies to this rule.
128
        e.g.
129
        { "trigger.from" :
130
            { "pattern": "@gmail.com$"
131
            , "type": "matchregex" }
132
        , "trigger.subject" :
133
            { "pattern": "RE:"
134
            , "operator": "contain" }
135
        }
136
        action: Specification of the action to execute and the mappings to apply.
137
        expected arguments are name, parameters.
138
        e.g.
139
        "action":
140
        { "name": "st2.action.foo"
141
        , "parameters":
142
            { "command": "{{ system.foo }}"
143
            , "args": "--email {{ trigger.from }} --subject \'{{ user[stanley].ALERT_SUBJECT }}\'"}
144
        }
145
        status: enabled or disabled. If disabled occurrence of the trigger
146
        does not lead to execution of a action and vice-versa.
147
    """
148
    model = RuleDB
149
    schema = {
150
        'type': 'object',
151
        'properties': {
152
            'id': {
153
                'type': 'string',
154
                'default': None
155
            },
156
            "ref": {
157
                "description": "System computed user friendly reference for the action. \
158
                                Provided value will be overridden by computed value.",
159
                "type": "string"
160
            },
161
            'uid': {
162
                'type': 'string'
163
            },
164
            'name': {
165
                'type': 'string',
166
                'required': True
167
            },
168
            'pack': {
169
                'type': 'string',
170
                'default': DEFAULT_PACK_NAME
171
            },
172
            'description': {
173
                'type': 'string'
174
            },
175
            'type': RuleTypeSpec.schema,
176
            'trigger': {
177
                'type': 'object',
178
                'required': True,
179
                'properties': {
180
                    'type': {
181
                        'type': 'string',
182
                        'required': True
183
                    },
184
                    'parameters': {
185
                        'type': 'object',
186
                        'default': {}
187
                    },
188
                    'ref': {
189
                        'type': 'string',
190
                        'required': False
191
                    }
192
                },
193
                'additionalProperties': True
194
            },
195
            'criteria': {
196
                'type': 'object',
197
                'default': {}
198
            },
199
            'action': REQUIRED_ATTR_SCHEMAS['action'],
200
            'enabled': {
201
                'type': 'boolean',
202
                'default': False
203
            },
204
            "tags": {
205
                "description": "User associated metadata assigned to this object.",
206
                "type": "array",
207
                "items": {"type": "object"}
208
            }
209
        },
210
        'additionalProperties': False
211
    }
212
213
    @classmethod
214
    def from_model(cls, model, mask_secrets=False, ignore_missing_trigger=False):
0 ignored issues
show
Bug introduced by
Arguments number differs from overridden 'from_model' method
Loading history...
215
        rule = cls._from_model(model, mask_secrets=mask_secrets)
216
        trigger_db = reference.get_model_by_resource_ref(Trigger, model.trigger)
217
218
        if not ignore_missing_trigger and not trigger_db:
219
            raise ValueError('Missing TriggerDB object for rule %s' % (rule['id']))
220
221
        if trigger_db:
222
            rule['trigger'] = {
223
                'type': trigger_db.type,
224
                'parameters': trigger_db.parameters,
225
                'ref': model.trigger
226
            }
227
228
        rule['tags'] = TagsHelper.from_model(model.tags)
229
        return cls(**rule)
230
231
    @classmethod
232
    def to_model(cls, rule):
233
        kwargs = {}
234
        kwargs['name'] = getattr(rule, 'name', None)
235
        kwargs['description'] = getattr(rule, 'description', None)
236
237
        # Validate trigger parameters
238
        # Note: This must happen before we create a trigger, otherwise create trigger could fail
239
        # with a cryptic error
240
        trigger = getattr(rule, 'trigger', {})
241
        trigger_type_ref = trigger.get('type', None)
242
        parameters = trigger.get('parameters', {})
243
244
        context = {}
245
        context.update({
246
            kv_constants.DATASTORE_PARENT_SCOPE: {
247
                kv_constants.SYSTEM_SCOPE: kv_service.KeyValueLookup(
248
                    scope=kv_constants.FULL_SYSTEM_SCOPE)
249
            }
250
        })
251
        parameters = jinja_utils.render_values(mapping=parameters, context=context,
252
                                               allow_undefined=True)
253
        rule.trigger['parameters'] = parameters
254
        LOG.debug('Rendered trigger parameters: %s', parameters)
255
        validator.validate_trigger_parameters(trigger_type_ref=trigger_type_ref,
256
                                              parameters=parameters)
257
258
        # Create a trigger for the provided rule
259
        trigger_db = TriggerService.create_trigger_db_from_rule(rule)
260
        kwargs['trigger'] = reference.get_str_resource_ref_from_model(trigger_db)
261
262
        kwargs['pack'] = getattr(rule, 'pack', DEFAULT_PACK_NAME)
263
        kwargs['ref'] = ResourceReference.to_string_reference(pack=kwargs['pack'],
264
                                                              name=kwargs['name'])
265
266
        # Validate criteria
267
        kwargs['criteria'] = dict(getattr(rule, 'criteria', {}))
268
        validator.validate_criteria(kwargs['criteria'])
269
270
        kwargs['action'] = ActionExecutionSpecDB(ref=rule.action['ref'],
271
                                                 parameters=rule.action.get('parameters', {}))
272
273
        rule_type = dict(getattr(rule, 'type', {}))
274
        if rule_type:
275
            kwargs['type'] = RuleTypeSpecDB(ref=rule_type['ref'],
276
                                            parameters=rule_type.get('parameters', {}))
277
278
        kwargs['enabled'] = getattr(rule, 'enabled', False)
279
        kwargs['tags'] = TagsHelper.to_model(getattr(rule, 'tags', []))
280
281
        model = cls.model(**kwargs)
282
        return model
283
284
285
class RuleViewAPI(RuleAPI):
286
287
    # Always deep-copy to avoid breaking the original.
288
    schema = copy.deepcopy(RuleAPI.schema)
289
    # Update the schema to include the description properties
290
    schema['properties']['action'].update({
291
        'description': {
292
            'type': 'string'
293
        }
294
    })
295
    schema['properties']['trigger'].update({
296
        'description': {
297
            'type': 'string'
298
        }
299
    })
300