Luigit
repositories / bugabinga.net

bugabinga.net

personal infrastructure for bugabinga!

owned by admin

services/luci/internal/trigger/trigger.go

Raw
package trigger

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"
)

type Event interface {
	Validate() error
	EventKind() string
}

type PushEvent struct {
	Kind string `json:"kind"`
	Repo string `json:"repo"`
	Old  string `json:"old"`
	New  string `json:"new"`
	Ref  string `json:"ref"`
}

func (e PushEvent) EventKind() string { return "push" }

func (e PushEvent) Validate() error {
	if e.Kind != "push" {
		return fmt.Errorf("push event kind must be push")
	}
	if strings.TrimSpace(e.Repo) == "" || strings.TrimSpace(e.New) == "" || strings.TrimSpace(e.Ref) == "" {
		return fmt.Errorf("push event requires repo, new, and ref")
	}
	return nil
}

type ScheduleEvent struct {
	Kind   string `json:"kind"`
	Repo   string `json:"repo"`
	Job    string `json:"job"`
	Rev    string `json:"rev"`
	Ref    string `json:"ref"`
	Minute string `json:"minute"`
}

func (e ScheduleEvent) EventKind() string { return "schedule" }

func (e ScheduleEvent) Validate() error {
	if e.Kind != "schedule" {
		return fmt.Errorf("schedule event kind must be schedule")
	}
	if strings.TrimSpace(e.Repo) == "" || strings.TrimSpace(e.Job) == "" || strings.TrimSpace(e.Rev) == "" || strings.TrimSpace(e.Ref) == "" {
		return fmt.Errorf("schedule event requires repo, job, revision, and ref")
	}
	if _, err := time.Parse("2006-01-02T15:04Z", e.Minute); err != nil {
		return fmt.Errorf("schedule event has invalid minute %q", e.Minute)
	}
	return nil
}

type ManualEvent struct {
	Kind        string  `json:"kind"`
	Repo        string  `json:"repo"`
	Job         string  `json:"job"`
	Ref         *string `json:"ref"`
	Rev         *string `json:"rev"`
	RequestedBy string  `json:"requested_by"`
	ResolvedRev string  `json:"resolved_rev,omitempty"`
}

func (e ManualEvent) EventKind() string { return "manual" }

func (e ManualEvent) Validate() error {
	if e.Kind != "manual" {
		return fmt.Errorf("manual event kind must be manual")
	}
	if strings.TrimSpace(e.Repo) == "" || strings.TrimSpace(e.Job) == "" {
		return fmt.Errorf("manual event requires repo and job")
	}
	hasRef := e.Ref != nil && strings.TrimSpace(*e.Ref) != ""
	hasRev := e.Rev != nil && strings.TrimSpace(*e.Rev) != ""
	if hasRef == hasRev {
		return fmt.Errorf("manual event requires exactly one of ref or rev")
	}
	return nil
}

func Decode(data []byte) (Event, error) {
	var header struct {
		Kind string `json:"kind"`
	}
	if err := json.Unmarshal(data, &header); err != nil {
		return nil, err
	}

	switch header.Kind {
	case "push":
		var event PushEvent
		if err := json.Unmarshal(data, &event); err != nil {
			return nil, err
		}
		return event, event.Validate()
	case "manual":
		var event ManualEvent
		if err := json.Unmarshal(data, &event); err != nil {
			return nil, err
		}
		return event, event.Validate()
	case "schedule":
		var event ScheduleEvent
		if err := json.Unmarshal(data, &event); err != nil {
			return nil, err
		}
		return event, event.Validate()
	default:
		return nil, fmt.Errorf("unknown trigger kind %q", header.Kind)
	}
}