← all branches

feat/solis

needs attention
de07ba1 · fullPR #271reviewed 2026-07-08 02:50 UTC2H · 11M · 10L · 6I
The branch
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
The changes (whole branch)
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+25586scripts/metrics+17190packages/integration-manifests+10002infra/cdk+4473packages/database+25914domains+215
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.
infra migration metrics-pipeline
CI checks· No CI checks on PR at review timeCodeRabbit· No .coderabbit.yamlLive SFN validation· 3562 Tinybird points, 3-day window, all 3 jobTypes SUCCEEDED; parity ≤2.2%

Findings · 13

correctness1

medium

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

medium

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

medium

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.

medium

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

medium

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.

medium

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.

medium

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.

medium

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

high

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.

high

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.

medium

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.

medium

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.

medium

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.

History · 2 commits

  1. de07ba1needs attentionfull2H · 11M · 10L2026-07-08 02:50current
  2. c3cb64aneeds attentionfull5H · 9M · 9L2026-07-07 21:29