New skills

This commit is contained in:
ivarsbariss
2026-02-27 13:20:38 +01:00
parent f207f55565
commit 68ad98ede4
4 changed files with 246 additions and 52 deletions

View File

@@ -13,22 +13,27 @@ Each query lives under the page in a folder named after the query:
- **Path**: `pages/<PageName>/queries/<query_name>/`
- **Files**:
- **`<query_name>.txt`** Raw SQL. This is the **source of truth** for the query body. Keep it readable (real newlines, no escaping).
- **`metadata.json`** Appsmith action config. The `body` inside `unpublishedAction.actionConfiguration` must match the SQL in the `.txt` file, stored as a single-line string with `\n` for newlines and `\"` for double quotes.
- **`<query_name>.txt`** -- Raw SQL. This is the **source of truth** for the query body. Keep it readable (real newlines, no escaping).
- **`metadata.json`** -- Appsmith action config. The `body` inside `unpublishedAction.actionConfiguration` must match the SQL in the `.txt` file, stored as a single-line string with `\n` for newlines and `\"` for double quotes.
**Naming**: Use lowercase with underscores (e.g. `units_ordered_by_series`, `units_shipped_by_power_input`). The query name is the identifier used in bindings (e.g. `{{query_name.data}}`).
**Naming**: Use lowercase with underscores (e.g. `units_ordered_by_series`, `pending_pos_slx_pending`). The query name is the identifier used in bindings (e.g. `{{query_name.data}}`).
## 2. Metadata.json required fields
- **`id`**: `"<PageName>_<query_name>"` (e.g. `"Sales - Units Ordered_units_ordered_by_series"`).
- **`gitSyncId`** (CRITICAL): Must be the **first field** in the root object. Format: `"<24-char-hex>_<uuid-v4>"`. Without this, Appsmith will auto-remove the query on the next pull/sync. Generate with:
```bash
python3 -c "import uuid; print(uuid.uuid4().hex[:24] + '_' + str(uuid.uuid4()))"
```
Use the same 24-char hex prefix as the page's `gitSyncId` but a unique UUID suffix.
- **`id`**: `"<PageName>_<query_name>"` (e.g. `"Pending POs - SLx Pending_pending_pos_slx_pending"`).
- **`name`**: `"<query_name>"` (must match the folder and the name used in bindings).
- **`unpublishedAction.actionConfiguration.body`**: The exact SQL from `<query_name>.txt`, escaped for JSON (newlines `\n`, `"``\"`).
- **`unpublishedAction.actionConfiguration.body`**: The exact SQL from `<query_name>.txt`, escaped for JSON (newlines -> `\n`, `"` -> `\"`).
- **`unpublishedAction.datasource`**: Use the same as other queries on the page (e.g. `xTuple_Sandbox`).
- **`unpublishedAction.pageId`**: `"<PageName>"`.
- **`unpublishedAction.dynamicBindingPathList`**: `[{"key":"body"}]` when the body contains `{{...}}` widget references.
- Keep **`pluginId`**: `"postgres-plugin"`, **`pluginType`**: `"DB"`, and existing flags like **`encodeParamsToggle`**, **`paginationType`**, **`pluginSpecifiedTemplates`**, **`timeoutInMillisecond`** consistent with other DB queries in the app.
When adding a new query, copy an existing querys `metadata.json` from the same page and change `id`, `name`, and `body` (and ensure `body` stays in sync with the new `.txt` file).
When adding a new query, copy an existing query's `metadata.json` from the same page and change `gitSyncId`, `id`, `name`, and `body` (and ensure `body` stays in sync with the new `.txt` file).
## 3. Wiring the query to a Table widget
@@ -36,7 +41,7 @@ When adding a new query, copy an existing querys `metadata.json` from the sam
- **`tableData`**: `"{{<query_name>.data}}"`
- Table columns read from the query result by **key**. The key is the **SELECT alias** from the query (e.g. `"Product"`, `"Qty Ordered"`). In `primaryColumns`, the column **id** can be a safe identifier (e.g. `Qty_Ordered`) while **originalId** / **alias** / **label** match the display name; **computedValue** must use the same key as in the query result, e.g. `currentRow["Qty Ordered"]`.
So: **query SELECT aliases = keys in the tables row object**. Keep column keys and any `currentRow["..."]` references in the table in sync with those aliases.
So: **query SELECT aliases = keys in the table's row object**. Keep column keys and any `currentRow["..."]` references in the table in sync with those aliases.
## 4. Parameter search syntax (widgets in SQL)
@@ -45,15 +50,15 @@ So: **query SELECT aliases = keys in the tables row object**. Keep column key
- **Syntax**: `{{WidgetName.property}}`
- **Examples**:
- DatePicker date: `{{SeriesDateFrom.selectedDate}}`, `{{PowerDateTo.selectedDate}}`
- Other widgets: use the widgets value property (e.g. `selectedOptionValue`, `text`) as per Appsmith docs.
- Other widgets: use the widget's value property (e.g. `selectedOptionValue`, `text`) as per Appsmith docs.
Values are injected as strings. For PostgreSQL dates you typically cast in SQL, e.g. `'{{DatePicker1.selectedDate}}'::date`.
### 4.2 Optional date range (no filter when either date is empty)
Use this pattern so that:
- If **either** date widget is empty the date condition is **not** applied (all dates allowed).
- If **both** are set filter by `date_column BETWEEN from ::date AND to ::date`.
- If **either** date widget is empty -> the date condition is **not** applied (all dates allowed).
- If **both** are set -> filter by `date_column BETWEEN from ::date AND to ::date`.
**SQL pattern** (replace widget names and column as needed):
@@ -72,30 +77,31 @@ AND (
### 4.3 Required parameters (always filter)
If the filter must always be applied (no show all when empty):
If the filter must always be applied (no "show all" when empty):
- Use the binding directly and ensure the widget always has a value, or use a default in the widget.
- Example: `AND cohead_orderdate BETWEEN '{{DateFrom.selectedDate}}'::date AND '{{DateTo.selectedDate}}'::date` then empty dates may produce invalid SQL or empty results, so prefer the optional pattern above unless the UI guarantees non-empty values.
- Example: `AND cohead_orderdate BETWEEN '{{DateFrom.selectedDate}}'::date AND '{{DateTo.selectedDate}}'::date` -- then empty dates may produce invalid SQL or empty results, so prefer the optional pattern above unless the UI guarantees non-empty values.
## 5. Checklist when adding a new query
1. Create `pages/<PageName>/queries/<query_name>/`.
2. Add `<query_name>.txt` with the full SQL (source of truth).
3. Add `metadata.json` with correct `id`, `name`, `body` (body = SQL from .txt, JSON-escaped), and same datasource/pageId as other page queries.
3. Add `metadata.json` with correct `gitSyncId`, `id`, `name`, `body` (body = SQL from .txt, JSON-escaped), and same datasource/pageId as other page queries.
4. If the SQL uses `{{...}}`, set `dynamicBindingPathList` to `[{"key":"body"}]`.
5. In the Table widget that should show the data, set `tableData` to `{{<query_name>.data}}`.
6. Ensure table column keys / `currentRow["..."]` match the querys SELECT aliases.
6. Ensure table column keys / `currentRow["..."]` match the query's SELECT aliases.
## 6. Renaming or replacing a query
- To **rename** (e.g. `units_shipped_by_series``units_ordered_by_series`):
- Create the new folder and files under the new name.
- Update every reference to the old query (e.g. `tableData`: `{{old_name.data}}``{{new_name.data}}`).
- To **rename** (e.g. `units_shipped_by_series` -> `units_ordered_by_series`):
- Create the new folder and files under the new name (with a new `gitSyncId`).
- Update every reference to the old query (e.g. `tableData`: `{{old_name.data}}` -> `{{new_name.data}}`).
- Remove the old query folder (both `.txt` and `metadata.json`).
- When **replacing** the SQL but keeping the same name, update the `.txt` first, then update the `body` in `metadata.json` to match (same content, JSON-escaped).
## Reference
- **gitSyncId**: `<24-char-hex>_<uuid-v4>`. Without this, Appsmith auto-removes the query on sync.
- **Optional date range**: NULLIF + IS NULL + OR + BETWEEN as above.
- **Table data binding**: `{{<query_name>.data}}`.
- **Column keys**: Must match query SELECT aliases (e.g. `"Product"`, `"Qty Ordered"`).