Configuration component. Loads config.edn from classpath.
Configuration component. Loads config.edn from classpath.
Entry point for the OpenADR 3.1.0 VTN server.
Entry point for the OpenADR 3.1.0 VTN server.
Legba routing-handler assembly.
Builds two handler maps — one for the BL port (full CRUD) and one for the VEN port (read + subscribe) — from the OpenAPI spec.
Legba routing-handler assembly. Builds two handler maps — one for the BL port (full CRUD) and one for the VEN port (read + subscribe) — from the OpenAPI spec.
Auth endpoint stubs: GET /auth/server, POST /auth/token.
Auth endpoint stubs: GET /auth/server, POST /auth/token.
Shared handler utilities: ID generation, metadata, body coercion, pagination, search-window construction, error responses.
The VTN's storage layer is canonically ZonedDateTime / Duration.
Wire-format request bodies arrive with string datetimes (parsed JSON);
the coerce-*-body helpers here normalise them to the storage canon
before reaching add-metadata and the storage protocol.
Shared handler utilities: ID generation, metadata, body coercion, pagination, search-window construction, error responses. The VTN's storage layer is canonically `ZonedDateTime` / `Duration`. Wire-format request bodies arrive with string datetimes (parsed JSON); the `coerce-*-body` helpers here normalise them to the storage canon before reaching `add-metadata` and the storage protocol.
API documentation handlers: filtered OpenAPI spec and Scalar UI.
API documentation handlers: filtered OpenAPI spec and Scalar UI.
Event CRUD handlers.
Event CRUD handlers.
GET /notifiers handler.
The response is built from per-port :notifiers config, which specifies which notification bindings this port advertises. Example config:
{:notifiers {:MQTT {:authentication {:method "ANONYMOUS"}}}}
MQTT broker URL and serialization are filled in automatically from the top-level config. Set :notifiers to nil or omit :WEBHOOK to suppress webhook advertising on a port.
GET /notifiers handler.
The response is built from per-port :notifiers config, which specifies
which notification bindings this port advertises. Example config:
{:notifiers {:MQTT {:authentication {:method "ANONYMOUS"}}}}
MQTT broker URL and serialization are filled in automatically from
the top-level config. Set :notifiers to nil or omit :WEBHOOK to
suppress webhook advertising on a port.Program CRUD handlers.
Program CRUD handlers.
Subscription CRUD handlers.
Subscription CRUD handlers.
MQTT topic discovery handlers (/notifiers/mqtt/topics/*).
MQTT topic discovery handlers (/notifiers/mqtt/topics/*).
HTTP server component wrapping Jetty.
HTTP server component wrapping Jetty.
Ring middleware: context path stripping, JSON response, and request logging.
Ring middleware: context path stripping, JSON response, and request logging.
MQTT publisher component using machine_head (Paho).
MQTT publisher component using machine_head (Paho).
Notifier component: dispatches C/U/D notifications to MQTT topics.
When a handler performs a successful create/update/delete, it calls (notify! notifier "PROGRAM" "CREATE" object). The notifier publishes to the appropriate MQTT topics.
Notifier component: dispatches C/U/D notifications to MQTT topics. When a handler performs a successful create/update/delete, it calls (notify! notifier "PROGRAM" "CREATE" object). The notifier publishes to the appropriate MQTT topics.
Optional nREPL server component for production inspection. Bind to localhost only — access via SSH tunnel or ECS execute-command.
Optional nREPL server component for production inspection. Bind to localhost only — access via SSH tunnel or ECS execute-command.
VTN-side entity coercion and notification payload construction.
Leverages clj-oa3's openadr3.entities for raw→coerced entity coercion. Provides additional helpers for building outbound notification payloads (the reverse direction: coerced/stored objects → wire-format notifications).
VTN-side entity coercion and notification payload construction. Leverages clj-oa3's openadr3.entities for raw→coerced entity coercion. Provides additional helpers for building outbound notification payloads (the reverse direction: coerced/stored objects → wire-format notifications).
VTN storage protocol definition.
VTN storage protocol definition.
DynamoDB-backed VtnStorage implementation using Cognitect aws-api.
Single-table design: PK: objectType (S) — PROGRAM, EVENT, SUBSCRIPTION SK: id (S) — UUID
GSIs: programName-index: PK=objectType, SK=programName programID-index: PK=programID, SK=id objectType-eventStart-index: PK=objectType, SK=eventStart (date-range queries) programID-eventStart-index: PK=programID, SK=eventStart (per-program date-range)
Wire-storage boundary
Entities held in memory are canonically ZonedDateTime /
Duration. At the DDB boundary:
* :data attr is a JSON blob; the JSONWriter protocol extension in
vtn.time serialises ZDT → canonical UTC Z and Duration → ISO.
* :eventStart GSI sort key is canonical UTC Z (lex-orderable
regardless of the wire offset on the input).
On read, the JSON blob is parsed and known datetime fields are
re-hydrated to ZDT / Duration before being returned.
Caching: Programs: cached with long TTL (default 1 hour) — rarely change Events: cached per-page with short TTL (default 5 min) keyed by canonical-string query args Caches invalidated on any mutation (create/update/delete)
DynamoDB-backed VtnStorage implementation using Cognitect aws-api.
Single-table design:
PK: objectType (S) — PROGRAM, EVENT, SUBSCRIPTION
SK: id (S) — UUID
GSIs:
programName-index: PK=objectType, SK=programName
programID-index: PK=programID, SK=id
objectType-eventStart-index: PK=objectType, SK=eventStart (date-range queries)
programID-eventStart-index: PK=programID, SK=eventStart (per-program date-range)
Wire-storage boundary
Entities held in memory are canonically `ZonedDateTime` /
`Duration`. At the DDB boundary:
* `:data` attr is a JSON blob; the JSONWriter protocol extension in
`vtn.time` serialises ZDT → canonical UTC Z and Duration → ISO.
* `:eventStart` GSI sort key is canonical UTC Z (lex-orderable
regardless of the wire offset on the input).
On read, the JSON blob is parsed and known datetime fields are
re-hydrated to ZDT / Duration before being returned.
Caching:
Programs: cached with long TTL (default 1 hour) — rarely change
Events: cached per-page with short TTL (default 5 min)
keyed by canonical-string query args
Caches invalidated on any mutation (create/update/delete)Atom-backed storage implementation with optional file persistence via duratom.
Holds entities canonically: datetime fields are ZonedDateTime,
duration fields are Duration. The handler layer coerces incoming
request bodies to this shape before reaching storage.
Atom-backed storage implementation with optional file persistence via duratom. Holds entities canonically: datetime fields are `ZonedDateTime`, duration fields are `Duration`. The handler layer coerces incoming request bodies to this shape before reaching storage.
Notifying storage decorator. Wraps any VtnStorage implementation and publishes MQTT notifications on create, update, and delete operations. Reads delegate unchanged.
Callers can suppress the notification for an individual write by
attaching ^:suppress-notify metadata to the input map — used by
bulk historical backfill to avoid flooding MQTT subscribers with
notifications for stale events.
As a Component, depends on :validated-storage and :notifier.
Notifying storage decorator. Wraps any VtnStorage implementation and publishes MQTT notifications on create, update, and delete operations. Reads delegate unchanged. Callers can suppress the notification for an individual write by attaching `^:suppress-notify` metadata to the input map — used by bulk historical backfill to avoid flooding MQTT subscribers with notifications for stale events. As a Component, depends on :validated-storage and :notifier.
Validating storage decorator. Wraps any VtnStorage implementation and validates entities against wire-format Malli schemas on create and update operations. Delegates all reads and deletes unchanged.
As a Component, depends on :raw-storage (the underlying VtnStorage impl). All existing system references to :storage get validation automatically.
Validating storage decorator. Wraps any VtnStorage implementation and validates entities against wire-format Malli schemas on create and update operations. Delegates all reads and deletes unchanged. As a Component, depends on :raw-storage (the underlying VtnStorage impl). All existing system references to :storage get validation automatically.
Component system map construction.
Component system map construction.
Time helpers for the VTN.
The VTN's internal canon is java.time.ZonedDateTime for any datetime
value (createdDateTime, modificationDateTime, intervalPeriod.start) and
java.time.Duration for any duration value (intervalPeriod.duration).
All zone-sensitive arithmetic happens on ZonedDateTime so DST is
handled correctly by the IANA rules.
At the wire boundary (HTTP response, MQTT publish, DDB :data attribute,
and the DDB :eventStart GSI key) datetimes are serialised to canonical
UTC Z form via zdt->utc-z. The OA3 spec strongly conventionalises Z
form, and the GSI relies on lex-orderable sort keys, so canonicalising
the wire keeps both consistent.
Inbound parsing accepts arbitrary RFC 3339 offsets (Z, +00:00,
-07:00, …) and the VTN-RI's non-standard space-separated form.
Time helpers for the VTN. The VTN's internal canon is `java.time.ZonedDateTime` for any datetime value (createdDateTime, modificationDateTime, intervalPeriod.start) and `java.time.Duration` for any duration value (intervalPeriod.duration). All zone-sensitive arithmetic happens on `ZonedDateTime` so DST is handled correctly by the IANA rules. At the wire boundary (HTTP response, MQTT publish, DDB :data attribute, and the DDB :eventStart GSI key) datetimes are serialised to canonical UTC `Z` form via `zdt->utc-z`. The OA3 spec strongly conventionalises Z form, and the GSI relies on lex-orderable sort keys, so canonicalising the wire keeps both consistent. Inbound parsing accepts arbitrary RFC 3339 offsets (`Z`, `+00:00`, `-07:00`, …) and the VTN-RI's non-standard space-separated form.
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 |