Functions:Action Endpoint - bettyblocks/cli GitHub Wiki
This makes it possible to call an action using normal HTTP methods (GET/POST/PUT/OPTION/ect).
Normally actions can be called using a GraphQL request, however sometimes it can be handier/necessary to call these using standard HTTP methods. For instance callbacks for authentication. This also makes it possible to change the behaviour of the request itself. For instance setting some headers / changing the http status code.
For 'simple' use-cases it is possible to not use custom functions. This block makes it possible to format the output of an action using the action builder.
Currently it is not possible to do a request containing non-text bodies.
Another thing is that this will forward url-encoded / multipart / json bodies, the parsing should be done by the custom function.
Create an action with an input variable request
which is a text kind (maybe other kinds are allowed as well)
Create a function, this can take an optional request
object which will contains additional request information that you can use in the action, such as headers, body, query parameters and HTTP method.
example request object of a GET request to api/runtime/<app-uuid>/action/<action-id>/sub/path?extra=query
{
"body": null,
"headers": [
["accept", "text/html,application/xhtml+xml,application/xml"],
["accept-encoding", "gzip, deflate, br"],
["accept-language", "en-US,en;q=0.9"],
["cache-control", "max-age=0"]
],
"method": "GET",
"path": ["sub", "path"],
"query": [["extra", "query"]]
}
example request object of a POST request to the same url:
curl -X POST --data "testing this is data" $URL
{
"body": "testing this is data",
"headers": [
["accept", "*/*"],
["content-type", "application/x-www-form-urlencoded"],
["user-agent", "curl/7.81.0"]
],
"method": "POST",
"path": ["sub", "path"],
"query": [["extra", "query"]]
}
This function should return an output object with result keys: body, statusCode and headers for example:
{
body: "data", // data as string
statusCode: 200, // integer
headers: [["content-type", "application/json"]], // list of lists
}
Note that in your code this object is set as an output variable so it look like:
let response = { body: "OK", statusCode: 200, headers: [] };
return { response: response }; // or just { response }
Attach the new function to the new action and forward the request variable from the action to the function/action-step.
set the output variable to the output of the action-step and your done.
Your action can now be called by doing a HTTP request to https://<app-identifier>.betty.app/api/runtime/<app-uuid>/action/<action-id>
Simple version of the http://httpbin.org/anything
endpoint
In the function.json
{
"description": "Description",
"label": "HttpBin",
"category": "Misc",
"icon": {
"name": "PlusIcon",
"color": "Green"
},
"options": [
{
"meta": {
"type": "Text"
},
"name": "request",
"label": "Request",
"info": "need",
"advanced": true,
"configuration": {
"placeholder": ""
}
},
{
"meta": {
"type": "Output",
"output": {
"type": "Text"
}
},
"name": "response",
"label": "Response",
"info": "Response"
}
],
"yields": "NONE"
}
in the index.js
const httpbin = async ({ request }) => {
return {
response: {
body: JSON.stringify(request),
statusCode: 200,
headers: [["content-type", "application/json"]],
},
};
};
export default httpbin;
output as html and do some simple routing
In the function.json
{
"description": "Render html template",
"label": "Templater",
"category": "Misc",
"icon": {
"name": "PlusIcon",
"color": "Green"
},
"options": [
{
"meta": {
"type": "Text"
},
"name": "request",
"label": "Request",
"info": "need",
"advanced": true,
"configuration": {
"placeholder": ""
}
},
{
"meta": {
"type": "Text"
},
"name": "text",
"label": "Text",
"info": "Text that is going to be returned",
"advanced": false,
"configuration": {
"placeholder": "Betty Blocks"
}
},
{
"meta": {
"type": "Output",
"output": {
"type": "Object"
}
},
"name": "out",
"label": "Out",
"info": "Nicely formated output."
}
],
"yields": "NONE"
}
in the index.js
const templater = async ({ request, text }) => {
if (request.method.toLowerCase() === "get") {
let html = `
<html>
<head>
<meta name="color-scheme" content="light dark">
</head>
<body>
<div>
<h2>A nice header</h2>
<p>${text}</p>
</div>
</body>
</html>
`;
return {
out: {
statusCode: 200,
body: html,
headers: [["content-type", "text/html"]],
},
};
} else {
return {
out: {
statusCode: 405,
body: "Method not allowed",
headers: [],
},
};
}
};
export default templater;
Redirects based on the request redirect
query parameter
in function.json
{
"description": "Description",
"label": "Redirecter",
"category": "Misc",
"icon": {
"name": "PlusIcon",
"color": "Green"
},
"options": [
{
"meta": {
"type": "Text"
},
"name": "request",
"label": "Request",
"info": "need",
"advanced": true,
"configuration": {
"placeholder": ""
}
},
{
"meta": {
"type": "Output",
"output": {
"type": "Object"
}
},
"name": "out",
"label": "Out",
"info": "Nicely formated output."
}
],
"yields": "NONE"
}
in index.js
const redirecter = async ({ request }) => {
let redirectPair = request.query.find((x) => x[0] == "redirect");
let redirectURL = "https://example.com";
if (!!redirectPair) {
redirectURL = redirectPair[1];
}
return {
out: {
body: "Redirect",
statusCode: 302,
headers: [["Location", redirectURL]],
},
};
};
export default redirecter;