Completed
Push — master ( 9f0a5f...ca638c )
by Edward
05:44
created

post()   C

Complexity

Conditions 8

Size

Total Lines 53

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 53
rs 5.8626
cc 8

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
import jsonschema
17
import pecan
18
import six
19
from pecan import rest
20
from st2common import log as logging
21
from st2common.models.api.base import jsexpose
22
from st2common.exceptions.db import StackStormDBObjectNotFoundError
23
from st2common.models.api.action import AliasExecutionAPI
24
from st2common.models.api.action import ActionAliasAPI
25
from st2common.models.api.auth import get_system_username
26
from st2common.models.api.execution import ActionExecutionAPI
27
from st2common.models.db.liveaction import LiveActionDB
28
from st2common.models.db.notification import NotificationSchema, NotificationSubSchema
29
from st2common.models.utils import action_alias_utils, action_param_utils
30
from st2common.persistence.actionalias import ActionAlias
31
from st2common.services import action as action_service
32
from st2common.util import action_db as action_utils
33
from st2common.util import reference
34
from st2common.util.api import get_requester
35
from st2common.util.jinja import render_values as render
36
from st2common.rbac.types import PermissionType
37
from st2common.rbac.utils import assert_request_user_has_resource_db_permission
38
39
40
http_client = six.moves.http_client
41
42
LOG = logging.getLogger(__name__)
43
44
CAST_OVERRIDES = {
45
    'array': (lambda cs_x: [v.strip() for v in cs_x.split(',')])
46
}
47
48
49
class ActionAliasExecutionController(rest.RestController):
50
51
    @jsexpose(body_cls=AliasExecutionAPI, status_code=http_client.CREATED)
52
    def post(self, payload):
53
        action_alias_name = payload.name if payload else None
54
55
        if not action_alias_name:
56
            pecan.abort(http_client.BAD_REQUEST, 'Alias execution "name" is required')
57
58
        format_str = payload.format or ''
59
        command = payload.command or ''
60
61
        try:
62
            action_alias_db = ActionAlias.get_by_name(action_alias_name)
63
        except ValueError:
64
            action_alias_db = None
65
66
        if not action_alias_db:
67
            msg = 'Unable to identify action alias with name "%s".' % (action_alias_name)
68
            pecan.abort(http_client.NOT_FOUND, msg)
69
            return
70
71
        if not action_alias_db.enabled:
72
            msg = 'Action alias with name "%s" is disabled.' % (action_alias_name)
73
            pecan.abort(http_client.BAD_REQUEST, msg)
74
            return
75
76
        execution_parameters = self._extract_parameters(action_alias_db=action_alias_db,
77
                                                        format_str=format_str,
78
                                                        param_stream=command)
79
        notify = self._get_notify_field(payload)
80
81
        context = {
82
            'action_alias_ref': reference.get_ref_from_model(action_alias_db),
83
            'api_user': payload.user,
84
            'user': get_requester(),
85
            'source_channel': payload.source_channel
86
        }
87
88
        execution = self._schedule_execution(action_alias_db=action_alias_db,
89
                                             params=execution_parameters,
90
                                             notify=notify,
91
                                             context=context)
92
93
        result = {
94
            'execution': execution,
95
            'actionalias': ActionAliasAPI.from_model(action_alias_db)
96
        }
97
98
        if action_alias_db.ack and 'format' in action_alias_db.ack:
99
            result.update({
100
                'message': render({'alias': action_alias_db.ack['format']}, result)['alias']
101
            })
102
103
        return result
104
105
    def _tokenize_alias_execution(self, alias_execution):
106
        tokens = alias_execution.strip().split(' ', 1)
107
        return (tokens[0], tokens[1] if len(tokens) > 1 else None)
108
109
    def _extract_parameters(self, action_alias_db, format_str, param_stream):
110
        if action_alias_db.formats and format_str in action_alias_db.formats:
111
            alias_format = format_str
112
        else:
113
            alias_format = None
114
115
        parser = action_alias_utils.ActionAliasFormatParser(alias_format=alias_format,
116
                                                            param_stream=param_stream)
117
        return parser.get_extracted_param_value()
118
119
    def _get_notify_field(self, payload):
120
        on_complete = NotificationSubSchema()
121
        route = (getattr(payload, 'notification_route', None) or
122
                 getattr(payload, 'notification_channel', None))
123
        on_complete.routes = [route]
124
        on_complete.data = {
125
            'user': payload.user,
126
            'source_channel': payload.source_channel
127
        }
128
        notify = NotificationSchema()
129
        notify.on_complete = on_complete
130
        return notify
131
132
    def _schedule_execution(self, action_alias_db, params, notify, context):
133
        action_ref = action_alias_db.action_ref
134
        action_db = action_utils.get_action_by_ref(action_ref)
135
136
        if not action_db:
137
            raise StackStormDBObjectNotFoundError('Action with ref "%s" not found ' % (action_ref))
138
139
        assert_request_user_has_resource_db_permission(request=pecan.request, resource_db=action_db,
140
            permission_type=PermissionType.ACTION_EXECUTE)
141
142
        try:
143
            # prior to shipping off the params cast them to the right type.
144
            params = action_param_utils.cast_params(action_ref=action_alias_db.action_ref,
145
                                                    params=params,
146
                                                    cast_overrides=CAST_OVERRIDES)
147
            if not context:
148
                context = {
149
                    'action_alias_ref': reference.get_ref_from_model(action_alias_db),
150
                    'user': get_system_username()
151
                }
152
            liveaction = LiveActionDB(action=action_alias_db.action_ref, context=context,
153
                                      parameters=params, notify=notify)
154
            _, action_execution_db = action_service.request(liveaction)
155
            return ActionExecutionAPI.from_model(action_execution_db)
156
        except ValueError as e:
157
            LOG.exception('Unable to execute action.')
158
            pecan.abort(http_client.BAD_REQUEST, str(e))
159
        except jsonschema.ValidationError as e:
160
            LOG.exception('Unable to execute action. Parameter validation failed.')
161
            pecan.abort(http_client.BAD_REQUEST, str(e))
162
        except Exception as e:
163
            LOG.exception('Unable to execute action. Unexpected error encountered.')
164
            pecan.abort(http_client.INTERNAL_SERVER_ERROR, str(e))
165