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 }