Shared plumbing for the admin HTTP layer.
Leaf namespace requiring no handler or route namespaces. Provides the
error mappings, query/form parsing, and handler helpers used by the handler
namespaces and the route definitions in wagoe.admin.shell.http.
Shared plumbing for the admin HTTP layer. Leaf namespace requiring no handler or route namespaces. Provides the error mappings, query/form parsing, and handler helpers used by the handler namespaces and the route definitions in `wagoe.admin.shell.http`.
(build-entity-detail-opts admin-service
schema-provider
config
entity-name
entity-config
record
request)Builds opts for entity-detail-page and the surrounding admin-layout. Shared between entity-detail-handler and update-entity-handler.
Returns a map with: :entities - all available entity names :entity-configs - map of entity-name -> entity-config :page-opts - opts map for entity-detail-page (related-records, return-to, parent-context, sibling-nav, display, field-options, workflow)
Builds opts for entity-detail-page and the surrounding admin-layout.
Shared between entity-detail-handler and update-entity-handler.
Returns a map with:
:entities - all available entity names
:entity-configs - map of entity-name -> entity-config
:page-opts - opts map for entity-detail-page
(related-records, return-to, parent-context, sibling-nav,
display, field-options, workflow)The :type -> status table admin errors are answered with. Admin throws the platform's own types, so the platform's status and this one agree (BOU-586).
The :type -> status table admin errors are answered with. Admin throws the platform's own types, so the platform's status and this one agree (BOU-586).
(create-config-error-response request
config
schema-provider
user
entity-name
entity-config)The admin page saying why entity-name cannot be created, or nil when it
can. A create form that could never be saved is refused up front (BOU-494).
A 500: the config is at fault, not the request.
The admin page saying why `entity-name` cannot be created, or nil when it can. A create form that could never be saved is refused up front (BOU-494). A 500: the config is at fault, not the request.
(create-disabled-response request user entity-name entity-config)The admin 403 page for an entity with :permissions {:create false}, or
nil when create is allowed. Names :create-hint when the config has one
(BOU-534).
The admin 403 page for an entity with `:permissions {:create false}`, or
nil when create is allowed. Names `:create-hint` when the config has one
(BOU-534).The zone timestamps are shown and entered in when neither the browser nor
:time-zone in :wagoe/settings names one. A presentation default only:
storage is zone-aware and the JVM runs in UTC (BOU-431), so this changes
what people see, never what is stored.
The zone timestamps are shown and entered in when neither the browser nor `:time-zone` in :wagoe/settings names one. A presentation default only: storage is zone-aware and the JVM runs in UTC (BOU-431), so this changes what people see, never what is stored.
(display-options config request)Zones, locale and date patterns for rendering stored timestamps in the admin UI.
Two zones (BOU-523). :server-zone-id is the JVM zone — the one the
database reads a zone-less timestamp in, so the one such a value is read
back in. :zone-id is the zone timestamps are shown and entered in: the
browser's, from the wagoe_tz cookie; else the application's configured
:time-zone; else default-time-zone, Europe/Amsterdam. Not the server's:
that is UTC by design (BOU-431) and says nothing about the people using it.
Read here because wagoe.admin.core.ui.base may not: check:fcis bans
ZoneId/systemDefault in a core namespace. The patterns come from the
application's :wagoe/settings (BOU-382).
Zones, locale and date patterns for rendering stored timestamps in the admin UI. Two zones (BOU-523). `:server-zone-id` is the JVM zone — the one the database reads a zone-less timestamp in, so the one such a value is read back in. `:zone-id` is the zone timestamps are shown and entered in: the browser's, from the `wagoe_tz` cookie; else the application's configured `:time-zone`; else `default-time-zone`, Europe/Amsterdam. Not the server's: that is UTC by design (BOU-431) and says nothing about the people using it. Read here because `wagoe.admin.core.ui.base` may not: check:fcis bans `ZoneId/systemDefault` in a core namespace. The patterns come from the application's `:wagoe/settings` (BOU-382).
(foreign-key-options admin-service config entity-configs entity-name values)The choices for each editable foreign key of entity-name: the parent's
rows as [id label], where the label is the parent's first search or list
field. Only when every parent fits in the maximum page size: a select
capped there would silently offer a subset, so past it the key stays a
text input. An optional key can be left empty.
The choices for each editable foreign key of `entity-name`: the parent's rows as [id label], where the label is the parent's first search or list field. Only when every parent fits in the maximum page size: a select capped there would silently offer a subset, so past it the key stays a text input. An optional key can be left empty.
(foreign-key-prefill entity-name entity-configs query-params)Starting values for a create form of entity-name, from the query string
a parent's "New" link builds. Only a field some entity names as the
:foreign-key of a has-many on entity-name is taken, and only when its
value is a UUID.
Starting values for a create form of `entity-name`, from the query string a parent's "New" link builds. Only a field some entity names as the `:foreign-key` of a has-many on `entity-name` is taken, and only when its value is a UUID.
(form-zone-options config request params)The zones to parse a submitted form's :instant fields in, and params
without the __zone field that carried them.
The form says which zone it was rendered in, and parsing uses exactly that:
deriving it from the cookie again would shift every value on the first
visit, when the page was rendered before the cookie existed. A missing or
unknown __zone falls back to the same resolution the page used.
The zones to parse a submitted form's :instant fields in, and `params` without the `__zone` field that carried them. The form says which zone it was rendered in, and parsing uses exactly that: deriving it from the cookie again would shift every value on the first visit, when the page was rendered before the cookie existed. A missing or unknown `__zone` falls back to the same resolution the page used.
(get-current-user request)Extract authenticated user from request.
The authentication middleware sets [:user {...}] with the full user entity (including :id, :role, :email, :name, etc.).
Args: request: Ring request map
Returns: Full user entity map, or nil if not authenticated
Extract authenticated user from request.
The authentication middleware sets [:user {...}] with the full user entity
(including :id, :role, :email, :name, etc.).
Args:
request: Ring request map
Returns:
Full user entity map, or nil if not authenticated(get-entity-id request)Extract entity ID from path parameters.
Args: request: Ring request map
Returns: Entity ID as UUID
Extract entity ID from path parameters. Args: request: Ring request map Returns: Entity ID as UUID
(get-entity-name request)Extract entity name from path parameters.
Args: request: Ring request map
Returns: Entity name as keyword
Extract entity name from path parameters. Args: request: Ring request map Returns: Entity name as keyword
(html-response request html)Create HTML response with standard headers, resolving [:t ...] i18n markers.
Args: request: Ring request map (used to extract :i18n/t translation function) html: Hiccup data structure or HTML string
Returns: Ring response map
Create HTML response with standard headers, resolving [:t ...] i18n markers. Args: request: Ring request map (used to extract :i18n/t translation function) html: Hiccup data structure or HTML string Returns: Ring response map
(htmx-fragment-response request html)Create HTMX fragment response, resolving [:t ...] i18n markers.
Args: request: Ring request map html: Hiccup data or HTML string
Returns: Ring response map with HTMX headers
Create HTMX fragment response, resolving [:t ...] i18n markers. Args: request: Ring request map html: Hiccup data or HTML string Returns: Ring response map with HTMX headers
(nested-relationships schema-provider entity-config)The has-many entries entity-config is created with, as
forms/nested-relationships finds them (BOU-570).
The has-many entries `entity-config` is created with, as `forms/nested-relationships` finds them (BOU-570).
(parse-advanced-filters params)Parse nested filter parameters from query string (Week 2).
Expected formats:
Args: params: Ring query-params map
Returns: Map of field-name -> filter-spec {:field-name {:op :operator :value val} :other-field {:op :between :min 10 :max 100}}
Example: (parse-advanced-filters {"filters[created-at][op]" "gte" "filters[created-at][value]" "2024-01-01"}) => {:created-at {:op :gte :value "2024-01-01"}}
Parse nested filter parameters from query string (Week 2).
Expected formats:
- filters[field][op]=operator
- filters[field][value]=single-value
- filters[field][values][]=multi-value-1
- filters[field][values][]=multi-value-2
- filters[field][min]=min-value
- filters[field][max]=max-value
Args:
params: Ring query-params map
Returns:
Map of field-name -> filter-spec
{:field-name {:op :operator :value val}
:other-field {:op :between :min 10 :max 100}}
Example:
(parse-advanced-filters {"filters[created-at][op]" "gte"
"filters[created-at][value]" "2024-01-01"})
=> {:created-at {:op :gte :value "2024-01-01"}}(parse-form-params params entity-config)(parse-form-params params
entity-config
{:keys [input-zone server-zone offsets]})Parse form parameters into entity data map.
Converts string form values to appropriate types based on field config.
Args: params: Ring form-params map (all string values) entity-config: Entity configuration with field metadata
Returns: Entity data map with typed values
Examples: (parse-form-params {name John active true} entity-config) => {:name John :active true}
(parse-form-params {price 19.99 quantity 5} entity-config) => {:price 19.99 :quantity 5}
Parse form parameters into entity data map.
Converts string form values to appropriate types based on field config.
Args:
params: Ring form-params map (all string values)
entity-config: Entity configuration with field metadata
Returns:
Entity data map with typed values
Examples:
(parse-form-params {name John active true} entity-config)
=> {:name John :active true}
(parse-form-params {price 19.99 quantity 5} entity-config)
=> {:price 19.99 :quantity 5}(parse-form-params-checked params entity-config)(parse-form-params-checked params entity-config zones)Like parse-form-params, but returns [data field-errors] instead of
throwing on a value that cannot be read as its field's type.
parse-form-params throws a :validation-error on the first bad field, and
the create and update handlers call it outside their error handling, so an
invalid integer, decimal, UUID or date answered 500 rather than re-rendering
the form (BOU-521). Parsing field by field reports every bad field at once,
in the {field [message]} shape the form already renders, and keeps what the
user typed in data so the re-rendered form shows it.
Like `parse-form-params`, but returns `[data field-errors]` instead of
throwing on a value that cannot be read as its field's type.
`parse-form-params` throws a :validation-error on the first bad field, and
the create and update handlers call it outside their error handling, so an
invalid integer, decimal, UUID or date answered 500 rather than re-rendering
the form (BOU-521). Parsing field by field reports every bad field at once,
in the {field [message]} shape the form already renders, and keeps what the
user typed in `data` so the re-rendered form shows it.(parse-query-params params)Parse query parameters into admin service options.
Extracts and normalizes:
Args: params: Ring query-params map (all string values)
Returns: Options map with normalized keys and parsed values
Examples: (parse-query-params {page 2 page-size 25 search john}) => {:page 2 :page-size 25 :search john}
(parse-query-params {sort email sort-dir desc role admin}) => {:sort :email :sort-dir :desc :filters {:role admin}}
Parse query parameters into admin service options.
Extracts and normalizes:
- Pagination: page, page-size, limit, offset
- Sorting: sort, sort-dir
- Search: search (text search across search-fields)
- Filters: Any other params become field filters
Args:
params: Ring query-params map (all string values)
Returns:
Options map with normalized keys and parsed values
Examples:
(parse-query-params {page 2 page-size 25 search john})
=> {:page 2 :page-size 25 :search john}
(parse-query-params {sort email sort-dir desc role admin})
=> {:sort :email :sort-dir :desc :filters {:role admin}}(require-admin-user! request)Assert current user is an admin, throw if not.
Args: request: Ring request map
Returns: User entity map if admin
Throws: ExceptionInfo with :type :forbidden if not admin
Assert current user is an admin, throw if not. Args: request: Ring request map Returns: User entity map if admin Throws: ExceptionInfo with :type :forbidden if not admin
(safe-return-to request)The request's return_to, or nil unless it is a path inside the admin, so
it cannot redirect off-site.
The request's `return_to`, or nil unless it is a path inside the admin, so it cannot redirect off-site.
(t-fn request)The request's translation function; without i18n on the request, one that answers the key's name.
The request's translation function; without i18n on the request, one that answers the key's name.
(valid-date-pattern pattern)The pattern, or nil with a warning when DateTimeFormatter rejects it.
Dropping it means one bad character in config.edn costs a column its
formatting rather than the whole page — the renderer falls back to its
default pattern.
Called once from the module wiring, not per request: a misconfigured application should say so at boot, rather than log the same warning on every admin page load and every HTMX table refresh for the life of the process.
The pattern, or nil with a warning when `DateTimeFormatter` rejects it. Dropping it means one bad character in `config.edn` costs a column its formatting rather than the whole page — the renderer falls back to its default pattern. Called once from the module wiring, not per request: a misconfigured application should say so at boot, rather than log the same warning on every admin page load and every HTMX table refresh for the life of the process.
(with-workflow-states config entity-config records)records, each with :admin/workflow {:instance-id :state} when it has a
workflow instance. Only for an entity with a :workflow config, and only
when the workflow module is on.
`records`, each with `:admin/workflow` {:instance-id :state} when it has a
workflow instance. Only for an entity with a `:workflow` config, and only
when the workflow module is on.Set by init.js to the browser's IANA zone (Intl…resolvedOptions().timeZone).
Set by init.js to the browser's IANA zone (`Intl…resolvedOptions().timeZone`).
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |