Package su.onno.ui
Class WidgetFilter
java.lang.Object
su.onno.ui.WidgetFilter
Parses an authored count/metric-card
filter predicate into a safe SQL
fragment with bound parameters. The grammar is deliberately small — a chain of
field op value comparisons joined by AND — so a dashboard author
can write config("filter", "status != cancelled") without the framework
ever interpolating user text into SQL.
Injection safety rests on two rules: the left-hand side must be a known column (validated against the entity's columns plus a small system allowlist) matching a strict identifier pattern, and the right-hand value is always a bound parameter. An unrecognised column is skipped with a warning rather than failing the whole card, so a typo degrades to "no filter" instead of an error surface.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordA compiled predicate: aWHERE-ready fragment (no leading AND) and its bindings. -
Method Summary
Modifier and TypeMethodDescriptionstatic WidgetFilter.Resultstatic WidgetFilter.ResultLikeparse(String, Set), but values compared against a column inuuidColumns(ref / enum columns, whose SQL type isUUID) are bound as typedjava.util.UUIDs.
-
Method Details
-
parse
- Parameters:
filter- the authored predicate, e.g."status != cancelled AND _posted = true"allowedColumns- the entity's column names; the system allowlist is added automatically
-
parse
public static WidgetFilter.Result parse(String filter, Set<String> allowedColumns, Set<String> uuidColumns) Likeparse(String, Set), but values compared against a column inuuidColumns(ref / enum columns, whose SQL type isUUID) are bound as typedjava.util.UUIDs. PostgreSQL rejects a varchar parameter against a uuid column (H2 coerces silently, same class of problem as #163) — the ref picker's cascadingrefFiltercompares record ids, so it passes the ref/enum column set here. A malformed uuid value degrades to a varchar bind (the clause then matches nothing on H2 and errors on PG only for a hand-crafted request).
-