Making a POST request

Hi guys,

I am doing my first steps with RPA Express and maybe someone of you can help me get going.

I need my robot to fetch work from an external API, so I basically need to make a POST request to fetch a work item with some data that can be used by the robot to feed some UI.

How do I make the request? I assume a custom script is the way to go. I tried this, but it seems hard to do without being able to use any external library. Is there a way to use imports or Grab or can I use something that may be even provided by the tool already?

Being able to make simple REST calls is quite a fundamental feature, so I am wondering if I missed some easier way of implementing this. Other RPA tools provide this out of the box.

Thanks,
Ben

Hi @ben, you can make a POST request in the custom action.
Perhaps, this example of GET request by @stuti.verma can be useful

You can also export your script to a bot task and use http-extended plugin. You can find documentation about it in WorkFusion Studio: Help - Help Contents - Bot task plugins.

1 Like

Hi @ashapkina,

first of all, thanks for your help! I got my Custom Action working now, although debugging was a bit painful.

Still, I think I have some problems understanding the concepts here. From what I understood, I can publish a recorder script to Control Tower and that becomes available as a workflow and as a bot task at the same time.

If I export the recorder script to a bot task (e.g. to use one of the plugins you mentioned), will I ever be able to re-import it as a recorder script or will I be stuck with the pure xml view forever? Also, if export my working recorder script to a bot task, that bot task will show errors in Studio and will not run nor be available for debugging.

Maybe you can help me with some background information here. I tried to find something in the KB or in the Academy videos, but without success.

Thanks,
Ben

Hi

Could you share your custom script here? I have a similar issue and having a working start point would be extremely useful.

Thank you!

Hi @Bonnero,

sure, I hope that saves you the pain I had while debugging this :slight_smile:

The JsonSlurper returns and Object and I confess I did some dirty hacking here to get the information I needed. There is a cleaner solution for this for sure.

Also, for some reason I have to explicitly call the method at the end of the script, otherwise it would not execute. The Knowlege Base states something different.

I used a TCPMON for debugging, that allowed me to actually see what was sent back and forth.

@CustomScriptAction(
        output = 'script_result'
)

import groovy.json.JsonSlurper


def customScript() {

    def url = new URL('http://localhost:8080/rest/external-task/fetchAndLock')

    def body = '{ \
  "workerId":"workfusion-robot", \
  "maxTasks":1,                  \
  "usePriority":true,            \
  "topics": \
      [{"topicName": "rpa-task", \
      "lockDuration": 20000, \
      "variables": ["cashIn"] \
      }] \
}'

    HttpURLConnection http = url.openConnection()

    http.setDoOutput(true)
    http.setRequestMethod('POST')
    http.setRequestProperty('Content-type', 'application/json')

    def out = new OutputStreamWriter(http.outputStream)
    out.write(body)
    out.close()

    def slurper = new JsonSlurper()

    def result = slurper.parse(http.getInputStream())
    
    
    if (((ArrayList) result).size() > 0) {

        def extTaskId = ((LinkedHashMap) result).get("id")
        def vars = (LinkedHashMap) ((LinkedHashMap) result).get("variables")

        def varName = (String) vars.keySet().toArray()[0]

        def varValue = ((LinkedHashMap) vars.get(varName)).get("value")

        script_result = RTable.builder()
                .row("task_id", extTaskId)
                .row(varName, varValue)
                .build()

    } else {
        script_result = RTable.builder().build()
           }
}
customScript()
3 Likes

Hi @ben

Once you export the recording to a bot task and modify the code, you cannot re-import it back to the recorder, but you can execute it from the code perspective. It should run exactly as from the Recorder. Can you share what errors it showed when you ran as a bot task?

To debug a script with a custom action, you need to add an additional function call just like you did, I think we have it mentioned in the KB. When debugging in the Studio, you can see the values stored in the variables, but in your case, when you need to see what is sent/received from the server, it will not work, so it is better to use some utility tool for it.

@ben thanks for sharing. Just came across this and I had ended up with very similar code as you, but with a few adjustments that I thought were cleaner in the end:

For anyone that comes across similar needs for Camunda usage.

There seems to be issues with placing the imports below the CustomScriptAction annotation. Have run into multiple scenarios where code was not executing as expected, but threw no errors. Seems to be partially from the way CustomScriptActions are pre-processed through AST transformations, Freemarker scripts, and likely other “magic” under the hood that RPA express is implementing.

Regardless, we end up with the following

Fetch and Lock:

import groovy.json.JsonBuilder
import groovy.json.JsonOutput
import groovy.json.JsonSlurper

@CustomScriptAction(
        output = 'task_query_result'
)

def customScript() {
    try{
        URL url = new URL('http://MyCamundaServer:8080/engine-rest/external-task/fetchAndLock')
        HttpURLConnection http = (HttpURLConnection) url.openConnection()
        http.setRequestMethod('POST')
        http.setConnectTimeout(5000)
        http.setReadTimeout(30000)
        http.setRequestProperty('Content-Type', 'application/json; charset=UTF-8')
        http.setRequestProperty("Accept", "application/json");
        http.setDoInput(true)
        http.setDoOutput(true)

        String body = '''
{
    "workerId":"workfusion-robot",
    "maxTasks": 1,
    "usePriority": true,
    "topics":[
        {
            "topicName": "some-topic",
            "lockDuration": 20000,
            "variables": ["someVar1", "someVar2"]
        }
    ]
}
'''
        JsonSlurper slurper = new JsonSlurper()
        def json = new JsonBuilder(slurper.parseText(body)).toPrettyString()

        http.getOutputStream().write(json.getBytes('UTF-8'))

        def responseCode = http.getResponseCode()
        if (responseCode != 200){
            throw new Exception('Did not receive response code 200 from Camunda')
        }

        def response  = http.getInputStream()
        def tasks = slurper.parse(response)

        if (tasks.size() == 0) {
            task_query_result = RString.of('')
        } else {
            task_query_result = RString.of(JsonOutput.toJson(tasks[0]))
        }

    } catch(all){
        task_query_result = RString.of("Error: ${all.getMessage()}")
    }

}

Notice how I went with storing the responses as Strings rather than your model of a Table. I did this because I noticed that the “Expressions” in RPA Express are actually java/groovy evals, thus we can take advantage of that in our logic handling!!

So because the Custom Script can only have 1 returned variable, we will send the entire JSON body into the “task_query_result” variable, which is defined as a String in the Recorder Variables.

Then we use the Constant Variable action that points to the Recorder Variable task_id and a “set value” of: ${new groovy.json.JsonSlurper().parseText(task_query_result.toString()).id}. We use the toString() to just double ensure no weirdness from the RPA Express classes of the RString stuff…

So we can do this for variable needed that was contained within the Json response that was stored.

Then We do our work as per the image above, where you are clicking through a website or whatever.

We eval the response and if everything was good, we return our completion to camunda with:

import groovy.json.JsonBuilder
import groovy.json.JsonSlurper

@CustomScriptAction(
        input = ['task_id', 'some_recorder_var'],
        output = 'task_completion_result'
)

def customScript() {
    try{
        String taskId = task_id.toString()
        String someRecorderVar = some_recorder_var.toString()

        URL url = new URL("http://MyCamundaServer:8080/engine-rest/external-task/${taskId}/complete")
        HttpURLConnection http = (HttpURLConnection) url.openConnection()
        http.setRequestMethod('POST')
        http.setConnectTimeout(5000)
        http.setReadTimeout(30000)
        http.setRequestProperty('Content-Type', 'application/json; charset=UTF-8')
        http.setRequestProperty("Accept", "application/json")
        http.setDoInput(true)
        http.setDoOutput(true)

        String body = $/
{
    "workerId":"workfusion-robot",
    "variables":
    {
        "issueCreated": {"value": "${someRecorderVar}"}
    }
}
/$
        JsonSlurper slurper = new JsonSlurper()
        def json = new JsonBuilder(slurper.parseText(body)).toPrettyString()
        http.getOutputStream().write(json.getBytes('UTF-8'))

        def responseCode = http.getResponseCode()
        if (responseCode != 204){
            throw new Exception("Did not get 204 back from Camunda Task Completion: ${response}")
        }

    } catch(all){
        task_completion_result = RString.of('error ' + all.getMessage())
    }
}

Notice the use of $/ ... /$ which allows multi-line strings with groovy ${} expression variables. No need for the manual backslashes!

With this setup, you can process JSON within the custom action, as well as eval the json from the RPAExpress “expressions” such as doing a IF action that evaluated a value within the JSON, rather than having to pollute the Recorder Variables.

4 Likes