Hi,
I have created the following One Data function that sets the input arguments of a Production line to be executed:
import requests
import json
def handle(req):
"""
triggers the PL that is defined in the
environments and endpoints input table
:input:
- body, JSON-encoded parameters for the production line
- pl_id (str), ID of the production line
"""
api_endpoint = req["config"]["onedataBaseurl"]+ "/api/v1"
x_auth_token = req["config"]["authorization"]
body = req["args"]["body"]
pl_id = req["args"]["pl_id"]
#Set PL variables
url = api_endpoint + '/production-lines/' + pl_id
header = {'X-Auth-Token' : x_auth_token, 'Content-Type' : 'application/json'}
try:
put_response = requests.put(url=url, data=json.dumps(body), headers=header, verify=False)
except requests.exceptions.RequestException as e:
print("Login unsuccessful: ", e)
#raise SystemExit(e)
print(put_response.json())
When passing the following JSON as input to the function in the debugger mode of the function, I get the following error though.
{
"body" : "{\"stdout\":\"\",\"stderr\":\"\",\"response\":{\"owner\":\"a3fab4df-540c-4b26-b66e-e942006331e7\",\"id\":\"e91eac8f-35e2-403c-aa06-af9fdce26bc8\",\"name\":\"Trigger A03\.....",
"pl_id" : "e91eac8f-35e2-403c-aa06-af9fdce26bc8"
}
Please note that the JSON is very length, yet it is formatted correctly, as the execution of this very same function in a plain Python processor of One Data runs just fine with the exact same input. Upon request, I can provide a link to the function on One Data internal.
{
“stdout”: errors’: [‘JSON parse error: Cannot construct instance of de.onelogic.forecasting.exchange.productionline.ProductionLineDTO
(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value (\’{“stdout”:"",“stderr”:"",“response”
…
nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of de.onelogic.forecasting.exchange.productionline.ProductionLineDTO
(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value
…
at [Source: (PushbackInputStream)
Looks like the payload to the production line endpoint is malformed.
Have a look at stdout
, stderr
etc, which should not be part of the payload to the production line endpoint.
{
“body” : “{\“stdout\”:\”\",\“stderr\”:\"\",\“response\”:{\“owner\”:\“a3fab4df-540c-4b26-b66e-e942006331e7\”,\“id\”:\“e91eac8f-35e2-403c-aa06-af9fdce26bc8\”,\“name\”:\“Trigger A03\…”,
“pl_id” : “e91eac8f-35e2-403c-aa06-af9fdce26bc8”
}
I think you want something like:
{ "body": { "owner": "a3fab4df-540c-4b26-b66e-e942006331e7", "id": "e91eac8f-35e2-403c-aa06-af9fdce26bc8", [all other properties of the PL here] }, "pl_id": "e91eac8f-35e2-403c-aa06-af9fdce26bc8" }
I also think the escaping of the payload is not necessary. body
can be a JSON object. 
Thank you for the reply Tristan. Actually, by removing the print statement at the bottom of the function, the debugging mode now works with this input 
Actually Tristan you were right, and I got misled to believe that my function was working properly while it was not, as I was getting a 200 response, despite the variables not being properly set in the PUT request to the production line.
→ I managed to solve the problem with two extra modifications:
- I removed the
std
and stderr
fields from the dictionary, considering just the response
fields to be passed within my body
- By returning the
put_response
formatted as JSON (instead of printing it), I was able to visualize what was going wrong previously, and obtain a clean output as a response.
For future reference when wanting to pass variables to a workflow being triggered, this is the code I used as One Data function:
The input accepted is the output of a GET request to the corresponding production line, filtered on the response
field:
import requests
import json
def handle(req):
"""
triggers the PL that is defined in the
environments and endpoints input table
:input:
- body, JSON-encoded parameters for the production line
- pl_id (str), ID of the production line
"""
api_endpoint = req["config"]["onedataBaseurl"]+ "/api/v1"
x_auth_token = req["config"]["authorization"]
body = req["args"]["body"]
pl_id = req["args"]["pl_id"]
#Set PL variables
url = api_endpoint + '/production-lines/' + pl_id
header = {'X-Auth-Token' : x_auth_token, 'Content-Type' : 'application/json'}
try:
put_response = requests.put(url=url, data=body, headers=header, verify=False)
except requests.exceptions.RequestException as e:
return("Login unsuccessful: ", e)
#raise SystemExit(e)
return(put_response.json())
TLDR: DO NOT use print statements as output of One Data functions, always double/triple check their input, and make sure to get a meaningful response to ensure the output of a function is indeed correct.
1 Like
So using the PUT function above, the code was indeed working to set production line variables, however that caused a lot of overhead with my workflow, which caused the workflow to takte about ~2x as much time as the version with no REST API calls and functions.
After discussion with @laura.luckert and following the suggestions received by @Flogge , I hence implemented all my REST API calls in a “single-shot” call to a function that does exactly what I need, namely: triggers a production line, allowing to pass variables to the workflow in the production line in a simple manner . The documentation for this API call is the following, and this function has allowed me to reduce my workflow’s run time from 16 seconds (because of 3 REST API calls, and parsing of their output) to about 9 seconds.
http://od-api.pages.intranet.onelogic.de/#panel_production_lines__id__run
The code is the following:
import requests
import json
def handle(req):
"""
triggers the PL passed as input
:input:
- pl_id (str): the ID of the production line to be executed
"""
api_endpoint = req["config"]["onedataBaseurl"]+ "/api/v1"
x_auth_token = req["config"]["authorization"]
pl_id = req["args"]["pl_id"]
body = req["args"]["body"]
## trigger workflow via API
url_run = api_endpoint + '/production-lines/' + pl_id + '/run'
header = {'X-Auth-Token' : x_auth_token, 'Content-Type' : 'application/json'}
try:
post_response = requests.post(url=url_run, data=body, headers=header, verify=False)
#message = "PL was triggered successfully"
#od_output.add_data("output", df)
except requests.exceptions.RequestException as e:
print("Trigger unsuccessful. Exit with error code: ", post_response.status_code)
return(post_response.json())