Repository: cloudslang/cloud-slang
Issue: CLI log: Missing CALL_ARGUMENTS for Python action [1]

Observed the following in CLI log:

Java action - call arguments logged:

```
STEP_TYPE=ACTION, STEP_NAME=null, TYPE=EVENT_ACTION_START, CALL_ARGUMENTS={characterSet=UTF-8, agentForwarding=null, command=docker ps -a, timeout=600000, password=null, pty=false, privateKeyFile=c:/.../id_rsa, port=22, host=111.111.111.111, arguments=null, closeSession=false, username=core}
```

Python action - call arguments not logged:

```
STEP_TYPE=ACTION, STEP_NAME=null, TYPE=EVENT_ACTION_START, CALL_ARGUMENTS={}
```

however there are inputs declared in the operation.

Shouldn't call arguments appear in the log also for Python actions (EVENT_ACTION_START events)?


Changed files:
cloudslang-runtime/src/main/java/io/cloudslang/lang/runtime/events/LanguageEventData.java
cloudslang-runtime/src/main/java/io/cloudslang/lang/runtime/steps/ActionSteps.java
cloudslang-runtime/src/test/java/io/cloudslang/lang/runtime/steps/ActionStepsTest.java

diff --git a/cloudslang-runtime/src/main/java/io/cloudslang/lang/runtime/events/LanguageEventData.java b/cloudslang-runtime/src/main/java/io/cloudslang/lang/runtime/events/LanguageEventData.java
index 41527a5ae..a35b5371a 100644
--- a/cloudslang-runtime/src/main/java/io/cloudslang/lang/runtime/events/LanguageEventData.java
+++ b/cloudslang-runtime/src/main/java/io/cloudslang/lang/runtime/events/LanguageEventData.java
@@ -152,4 +152,9 @@ public class LanguageEventData extends HashMap<String, Serializable> {
     public void setAsyncLoopBoundExpression(List<Serializable> asyncLoopBoundExpression) {
         put(BOUND_ASYNC_LOOP_EXPRESSION, (Serializable) asyncLoopBoundExpression);
     }
+
+    public Map<String, Serializable> getCallArguments() {return (Map<String, Serializable>) get(CALL_ARGUMENTS);}
+
+    public void setCallArguments(Map<String, Serializable> callArguments) {put(CALL_ARGUMENTS, (Serializable) callArguments);
+    }
 }
diff --git a/cloudslang-runtime/src/main/java/io/cloudslang/lang/runtime/steps/ActionSteps.java b/cloudslang-runtime/src/main/java/io/cloudslang/lang/runtime/steps/ActionSteps.java
index 26d305fa5..18b57da47 100644
--- a/cloudslang-runtime/src/main/java/io/cloudslang/lang/runtime/steps/ActionSteps.java
+++ b/cloudslang-runtime/src/main/java/io/cloudslang/lang/runtime/steps/ActionSteps.java
@@ -17,11 +17,12 @@ import io.cloudslang.lang.entities.ScoreLangConstants;
 import io.cloudslang.lang.runtime.env.ReturnValues;
 import io.cloudslang.lang.runtime.env.RunEnvironment;
 import io.cloudslang.lang.runtime.events.LanguageEventData;
+import io.cloudslang.score.api.execution.ExecutionParametersConsts;
+import io.cloudslang.score.lang.ExecutionRuntimeServices;
 import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.SerializationUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.log4j.Logger;
-import io.cloudslang.score.api.execution.ExecutionParametersConsts;
-import io.cloudslang.score.lang.ExecutionRuntimeServices;
 import org.python.core.*;
 import org.python.util.PythonInterpreter;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -30,11 +31,7 @@ import org.springframework.stereotype.Component;
 import java.io.Serializable;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 import static io.cloudslang.score.api.execution.ExecutionParametersConsts.EXECUTION_RUNTIME_SERVICES;
 
@@ -63,10 +60,16 @@ public class ActionSteps extends AbstractSteps {
 
         Map<String, Serializable> returnValue = new HashMap<>();
         Map<String, Serializable> callArguments = runEnv.removeCallArguments();
+        Map<String, Serializable> callArgumentsDeepCopy = new HashMap<>();
+
+        for (Map.Entry<String, Serializable> entry : callArguments.entrySet()) {
+            callArgumentsDeepCopy.put(entry.getKey(),  SerializationUtils.clone(entry.getValue()));
+        }
+
         Map<String, SerializableSessionObject> serializableSessionData = runEnv.getSerializableDataMap();
         fireEvent(executionRuntimeServices, ScoreLangConstants.EVENT_ACTION_START, "Preparing to run action " + actionType,
                 runEnv.getExecutionPath().getParentPath(), LanguageEventData.StepType.ACTION, null,
-                Pair.of(LanguageEventData.CALL_ARGUMENTS, (Serializable) callArguments));
+                Pair.of(LanguageEventData.CALL_ARGUMENTS, (Serializable) callArgumentsDeepCopy));
         try {
             switch (actionType) {
                 case JAVA:
diff --git a/cloudslang-runtime/src/test/java/io/cloudslang/lang/runtime/steps/ActionStepsTest.java b/cloudslang-runtime/src/test/java/io/cloudslang/lang/runtime/steps/ActionStepsTest.java
index 721c7fa60..ec74928df 100644
--- a/cloudslang-runtime/src/test/java/io/cloudslang/lang/runtime/steps/ActionStepsTest.java
+++ b/cloudslang-runtime/src/test/java/io/cloudslang/lang/runtime/steps/ActionStepsTest.java
@@ -15,6 +15,7 @@ import com.hp.oo.sdk.content.plugin.SerializableSessionObject;
 import io.cloudslang.lang.entities.ScoreLangConstants;
 import io.cloudslang.lang.runtime.env.RunEnvironment;
 import io.cloudslang.lang.runtime.env.ReturnValues;
+import io.cloudslang.lang.runtime.events.LanguageEventData;
 import org.junit.Assert;
 import io.cloudslang.score.api.execution.ExecutionParametersConsts;
 import io.cloudslang.score.events.ScoreEvent;
@@ -135,6 +136,68 @@ public class ActionStepsTest {
         Assert.assertNotNull(actionEndEvent);
     }
 
+    @Test
+    public void doActionPythonActionCheckCallArgumentsOnEvent() {
+        //prepare doAction arguments
+        RunEnvironment runEnv = new RunEnvironment();
+        ExecutionRuntimeServices runtimeServices = new ExecutionRuntimeServices();
+
+        Map<String, Serializable> callArguments = new HashMap<>();
+        callArguments.put("index", 1);
+        runEnv.putCallArguments(callArguments);
+
+        String userPythonScript = "var= \\"hello\\"";
+
+        //invoke doAction
+        actionSteps.doAction(runEnv,new HashMap<String, Object>(), PYTHON, "", "", runtimeServices, userPythonScript, 2L, "operationName");
+
+        Collection<ScoreEvent> events = runtimeServices.getEvents();
+
+        Assert.assertFalse(events.isEmpty());
+        ScoreEvent eventActionStart = null;
+        for(ScoreEvent event:events){
+            if(event.getEventType().equals(ScoreLangConstants.EVENT_ACTION_START)){
+                eventActionStart = event;
+                break;
+            }
+        }
+
+        Assert.assertNotNull(eventActionStart);
+        LanguageEventData data = (LanguageEventData)eventActionStart.getData();
+        Map<String, Serializable> actualCallArguments = data.getCallArguments();
+        Assert.assertEquals("Python action call arguments are not as expected", callArguments, actualCallArguments);
+    }
+
+    @Test
+    public void doActionJavaActionCheckCallArgumentsOnEvent() {
+        //prepare doAction arguments
+        RunEnvironment runEnv = new RunEnvironment();
+        ExecutionRuntimeServices runtimeServices = new ExecutionRuntimeServices();
+
+        Map<String, Serializable> callArguments = new HashMap<>();
+        callArguments.put("index", 1);
+        runEnv.putCallArguments(callArguments);
+
+        //invoke doAction
+        actionSteps.doAction(runEnv, new HashMap<String, Object>(), JAVA, ContentTestActions.class.getName(), "doJavaSampleAction", runtimeServices, null, 2L, "operationName");
+
+        Collection<ScoreEvent> events = runtimeServices.getEvents();
+
+        Assert.assertFalse(events.isEmpty());
+        ScoreEvent eventActionStart = null;
+        for(ScoreEvent event:events){
+            if(event.getEventType().equals(ScoreLangConstants.EVENT_ACTION_START)){
+                eventActionStart = event;
+                break;
+            }
+        }
+
+        Assert.assertNotNull(eventActionStart);
+        LanguageEventData data = (LanguageEventData)eventActionStart.getData();
+        Map<String, Serializable> actualCallArguments = data.getCallArguments();
+        Assert.assertEquals("Java action call arguments are not as expected", callArguments, actualCallArguments);
+    }
+
     @Test(expected = RuntimeException.class, timeout = DEFAULT_TIMEOUT)
     public void doJavaActionWrongMethodTest() {
         //prepare doAction arguments