check / check (push) Waiting to run
Replaces #559, which conflicted. Model: opus-5-5
112 lines
2.8 KiB
Go
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
|
|
}
|