Loosen LtsMigrationService.select() to pick every bean in the (from, to]
range regardless of family, so a 4.3.x -> 4.4 offline upgrade runs the real
in-range 4.3.1.x beans plus a new 4.4.0.0 baseline bean, each exactly once.
This replaces the ad-hoc explicit V4_3_1_3Migration.apply() call in
DefaultDataUpdateService and moves the 4.4 baseline flat DDL out of
basic/schema_update.sql into V4_4_0_0Migration / lts/4.4.0.0/schema_update.sql.
Restore object-identity tracking (track widget) for the dashboard widget
grid loop. The Angular 20 migration commit ff0e33c07 changed it to
'track widget.widgetId', which stopped the WidgetComponent from being
recreated when doCheck() swaps in a new DashboardWidget on a config edit.
Since widgetContext.customHeaderActions / actionsApi are built only in
ngOnInit, applied action changes did not appear until a full reload.
- Drop the dangling "spec §7" reference from the test comments; inline the
actual rule (a lexical MIN/MAX result over string telemetry stays a string).
- Use a zero-padded code ("0009") for the string-result case - a clearer example
of a genuine string that must not be coerced to a number (would lose padding).
- Define the maxCode metric in the test CF configuration alongside consumption
and avgConsumption, so all parameterized metric names are configured.
Address review feedback on the integration test:
- Read latest telemetry with useStrictDataTypes=true always, so the value node
keeps its stored type (numeric -> JSON number, str_v -> JSON string).
- Assert the aggregation result is a numeric node across all existing scenarios
via a shared assertNumericValue helper, instead of a separate standalone test.
isNumber() is the actual regression guard; asLong()/asText() would coerce a
str_v string and miss it, so the type is asserted explicitly and the value is
compared numerically.
- Drop the redundant standalone test that duplicated an existing scenario.
Cover the fix that makes EntityAggregationCalculatedFieldState.toResult()
emit numeric aggregation results as numeric JSON nodes (dbl_v/long_v) instead
of JSON strings (str_v), which had broken server-side AVG/SUM aggregation.
- New unit test EntityAggregationCalculatedFieldStateTest: asserts toResult()
serializes a numeric result as a numeric node and a genuine string result
(lexical MIN/MAX over string telemetry) as a string node. Assertions check
the node type (isNumber/isTextual) rather than asText(), since asText()
coerces both node kinds identically - which is why the bug went unnoticed.
- EntityAggregationCalculatedFieldTest: add a strict-types read helper
(useStrictDataTypes=true) and a test asserting the stored SUM/AVG telemetry
are numeric JSON nodes end-to-end. Existing asText()-based assertions are
left untouched (they read via the non-strict endpoint, which stringifies
every value and therefore cannot observe the storage type).
EntityAggregationCalculatedFieldState.toResult() serialized every metric
result via ObjectNode.put(name, JacksonUtil.toString(value)), which produces
a JSON *string* node even for numeric aggregation results (SUM/AVG/COUNT/...).
When persisted (in particular with transport.json.type_cast_enabled=false, or
for non-parsable values) the result lands in ts_kv.str_v, so server-side AVG/SUM
aggregation returns no data in widgets/queries while COUNT/MIN/MAX still return.
Serialize with JacksonUtil.valueToTree(...) so numeric results are emitted as
numeric JSON nodes (-> dbl_v/long_v), mirroring
RelatedEntitiesAggregationCalculatedFieldState. Genuine string MIN/MAX results
over string telemetry are preserved as string nodes. The instanceof Number
guard is retained (it only governs rounding).