We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
3.0 - `source_channel` no longer working for Mistral workflows
##### SUMMARY

In ST2 3.0, Mistral workflows no longer able to identify `source_channel` that alias was called from. 

##### ISSUE TYPE
 - Bug Report

##### STACKSTORM VERSION

`st2 3.0.0, on Python 2.7.12`

##### OS / ENVIRONMENT / INSTALL METHOD

one-liner, on Ubuntu 16.04. 

System upgraded from 2.10.4 to 3.0.0.

##### STEPS TO REPRODUCE

Create a Mistral workflow that uses `source_channel`, and call it via a Chat alias.

Mistral workflow [here](https://github.com/StackStorm/st2_demos/blob/master/actions/workflows/multicap.yaml#L20).

Alias [here](https://github.com/StackStorm/st2_demos/blob/master/aliases/multicap.yaml).

Key lines:
```yaml
input:
    channel: "<% env().get('__actions').get('st2.action').st2_context.parent.source_channel %>"
    message: "Setting up environment and starting captures now...{~}"
on-success:
```

##### EXPECTED RESULTS

Workflow should post back to the original channel with my message 

> Setting up environment and starting captures now...

##### ACTUAL RESULTS

Workflow fails, because it can't get the value of source_channel. Error similar to

```
"Failed to run task [error=Can not evaluate YAQL expression [expression=env().get('__actions').get('st2.action').st2_context.parent.source_channel, error=u'source_channel', data={}], wf=st2_demos.multicap.main, task=notify_slack]:
```

This stems from https://github.com/StackStorm/st2/pull/4609/files#diff-f2911a69b88d56311f8945335e9457b7L148, which is part of PR #4609. If I add that removed line back to `aliasexecution.py`, it works as expected.

Looks like this data may be available in the `notify` dict.

/cc @blag @m4dcoder @emptywee 
3.0 - `source_channel` no longer working for Mistral workflows
##### SUMMARY

In ST2 3.0, Mistral workflows no longer able to identify `source_channel` that alias was called from. 

##### ISSUE TYPE
 - Bug Report

##### STACKSTORM VERSION

`st2 3.0.0, on Python 2.7.12`

##### OS / ENVIRONMENT / INSTALL METHOD

one-liner, on Ubuntu 16.04. 

System upgraded from 2.10.4 to 3.0.0.

##### STEPS TO REPRODUCE

Create a Mistral workflow that uses `source_channel`, and call it via a Chat alias.

Mistral workflow [here](https://github.com/StackStorm/st2_demos/blob/master/actions/workflows/multicap.yaml#L20).

Alias [here](https://github.com/StackStorm/st2_demos/blob/master/aliases/multicap.yaml).

Key lines:
```yaml
input:
    channel: "<% env().get('__actions').get('st2.action').st2_context.parent.source_channel %>"
    message: "Setting up environment and starting captures now...{~}"
on-success:
```

##### EXPECTED RESULTS

Workflow should post back to the original channel with my message 

> Setting up environment and starting captures now...

##### ACTUAL RESULTS

Workflow fails, because it can't get the value of source_channel. Error similar to

```
"Failed to run task [error=Can not evaluate YAQL expression [expression=env().get('__actions').get('st2.action').st2_context.parent.source_channel, error=u'source_channel', data={}], wf=st2_demos.multicap.main, task=notify_slack]:
```

This stems from https://github.com/StackStorm/st2/pull/4609/files#diff-f2911a69b88d56311f8945335e9457b7L148, which is part of PR #4609. If I add that removed line back to `aliasexecution.py`, it works as expected.

Looks like this data may be available in the `notify` dict.

/cc @blag @m4dcoder @emptywee 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `st2api/st2api/controllers/v1/aliasexecution.py`
Content:
```
1 # Copyright 2019 Extreme Networks, Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 import six
16 import jsonschema
17 from jinja2.exceptions import UndefinedError
18 from oslo_config import cfg
19 
20 from st2api.controllers.base import BaseRestControllerMixin
21 from st2common import log as logging
22 from st2common.exceptions.actionalias import ActionAliasAmbiguityException
23 from st2common.exceptions.db import StackStormDBObjectNotFoundError
24 from st2common.models.api.action import ActionAliasAPI
25 from st2common.models.api.action import AliasMatchAndExecuteInputAPI
26 from st2common.models.api.auth import get_system_username
27 from st2common.models.api.execution import ActionExecutionAPI
28 from st2common.models.db.auth import UserDB
29 from st2common.models.db.liveaction import LiveActionDB
30 from st2common.models.db.notification import NotificationSchema, NotificationSubSchema
31 from st2common.models.utils import action_param_utils
32 from st2common.models.utils.action_alias_utils import extract_parameters_for_action_alias_db
33 from st2common.persistence.actionalias import ActionAlias
34 from st2common.services import action as action_service
35 from st2common.util import action_db as action_utils
36 from st2common.util import reference
37 from st2common.util.actionalias_matching import get_matching_alias
38 from st2common.util.jinja import render_values as render
39 from st2common.rbac.types import PermissionType
40 from st2common.rbac.backends import get_rbac_backend
41 from st2common.router import abort
42 from st2common.router import Response
43 
44 http_client = six.moves.http_client
45 
46 LOG = logging.getLogger(__name__)
47 
48 CAST_OVERRIDES = {
49     'array': (lambda cs_x: [v.strip() for v in cs_x.split(',')])
50 }
51 
52 
53 class ActionAliasExecutionController(BaseRestControllerMixin):
54     def match_and_execute(self, input_api, requester_user, show_secrets=False):
55         """
56             Try to find a matching alias and if one is found, schedule a new
57             execution by parsing parameters from the provided command against
58             the matched alias.
59 
60             Handles requests:
61                 POST /aliasexecution/match_and_execute
62         """
63         command = input_api.command
64 
65         try:
66             format_ = get_matching_alias(command=command)
67         except ActionAliasAmbiguityException as e:
68             LOG.exception('Command "%s" matched (%s) patterns.', e.command, len(e.matches))
69             return abort(http_client.BAD_REQUEST, six.text_type(e))
70 
71         action_alias_db = format_['alias']
72         representation = format_['representation']
73 
74         params = {
75             'name': action_alias_db.name,
76             'format': representation,
77             'command': command,
78             'user': input_api.user,
79             'source_channel': input_api.source_channel,
80         }
81 
82         # Add in any additional parameters provided by the user
83         if input_api.notification_channel:
84             params['notification_channel'] = input_api.notification_channel
85 
86         if input_api.notification_route:
87             params['notification_route'] = input_api.notification_route
88 
89         alias_execution_api = AliasMatchAndExecuteInputAPI(**params)
90         results = self._post(
91             payload=alias_execution_api,
92             requester_user=requester_user,
93             show_secrets=show_secrets,
94             match_multiple=format_['match_multiple'])
95         return Response(json={'results': results}, status=http_client.CREATED)
96 
97     def _post(self, payload, requester_user, show_secrets=False, match_multiple=False):
98         action_alias_name = payload.name if payload else None
99 
100         if not action_alias_name:
101             abort(http_client.BAD_REQUEST, 'Alias execution "name" is required')
102             return
103 
104         if not requester_user:
105             requester_user = UserDB(cfg.CONF.system_user.user)
106 
107         format_str = payload.format or ''
108         command = payload.command or ''
109 
110         try:
111             action_alias_db = ActionAlias.get_by_name(action_alias_name)
112         except ValueError:
113             action_alias_db = None
114 
115         if not action_alias_db:
116             msg = 'Unable to identify action alias with name "%s".' % (action_alias_name)
117             abort(http_client.NOT_FOUND, msg)
118             return
119 
120         if not action_alias_db.enabled:
121             msg = 'Action alias with name "%s" is disabled.' % (action_alias_name)
122             abort(http_client.BAD_REQUEST, msg)
123             return
124 
125         if match_multiple:
126             multiple_execution_parameters = extract_parameters_for_action_alias_db(
127                 action_alias_db=action_alias_db,
128                 format_str=format_str,
129                 param_stream=command,
130                 match_multiple=match_multiple)
131         else:
132             multiple_execution_parameters = [
133                 extract_parameters_for_action_alias_db(
134                     action_alias_db=action_alias_db,
135                     format_str=format_str,
136                     param_stream=command,
137                     match_multiple=match_multiple)
138             ]
139 
140         notify = self._get_notify_field(payload)
141 
142         context = {
143             'action_alias_ref': reference.get_ref_from_model(action_alias_db),
144             'api_user': payload.user,
145             'user': requester_user.name,
146         }
147 
148         results = []
149         for execution_parameters in multiple_execution_parameters:
150             execution = self._schedule_execution(action_alias_db=action_alias_db,
151                                                  params=execution_parameters,
152                                                  notify=notify,
153                                                  context=context,
154                                                  show_secrets=show_secrets,
155                                                  requester_user=requester_user)
156 
157             result = {
158                 'execution': execution,
159                 'actionalias': ActionAliasAPI.from_model(action_alias_db)
160             }
161 
162             if action_alias_db.ack:
163                 try:
164                     if 'format' in action_alias_db.ack:
165                         message = render({'alias': action_alias_db.ack['format']}, result)['alias']
166 
167                         result.update({
168                             'message': message
169                         })
170                 except UndefinedError as e:
171                     result.update({
172                         'message': ('Cannot render "format" in field "ack" for alias. ' +
173                                     six.text_type(e))
174                     })
175 
176                 try:
177                     if 'extra' in action_alias_db.ack:
178                         result.update({
179                             'extra': render(action_alias_db.ack['extra'], result)
180                         })
181                 except UndefinedError as e:
182                     result.update({
183                         'extra': ('Cannot render "extra" in field "ack" for alias. ' +
184                                   six.text_type(e))
185                     })
186 
187             results.append(result)
188 
189         return results
190 
191     def post(self, payload, requester_user, show_secrets=False):
192         results = self._post(payload, requester_user, show_secrets, match_multiple=False)
193         return Response(json=results[0], status=http_client.CREATED)
194 
195     def _tokenize_alias_execution(self, alias_execution):
196         tokens = alias_execution.strip().split(' ', 1)
197         return (tokens[0], tokens[1] if len(tokens) > 1 else None)
198 
199     def _get_notify_field(self, payload):
200         on_complete = NotificationSubSchema()
201         route = (getattr(payload, 'notification_route', None) or
202                  getattr(payload, 'notification_channel', None))
203         on_complete.routes = [route]
204         on_complete.data = {
205             'user': payload.user,
206             'source_channel': payload.source_channel,
207             'source_context': getattr(payload, 'source_context', None),
208         }
209         notify = NotificationSchema()
210         notify.on_complete = on_complete
211         return notify
212 
213     def _schedule_execution(self, action_alias_db, params, notify, context, requester_user,
214                             show_secrets):
215         action_ref = action_alias_db.action_ref
216         action_db = action_utils.get_action_by_ref(action_ref)
217 
218         if not action_db:
219             raise StackStormDBObjectNotFoundError('Action with ref "%s" not found ' % (action_ref))
220 
221         rbac_utils = get_rbac_backend().get_utils_class()
222         permission_type = PermissionType.ACTION_EXECUTE
223         rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
224                                                           resource_db=action_db,
225                                                           permission_type=permission_type)
226 
227         try:
228             # prior to shipping off the params cast them to the right type.
229             params = action_param_utils.cast_params(action_ref=action_alias_db.action_ref,
230                                                     params=params,
231                                                     cast_overrides=CAST_OVERRIDES)
232             if not context:
233                 context = {
234                     'action_alias_ref': reference.get_ref_from_model(action_alias_db),
235                     'user': get_system_username()
236                 }
237             liveaction = LiveActionDB(action=action_alias_db.action_ref, context=context,
238                                       parameters=params, notify=notify)
239             _, action_execution_db = action_service.request(liveaction)
240             mask_secrets = self._get_mask_secrets(requester_user, show_secrets=show_secrets)
241             return ActionExecutionAPI.from_model(action_execution_db, mask_secrets=mask_secrets)
242         except ValueError as e:
243             LOG.exception('Unable to execute action.')
244             abort(http_client.BAD_REQUEST, six.text_type(e))
245         except jsonschema.ValidationError as e:
246             LOG.exception('Unable to execute action. Parameter validation failed.')
247             abort(http_client.BAD_REQUEST, six.text_type(e))
248         except Exception as e:
249             LOG.exception('Unable to execute action. Unexpected error encountered.')
250             abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e))
251 
252 
253 action_alias_execution_controller = ActionAliasExecutionController()
254
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/st2api/st2api/controllers/v1/aliasexecution.py b/st2api/st2api/controllers/v1/aliasexecution.py
--- a/st2api/st2api/controllers/v1/aliasexecution.py
+++ b/st2api/st2api/controllers/v1/aliasexecution.py
@@ -143,6 +143,7 @@
             'action_alias_ref': reference.get_ref_from_model(action_alias_db),
             'api_user': payload.user,
             'user': requester_user.name,
+            'source_channel': payload.source_channel,
         }
 
         results = []

{"golden_diff": "diff --git a/st2api/st2api/controllers/v1/aliasexecution.py b/st2api/st2api/controllers/v1/aliasexecution.py\n--- a/st2api/st2api/controllers/v1/aliasexecution.py\n+++ b/st2api/st2api/controllers/v1/aliasexecution.py\n@@ -143,6 +143,7 @@\n             'action_alias_ref': reference.get_ref_from_model(action_alias_db),\n             'api_user': payload.user,\n             'user': requester_user.name,\n+            'source_channel': payload.source_channel,\n         }\n \n         results = []\n", "issue": "3.0 - `source_channel` no longer working for Mistral workflows\n##### SUMMARY\r\n\r\nIn ST2 3.0, Mistral workflows no longer able to identify `source_channel` that alias was called from. \r\n\r\n##### ISSUE TYPE\r\n - Bug Report\r\n\r\n##### STACKSTORM VERSION\r\n\r\n`st2 3.0.0, on Python 2.7.12`\r\n\r\n##### OS / ENVIRONMENT / INSTALL METHOD\r\n\r\none-liner, on Ubuntu 16.04. \r\n\r\nSystem upgraded from 2.10.4 to 3.0.0.\r\n\r\n##### STEPS TO REPRODUCE\r\n\r\nCreate a Mistral workflow that uses `source_channel`, and call it via a Chat alias.\r\n\r\nMistral workflow [here](https://github.com/StackStorm/st2_demos/blob/master/actions/workflows/multicap.yaml#L20).\r\n\r\nAlias [here](https://github.com/StackStorm/st2_demos/blob/master/aliases/multicap.yaml).\r\n\r\nKey lines:\r\n```yaml\r\ninput:\r\n    channel: \"<% env().get('__actions').get('st2.action').st2_context.parent.source_channel %>\"\r\n    message: \"Setting up environment and starting captures now...{~}\"\r\non-success:\r\n```\r\n\r\n##### EXPECTED RESULTS\r\n\r\nWorkflow should post back to the original channel with my message \r\n\r\n> Setting up environment and starting captures now...\r\n\r\n##### ACTUAL RESULTS\r\n\r\nWorkflow fails, because it can't get the value of source_channel. Error similar to\r\n\r\n```\r\n\"Failed to run task [error=Can not evaluate YAQL expression [expression=env().get('__actions').get('st2.action').st2_context.parent.source_channel, error=u'source_channel', data={}], wf=st2_demos.multicap.main, task=notify_slack]:\r\n```\r\n\r\nThis stems from https://github.com/StackStorm/st2/pull/4609/files#diff-f2911a69b88d56311f8945335e9457b7L148, which is part of PR #4609. If I add that removed line back to `aliasexecution.py`, it works as expected.\r\n\r\nLooks like this data may be available in the `notify` dict.\r\n\r\n/cc @blag @m4dcoder @emptywee \n3.0 - `source_channel` no longer working for Mistral workflows\n##### SUMMARY\r\n\r\nIn ST2 3.0, Mistral workflows no longer able to identify `source_channel` that alias was called from. \r\n\r\n##### ISSUE TYPE\r\n - Bug Report\r\n\r\n##### STACKSTORM VERSION\r\n\r\n`st2 3.0.0, on Python 2.7.12`\r\n\r\n##### OS / ENVIRONMENT / INSTALL METHOD\r\n\r\none-liner, on Ubuntu 16.04. \r\n\r\nSystem upgraded from 2.10.4 to 3.0.0.\r\n\r\n##### STEPS TO REPRODUCE\r\n\r\nCreate a Mistral workflow that uses `source_channel`, and call it via a Chat alias.\r\n\r\nMistral workflow [here](https://github.com/StackStorm/st2_demos/blob/master/actions/workflows/multicap.yaml#L20).\r\n\r\nAlias [here](https://github.com/StackStorm/st2_demos/blob/master/aliases/multicap.yaml).\r\n\r\nKey lines:\r\n```yaml\r\ninput:\r\n    channel: \"<% env().get('__actions').get('st2.action').st2_context.parent.source_channel %>\"\r\n    message: \"Setting up environment and starting captures now...{~}\"\r\non-success:\r\n```\r\n\r\n##### EXPECTED RESULTS\r\n\r\nWorkflow should post back to the original channel with my message \r\n\r\n> Setting up environment and starting captures now...\r\n\r\n##### ACTUAL RESULTS\r\n\r\nWorkflow fails, because it can't get the value of source_channel. Error similar to\r\n\r\n```\r\n\"Failed to run task [error=Can not evaluate YAQL expression [expression=env().get('__actions').get('st2.action').st2_context.parent.source_channel, error=u'source_channel', data={}], wf=st2_demos.multicap.main, task=notify_slack]:\r\n```\r\n\r\nThis stems from https://github.com/StackStorm/st2/pull/4609/files#diff-f2911a69b88d56311f8945335e9457b7L148, which is part of PR #4609. If I add that removed line back to `aliasexecution.py`, it works as expected.\r\n\r\nLooks like this data may be available in the `notify` dict.\r\n\r\n/cc @blag @m4dcoder @emptywee \n", "before_files": [{"content": "# Copyright 2019 Extreme Networks, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport six\nimport jsonschema\nfrom jinja2.exceptions import UndefinedError\nfrom oslo_config import cfg\n\nfrom st2api.controllers.base import BaseRestControllerMixin\nfrom st2common import log as logging\nfrom st2common.exceptions.actionalias import ActionAliasAmbiguityException\nfrom st2common.exceptions.db import StackStormDBObjectNotFoundError\nfrom st2common.models.api.action import ActionAliasAPI\nfrom st2common.models.api.action import AliasMatchAndExecuteInputAPI\nfrom st2common.models.api.auth import get_system_username\nfrom st2common.models.api.execution import ActionExecutionAPI\nfrom st2common.models.db.auth import UserDB\nfrom st2common.models.db.liveaction import LiveActionDB\nfrom st2common.models.db.notification import NotificationSchema, NotificationSubSchema\nfrom st2common.models.utils import action_param_utils\nfrom st2common.models.utils.action_alias_utils import extract_parameters_for_action_alias_db\nfrom st2common.persistence.actionalias import ActionAlias\nfrom st2common.services import action as action_service\nfrom st2common.util import action_db as action_utils\nfrom st2common.util import reference\nfrom st2common.util.actionalias_matching import get_matching_alias\nfrom st2common.util.jinja import render_values as render\nfrom st2common.rbac.types import PermissionType\nfrom st2common.rbac.backends import get_rbac_backend\nfrom st2common.router import abort\nfrom st2common.router import Response\n\nhttp_client = six.moves.http_client\n\nLOG = logging.getLogger(__name__)\n\nCAST_OVERRIDES = {\n    'array': (lambda cs_x: [v.strip() for v in cs_x.split(',')])\n}\n\n\nclass ActionAliasExecutionController(BaseRestControllerMixin):\n    def match_and_execute(self, input_api, requester_user, show_secrets=False):\n        \"\"\"\n            Try to find a matching alias and if one is found, schedule a new\n            execution by parsing parameters from the provided command against\n            the matched alias.\n\n            Handles requests:\n                POST /aliasexecution/match_and_execute\n        \"\"\"\n        command = input_api.command\n\n        try:\n            format_ = get_matching_alias(command=command)\n        except ActionAliasAmbiguityException as e:\n            LOG.exception('Command \"%s\" matched (%s) patterns.', e.command, len(e.matches))\n            return abort(http_client.BAD_REQUEST, six.text_type(e))\n\n        action_alias_db = format_['alias']\n        representation = format_['representation']\n\n        params = {\n            'name': action_alias_db.name,\n            'format': representation,\n            'command': command,\n            'user': input_api.user,\n            'source_channel': input_api.source_channel,\n        }\n\n        # Add in any additional parameters provided by the user\n        if input_api.notification_channel:\n            params['notification_channel'] = input_api.notification_channel\n\n        if input_api.notification_route:\n            params['notification_route'] = input_api.notification_route\n\n        alias_execution_api = AliasMatchAndExecuteInputAPI(**params)\n        results = self._post(\n            payload=alias_execution_api,\n            requester_user=requester_user,\n            show_secrets=show_secrets,\n            match_multiple=format_['match_multiple'])\n        return Response(json={'results': results}, status=http_client.CREATED)\n\n    def _post(self, payload, requester_user, show_secrets=False, match_multiple=False):\n        action_alias_name = payload.name if payload else None\n\n        if not action_alias_name:\n            abort(http_client.BAD_REQUEST, 'Alias execution \"name\" is required')\n            return\n\n        if not requester_user:\n            requester_user = UserDB(cfg.CONF.system_user.user)\n\n        format_str = payload.format or ''\n        command = payload.command or ''\n\n        try:\n            action_alias_db = ActionAlias.get_by_name(action_alias_name)\n        except ValueError:\n            action_alias_db = None\n\n        if not action_alias_db:\n            msg = 'Unable to identify action alias with name \"%s\".' % (action_alias_name)\n            abort(http_client.NOT_FOUND, msg)\n            return\n\n        if not action_alias_db.enabled:\n            msg = 'Action alias with name \"%s\" is disabled.' % (action_alias_name)\n            abort(http_client.BAD_REQUEST, msg)\n            return\n\n        if match_multiple:\n            multiple_execution_parameters = extract_parameters_for_action_alias_db(\n                action_alias_db=action_alias_db,\n                format_str=format_str,\n                param_stream=command,\n                match_multiple=match_multiple)\n        else:\n            multiple_execution_parameters = [\n                extract_parameters_for_action_alias_db(\n                    action_alias_db=action_alias_db,\n                    format_str=format_str,\n                    param_stream=command,\n                    match_multiple=match_multiple)\n            ]\n\n        notify = self._get_notify_field(payload)\n\n        context = {\n            'action_alias_ref': reference.get_ref_from_model(action_alias_db),\n            'api_user': payload.user,\n            'user': requester_user.name,\n        }\n\n        results = []\n        for execution_parameters in multiple_execution_parameters:\n            execution = self._schedule_execution(action_alias_db=action_alias_db,\n                                                 params=execution_parameters,\n                                                 notify=notify,\n                                                 context=context,\n                                                 show_secrets=show_secrets,\n                                                 requester_user=requester_user)\n\n            result = {\n                'execution': execution,\n                'actionalias': ActionAliasAPI.from_model(action_alias_db)\n            }\n\n            if action_alias_db.ack:\n                try:\n                    if 'format' in action_alias_db.ack:\n                        message = render({'alias': action_alias_db.ack['format']}, result)['alias']\n\n                        result.update({\n                            'message': message\n                        })\n                except UndefinedError as e:\n                    result.update({\n                        'message': ('Cannot render \"format\" in field \"ack\" for alias. ' +\n                                    six.text_type(e))\n                    })\n\n                try:\n                    if 'extra' in action_alias_db.ack:\n                        result.update({\n                            'extra': render(action_alias_db.ack['extra'], result)\n                        })\n                except UndefinedError as e:\n                    result.update({\n                        'extra': ('Cannot render \"extra\" in field \"ack\" for alias. ' +\n                                  six.text_type(e))\n                    })\n\n            results.append(result)\n\n        return results\n\n    def post(self, payload, requester_user, show_secrets=False):\n        results = self._post(payload, requester_user, show_secrets, match_multiple=False)\n        return Response(json=results[0], status=http_client.CREATED)\n\n    def _tokenize_alias_execution(self, alias_execution):\n        tokens = alias_execution.strip().split(' ', 1)\n        return (tokens[0], tokens[1] if len(tokens) > 1 else None)\n\n    def _get_notify_field(self, payload):\n        on_complete = NotificationSubSchema()\n        route = (getattr(payload, 'notification_route', None) or\n                 getattr(payload, 'notification_channel', None))\n        on_complete.routes = [route]\n        on_complete.data = {\n            'user': payload.user,\n            'source_channel': payload.source_channel,\n            'source_context': getattr(payload, 'source_context', None),\n        }\n        notify = NotificationSchema()\n        notify.on_complete = on_complete\n        return notify\n\n    def _schedule_execution(self, action_alias_db, params, notify, context, requester_user,\n                            show_secrets):\n        action_ref = action_alias_db.action_ref\n        action_db = action_utils.get_action_by_ref(action_ref)\n\n        if not action_db:\n            raise StackStormDBObjectNotFoundError('Action with ref \"%s\" not found ' % (action_ref))\n\n        rbac_utils = get_rbac_backend().get_utils_class()\n        permission_type = PermissionType.ACTION_EXECUTE\n        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,\n                                                          resource_db=action_db,\n                                                          permission_type=permission_type)\n\n        try:\n            # prior to shipping off the params cast them to the right type.\n            params = action_param_utils.cast_params(action_ref=action_alias_db.action_ref,\n                                                    params=params,\n                                                    cast_overrides=CAST_OVERRIDES)\n            if not context:\n                context = {\n                    'action_alias_ref': reference.get_ref_from_model(action_alias_db),\n                    'user': get_system_username()\n                }\n            liveaction = LiveActionDB(action=action_alias_db.action_ref, context=context,\n                                      parameters=params, notify=notify)\n            _, action_execution_db = action_service.request(liveaction)\n            mask_secrets = self._get_mask_secrets(requester_user, show_secrets=show_secrets)\n            return ActionExecutionAPI.from_model(action_execution_db, mask_secrets=mask_secrets)\n        except ValueError as e:\n            LOG.exception('Unable to execute action.')\n            abort(http_client.BAD_REQUEST, six.text_type(e))\n        except jsonschema.ValidationError as e:\n            LOG.exception('Unable to execute action. Parameter validation failed.')\n            abort(http_client.BAD_REQUEST, six.text_type(e))\n        except Exception as e:\n            LOG.exception('Unable to execute action. Unexpected error encountered.')\n            abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e))\n\n\naction_alias_execution_controller = ActionAliasExecutionController()\n", "path": "st2api/st2api/controllers/v1/aliasexecution.py"}], "after_files": [{"content": "# Copyright 2019 Extreme Networks, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport six\nimport jsonschema\nfrom jinja2.exceptions import UndefinedError\nfrom oslo_config import cfg\n\nfrom st2api.controllers.base import BaseRestControllerMixin\nfrom st2common import log as logging\nfrom st2common.exceptions.actionalias import ActionAliasAmbiguityException\nfrom st2common.exceptions.db import StackStormDBObjectNotFoundError\nfrom st2common.models.api.action import ActionAliasAPI\nfrom st2common.models.api.action import AliasMatchAndExecuteInputAPI\nfrom st2common.models.api.auth import get_system_username\nfrom st2common.models.api.execution import ActionExecutionAPI\nfrom st2common.models.db.auth import UserDB\nfrom st2common.models.db.liveaction import LiveActionDB\nfrom st2common.models.db.notification import NotificationSchema, NotificationSubSchema\nfrom st2common.models.utils import action_param_utils\nfrom st2common.models.utils.action_alias_utils import extract_parameters_for_action_alias_db\nfrom st2common.persistence.actionalias import ActionAlias\nfrom st2common.services import action as action_service\nfrom st2common.util import action_db as action_utils\nfrom st2common.util import reference\nfrom st2common.util.actionalias_matching import get_matching_alias\nfrom st2common.util.jinja import render_values as render\nfrom st2common.rbac.types import PermissionType\nfrom st2common.rbac.backends import get_rbac_backend\nfrom st2common.router import abort\nfrom st2common.router import Response\n\nhttp_client = six.moves.http_client\n\nLOG = logging.getLogger(__name__)\n\nCAST_OVERRIDES = {\n    'array': (lambda cs_x: [v.strip() for v in cs_x.split(',')])\n}\n\n\nclass ActionAliasExecutionController(BaseRestControllerMixin):\n    def match_and_execute(self, input_api, requester_user, show_secrets=False):\n        \"\"\"\n            Try to find a matching alias and if one is found, schedule a new\n            execution by parsing parameters from the provided command against\n            the matched alias.\n\n            Handles requests:\n                POST /aliasexecution/match_and_execute\n        \"\"\"\n        command = input_api.command\n\n        try:\n            format_ = get_matching_alias(command=command)\n        except ActionAliasAmbiguityException as e:\n            LOG.exception('Command \"%s\" matched (%s) patterns.', e.command, len(e.matches))\n            return abort(http_client.BAD_REQUEST, six.text_type(e))\n\n        action_alias_db = format_['alias']\n        representation = format_['representation']\n\n        params = {\n            'name': action_alias_db.name,\n            'format': representation,\n            'command': command,\n            'user': input_api.user,\n            'source_channel': input_api.source_channel,\n        }\n\n        # Add in any additional parameters provided by the user\n        if input_api.notification_channel:\n            params['notification_channel'] = input_api.notification_channel\n\n        if input_api.notification_route:\n            params['notification_route'] = input_api.notification_route\n\n        alias_execution_api = AliasMatchAndExecuteInputAPI(**params)\n        results = self._post(\n            payload=alias_execution_api,\n            requester_user=requester_user,\n            show_secrets=show_secrets,\n            match_multiple=format_['match_multiple'])\n        return Response(json={'results': results}, status=http_client.CREATED)\n\n    def _post(self, payload, requester_user, show_secrets=False, match_multiple=False):\n        action_alias_name = payload.name if payload else None\n\n        if not action_alias_name:\n            abort(http_client.BAD_REQUEST, 'Alias execution \"name\" is required')\n            return\n\n        if not requester_user:\n            requester_user = UserDB(cfg.CONF.system_user.user)\n\n        format_str = payload.format or ''\n        command = payload.command or ''\n\n        try:\n            action_alias_db = ActionAlias.get_by_name(action_alias_name)\n        except ValueError:\n            action_alias_db = None\n\n        if not action_alias_db:\n            msg = 'Unable to identify action alias with name \"%s\".' % (action_alias_name)\n            abort(http_client.NOT_FOUND, msg)\n            return\n\n        if not action_alias_db.enabled:\n            msg = 'Action alias with name \"%s\" is disabled.' % (action_alias_name)\n            abort(http_client.BAD_REQUEST, msg)\n            return\n\n        if match_multiple:\n            multiple_execution_parameters = extract_parameters_for_action_alias_db(\n                action_alias_db=action_alias_db,\n                format_str=format_str,\n                param_stream=command,\n                match_multiple=match_multiple)\n        else:\n            multiple_execution_parameters = [\n                extract_parameters_for_action_alias_db(\n                    action_alias_db=action_alias_db,\n                    format_str=format_str,\n                    param_stream=command,\n                    match_multiple=match_multiple)\n            ]\n\n        notify = self._get_notify_field(payload)\n\n        context = {\n            'action_alias_ref': reference.get_ref_from_model(action_alias_db),\n            'api_user': payload.user,\n            'user': requester_user.name,\n            'source_channel': payload.source_channel,\n        }\n\n        results = []\n        for execution_parameters in multiple_execution_parameters:\n            execution = self._schedule_execution(action_alias_db=action_alias_db,\n                                                 params=execution_parameters,\n                                                 notify=notify,\n                                                 context=context,\n                                                 show_secrets=show_secrets,\n                                                 requester_user=requester_user)\n\n            result = {\n                'execution': execution,\n                'actionalias': ActionAliasAPI.from_model(action_alias_db)\n            }\n\n            if action_alias_db.ack:\n                try:\n                    if 'format' in action_alias_db.ack:\n                        message = render({'alias': action_alias_db.ack['format']}, result)['alias']\n\n                        result.update({\n                            'message': message\n                        })\n                except UndefinedError as e:\n                    result.update({\n                        'message': ('Cannot render \"format\" in field \"ack\" for alias. ' +\n                                    six.text_type(e))\n                    })\n\n                try:\n                    if 'extra' in action_alias_db.ack:\n                        result.update({\n                            'extra': render(action_alias_db.ack['extra'], result)\n                        })\n                except UndefinedError as e:\n                    result.update({\n                        'extra': ('Cannot render \"extra\" in field \"ack\" for alias. ' +\n                                  six.text_type(e))\n                    })\n\n            results.append(result)\n\n        return results\n\n    def post(self, payload, requester_user, show_secrets=False):\n        results = self._post(payload, requester_user, show_secrets, match_multiple=False)\n        return Response(json=results[0], status=http_client.CREATED)\n\n    def _tokenize_alias_execution(self, alias_execution):\n        tokens = alias_execution.strip().split(' ', 1)\n        return (tokens[0], tokens[1] if len(tokens) > 1 else None)\n\n    def _get_notify_field(self, payload):\n        on_complete = NotificationSubSchema()\n        route = (getattr(payload, 'notification_route', None) or\n                 getattr(payload, 'notification_channel', None))\n        on_complete.routes = [route]\n        on_complete.data = {\n            'user': payload.user,\n            'source_channel': payload.source_channel,\n            'source_context': getattr(payload, 'source_context', None),\n        }\n        notify = NotificationSchema()\n        notify.on_complete = on_complete\n        return notify\n\n    def _schedule_execution(self, action_alias_db, params, notify, context, requester_user,\n                            show_secrets):\n        action_ref = action_alias_db.action_ref\n        action_db = action_utils.get_action_by_ref(action_ref)\n\n        if not action_db:\n            raise StackStormDBObjectNotFoundError('Action with ref \"%s\" not found ' % (action_ref))\n\n        rbac_utils = get_rbac_backend().get_utils_class()\n        permission_type = PermissionType.ACTION_EXECUTE\n        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,\n                                                          resource_db=action_db,\n                                                          permission_type=permission_type)\n\n        try:\n            # prior to shipping off the params cast them to the right type.\n            params = action_param_utils.cast_params(action_ref=action_alias_db.action_ref,\n                                                    params=params,\n                                                    cast_overrides=CAST_OVERRIDES)\n            if not context:\n                context = {\n                    'action_alias_ref': reference.get_ref_from_model(action_alias_db),\n                    'user': get_system_username()\n                }\n            liveaction = LiveActionDB(action=action_alias_db.action_ref, context=context,\n                                      parameters=params, notify=notify)\n            _, action_execution_db = action_service.request(liveaction)\n            mask_secrets = self._get_mask_secrets(requester_user, show_secrets=show_secrets)\n            return ActionExecutionAPI.from_model(action_execution_db, mask_secrets=mask_secrets)\n        except ValueError as e:\n            LOG.exception('Unable to execute action.')\n            abort(http_client.BAD_REQUEST, six.text_type(e))\n        except jsonschema.ValidationError as e:\n            LOG.exception('Unable to execute action. Parameter validation failed.')\n            abort(http_client.BAD_REQUEST, six.text_type(e))\n        except Exception as e:\n            LOG.exception('Unable to execute action. Unexpected error encountered.')\n            abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e))\n\n\naction_alias_execution_controller = ActionAliasExecutionController()\n", "path": "st2api/st2api/controllers/v1/aliasexecution.py"}]}