Files
hiveops-guide/backend/src/main/resources/content/technical/analytics/overview.md
T
johannes 6542c4f348 content(guide): per-service internal knowledge base — 19 technical.<svc> guides (DRAFT, Refs #3)
One code-grounded internal guide per backend service (Responsibility / Data /
Kafka / API surface / Gotchas), authored by parallel per-service agents reading
each service's own source, then an adversarial verify pass (DB names, topics,
endpoints spot-checked against code — 0 hard errors). Several guides correctly
document CODE over stale CLAUDE.md (remote = HTTP long-poll not WebSocket;
analytics = has a DB, not stateless; devices = has its own backend).
Unconfirmed facts are marked (unverified). audience: internal, all DRAFT.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 09:01:58 -04:00

5.5 KiB

module, title, tab, order, audience
module title tab order audience
technical.analytics Analytics — fleet dashboard aggregation & snapshots Overview 10 internal

DRAFT · internal. Written 2026-07-01 from hiveops-analytics source. Verify against code before relying on specifics.

Responsibility

hiveops-analytics computes fleet dashboard analytics by aggregating data from hiveops-incident and hiveops-journal (and hiveops-mgmt). It exposes read-oriented dashboard endpoints (fleet health, uptime, incident trends, cash intelligence, journal coverage, agent insights) and persists periodic snapshots plus AI insights ("Adoons"-style callouts) in its own database. Runs on port 8089 (PORT env override). Cross-link [platform.service-ownership].

Note: the in-repo CLAUDE.md still describes the service as "stateless — no database," but the code has since gained its own DB (analytics_snapshots, ai_insights), a snapshot scheduler, and Kafka consumers. Treat it as stateful with an aggregation cache.

Data

Database: hiveiq_analytics (PostgreSQL in prod, H2 in-memory in dev). Cross-link [platform.data-stores].

Prod connection uses split env vars DB_HOST / DB_PORT / DB_NAME / DB_USERNAME / DB_PASSWORD (JDBC URL assembled in application.yml), not a single DB_URL. DB_NAME defaults to hiveiq_analytics.

Table Purpose
analytics_snapshots Point-in-time fleet metrics: atm counts (total/connected/disconnected/never-connected), incidents by severity (open/critical/high/medium/low), tasks (pending/in-progress/completed-today), transactions today + failed, plus opened_incidents_today / closed_incidents_today (V2). Unique on (snapshot_date, snapshot_hour).
ai_insights Generated insight cards: category, severity, title, body, optional snapshot_id FK, expires_at, acknowledged / acknowledged_at / acknowledged_by_user_id.

Flyway migrations in src/main/resources/db/migration/ (V1 init, V2 open/close incident columns, V3 snapshot_hour type fix). Cache TTL for aggregations via ANALYTICS_CACHE_TTL (default 300s).

Kafka

Topic constants in kafka/AnalyticsKafkaConfig.java. Cross-link [platform.kafka].

Produced

Topic Producer class Notes
analytics.snapshot-ready SnapshotEventProducer Emitted (key "snapshot", SnapshotReadyEvent{snapshotDate, snapshotHour}) after a snapshot is collected by SnapshotCollectionService.

Consumed

Topic Listener class
hiveops.incidents.created IncidentEventConsumer
hiveops.incidents.updated IncidentEventConsumer
hiveops.incidents.resolved IncidentEventConsumer
journal.file-parsed JournalFileParsedConsumer

Consumer group hiveops-analytics, auto-offset-reset: latest. Incident/journal events trigger real-time snapshot collect(); a @Scheduled(cron = "0 0 */6 * * *") job in SnapshotCollectionService is the 6-hour fallback.

API surface

Most controllers sit under base /api/analytics; snapshots and insights have their own sub-paths; there is one unauthenticated public path.

Method Path Purpose
GET /api/analytics/dashboard/overview Fleet overview: atm counts, incidents, tasks, transactions
GET /api/analytics/fleet-health Fleet health summary
GET /api/analytics/uptime Uptime metrics
GET /api/analytics/incident-trends Incident trends over a period
GET /api/analytics/cash-intelligence Cash intelligence metrics
GET /api/analytics/journal-coverage Journal coverage/reporting metrics
GET /api/analytics/agents Agent insights
GET /api/analytics/snapshots/latest Most recent snapshot
GET /api/analytics/snapshots List snapshots
GET /api/analytics/snapshots/range Snapshots for a date range
GET /api/analytics/insights All AI insights
GET /api/analytics/insights/active Active (unacknowledged, unexpired) insights
POST /api/analytics/insights Create an insight — hasAnyRole('MSP_ADMIN','BCOS_ADMIN')
GET /api/public/stats Public stats (no auth)

Not exhaustive.

Gotchas

  • /api/public/** and /actuator/health are permitAll() in SecurityConfig; everything else requires an authenticated JWT. /api/public/stats is intentionally unauthenticated — do not put sensitive fleet data there.
  • Only POST /api/analytics/insights is role-gated (hasAnyRole('MSP_ADMIN','BCOS_ADMIN')); the many GET endpoints are .anyRequest().authenticated() with no role restriction.
  • Service-to-service auth forwards the caller's JWT to incident/journal/mgmt (WebClientConfig); there is no separate service account. Downstream calls fail if the user token lacks access.
  • Downstream URLs are env-configured (INCIDENT_SERVICE_URL, JOURNAL_SERVICE_URL, MGMT_SERVICE_URL), defaulting to in-cluster hostnames (hiveops-incident-backend:8080, hiveops-journal:8087, hiveops-mgmt:8080).
  • Snapshots are event-driven with a 6h cron fallback; if incident/journal Kafka events stop flowing, dashboard freshness degrades to the 6-hour cadence.
  • Prod uses split DB_HOST/DB_PORT/DB_NAME env vars (unlike hiveops-journal, which expects a single DB_URL).
  • dev profile uses H2 with ddl-auto: create-drop and Flyway disabled; prod uses PostgreSQL with ddl-auto: validate and Flyway enabled — schema drift surfaces only in prod validation.