feat/solis
needs attentionde07ba1 · fullPR #271reviewed 2026-07-08 02:50 UTC2H · 11M · 10L · 6I- Purpose
- Port the SolisCloud integration (91 devices) from the legacy serverless-batu-metrics Lambda to the v2 metrics engine, enabling 5-min native data collection via the universal SFN pipeline.
- Goal
- Complete the 2nd of 12 planned legacy-integration ports (SolisCloud, 91 devices) — stateless HMAC auth, metrics + connection + exploration workers, CDK stack, live-validated.
- Sub-goals
- SG-1: API probe (HMAC signing, 5m native confirmation, secret key fields) + workers + 71 unit tests
- SG-2: CDK stack (3 workers, SM-only IAM) + coordinator grants; synth + template verified
- SG-3: Preview data plane (catalog seed, D2 secrets, D3 assets/streams, ARN seed)
- SG-4: Live Site-SFN validation (all 3 jobTypes) + Tinybird + parity evidence
- SG-N: 10 framework learnings → scope.md; Solis Tier-3 caveats → integrations CLAUDE.md
- What
- New services/metrics/integrations/solis/ worker package (3 Lambda handlers, HMAC client, translation layer, 71 tests). CDK IntegrationSolisLambdaStack. solis manifest + 10 other brand manifests. DB catalog seed updates. Migration scripts (D2 legacy-secrets + D3 registry). paths.ts entries for all 12 brands. Coordinator wired with solis ARN grants.
- Why
- Solis is the first 3-jobType port (metrics + connection + exploration) and the second overall port. Proven recipe from victron extended to a stateless-auth brand. 91 devices move to 5-min native collection, dropping legacy 288-slot smear and generation-curve synthesis.
- Areas
- services/metrics/integrations/solis+2558−6scripts/metrics+1719−0packages/integration-manifests+1000−2infra/cdk+447−3packages/database+259−14domains+21−5
- Blast
- 57 files, +5200/−200. Core impl = services/metrics/integrations/solis/ (new). CDK stack new. Migration scripts one-shot CLI. Manifests pure data. Coordinator gains 3 SSM reads + IAM grants.
Findings · 13
correctness1
credentialValid: false emitted for non-auth upstream failures
services/metrics/integrations/solis/src/handlers/connection.lambda.ts:73
probeFailurePayload returns credentialValid: false for rate-limit/timeout/unreachable too. Operators may rotate valid credentials after transient failures.
security1
IAM wildcard {env}/solis/* grants read on portal + api-duplicate secrets
infra/cdk/src/stacks/services/metrics/integrations/solis/iam.ts:43
Covers /portal/oid=* (username/password) and /api/oid=33 variants the worker must not touch. Narrow to {env}/solis/oid=* post-D2 revocation.
conventions2
Stale comment in seed-metrics-catalog.ts claims solis has no manifest
packages/database/src/seed-metrics-catalog.ts:62
Comment predates feat/solis. solisManifest is now registered. Remove the stale warning.
VariableDef.externalVariableId holds vendor channel names, not the pipeline routing key
packages/integration-manifests/src/manifests/solis.ts:52
Pipeline routes on map KEY per metrics-pipeline.md gotcha #1. The field value (energy, consumeEnergy) is display-only. JSDoc in types.ts is misleading. Add clarification.
tests4
DST fall-back dedup (last-write-wins) not tested in dayPointsToBatu
services/metrics/integrations/solis/src/__tests__/points-to-batu.test.ts:104
Named invariant in production code (L256-258), spring-forward IS tested but fall-back is not.
powerToWatts/energyToKwh: NaN raw input and undefined unitStr default not tested
services/metrics/integrations/solis/src/__tests__/points-to-batu.test.ts:43
Two distinct test gaps: NaN raw returns NaN (not null), guarded by caller but unverified; undefined unitStr defaults not exercised.
localDaysInWindow: single-day window not tested
services/metrics/integrations/solis/src/__tests__/points-to-batu.test.ts:84
Multi-day and DST-crossing covered; degenerate single-day window not.
monthRecordsToBatu: missing/undefined dateStr record not tested
services/metrics/integrations/solis/src/__tests__/points-to-batu.test.ts:183
Records with undefined dateStr should be silently skipped (L318 regex guard). Not tested.
improvement5
success()/failure() envelope helpers duplicated across 7 handler files
services/metrics/integrations/solis/src/handlers/metrics.lambda.ts:291
Identical 40-line pairs across solis/{metrics,connection,explore}, egauge/{metrics,connection}, growatt/{metrics,explore}. Extract to @batu/metrics-engine/src/lib/invocation-helpers.ts before next port.
Timezone helpers duplicated across solis + victron — will duplicate into every TOU brand
services/metrics/integrations/solis/src/translation/points-to-batu.ts:103
6 pure Intl functions verbatim from victron spike. Move to @batu/metrics-engine/src/lib/local-time.ts before Huawei/SolArk/Fronius land.
normalizeTimezoneLabel stranded in points-to-batu.ts — explore-only logic
services/metrics/integrations/solis/src/translation/points-to-batu.ts:359
Used only by explore.lambda.ts. Move alongside timezone helpers.
CDK Lambda stack repeats NodejsFunction config 3x — no factory function
infra/cdk/src/stacks/services/metrics/integrations/solis/lambda.stack.ts:77
runtime/logRetention/environment/bundling identical across 3 workers. Internal factory helper would cut stack to ~90 lines.
probeFailurePayload credentialValid: false semantically wrong for non-auth failures
services/metrics/integrations/solis/src/handlers/connection.lambda.ts:96
Introduce tristate before ConnectionPayload is consumed by UI or alerting.