Files
dd0c/products/04-lightweight-idp/migrations/005_analytics.sql
Max cfe269a031
Some checks failed
CI — P4 Portal / test (push) Failing after 32s
CI — P4 Portal / build-push (push) Has been skipped
feat(portal): add CloudFormation/APIGateway scanners, analytics endpoints, search caching
- CloudFormation scanner: discovers stacks and maps resources to services
- API Gateway scanner: discovers REST/HTTP APIs and routes
- Analytics API: ownership coverage, health scorecards, tech debt indicators
- Redis prefix cache for Cmd+K search (60s TTL)
- 005_analytics.sql migration for aggregation helpers
2026-03-03 06:36:24 +00:00

67 lines
2.6 KiB
PL/PgSQL

-- dd0c/portal analytics helpers + scanner constraint updates
-- Update scan_history scanner check to include new scanner types
ALTER TABLE scan_history DROP CONSTRAINT IF EXISTS scan_history_scanner_check;
ALTER TABLE scan_history ADD CONSTRAINT scan_history_scanner_check
CHECK (scanner IN ('aws', 'github', 'cloudformation', 'apigateway'));
-- Update staged_updates source check to include new sources
ALTER TABLE staged_updates DROP CONSTRAINT IF EXISTS staged_updates_source_check;
ALTER TABLE staged_updates ADD CONSTRAINT staged_updates_source_check
CHECK (source IN ('aws', 'github', 'cloudformation', 'apigateway', 'manual'));
-- Materialized view: ownership coverage summary
CREATE MATERIALIZED VIEW IF NOT EXISTS mv_ownership_coverage AS
SELECT
tenant_id,
COUNT(*)::int AS total_services,
COUNT(*) FILTER (WHERE owner != 'unknown')::int AS owned_services,
COUNT(*) FILTER (WHERE owner = 'unknown')::int AS unowned_services,
ROUND(
(COUNT(*) FILTER (WHERE owner != 'unknown')::numeric / NULLIF(COUNT(*), 0)) * 100, 1
) AS coverage_pct
FROM services
WHERE lifecycle = 'active'
GROUP BY tenant_id;
CREATE UNIQUE INDEX IF NOT EXISTS idx_mv_ownership_tenant ON mv_ownership_coverage(tenant_id);
-- Materialized view: health scorecards by tier
CREATE MATERIALIZED VIEW IF NOT EXISTS mv_health_by_tier AS
SELECT
tenant_id,
tier,
COUNT(*)::int AS count,
COUNT(*) FILTER (WHERE last_discovered_at < NOW() - INTERVAL '7 days')::int AS stale_count
FROM services
WHERE lifecycle = 'active'
GROUP BY tenant_id, tier;
CREATE UNIQUE INDEX IF NOT EXISTS idx_mv_health_tier ON mv_health_by_tier(tenant_id, tier);
-- Materialized view: tech debt indicators
CREATE MATERIALIZED VIEW IF NOT EXISTS mv_tech_debt AS
SELECT
tenant_id,
COUNT(*)::int AS total_active,
COUNT(*) FILTER (WHERE description IS NULL OR description = '')::int AS missing_description,
COUNT(*) FILTER (WHERE owner = 'unknown')::int AS missing_owner,
COUNT(*) FILTER (WHERE owner_source = 'heuristic' AND owner != 'unknown')::int AS heuristic_ownership,
COUNT(*) FILTER (WHERE links = '{}' OR links IS NULL)::int AS missing_links,
COUNT(*) FILTER (WHERE last_discovered_at IS NULL)::int AS never_discovered
FROM services
WHERE lifecycle = 'active'
GROUP BY tenant_id;
CREATE UNIQUE INDEX IF NOT EXISTS idx_mv_tech_debt_tenant ON mv_tech_debt(tenant_id);
-- Helper function to refresh all analytics materialized views
CREATE OR REPLACE FUNCTION refresh_analytics_views()
RETURNS void AS $$
BEGIN
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_ownership_coverage;
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_health_by_tier;
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_tech_debt;
END;
$$ LANGUAGE plpgsql;