]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commit
fix(ui-server): reconcile evses wire type so /metrics works on OCPP 2.0.x (#2048)
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Mon, 27 Jul 2026 12:54:30 +0000 (14:54 +0200)
committerGitHub <noreply@github.com>
Mon, 27 Jul 2026 12:54:30 +0000 (14:54 +0200)
commitafdf433bacac02c03fd966a60e13b35d362547c7
treeb08a69b42fa6d252d64968ff7086d7cd8186772e
parent82ae6d1a78470403022fbb31dac35dfd46e91c85
fix(ui-server): reconcile evses wire type so /metrics works on OCPP 2.0.x (#2048)

* fix(ui-server): reconcile evses wire type so /metrics works on OCPP 2.0.x

The worker->main producer `buildEvseEntries` emits `evseStatus.connectors`
as a `ConnectorEntry[]` array, but `ChargingStationData.evses` was typed
with the in-memory `EvseStatus` (connectors: Map), hidden by an
`as unknown as EvseEntry[]` cast. On a live scrape the metrics consumer
`iterateConnectors` tuple-destructured each array element as a Map entry,
throwing `TypeError: ... is not iterable` inside prom-client's
`Registry.metrics()` -> HTTP 500 for every OCPP 2.0.x station.
`countConnectors` also read `.size` on the array -> NaN. OCPP 1.6 was
unaffected because its `data.connectors` path already yields ConnectorEntry
objects without destructuring.

Introduce dedicated wire types `EvseEntryData`/`EvseStatusData`
(connectors: ConnectorEntry[]), type `ChargingStationData.evses` and
`buildEvseEntries` with them, and drop the cast so the compiler enforces
the array shape end to end. `iterateConnectors` now yields the entry
directly and `countConnectors` uses `.length`. The array shape matches what
the Web UI and CLI already consume over the JSON wire; a Map would not
survive JSON serialization. The in-memory `EvseStatus` (connectors: Map)
is unchanged.

Add a regression test feeding the actual `buildEvseEntries` output through
the /metrics scrape (fails pre-fix with 500/NaN, passes post-fix) plus
zero-id and empty-evses edge cases, and migrate the existing EVSE-mode test
off its synthetic Map fixture that masked the defect.

Closes #2046

* docs(types): document EvseEntryData/EvseStatusData wire projection

Add JSDoc on the wire types introduced for #2046:
- EvseEntryData: anchor its relation to the in-memory EvseEntry (Map) and to
  the structurally-identical ui-common EvseEntry (duplicated, no cross-package
  re-export), removing the naming ambiguity.
- EvseStatusData: document that MeterValues is intentionally omitted (the
  producer never emits it, no UI-facing consumer reads it off the wire),
  resolving the PR review comment on the omission.

Doc-only; no type or runtime change.

* test(ui-server): harden #2046 metrics regression guards

Address review findings on the #2046 regression tests:
- Convert the "empty evses" test to a populated EVSE with an empty connectors
  array. The zero-EVSE case short-circuits `reduce` (returns 0 even pre-fix),
  so it guarded nothing; a populated EVSE with `connectors: []` is the case
  that pre-fix reads `.size` on an array -> undefined -> NaN. Verified: fails
  pre-fix (connectors_total Nan), passes post-fix (0).
- Type the buildEvseEntries stub as `Pick<ChargingStation, 'iterateEvses'>`
  instead of `as unknown as`, so `iterateEvses`/`evseStatus` stay type-checked
  (resolves the PR review comment on the stub cast).
- Use `satisfies` instead of `as` on the migrated EVSE-mode fixture, so a
  Map/array shape drift is caught at compile time (the unchecked-cast class
  that masked the original bug).

* refactor(types): make EvseStatusData an immutable wire snapshot

Mark EvseStatusData.availability and connectors readonly (and connectors a
readonly ConnectorEntry[]), matching the readonly EvseEntryData wrapper. The
wire projection is a produced-once snapshot never mutated by any consumer
(countConnectors reads .length, iterateConnectors iterates read-only), so
immutability is the accurate contract and removes the wrapper/payload
readonly asymmetry. In-memory EvseStatus (mutable Map) is unchanged.
src/charging-station/ui-server/AbstractUIServer.ts
src/types/ChargingStationWorker.ts
src/types/Evse.ts
src/types/index.ts
src/utils/ChargingStationConfigurationUtils.ts
tests/charging-station/ui-server/UIMetricsEndpoint.test.ts
tests/utils/ChargingStationConfigurationUtils.test.ts