Action Apps

Action Apps

Actions allow apps to expose an autogenerated UI for simple backend actions. For use cases where an existing CLI application or API needs to be exposed as a web app, actions provide an easy solution. An app can have one or more actions defined. Each action has to be given a unique path, which does not conflict with any other route defined for the app.

Sample Action

First, define the parameters to be exposed in the form UI. Create a params.star file with the params. For example,

params.star
param("dir", description="The directory to list files from", default="/tmp")

param("detail", type=BOOLEAN, description="Whether to show file details", default=True)

This app defines a run handler which runs ls on the specified directory. The output text is returned.

app.star
load ("exec.in", "exec")

def run(dry_run, args):
   if args.dir == "." or args.dir.startswith("./") or args.dir == ".." or args.dir.startswith("../"):
       return ace.result("Validation failed", param_errors={"dir": "relative paths not supported"})

   cmd_args = ["-Lla" if args.detail else "-La", args.dir]
   out = exec.run("ls", cmd_args)
   if out.error:
       return ace.result(out.error)
   return ace.result("File listing for " + args.dir, out.value)

app = ace.app("List Files",
   actions=[ace.action("List Files", "/", run, description="Show the ls -a output for specified directory")],
   permissions=[
     ace.permission("exec.in", "run", ["ls"]),
   ],
)

The app, when accessed will look as shown below, with the ls command output displayed:

List files app

Action Definition

An action is defined using the ace.action struct. The fields in this structure are:

PropertyOptionalTypeDefaultNotes
namefalsestringThe action name
pathfalsestringThe path to use within app path
runfalsefunctionThe function to run on execution
suggesttruefunctionnoneThe function to run on suggest, currently unused
descriptiontruestringnoneThe description for the action

The name and description are shown in the app UI. The app params are displayed in a form. BOOLEAN types are checkboxes, others are text boxes.

When the form is submitted, the run function is called. The params are passed as an args argument. The response as returned by the handler is shown on the UI.

⚠️
In the action handler function, use args argument to get the values from the form. Referencing params will give the default values for the parameters, not the actual values passed in.

Action Result

The handler returns an ace.result struct. The fields in this structure are:

PropertyOptionalTypeDefaultNotes
statustruestringThe action status message
valuestruelist[]The actions output, list of strings or list of dicts
reporttruestringace.AUTOThe type of report to generate. Default is ace.AUTO, where it is selected based on response type. Other options are ace.JSON, ace.TEXT, ace.TABLE. Any other value is a custom template name.
param_errorstruedict{}The validation errors to report for each param. The key is the param name, the value is the error message

Validating Params

The run handler can validate the parameters. If there are errors, it can return a validation error like

app.star
   if args.dir == "." or args.dir.startswith("./") or args.dir == ".." or args.dir.startswith("../"):
       return ace.result("Validation failed", param_errors={"dir": "relative paths not supported"})

Errors can be reported for multiple params.

Report Types

The response values can be a list of string or a list of dicts. The report is generated automatically by default. For list of strings, the report is a TEXT report. For list of dicts, the report can be either

  • TABLE - selected if all dict values for the first row are simple types
  • JSON - selected if any of the values for the first row is a complex type (like dict or list)

For TABLE report, the fields from the first row are used as columns. Extra fields in subsequent rows are ignored. For JSON report, a JSON tree representation of each row is shown. The report type can be set to specific type instead of using AUTO.

Custom Templates

If the report type is set to any value other than ace.AUTO/TEXT/JSON/TABLE, that is treated as a custom template to use. The template should be defined in a *.go.html file. Either the file name can be used or a template/block name can be used. See template for details.

For styling, Clace uses DaisyUI by default, so default styles are reset. The custom template can use inline styles or it can use TailwindCSS/DaisyUI. For DaisyUI, the app has to be run in dev mode first for the style.css to be generated. See styling for details.

See dictionary code:demo for an actions example app which shows different type of reports.

Param Value Selector

For some params, it is useful to be able to provide a list of values from which the user can choose. The way this is supported is by using an options param. If param1 is a param which should show up as a selector, then define another param with the name options-param1, of type LIST. Set a default value for options-param1 with the values to show in the selector dropdown. For example

params.star
param("param1", description="The param1 description", default="option1")

param("options-param1", type=LIST, description="Options for param1", default=["option1", "option2"])

In the UI, options-param1 is not displayed. param1 is show with a selector, having option1 and option2 as options. See dictionary for an app which uses this.

This approach is used for flexibility, instead of directly allowing the options to be configured for the param. The options param approach has the flexibility that when an app is installed, the options can be configured for the installation. This avoids having to maintain different copies of the app code. For example:

clace app create --approve --param options-param1='["option1", "option2", "options3"]' /mycode /myapp

adds a new options3 option.