Repository: StackStorm/st2

Issue:
Shell script action not serializing array properly.
## SUMMARY

According to the documentation (https://docs.stackstorm.com/actions.html#update-argument-parsing-in-the-script), arrays should be passed to bash script actions as a comma-delimited string.  

> array - Serialized as a comma delimited string (e.g. foo,bar,baz).

But it's coming through as a python list serialized into a string.

`[u'foo', u'bar', u'baz']`

### STACKSTORM VERSION

Paste the output of ``st2 --version``:

```
stanley@st2-dev:~$ st2 --version
st2 3.1.0, on Python 2.7.12
```

##### OS, environment, install method

Post what OS you are running this on, along with any other relevant information.

Ubuntu 16.04 LTS installed via apt packaging.

## Steps to reproduce the problem

**Example Action YAML File**

```
---
name: "test_bash_array"
runner_type: "remote-shell-script"
description: "Tests an array passed to a bash script."
enabled: true
entry_point: "test_bash_array.sh"
parameters:
    test_array:
        type: "array"
        description: "Test array in bash"
        position: 0
```

**Example Action Bash Script**

```
#!/usr/bin/env bash

TEST_ARRAY=$1

IFS=","; for i in ${TEST_ARRAY}; do echo "$i"; done

exit 0
```

**Invoking Action**

```
test_array:
  - foo
  - bar
  - baz
```


## Expected Results

What did you expect to happen when running the steps above?

Given the above bash script, I'd expect

```
foo
bar
baz
```

## Actual Results

What happened? What output did you get?

Instead I'm getting

```
[u'foo'
 u'bar'
 u'baz']
```

Making sure to follow these steps will guarantee the quickest resolution possible.

Thanks!



Discussion:
I'm working on this issue and am close to come up with a PR or maybe some implementation details to be discussed soon.  

Resolving patch:
diff --git a/st2common/st2common/util/action_db.py b/st2common/st2common/util/action_db.py
--- a/st2common/st2common/util/action_db.py
+++ b/st2common/st2common/util/action_db.py
@@ -283,9 +283,9 @@ def serialize_positional_argument(argument_type, argument_value):
             argument_value = '1' if bool(argument_value) else '0'
         else:
             argument_value = ''
-    elif argument_type == 'list':
+    elif argument_type in ['array', 'list']:
         # Lists are serialized a comma delimited string (foo,bar,baz)
-        argument_value = ','.join(argument_value) if argument_value else ''
+        argument_value = ','.join(map(str, argument_value)) if argument_value else ''
     elif argument_type == 'object':
         # Objects are serialized as JSON
         argument_value = json.dumps(argument_value) if argument_value else ''


Test patch:
diff --git a/st2common/tests/unit/test_action_db_utils.py b/st2common/tests/unit/test_action_db_utils.py
--- a/st2common/tests/unit/test_action_db_utils.py
+++ b/st2common/tests/unit/test_action_db_utils.py
@@ -327,7 +327,7 @@ def test_get_args(self):
             'runnerint': 555
         }
         pos_args, named_args = action_db_utils.get_args(params, ActionDBUtilsTestCase.action_db)
-        self.assertListEqual(pos_args, ['20', '', 'foo', '', '', '', ''],
+        self.assertListEqual(pos_args, ['20', '', 'foo', '', '', '', '', ''],
                             'Positional args not parsed correctly.')
         self.assertNotIn('actionint', named_args)
         self.assertNotIn('actionstr', named_args)
@@ -340,6 +340,7 @@ def test_get_args(self):
             'actionfloat': 1.5,
             'actionstr': 'string value',
             'actionbool': True,
+            'actionarray': ['foo', 'bar', 'baz', 'qux'],
             'actionlist': ['foo', 'bar', 'baz'],
             'actionobject': {'a': 1, 'b': '2'},
         }
@@ -348,6 +349,7 @@ def test_get_args(self):
             '1.5',
             'string value',
             '1',
+            'foo,bar,baz,qux',
             'foo,bar,baz',
             '{"a": 1, "b": "2"}',
             ''
@@ -361,6 +363,7 @@ def test_get_args(self):
             'actionfloat': 1.5,
             'actionstr': 'string value',
             'actionbool': False,
+            'actionarray': [],
             'actionlist': [],
             'actionobject': {'a': 1, 'b': '2'},
         }
@@ -370,6 +373,7 @@ def test_get_args(self):
             'string value',
             '0',
             '',
+            '',
             '{"a": 1, "b": "2"}',
             ''
         ]
@@ -383,6 +387,7 @@ def test_get_args(self):
             'actionfloat': None,
             'actionstr': None,
             'actionbool': None,
+            'actionarray': None,
             'actionlist': None,
             'actionobject': None,
         }
@@ -393,6 +398,7 @@ def test_get_args(self):
             '',
             '',
             '',
+            '',
             ''
         ]
         pos_args, _ = action_db_utils.get_args(params, ActionDBUtilsTestCase.action_db)
@@ -412,10 +418,31 @@ def test_get_args(self):
             '',
             '',
             '',
+            '',
             ''
         ]
         pos_args, named_args = action_db_utils.get_args(params, ActionDBUtilsTestCase.action_db)
         self.assertListEqual(pos_args, expected_pos_args, 'Positional args not parsed correctly.')
+
+        # Test arrays and lists with values of different types
+        params = {
+            'actionarray': [None, False, 1, 4.2e1, '1e3', 'foo'],
+            'actionlist': [None, False, 1, 73e-2, '1e2', 'bar']
+        }
+        expected_pos_args = [
+            '',
+            '',
+            '',
+            '',
+            'None,False,1,42.0,1e3,foo',
+            'None,False,1,0.73,1e2,bar',
+            '',
+            ''
+        ]
+        pos_args, _ = action_db_utils.get_args(params, ActionDBUtilsTestCase.action_db)
+        self.assertListEqual(pos_args, expected_pos_args,
+                             'Positional args not parsed / serialized correctly.')
+
         self.assertNotIn('actionint', named_args)
         self.assertNotIn('actionstr', named_args)
         self.assertEqual(named_args.get('runnerint'), 555)
@@ -463,9 +490,10 @@ def setup_action_models(cls):
             'actionfloat': {'type': 'float', 'required': False, 'position': 1},
             'actionstr': {'type': 'string', 'required': True, 'position': 2},
             'actionbool': {'type': 'boolean', 'required': False, 'position': 3},
-            'actionlist': {'type': 'list', 'required': False, 'position': 4},
-            'actionobject': {'type': 'object', 'required': False, 'position': 5},
-            'actionnull': {'type': 'null', 'required': False, 'position': 6},
+            'actionarray': {'type': 'array', 'required': False, 'position': 4},
+            'actionlist': {'type': 'list', 'required': False, 'position': 5},
+            'actionobject': {'type': 'object', 'required': False, 'position': 6},
+            'actionnull': {'type': 'null', 'required': False, 'position': 7},
 
             'runnerdummy': {'type': 'string', 'default': 'actiondummy'}
         }