feat/fronius
needs attention0d9542d · fullPR #276reviewed 2026-07-09 19:55 UTC2H · 8M · 8L · 3I- 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
- 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/+1351−0domains/metrics/src/site-metrics/+271−0domains/cross-domain/src/+169−10infra/cdk/src/+306−1packages/integration-manifests/src/+628−15packages/database/src/+234−13scripts/metrics/+898−0
- 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).
Findings · 12
correctness3
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.
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).
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
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:'.
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
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.
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.
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.
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
1d fetch-failure path has no handler-level test
services/metrics/integrations/fronius/src/__tests__/metrics-handler.test.ts
fetchAggDays error-tag mapping untested.
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.
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.