Files
webhooker/internal/handlers/entrypoint_view.go
T
sneak 537374e4cc
check / check (push) Waiting to run
Deploy main eacf1e7 to prod
Replaces #559, which conflicted.

Model: opus-5-5
2026-10-25 01:55:36 +00:00

112 lines
2.8 KiB
Go

package handlers
import (
"fmt"
"time"
"github.com/dustin/go-humanize"
"gorm.io/gorm"
"sneak.berlin/go/webhooker/internal/database"
)
// EntrypointView is the display-safe projection of an entrypoint for
// the UI, in the same way delivery.TargetView is one for a target.
type EntrypointView struct {
ID string
Path string
Description string
Active bool
// Events is how many events arrived on the entrypoint's URL within
// the webhook's retention period. LastEvent is when the newest
// event ever to arrive on it did, relative, and LastEventUTC the
// full time; both are empty when none ever did.
Events int64
LastEvent string
LastEventUTC string
}
// entrypointEvents is one entrypoint's count read by
// addEntrypointEvents.
type entrypointEvents struct {
EntrypointID string
Events int64
}
// NewEntrypointViews projects entrypoints for rendering.
func NewEntrypointViews(
entrypoints []database.Entrypoint,
) []EntrypointView {
views := make([]EntrypointView, 0, len(entrypoints))
for i := range entrypoints {
e := &entrypoints[i]
views = append(views, EntrypointView{
ID: e.ID,
Path: e.Path,
Description: e.Description,
Active: e.Active,
})
}
return views
}
// addEntrypointEvents fills in each view's event figures from the
// webhook's event database: when the last event arrived on its URL,
// from its EntrypointTotals row, and how many events arrived on it
// since the webhook's retention cutoff, counted in one query over the
// events' entrypoint_id index. Resubmitted copies did not arrive on
// the URL and are left out of both.
func addEntrypointEvents(
webhookDB *gorm.DB,
webhook *database.Webhook,
views []EntrypointView,
now time.Time,
) error {
ids := make([]string, len(views))
byID := make(map[string]*EntrypointView, len(views))
for i := range views {
ids[i] = views[i].ID
byID[views[i].ID] = &views[i]
}
var totals []database.EntrypointTotals
err := webhookDB.Where("entrypoint_id IN ?", ids).Find(&totals).Error
if err != nil {
return fmt.Errorf("reading entrypoint totals: %w", err)
}
query := webhookDB.Model(&database.Event{}).
Select("entrypoint_id, count(*) AS events").
Where("entrypoint_id IN ? AND resubmitted_from_id IS NULL", ids)
cutoff, finite := webhook.RetentionCutoff(now)
if finite {
query = query.Where("created_at >= ?", cutoff)
}
var counts []entrypointEvents
err = query.Group("entrypoint_id").Find(&counts).Error
if err != nil {
return fmt.Errorf("counting events by entrypoint: %w", err)
}
for _, row := range totals {
view := byID[row.EntrypointID]
view.LastEvent = humanize.Time(row.LastEventAt)
view.LastEventUTC =
row.LastEventAt.UTC().Format(time.DateTime) + " UTC"
}
for _, row := range counts {
byID[row.EntrypointID].Events = row.Events
}
return nil
}