Repository: StackStorm/st2

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 


Discussion:
@blag Can you take a look at this issue and comment?  Can we add `source_channel` back into context? Or should `source_channel` be access from the `notify` dict?
### Fix ###

In the past, I believe `source_channel` was directly available to the action, without going through the `notify` dict. So this is a bug, and adding that line back in should fix it.

### Possible Workaround ###

For now, if the source channel is available in the `notify` dict, users should use that. I'll have to dig into this more to figure out if this is a viable workaround. However, we may not want to use this workaround if we plan on releasing a `3.0.1` version "soon" with fixes for some of the non-blocking issues we discovered during release.

### Future ###

All of this code is ripe for cleanup and migration in the future. I'm of the opinion that since ChatOps is aiming to be agnostic to the chat provider, we shouldn't have special cases for each provider, or for some providers but not all (like here, where we have a special case for chat providers that have Slack-like APIs). In the future, I think all source information should be stuffed into `source_context`, and should be extracted either manually or using an abstraction/helper functions that abstract it away. This preserves the "raw" data and keeps it accessible for providers that aren't well supported, while also making sure most users of well supported providers can easily access information relevant to them.
@blag Can you take a look at this issue and comment?  Can we add `source_channel` back into context? Or should `source_channel` be access from the `notify` dict?
### Fix ###

In the past, I believe `source_channel` was directly available to the action, without going through the `notify` dict. So this is a bug, and adding that line back in should fix it.

### Possible Workaround ###

For now, if the source channel is available in the `notify` dict, users should use that. I'll have to dig into this more to figure out if this is a viable workaround. However, we may not want to use this workaround if we plan on releasing a `3.0.1` version "soon" with fixes for some of the non-blocking issues we discovered during release.

### Future ###

All of this code is ripe for cleanup and migration in the future. I'm of the opinion that since ChatOps is aiming to be agnostic to the chat provider, we shouldn't have special cases for each provider, or for some providers but not all (like here, where we have a special case for chat providers that have Slack-like APIs). In the future, I think all source information should be stuffed into `source_context`, and should be extracted either manually or using an abstraction/helper functions that abstract it away. This preserves the "raw" data and keeps it accessible for providers that aren't well supported, while also making sure most users of well supported providers can easily access information relevant to them.

Resolving patch:
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 @@ def _post(self, payload, requester_user, show_secrets=False, match_multiple=Fals
             '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 = []


Test patch:
diff --git a/st2api/tests/unit/controllers/v1/test_alias_execution.py b/st2api/tests/unit/controllers/v1/test_alias_execution.py
--- a/st2api/tests/unit/controllers/v1/test_alias_execution.py
+++ b/st2api/tests/unit/controllers/v1/test_alias_execution.py
@@ -187,9 +187,9 @@ def test_match_and_execute_matches_many(self, mock_request):
                        return_value=(None, EXECUTION))
     def test_match_and_execute_matches_one(self, mock_request):
         base_data = {
-            'source_channel': 'chat',
+            'source_channel': 'chat-channel',
             'notification_route': 'hubot',
-            'user': 'chat-user'
+            'user': 'chat-user',
         }
 
         # Command matches - should result in action execution
@@ -204,6 +204,15 @@ def test_match_and_execute_matches_one(self, mock_request):
         expected_parameters = {'cmd': 'date', 'hosts': 'localhost'}
         self.assertEquals(mock_request.call_args[0][0].parameters, expected_parameters)
 
+        # Also check for source_channel - see
+        # https://github.com/StackStorm/st2/issues/4650
+        actual_context = mock_request.call_args[0][0].context
+
+        self.assertTrue('source_channel' in mock_request.call_args[0][0].context.keys())
+        self.assertEquals(actual_context['source_channel'], 'chat-channel')
+        self.assertEquals(actual_context['api_user'], 'chat-user')
+        self.assertEquals(actual_context['user'], 'stanley')
+
     @mock.patch.object(action_service, 'request',
                        return_value=(None, EXECUTION))
     def test_match_and_execute_matches_one_multiple_match(self, mock_request):