← all branches

feat/fronius

needs attention
0d9542d · fullPR #276reviewed 2026-07-09 19:55 UTC2H · 8M · 8L · 3I
The branch
Purpose
Port the Fronius Solar.web device integration (8 live devices, oid 69) into the v2 metrics engine as part of the 12-brand device-integration port series landed on feat/int-base.
Goal
5m native histdata worker + 1d aggdata fallback, stateless AccessKeyId/AccessKeyValue auth, CDK stack, live Site-SFN validation with 20/20 parity.
Sub-goals
  • SG-1: API probe + worker + unit tests
  • SG-2: CDK + main.ts + coordinator grant; synth clean
  • SG-3: Preview data plane (D2 secret + D3 registry + ARN seed)
  • SG-4: Live Site-SFN validation + Tinybird + legacy parity
  • SG-N: Framework learnings fold-back
  • SG-6: Rebase onto updated feat/int-base (multi-gran seam + 1M foundation)
  • SG-7: Multi-granularity worker (5m/1d/1M) — REMAINING
  • SG-8: Live multi-granularity validation — REMAINING
The changes (whole branch)
What
New Fronius Solar.web vendor package (solarweb-client + metrics.lambda + points-to-batu); CDK IntegrationFroniusLambdaStack; fronius manifest in integration-manifests; 1M calendar-month seam additions to domains/metrics transforms + cross-domain shell; 11 new SecretProvider enum values; paths.ts entries; migration scripts.
Why
Fronius is one of 8 active device brands on one shared org (oid 69); porting from the legacy Python Lambda to the v2 TypeScript engine with full parity validation.
Areas
services/metrics/integrations/fronius/+13510domains/metrics/src/site-metrics/+2710domains/cross-domain/src/+16910infra/cdk/src/+3061packages/integration-manifests/src/+62815packages/database/src/+23413scripts/metrics/+8980
Blast
76 files, +7025/-185 across 7 areas; core Fronius package is additive-only; 1M seam changes touch cross-domain shell and metrics transforms; SecretProvider enum is TS-only (no migration).
ssrf-links-next non-resilient-multi-gran cdk-tags-missing sg7-remaining
live-SFN-validation· 5×24h chunks, 2880 pts/device, 20/20 day-sum parity worst Δ 0.0087%typecheck+tests· 68/68 tests green, full typecheck clean post-rebase onto feat/int-baseCI· no GitHub Actions checks on branchcoderabbit· no .coderabbit.yaml in repo

Findings · 12

correctness3

low

1d aggdata uses uniform 86400s for Wh→W — ~4% off on Tijuana DST-transition days

services/metrics/integrations/fronius/src/translation/points-to-batu.ts

Energy billing unaffected; maxPowerKW/max15min slightly wrong on 2 days/year.

low

UTC fallback for 1M stream + DST site silently produces wrong month widths

domains/cross-domain/src/site-energy-metrics.shells.ts

Warning logged but computation proceeds. Not production-impacting (1M Fronius not live yet).

low

403/pvSystemId not authorized → TranslationFailed statusCode 500 → SFN retries on permanent fact

services/metrics/integrations/fronius/src/handlers/metrics.lambda.ts

Wastes retry budget on bad externalId. Pre-existing type-system constraint.

security2

medium

SSRF via unvalidated vendor-supplied links.next URL

services/metrics/integrations/fronius/src/engine/solarweb-client.ts:187

Vendor-supplied next-page URL passed to swqGet without origin validation. A crafted response could redirect Lambda to EC2 IMDS with credentials attached. Fix: assert host === 'api.solarweb.com' and protocol === 'https:'.

medium

Caller-controlled assetMetadata.deviceId can target foreign pvSystemId

services/metrics/integrations/fronius/src/handlers/metrics.lambda.ts:144

Any caller with lambda:InvokeFunction can set deviceId to an arbitrary GUID, causing the org's credential to probe a system it doesn't own. Impact: read-only telemetry.

conventions4

high

groupChannelsByGranularity not used + non-resilient cross-granularity failure semantics

services/metrics/integrations/fronius/src/handlers/metrics.lambda.ts:159

INTEGRATION_STANDARDS §9 mandates groupChannelsByGranularity. Worker uses Set-dedup instead. A 5m failure blocks 1d collection (non-resilient). Tracked as SG-7 remaining work.

high

CDK stack missing batu:env, batu:owner, batu:costCenter tags

infra/cdk/src/stacks/services/metrics/integrations/fronius/lambda.stack.ts:50

3 of 8 mandatory tags absent from stack constructor. Compensated by applyBatuTags(app,…) in main.ts via CDK inheritance. Pre-existing gap across all integration stacks.

medium

Fronius manifest VariableDef.externalVariableId holds vendor channel names, not connector-local keys

packages/integration-manifests/src/manifests/fronius.ts:46

Convention violation vs all other manifests. Runtime correct; misleading docstring.

medium

Translation functions use custom ok/fail union, not Result<T,E> from @batu/result

services/metrics/integrations/fronius/src/translation/points-to-batu.ts

FCIS requires Result<T,E>. Functions are worker-internal only. Pre-existing pattern in eGauge reference.

tests3

medium

1d fetch-failure path has no handler-level test

services/metrics/integrations/fronius/src/__tests__/metrics-handler.test.ts

fetchAggDays error-tag mapping untested.

medium

1d empty-result (zero points) has no handler-level test

services/metrics/integrations/fronius/src/__tests__/metrics-handler.test.ts

Zero-point guard only exercised via 5m mocks.

medium

verifyHistdataCadence ±30s band: lower boundary (270s) and just-over-upper (331s) untested

services/metrics/integrations/fronius/src/__tests__/points-to-batu.test.ts:52

Only 301s (inside) and 900s (far outside) tested.