Functions:Action Endpoint - bettyblocks/cli GitHub Wiki

Action Endpoint

This makes it possible to call an action using normal HTTP methods (GET/POST/PUT/OPTION/ect).

Introduction

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.

Restrictions

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.

Prerequisite

Create an action

Create an action with an input variable request which is a text kind (maybe other kinds are allowed as well)

Create function

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 }

Action step

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>

Example functions

Return the request object

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;

Simple template renderer

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;

Redirect

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;
⚠️ **GitHub.com Fallback** ⚠️