App Builder

Embed
Preview of the card
<a href="https://watchtowerops.net/docs/app-builder"><img src="https://watchtowerops.net/docs/_api/public/orgs/aa5959cb-1a0b-4a9a-bc9a-913e7ac31cd9/spaces/b7aac181-9916-4805-b253-becc5fd2ca62/pages/120f4d17-75de-4fae-bb2f-5430fac0b4df/social-card.svg?v=3e7ae33eec8b" alt="App Builder" width="600" height="315"></a>

An app is a small console you define as JSON: inputs and selects, tables, charts and stats bound to queries, and buttons that start a workflow. Open App builder (/apps) in the console to edit an app next to a live preview. Apps can also be embedded in a dashboard as an app panel; the panel's options carry the app id.

Components

An app holds 1 to 50 components, and a project holds up to 200 apps. Each component has an id ([a-z][a-z0-9_]{0,31}, unique in the app) and a type:

Type

Needs

Shows

select

options_query

A drop-down; the first column of each row is an option. Its choice is {{id.value}}.

input

none (required, placeholder, multiline optional)

A text box; its text is {{id.value}}.

text

text

Static text, which may read other values.

stat

query

The first cell of the first row, as a large value.

table

query

The rows of the query.

chart

query

A line chart of the query's rows.

button

on_click

A button that starts a workflow run.

Queries

A query or options_query is {"kind": "sql" | "promql", "text": "..."}, at most 8 KiB.

  • sql runs through the project SQL API over the dashboard time range (at most 1,000 rows).

  • promql runs as a range query over the same range; the latest value of each series becomes a series, value row.

A query, a text or a button's inputs and confirm may contain {{id.value}} placeholders. They are replaced with the current value of that select or input before the query runs, so choosing a value refreshes everything that reads it. Saving fails with 422 if a placeholder names a component that does not hold a value.

Buttons

on_click names a workflow (wfl_...) and the inputs to start it with, plus an optional confirm prompt shown first. The button calls POST /v1/projects/{prj}/workflows/{id}/runs with a fresh idempotency key and the resolved inputs, so the workflow needs a manual trigger that declares those inputs. A button stays disabled while a required input it reads is empty, and shows whether the run started.

[
  {"id": "ns", "type": "select", "label": "Namespace",
   "options_query": {"kind": "sql", "text": "SELECT DISTINCT namespace FROM kube_pods ORDER BY namespace"}},
  {"id": "pods", "type": "table", "label": "Pods",
   "query": {"kind": "sql", "text": "SELECT pod, status FROM kube_pods WHERE namespace = '{{ns.value}}'"}},
  {"id": "restart", "type": "button", "label": "Restart namespace",
   "on_click": {"workflow": "wfl_...", "inputs": {"namespace": "{{ns.value}}"}, "confirm": "Restart everything in {{ns.value}}?"}}
]

API

All under /v1/projects/{prj}/apps:

  • POST creates an app ({name, components}), GET lists them with cursor pagination, GET /{id} returns one, PUT /{id} replaces it and DELETE /{id} removes it.

  • GET /{id}/render returns the payload a dashboard panel or embed draws.

Creating, replacing and deleting need the dashboard edit permission; reading and rendering need telemetry read access.

Updated