Files
beacon-server/db/sqlc/observer_directory.sql.go
T
MrAlders0n 041d9c1f45 release: v2.0.2
Release the changes on `dev` as v2.0.2: authenticated MeshMapper imports and the traffic-sorted observer directory, including rolling seven-day counts from available history. Bump the API version and regenerate Swagger.

MeshMapper integration requires an API key. Regional admins’ existing per-region or grouped-region coverage keys also work for these APIs. Deployments should set `MESHMAPPER_API_KEY` and restart Beacon.

Validation: `go build ./...`, `gofmt -l .` (empty), `go vet ./...`, `go test ./...`, `govulncheck ./...`, and race tests for MeshMapper and scopestore passed in a clean checkout. PostgreSQL integration tests require CI because Docker is unavailable locally.
2026-10-05 18:35:41 -04:00

141 lines
6.6 KiB
Go

// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: observer_directory.sql
package db
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const listObserverDirectory = `-- name: ListObserverDirectory :one
WITH hours AS MATERIALIZED (
SELECT g.h, coalesce(r.status, 'missing') AS status,
coalesce(r.status = 'complete', false) AND g.h >= $1::timestamptz
AND g.h + interval '1 hour' <= $2::timestamptz AS use_rollup
FROM generate_series(date_trunc('hour', $1::timestamptz, 'UTC'), $2::timestamptz, interval '1 hour') g(h)
LEFT JOIN analytics_rollup_hours r ON r.hour = g.h
WHERE g.h < $2::timestamptz
), candidates AS MATERIALIZED (
SELECT o.id, o.display_name, o.observer_type,
coalesce(o.last_iata, '')::text AS iata,
CASE WHEN greatest(coalesce(o.last_status_at, o.last_seen), o.last_seen) > now() - interval '5 minutes'
THEN 'online' ELSE 'offline' END AS status,
CASE WHEN o.radio_freq_mhz IS NOT NULL AND o.radio_bw_khz IS NOT NULL AND o.radio_sf IS NOT NULL
THEN concat(o.radio_freq_mhz, ',', o.radio_bw_khz, ',', o.radio_sf) END AS radio,
coalesce((SELECT jsonb_agg(ts.name ORDER BY ts.name) FROM observer_scopes os JOIN transport_scopes ts ON ts.id = os.scope_id WHERE os.observer_id = o.id), '[]'::jsonb) AS scopes
FROM observers o
WHERE NOT $12::boolean AND (coalesce(cardinality($3::bpchar[]), 0) = 0 OR o.last_iata = ANY($3::bpchar[]))
AND ($4::text = '' OR EXISTS (SELECT 1 FROM observer_brokers ob WHERE ob.observer_id = o.id AND ob.broker_name = $4))
AND ($5::text = '' OR o.display_name ILIKE '%' || $5 || '%')
AND ($6::text = '' OR CASE WHEN greatest(coalesce(o.last_status_at, o.last_seen), o.last_seen) > now() - interval '5 minutes' THEN 'online' ELSE 'offline' END = $6)
AND ($7::text = '' OR EXISTS (SELECT 1 FROM observer_scopes os JOIN transport_scopes ts ON ts.id = os.scope_id WHERE os.observer_id = o.id AND ts.name = $7))
), raw_traffic AS MATERIALIZED (
-- Disjoint hour slices keep raw scans bounded and never recount a completed whole hour.
SELECT h.h, r.observer_id, r.observations
FROM hours h
CROSS JOIN LATERAL (
SELECT o.observer_id, count(*)::bigint AS observations
FROM packet_observations o
WHERE o.heard_at >= greatest(h.h, $1::timestamptz)
AND o.heard_at < least(h.h + interval '1 hour', $2::timestamptz)
AND (coalesce(cardinality($3::bpchar[]), 0) = 0 OR o.iata = ANY($3::bpchar[]))
GROUP BY o.observer_id
) r
JOIN candidates c ON c.id = r.observer_id
WHERE NOT h.use_rollup
), coverage AS (
SELECT count(*)::integer AS expected,
count(*) FILTER (WHERE h.use_rollup)::integer AS complete,
count(*) FILTER (WHERE NOT h.use_rollup AND (h.status IN ('complete', 'partial') OR r.h IS NOT NULL))::integer AS partial,
count(*) FILTER (WHERE NOT h.use_rollup AND h.status = 'missing' AND r.h IS NULL)::integer AS missing
FROM hours h LEFT JOIN (SELECT DISTINCT h FROM raw_traffic) r ON r.h = h.h
), traffic AS (
SELECT observer_id, sum(observations)::bigint AS observations
FROM (
SELECT a.observer_id, a.observation_count AS observations
FROM analytics_hourly_observer_identity a
JOIN hours h ON h.h = a.hour AND h.use_rollup
JOIN candidates c ON c.id = a.observer_id
WHERE (coalesce(cardinality($3::bpchar[]), 0) = 0 OR a.iata = ANY($3::bpchar[]))
UNION ALL
SELECT observer_id, observations FROM raw_traffic
) counts
GROUP BY observer_id
), listed AS (
SELECT c.id, c.display_name, c.observer_type, c.iata, c.status, c.radio, c.scopes, coalesce(t.observations, 0)::bigint AS observations
FROM candidates c LEFT JOIN traffic t ON t.observer_id = c.id
WHERE $8::text = '' OR c.observer_type = $8
), paged AS (
SELECT id, display_name, observer_type, iata, status, radio, scopes, observations FROM listed
ORDER BY CASE WHEN $9::text = 'traffic' THEN observations END DESC NULLS LAST,
lower(coalesce(display_name, '')) COLLATE "C", id
LIMIT $11::integer OFFSET $10::bigint
), packed AS (
SELECT coalesce(jsonb_agg(
jsonb_strip_nulls(jsonb_build_object('id', id, 'displayName', display_name, 'observerType', observer_type, 'iata', iata, 'status', status, 'radio', radio, 'scopes', scopes))
|| jsonb_build_object('observationCount', observations)
ORDER BY CASE WHEN $9::text = 'traffic' THEN observations END DESC NULLS LAST,
lower(coalesce(display_name, '')) COLLATE "C", id
), '[]'::jsonb) AS items
FROM paged
), totals AS (
SELECT count(*) AS n, max(observations) AS maximum FROM listed
)
SELECT jsonb_build_object(
'items', p.items,
'generatedAt', (extract(epoch FROM now())*1000)::bigint,
'hasMore', t.n > $10::bigint + $11::integer,
'nextCursor', CASE WHEN t.n > $10::bigint + $11::integer THEN $10::bigint + $11::integer END,
'windowStart', (extract(epoch FROM $1::timestamptz)*1000)::bigint,
'windowEnd', (extract(epoch FROM $2::timestamptz)*1000)::bigint,
'sort', $9::text, 'effectiveSort', $9::text,
'maxObservationCount', coalesce(t.maximum, 0),
'observerTypes', (SELECT coalesce(jsonb_agg(t.observer_type ORDER BY t.observer_type), '[]'::jsonb) FROM (SELECT DISTINCT observer_type FROM candidates WHERE observer_type IS NOT NULL AND observer_type <> '') t),
'coverage', jsonb_build_object('status', CASE WHEN v.complete = v.expected THEN 'complete' WHEN v.complete > 0 OR v.partial > 0 THEN 'partial' ELSE 'unavailable' END,
'expectedHours', v.expected, 'completeHours', v.complete, 'partialHours', v.partial, 'missingHours', v.missing)
)::text AS page
FROM packed p CROSS JOIN coverage v CROSS JOIN totals t
`
type ListObserverDirectoryParams struct {
Column1 pgtype.Timestamptz `json:"column_1"`
Column2 pgtype.Timestamptz `json:"column_2"`
Column3 []string `json:"column_3"`
Column4 string `json:"column_4"`
Column5 string `json:"column_5"`
Column6 string `json:"column_6"`
Column7 string `json:"column_7"`
Column8 string `json:"column_8"`
Column9 string `json:"column_9"`
Column10 int64 `json:"column_10"`
Column11 int32 `json:"column_11"`
Column12 bool `json:"column_12"`
}
// Copyright 2026 Beacon Contributors
// SPDX-License-Identifier: AGPL-3.0-or-later
func (q *Queries) ListObserverDirectory(ctx context.Context, arg ListObserverDirectoryParams) (string, error) {
row := q.db.QueryRow(ctx, listObserverDirectory,
arg.Column1,
arg.Column2,
arg.Column3,
arg.Column4,
arg.Column5,
arg.Column6,
arg.Column7,
arg.Column8,
arg.Column9,
arg.Column10,
arg.Column11,
arg.Column12,
)
var page string
err := row.Scan(&page)
return page, err
}