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
This commit is contained in:
66
products/04-lightweight-idp/migrations/005_analytics.sql
Normal file
66
products/04-lightweight-idp/migrations/005_analytics.sql
Normal file
@@ -0,0 +1,66 @@
|
||||
-- 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;
|
||||
Reference in New Issue
Block a user