90 lines
4.3 KiB
MySQL
90 lines
4.3 KiB
MySQL
|
|
-- 13_inc_search_fn.sql — fleettickets · INC ticket explorer (search) function
|
||
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
||
|
|
-- reporting.fn_inc_search — ad-hoc ticket lookup by id / engineer / cluster /
|
||
|
|
-- status / state / time, for the FleetOps "Ticket explorer" card. Returns
|
||
|
|
-- { count, truncated, limit, state, rows }. Consumed by dashboard_api
|
||
|
|
-- GET /webhook/inc-search.
|
||
|
|
--
|
||
|
|
-- RECOVERED INTO VERSION CONTROL 2026-06-26: this migration was applied to the live
|
||
|
|
-- DB on 2026-06-19 but the file was never committed. Recovered verbatim from the live
|
||
|
|
-- definition (pg_get_functiondef) so a fresh DB rebuilds faithfully; the live ledger
|
||
|
|
-- already lists it, so run_migrations skips it there. The crq mirror is in 16.
|
||
|
|
-- Idempotent (CREATE OR REPLACE).
|
||
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
SET search_path = tickets, public;
|
||
|
|
|
||
|
|
CREATE OR REPLACE FUNCTION reporting.fn_inc_search(
|
||
|
|
p_ticket_id text DEFAULT NULL,
|
||
|
|
p_owner text DEFAULT NULL,
|
||
|
|
p_cluster text DEFAULT NULL,
|
||
|
|
p_status text DEFAULT NULL,
|
||
|
|
p_state text DEFAULT 'closed',
|
||
|
|
p_from timestamptz DEFAULT NULL,
|
||
|
|
p_to timestamptz DEFAULT NULL,
|
||
|
|
p_limit integer DEFAULT 500
|
||
|
|
)
|
||
|
|
RETURNS jsonb LANGUAGE plpgsql STABLE AS $function$
|
||
|
|
DECLARE
|
||
|
|
v_state text := lower(COALESCE(NULLIF(p_state, ''), 'closed'));
|
||
|
|
v_limit integer := LEAST(GREATEST(COALESCE(p_limit, 500), 1), 5000);
|
||
|
|
v_result jsonb;
|
||
|
|
BEGIN
|
||
|
|
p_ticket_id := NULLIF(trim(p_ticket_id), '');
|
||
|
|
p_owner := NULLIF(trim(p_owner), '');
|
||
|
|
p_cluster := NULLIF(p_cluster, '');
|
||
|
|
p_status := NULLIF(p_status, '');
|
||
|
|
|
||
|
|
WITH hits AS (
|
||
|
|
SELECT ticket_id, normalized_status, cluster, region, location_name,
|
||
|
|
initcap(lower(NULLIF(owner, ''))) AS owner, assigned_team,
|
||
|
|
sla_status, mttr, closed_at, created_at_service, is_actionable,
|
||
|
|
CASE WHEN geom IS NOT NULL THEN ST_Y(geom) END AS lat,
|
||
|
|
CASE WHEN geom IS NOT NULL THEN ST_X(geom) END AS lng
|
||
|
|
FROM tickets.inc
|
||
|
|
WHERE (p_ticket_id IS NULL OR ticket_id ILIKE '%' || p_ticket_id || '%')
|
||
|
|
AND (p_owner IS NULL OR lower(owner) LIKE '%' || lower(p_owner) || '%')
|
||
|
|
AND (p_cluster IS NULL OR cluster = p_cluster)
|
||
|
|
AND (p_status IS NULL OR normalized_status = p_status)
|
||
|
|
AND CASE v_state
|
||
|
|
WHEN 'open' THEN COALESCE(is_actionable, false)
|
||
|
|
WHEN 'all' THEN COALESCE(is_actionable, false)
|
||
|
|
OR (closed_at IS NOT NULL
|
||
|
|
AND (p_from IS NULL OR closed_at >= p_from)
|
||
|
|
AND (p_to IS NULL OR closed_at < p_to))
|
||
|
|
ELSE NOT COALESCE(is_actionable, false) -- 'closed'
|
||
|
|
AND closed_at IS NOT NULL
|
||
|
|
AND (p_from IS NULL OR closed_at >= p_from)
|
||
|
|
AND (p_to IS NULL OR closed_at < p_to)
|
||
|
|
END
|
||
|
|
),
|
||
|
|
total AS (SELECT count(*) AS n FROM hits),
|
||
|
|
page AS (
|
||
|
|
SELECT * FROM hits
|
||
|
|
ORDER BY closed_at DESC NULLS LAST, created_at_service DESC NULLS LAST
|
||
|
|
LIMIT v_limit
|
||
|
|
)
|
||
|
|
SELECT jsonb_build_object(
|
||
|
|
'count', (SELECT n FROM total),
|
||
|
|
'truncated', (SELECT n FROM total) > v_limit,
|
||
|
|
'limit', v_limit,
|
||
|
|
'state', v_state,
|
||
|
|
'rows', COALESCE((SELECT jsonb_agg(to_jsonb(page)
|
||
|
|
ORDER BY page.closed_at DESC NULLS LAST,
|
||
|
|
page.created_at_service DESC NULLS LAST)
|
||
|
|
FROM page), '[]'::jsonb)
|
||
|
|
) INTO v_result;
|
||
|
|
|
||
|
|
RETURN v_result;
|
||
|
|
END $function$;
|
||
|
|
|
||
|
|
DO $grants$
|
||
|
|
BEGIN
|
||
|
|
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'dashboard_ro') THEN
|
||
|
|
GRANT EXECUTE ON FUNCTION reporting.fn_inc_search(text, text, text, text, text, timestamptz, timestamptz, integer) TO dashboard_ro;
|
||
|
|
END IF;
|
||
|
|
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'grafana_ro') THEN
|
||
|
|
GRANT EXECUTE ON FUNCTION reporting.fn_inc_search(text, text, text, text, text, timestamptz, timestamptz, integer) TO grafana_ro;
|
||
|
|
END IF;
|
||
|
|
END $grants$;
|