Completed
Push — master ( 32524f...9c26f2 )
by Edward
13:28 queued 13:28
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
        formats = []
111
        for formatstring in action_alias_db.formats:
112
            if 'representation' in formatstring:
113
                formats.extend(formatstring['representation'])
114
            else:
115
                formats.append(formatstring)
116
        if formats and format_str in formats:
117
            alias_format = format_str
118
        else:
119
            alias_format = None
120
121
        parser = action_alias_utils.ActionAliasFormatParser(alias_format=alias_format,
122
                                                            param_stream=param_stream)
123
        return parser.get_extracted_param_value()
124
125
    def _get_notify_field(self, payload):
126
        on_complete = NotificationSubSchema()
127
        route = (getattr(payload, 'notification_route', None) or
128
                 getattr(payload, 'notification_channel', None))
129
        on_complete.routes = [route]
130
        on_complete.data = {
131
            'user': payload.user,
132
            'source_channel': payload.source_channel
133
        }
134
        notify = NotificationSchema()
135
        notify.on_complete = on_complete
136
        return notify
137
138
    def _schedule_execution(self, action_alias_db, params, notify, context):
139
        action_ref = action_alias_db.action_ref
140
        action_db = action_utils.get_action_by_ref(action_ref)
141
142
        if not action_db:
143
            raise StackStormDBObjectNotFoundError('Action with ref "%s" not found ' % (action_ref))
144
145
        assert_request_user_has_resource_db_permission(request=pecan.request, resource_db=action_db,
146
            permission_type=PermissionType.ACTION_EXECUTE)
147
148
        try:
149
            # prior to shipping off the params cast them to the right type.
150
            params = action_param_utils.cast_params(action_ref=action_alias_db.action_ref,
151
                                                    params=params,
152
                                                    cast_overrides=CAST_OVERRIDES)
153
            if not context:
154
                context = {
155
                    'action_alias_ref': reference.get_ref_from_model(action_alias_db),
156
                    'user': get_system_username()
157
                }
158
            liveaction = LiveActionDB(action=action_alias_db.action_ref, context=context,
159
                                      parameters=params, notify=notify)
160
            _, action_execution_db = action_service.request(liveaction)
161
            return ActionExecutionAPI.from_model(action_execution_db)
162
        except ValueError as e:
163
            LOG.exception('Unable to execute action.')
164
            pecan.abort(http_client.BAD_REQUEST, str(e))
165
        except jsonschema.ValidationError as e:
166
            LOG.exception('Unable to execute action. Parameter validation failed.')
167
            pecan.abort(http_client.BAD_REQUEST, str(e))
168
        except Exception as e:
169
            LOG.exception('Unable to execute action. Unexpected error encountered.')
170
            pecan.abort(http_client.INTERNAL_SERVER_ERROR, str(e))
171