1// Copyright 2014 The Gogs Authors. All rights reserved.
2// Copyright 2017 The Gitea Authors. All rights reserved.
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file.
5
6package structs
7
8import (
9	"errors"
10	"strings"
11	"time"
12
13	"code.gitea.io/gitea/modules/json"
14)
15
16var (
17	// ErrInvalidReceiveHook FIXME
18	ErrInvalidReceiveHook = errors.New("Invalid JSON payload received over webhook")
19)
20
21// Hook a hook is a web hook when one repository changed
22type Hook struct {
23	ID     int64             `json:"id"`
24	Type   string            `json:"type"`
25	URL    string            `json:"-"`
26	Config map[string]string `json:"config"`
27	Events []string          `json:"events"`
28	Active bool              `json:"active"`
29	// swagger:strfmt date-time
30	Updated time.Time `json:"updated_at"`
31	// swagger:strfmt date-time
32	Created time.Time `json:"created_at"`
33}
34
35// HookList represents a list of API hook.
36type HookList []*Hook
37
38// CreateHookOptionConfig has all config options in it
39// required are "content_type" and "url" Required
40type CreateHookOptionConfig map[string]string
41
42// CreateHookOption options when create a hook
43type CreateHookOption struct {
44	// required: true
45	// enum: dingtalk,discord,gitea,gogs,msteams,slack,telegram,feishu,wechatwork
46	Type string `json:"type" binding:"Required"`
47	// required: true
48	Config       CreateHookOptionConfig `json:"config" binding:"Required"`
49	Events       []string               `json:"events"`
50	BranchFilter string                 `json:"branch_filter" binding:"GlobPattern"`
51	// default: false
52	Active bool `json:"active"`
53}
54
55// EditHookOption options when modify one hook
56type EditHookOption struct {
57	Config       map[string]string `json:"config"`
58	Events       []string          `json:"events"`
59	BranchFilter string            `json:"branch_filter" binding:"GlobPattern"`
60	Active       *bool             `json:"active"`
61}
62
63// Payloader payload is some part of one hook
64type Payloader interface {
65	JSONPayload() ([]byte, error)
66}
67
68// PayloadUser represents the author or committer of a commit
69type PayloadUser struct {
70	// Full name of the commit author
71	Name string `json:"name"`
72	// swagger:strfmt email
73	Email    string `json:"email"`
74	UserName string `json:"username"`
75}
76
77// FIXME: consider using same format as API when commits API are added.
78//        applies to PayloadCommit and PayloadCommitVerification
79
80// PayloadCommit represents a commit
81type PayloadCommit struct {
82	// sha1 hash of the commit
83	ID           string                     `json:"id"`
84	Message      string                     `json:"message"`
85	URL          string                     `json:"url"`
86	Author       *PayloadUser               `json:"author"`
87	Committer    *PayloadUser               `json:"committer"`
88	Verification *PayloadCommitVerification `json:"verification"`
89	// swagger:strfmt date-time
90	Timestamp time.Time `json:"timestamp"`
91	Added     []string  `json:"added"`
92	Removed   []string  `json:"removed"`
93	Modified  []string  `json:"modified"`
94}
95
96// PayloadCommitVerification represents the GPG verification of a commit
97type PayloadCommitVerification struct {
98	Verified  bool         `json:"verified"`
99	Reason    string       `json:"reason"`
100	Signature string       `json:"signature"`
101	Signer    *PayloadUser `json:"signer"`
102	Payload   string       `json:"payload"`
103}
104
105var (
106	_ Payloader = &CreatePayload{}
107	_ Payloader = &DeletePayload{}
108	_ Payloader = &ForkPayload{}
109	_ Payloader = &PushPayload{}
110	_ Payloader = &IssuePayload{}
111	_ Payloader = &IssueCommentPayload{}
112	_ Payloader = &PullRequestPayload{}
113	_ Payloader = &RepositoryPayload{}
114	_ Payloader = &ReleasePayload{}
115)
116
117// _________                        __
118// \_   ___ \_______   ____ _____ _/  |_  ____
119// /    \  \/\_  __ \_/ __ \\__  \\   __\/ __ \
120// \     \____|  | \/\  ___/ / __ \|  | \  ___/
121//  \______  /|__|    \___  >____  /__|  \___  >
122//         \/             \/     \/          \/
123
124// CreatePayload FIXME
125type CreatePayload struct {
126	Sha     string      `json:"sha"`
127	Ref     string      `json:"ref"`
128	RefType string      `json:"ref_type"`
129	Repo    *Repository `json:"repository"`
130	Sender  *User       `json:"sender"`
131}
132
133// JSONPayload return payload information
134func (p *CreatePayload) JSONPayload() ([]byte, error) {
135	return json.MarshalIndent(p, "", "  ")
136}
137
138// ParseCreateHook parses create event hook content.
139func ParseCreateHook(raw []byte) (*CreatePayload, error) {
140	hook := new(CreatePayload)
141	if err := json.Unmarshal(raw, hook); err != nil {
142		return nil, err
143	}
144
145	// it is possible the JSON was parsed, however,
146	// was not from Gogs (maybe was from Bitbucket)
147	// So we'll check to be sure certain key fields
148	// were populated
149	switch {
150	case hook.Repo == nil:
151		return nil, ErrInvalidReceiveHook
152	case len(hook.Ref) == 0:
153		return nil, ErrInvalidReceiveHook
154	}
155	return hook, nil
156}
157
158// ________         .__          __
159// \______ \   ____ |  |   _____/  |_  ____
160//  |    |  \_/ __ \|  | _/ __ \   __\/ __ \
161//  |    `   \  ___/|  |_\  ___/|  | \  ___/
162// /_______  /\___  >____/\___  >__|  \___  >
163//         \/     \/          \/          \/
164
165// PusherType define the type to push
166type PusherType string
167
168// describe all the PusherTypes
169const (
170	PusherTypeUser PusherType = "user"
171)
172
173// DeletePayload represents delete payload
174type DeletePayload struct {
175	Ref        string      `json:"ref"`
176	RefType    string      `json:"ref_type"`
177	PusherType PusherType  `json:"pusher_type"`
178	Repo       *Repository `json:"repository"`
179	Sender     *User       `json:"sender"`
180}
181
182// JSONPayload implements Payload
183func (p *DeletePayload) JSONPayload() ([]byte, error) {
184	return json.MarshalIndent(p, "", "  ")
185}
186
187// ___________           __
188// \_   _____/__________|  | __
189//  |    __)/  _ \_  __ \  |/ /
190//  |     \(  <_> )  | \/    <
191//  \___  / \____/|__|  |__|_ \
192//      \/                   \/
193
194// ForkPayload represents fork payload
195type ForkPayload struct {
196	Forkee *Repository `json:"forkee"`
197	Repo   *Repository `json:"repository"`
198	Sender *User       `json:"sender"`
199}
200
201// JSONPayload implements Payload
202func (p *ForkPayload) JSONPayload() ([]byte, error) {
203	return json.MarshalIndent(p, "", "  ")
204}
205
206// HookIssueCommentAction defines hook issue comment action
207type HookIssueCommentAction string
208
209// all issue comment actions
210const (
211	HookIssueCommentCreated HookIssueCommentAction = "created"
212	HookIssueCommentEdited  HookIssueCommentAction = "edited"
213	HookIssueCommentDeleted HookIssueCommentAction = "deleted"
214)
215
216// IssueCommentPayload represents a payload information of issue comment event.
217type IssueCommentPayload struct {
218	Action     HookIssueCommentAction `json:"action"`
219	Issue      *Issue                 `json:"issue"`
220	Comment    *Comment               `json:"comment"`
221	Changes    *ChangesPayload        `json:"changes,omitempty"`
222	Repository *Repository            `json:"repository"`
223	Sender     *User                  `json:"sender"`
224	IsPull     bool                   `json:"is_pull"`
225}
226
227// JSONPayload implements Payload
228func (p *IssueCommentPayload) JSONPayload() ([]byte, error) {
229	return json.MarshalIndent(p, "", "  ")
230}
231
232// __________       .__
233// \______   \ ____ |  |   ____ _____    ______ ____
234//  |       _// __ \|  | _/ __ \\__  \  /  ___// __ \
235//  |    |   \  ___/|  |_\  ___/ / __ \_\___ \\  ___/
236//  |____|_  /\___  >____/\___  >____  /____  >\___  >
237//         \/     \/          \/     \/     \/     \/
238
239// HookReleaseAction defines hook release action type
240type HookReleaseAction string
241
242// all release actions
243const (
244	HookReleasePublished HookReleaseAction = "published"
245	HookReleaseUpdated   HookReleaseAction = "updated"
246	HookReleaseDeleted   HookReleaseAction = "deleted"
247)
248
249// ReleasePayload represents a payload information of release event.
250type ReleasePayload struct {
251	Action     HookReleaseAction `json:"action"`
252	Release    *Release          `json:"release"`
253	Repository *Repository       `json:"repository"`
254	Sender     *User             `json:"sender"`
255}
256
257// JSONPayload implements Payload
258func (p *ReleasePayload) JSONPayload() ([]byte, error) {
259	return json.MarshalIndent(p, "", "  ")
260}
261
262// __________             .__
263// \______   \__ __  _____|  |__
264//  |     ___/  |  \/  ___/  |  \
265//  |    |   |  |  /\___ \|   Y  \
266//  |____|   |____//____  >___|  /
267//                      \/     \/
268
269// PushPayload represents a payload information of push event.
270type PushPayload struct {
271	Ref        string           `json:"ref"`
272	Before     string           `json:"before"`
273	After      string           `json:"after"`
274	CompareURL string           `json:"compare_url"`
275	Commits    []*PayloadCommit `json:"commits"`
276	HeadCommit *PayloadCommit   `json:"head_commit"`
277	Repo       *Repository      `json:"repository"`
278	Pusher     *User            `json:"pusher"`
279	Sender     *User            `json:"sender"`
280}
281
282// JSONPayload FIXME
283func (p *PushPayload) JSONPayload() ([]byte, error) {
284	return json.MarshalIndent(p, "", "  ")
285}
286
287// ParsePushHook parses push event hook content.
288func ParsePushHook(raw []byte) (*PushPayload, error) {
289	hook := new(PushPayload)
290	if err := json.Unmarshal(raw, hook); err != nil {
291		return nil, err
292	}
293
294	switch {
295	case hook.Repo == nil:
296		return nil, ErrInvalidReceiveHook
297	case len(hook.Ref) == 0:
298		return nil, ErrInvalidReceiveHook
299	}
300	return hook, nil
301}
302
303// Branch returns branch name from a payload
304func (p *PushPayload) Branch() string {
305	return strings.ReplaceAll(p.Ref, "refs/heads/", "")
306}
307
308// .___
309// |   | ______ ________ __   ____
310// |   |/  ___//  ___/  |  \_/ __ \
311// |   |\___ \ \___ \|  |  /\  ___/
312// |___/____  >____  >____/  \___  >
313//          \/     \/            \/
314
315// HookIssueAction FIXME
316type HookIssueAction string
317
318const (
319	// HookIssueOpened opened
320	HookIssueOpened HookIssueAction = "opened"
321	// HookIssueClosed closed
322	HookIssueClosed HookIssueAction = "closed"
323	// HookIssueReOpened reopened
324	HookIssueReOpened HookIssueAction = "reopened"
325	// HookIssueEdited edited
326	HookIssueEdited HookIssueAction = "edited"
327	// HookIssueAssigned assigned
328	HookIssueAssigned HookIssueAction = "assigned"
329	// HookIssueUnassigned unassigned
330	HookIssueUnassigned HookIssueAction = "unassigned"
331	// HookIssueLabelUpdated label_updated
332	HookIssueLabelUpdated HookIssueAction = "label_updated"
333	// HookIssueLabelCleared label_cleared
334	HookIssueLabelCleared HookIssueAction = "label_cleared"
335	// HookIssueSynchronized synchronized
336	HookIssueSynchronized HookIssueAction = "synchronized"
337	// HookIssueMilestoned is an issue action for when a milestone is set on an issue.
338	HookIssueMilestoned HookIssueAction = "milestoned"
339	// HookIssueDemilestoned is an issue action for when a milestone is cleared on an issue.
340	HookIssueDemilestoned HookIssueAction = "demilestoned"
341	// HookIssueReviewed is an issue action for when a pull request is reviewed
342	HookIssueReviewed HookIssueAction = "reviewed"
343)
344
345// IssuePayload represents the payload information that is sent along with an issue event.
346type IssuePayload struct {
347	Action     HookIssueAction `json:"action"`
348	Index      int64           `json:"number"`
349	Changes    *ChangesPayload `json:"changes,omitempty"`
350	Issue      *Issue          `json:"issue"`
351	Repository *Repository     `json:"repository"`
352	Sender     *User           `json:"sender"`
353}
354
355// JSONPayload encodes the IssuePayload to JSON, with an indentation of two spaces.
356func (p *IssuePayload) JSONPayload() ([]byte, error) {
357	return json.MarshalIndent(p, "", "  ")
358}
359
360// ChangesFromPayload FIXME
361type ChangesFromPayload struct {
362	From string `json:"from"`
363}
364
365// ChangesPayload represents the payload information of issue change
366type ChangesPayload struct {
367	Title *ChangesFromPayload `json:"title,omitempty"`
368	Body  *ChangesFromPayload `json:"body,omitempty"`
369	Ref   *ChangesFromPayload `json:"ref,omitempty"`
370}
371
372// __________      .__  .__    __________                                     __
373// \______   \__ __|  | |  |   \______   \ ____  ________ __   ____   _______/  |_
374//  |     ___/  |  \  | |  |    |       _// __ \/ ____/  |  \_/ __ \ /  ___/\   __\
375//  |    |   |  |  /  |_|  |__  |    |   \  ___< <_|  |  |  /\  ___/ \___ \  |  |
376//  |____|   |____/|____/____/  |____|_  /\___  >__   |____/  \___  >____  > |__|
377//                                     \/     \/   |__|           \/     \/
378
379// PullRequestPayload represents a payload information of pull request event.
380type PullRequestPayload struct {
381	Action      HookIssueAction `json:"action"`
382	Index       int64           `json:"number"`
383	Changes     *ChangesPayload `json:"changes,omitempty"`
384	PullRequest *PullRequest    `json:"pull_request"`
385	Repository  *Repository     `json:"repository"`
386	Sender      *User           `json:"sender"`
387	Review      *ReviewPayload  `json:"review"`
388}
389
390// JSONPayload FIXME
391func (p *PullRequestPayload) JSONPayload() ([]byte, error) {
392	return json.MarshalIndent(p, "", "  ")
393}
394
395// ReviewPayload FIXME
396type ReviewPayload struct {
397	Type    string `json:"type"`
398	Content string `json:"content"`
399}
400
401//__________                           .__  __
402//\______   \ ____ ______   ____  _____|__|/  |_  ___________ ___.__.
403// |       _// __ \\____ \ /  _ \/  ___/  \   __\/  _ \_  __ <   |  |
404// |    |   \  ___/|  |_> >  <_> )___ \|  ||  | (  <_> )  | \/\___  |
405// |____|_  /\___  >   __/ \____/____  >__||__|  \____/|__|   / ____|
406//        \/     \/|__|              \/                       \/
407
408// HookRepoAction an action that happens to a repo
409type HookRepoAction string
410
411const (
412	// HookRepoCreated created
413	HookRepoCreated HookRepoAction = "created"
414	// HookRepoDeleted deleted
415	HookRepoDeleted HookRepoAction = "deleted"
416)
417
418// RepositoryPayload payload for repository webhooks
419type RepositoryPayload struct {
420	Action       HookRepoAction `json:"action"`
421	Repository   *Repository    `json:"repository"`
422	Organization *User          `json:"organization"`
423	Sender       *User          `json:"sender"`
424}
425
426// JSONPayload JSON representation of the payload
427func (p *RepositoryPayload) JSONPayload() ([]byte, error) {
428	return json.MarshalIndent(p, "", " ")
429}
430