~james/core

core/spindle/models/webhook.go -rw-r--r-- 5.3 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 14 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package models

import (
	"fmt"
	"slices"
	"strings"
	"time"

	"github.com/bluesky-social/indigo/atproto/syntax"
)

type WebhookEvent string

const (
	WebhookEventPush                   WebhookEvent = "push"
	WebhookEventRepoRenamed            WebhookEvent = "repository:renamed"
	WebhookEventPullRequestCreated     WebhookEvent = "pull_request:created"
	WebhookEventPullRequestResubmitted WebhookEvent = "pull_request:resubmitted"
	WebhookEventPullRequestMerged      WebhookEvent = "pull_request:merged"
	WebhookEventPullRequestClosed      WebhookEvent = "pull_request:closed"
	WebhookEventPullRequestReopened    WebhookEvent = "pull_request:reopened"
)

type Webhook struct {
	Id        int64
	RepoDid   syntax.DID
	Url       string
	Secret    string
	Active    bool
	Events    []string // comma-separated event types
	CreatedAt time.Time
	UpdatedAt time.Time
}

func ParseWebhookEvents(events []string) ([]WebhookEvent, error) {
	known := map[string]WebhookEvent{
		string(WebhookEventPush):                   WebhookEventPush,
		string(WebhookEventRepoRenamed):            WebhookEventRepoRenamed,
		string(WebhookEventPullRequestCreated):     WebhookEventPullRequestCreated,
		string(WebhookEventPullRequestResubmitted): WebhookEventPullRequestResubmitted,
		string(WebhookEventPullRequestMerged):      WebhookEventPullRequestMerged,
		string(WebhookEventPullRequestClosed):      WebhookEventPullRequestClosed,
		string(WebhookEventPullRequestReopened):    WebhookEventPullRequestReopened,
	}
	var parsed []WebhookEvent
	for _, raw := range events {
		event, ok := known[strings.TrimSpace(raw)]
		if !ok {
			return nil, fmt.Errorf("unknown webhook event %q", raw)
		}
		if !slices.Contains(parsed, event) {
			parsed = append(parsed, event)
		}
	}
	return parsed, nil
}

// HasEvent checks if the webhook is subscribed to a specific event
func (w *Webhook) HasEvent(event WebhookEvent) bool {
	return slices.Contains(w.Events, string(event))
}

type WebhookDelivery struct {
	Id           int64
	WebhookId    int64
	Event        string
	DeliveryId   string // UUID for tracking
	Url          string
	RequestBody  string
	ResponseCode int
	ResponseBody string
	Success      bool
	CreatedAt    time.Time
}

// WebhookPayload represents the webhook payload structure
type WebhookPayload struct {
	Ref        string            `json:"ref"`
	Before     string            `json:"before"`
	After      string            `json:"after"`
	Repository WebhookRepository `json:"repository"`
	Pusher     WebhookUser       `json:"pusher"`
}

// WebhookRepository represents repository information in webhook payload.
//
// Note: spindle stores less repo metadata than the appview did, so
// Description, Website, StarsCount, OpenIssues and Fork are best-effort and
// may be empty/false when spindle has no record of them.
type WebhookRepository struct {
	Name        string      `json:"name"`
	FullName    string      `json:"full_name"`
	Description string      `json:"description"`
	Fork        bool        `json:"fork"`
	HtmlUrl     string      `json:"html_url"`
	CloneUrl    string      `json:"clone_url"`
	SshUrl      string      `json:"ssh_url"`
	Website     string      `json:"website,omitempty"`
	StarsCount  int         `json:"stars_count,omitempty"`
	OpenIssues  int         `json:"open_issues_count,omitempty"`
	CreatedAt   string      `json:"created_at"`
	UpdatedAt   string      `json:"updated_at"`
	Owner       WebhookUser `json:"owner"`
}

// WebhookUser represents user information in webhook payload
type WebhookUser struct {
	Did string `json:"did"`
}

// WebhookRenamePayload represents the payload for a repository:renamed event
type WebhookRenamePayload struct {
	OldName    string            `json:"old_name"`
	NewName    string            `json:"new_name"`
	Repository WebhookRepository `json:"repository"`
	Sender     WebhookUser       `json:"sender"`
}

// WebhookPullRequestPayload represents the payload for pull_request:* events
type WebhookPullRequestPayload struct {
	Action      string             `json:"action"`
	PullRequest WebhookPullRequest `json:"pull_request"`
	Repository  WebhookRepository  `json:"repository"`
	Sender      WebhookUser        `json:"sender"`
}

// WebhookPullRequest represents pull request information in webhook payload.
//
// knot2 pulls have no number, so Number stays 0, and spindle doesn't know the
// appview's url, so HtmlUrl and PatchUrl stay empty
type WebhookPullRequest struct {
	Number       int                       `json:"number"`
	Title        string                    `json:"title"`
	Body         string                    `json:"body"`
	State        string                    `json:"state"`
	TargetBranch string                    `json:"target_branch"`
	Source       *WebhookPullRequestSource `json:"source,omitempty"`
	RoundNumber  int                       `json:"round_number"`
	Owner        WebhookUser               `json:"owner"`
	HtmlUrl      string                    `json:"html_url,omitempty"`
	PatchUrl     string                    `json:"patch_url,omitempty"`
	CreatedAt    string                    `json:"created_at"`
}

// WebhookPullRequestSource represents the source of a branch- or fork-based
// pull request; absent for patch-based pull requests
type WebhookPullRequestSource struct {
	Branch string `json:"branch"`
	Repo   string `json:"repo,omitempty"`
	Sha    string `json:"sha,omitempty"`
}