Calling an external API
Call an instrument or service over HTTP from a skill, and keep the response with your project.
Some workflows need to talk to something outside the robot: trigger a plate reader, poll an instrument for a measurement, tell a service that a step finished. api_request is the supported way to make an HTTP call from inside a skill. Import it with from execution.execution_functions import *.
api_request(
url,
method="GET",
*,
json_body=None,
params=None,
headers=None,
save_name=None,
save_to_project=False,
export=False,
timeout=30,
)It calls url with the given method and returns a result dict. Like the other soft-failing runtime calls, it never raises into your skill: a timeout, a connection error, or a non-2xx status comes back as success: False instead of ending the run.
Parameters
url(str): the full request URL. There is no base-URL setting. The workflow supplies the address, usually as a workflow input, so the same skill can point at different instruments.method(str, default"GET"): the HTTP method, for example"GET","POST", or"PUT".json_body(defaultNone): a value sent as the JSON request body, forPOST,PUT, orPATCH.params(dict, defaultNone): query-string parameters.headers(dict, defaultNone): request headers.save_name(str, defaultNone): the basename, with no extension, for the saved response. When omitted, nothing is written even ifsave_to_projectorexportisTrue.save_to_project(bool, defaultFalse): setTrue, together withsave_name, to write the response todata/api/<run>/<save_name>.jsonin your project, keyed by run, so it travels with the project and syncs to the cloud. See the project data folder.export(bool, defaultFalse): setTrue, together withsave_name, to upload the response to your lab's data export bucket under<prefix>/<project>/<run>/api/<save_name>.json. With no bucket configured, or outside a run, it does nothing and logs one warning.timeout(float, default30): the per-request timeout, in seconds.
The two saving flags are independent destinations, and you can set both. See Two places run output can go.
Returns
A dict. On success:
{
"success": True,
"status": 200, # HTTP status code
"data": {...}, # parsed JSON, or the raw text if the body is not JSON
"saved_path": "...", # present only when the response was saved
"t": 1690000000.0, # request start time, epoch seconds
}On failure:
{"success": False, "status": None, "data": None, "t": ..., "error": "<message>"}Branch on result["success"] before you read result["data"].
What gets saved
When save_name is set together with save_to_project or export, the saved file holds the request url and method, the response status, the timestamp, and the parsed data. Request headers are not written, so an auth token you pass in headers does not end up in the file.
The saved file leaves this machine, either with your project or into your export bucket. Two things follow. Do not put secrets in the
url, because it is saved exactly as you passed it. And the responsedatais written as-is, so if an endpoint returns anything sensitive that goes with it.paramsandheadersare not written to the file, so pass credentials in one of those.
Example
The entry function's name matches the skill id, and its parameters are the skill's inputs. Take the instrument's address as an input so the workflow can set it:
def read_instrument(base_url: str):
# Trigger a reading, then fetch the result.
api_request(f"{base_url}/measure", method="POST", save_name="trigger", save_to_project=True)
res = api_request(f"{base_url}/latest", save_name="reading", save_to_project=True)
if not res["success"]:
print_log(f"instrument fetch failed: {res['error']}", runlog=True)
return {"success": False}
return {"success": True, "reading": res["data"]}Both saved responses land under data/api/<run>/ and travel with the project. On a rig whose lab has data export set up, swap save_to_project=True for export=True to put them in your own bucket instead, or pass both to do each.
Related
Updated about 2 months ago