Skip to content

Commit 482c11a

Browse files
committed
WIP
Signed-off-by: jolheiser <[email protected]>
1 parent cafa2dc commit 482c11a

File tree

7 files changed

+243
-0
lines changed

7 files changed

+243
-0
lines changed

modules/webhook/webhook.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2022 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package webhook
6+
7+
import (
8+
"errors"
9+
"io"
10+
11+
"gopkg.in/yaml.v2"
12+
)
13+
14+
// Webhook is a custom webhook
15+
type Webhook struct {
16+
ID string `yaml:"id"`
17+
HTTP string `yaml:"http"`
18+
Exec []string `yaml:"exec"`
19+
Form []Form `yaml:"form"`
20+
}
21+
22+
// Form is a webhook form
23+
type Form struct {
24+
ID string `yaml:"id"`
25+
Label string `yaml:"label"`
26+
Type string `yaml:"type"`
27+
Required bool `yaml:"required"`
28+
Default string `yaml:"default"`
29+
}
30+
31+
func (w *Webhook) validate() error {
32+
if w.ID == "" {
33+
return errors.New("webhook id is required")
34+
}
35+
if (w.HTTP == "" && len(w.Exec) == 0) || (w.HTTP != "" && len(w.Exec) > 0) {
36+
return errors.New("webhook requires one of exec or http")
37+
}
38+
for _, form := range w.Form {
39+
if form.ID == "" {
40+
return errors.New("form id is required")
41+
}
42+
if form.Label == "" {
43+
return errors.New("form label is required")
44+
}
45+
if form.Type == "" {
46+
return errors.New("form type is required")
47+
}
48+
}
49+
return nil
50+
}
51+
52+
// Parse parses a Webhooks from an io.Reader
53+
func Parse(r io.Reader) (*Webhook, error) {
54+
b, err := io.ReadAll(r)
55+
if err != nil {
56+
return nil, err
57+
}
58+
59+
var w Webhook
60+
if err := yaml.Unmarshal(b, &w); err != nil {
61+
return nil, err
62+
}
63+
64+
if err := w.validate(); err != nil {
65+
return nil, err
66+
}
67+
68+
return &w, nil
69+
}

modules/webhook/webhook_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package webhook
2+
3+
import (
4+
"bytes"
5+
"code.gitea.io/gitea/testdata"
6+
"github.com/stretchr/testify/assert"
7+
"testing"
8+
)
9+
10+
func TestWebhook(t *testing.T) {
11+
tt := []struct {
12+
Name string
13+
File string
14+
Err bool
15+
}{
16+
{
17+
Name: "Executable",
18+
File: "executable.yml",
19+
},
20+
{
21+
Name: "HTTP",
22+
File: "http.yml",
23+
},
24+
{
25+
Name: "Bad",
26+
File: "bad.yml",
27+
Err: true,
28+
},
29+
}
30+
31+
for _, tc := range tt {
32+
t.Run(tc.Name, func(t *testing.T) {
33+
contents, err := testdata.Webhook.ReadFile("webhook/" + tc.File)
34+
assert.NoError(t, err, "expected to read file")
35+
36+
_, err = Parse(bytes.NewReader(contents))
37+
if tc.Err {
38+
assert.Error(t, err, "expected to get an error")
39+
} else {
40+
assert.NoError(t, err, "expected to not get an error")
41+
}
42+
})
43+
}
44+
}

testdata/testdata.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package testdata
2+
3+
import "embed"
4+
5+
var (
6+
//go:embed webhook
7+
Webhook embed.FS
8+
)

testdata/webhook/bad.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Malformed or no data

testdata/webhook/executable.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package webhook
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"os"
8+
)
9+
10+
type Payload struct {
11+
Form map[string]struct {
12+
Label string `json:"label"`
13+
Type string `json:"type"`
14+
Required bool `json:"required"`
15+
Default interface{} `json:"default"`
16+
Value interface{} `json:"value"`
17+
} `json:"form"`
18+
}
19+
20+
func main() {
21+
data, err := io.ReadAll(os.Stdin)
22+
if err != nil {
23+
panic(err)
24+
}
25+
26+
var p Payload
27+
if err := json.Unmarshal(data, &p); err != nil {
28+
panic(err)
29+
}
30+
31+
username, ok := p.Form["username"]
32+
if !ok {
33+
panic("username not found in form data")
34+
}
35+
checkType(username.Type, username.Value)
36+
if username.Value.(string) != "jolheiser" {
37+
panic("username should be jolheiser")
38+
}
39+
40+
favNum, ok := p.Form["favorite-number"]
41+
if !ok {
42+
panic("favorite-number not found in form data")
43+
}
44+
checkType(favNum.Type, favNum.Value)
45+
if username.Value.(float64) != 12 {
46+
panic("favNum should be 12")
47+
}
48+
49+
pineapple, ok := p.Form["pineapple"]
50+
if !ok {
51+
panic("pineapple not found in form data")
52+
}
53+
checkType(pineapple.Type, pineapple.Value)
54+
if pineapple.Value.(bool) {
55+
panic("pineapple should be false")
56+
}
57+
58+
secret, ok := p.Form["secret"]
59+
if !ok {
60+
panic("secret not found in form data")
61+
}
62+
checkType(secret.Type, secret.Value)
63+
if secret.Value.(string) != "sn34ky" {
64+
panic("secret should be sn34ky")
65+
}
66+
}
67+
68+
func checkType(typ string, val interface{}) {
69+
var ok bool
70+
switch typ {
71+
case "text", "secret":
72+
_, ok = val.(string)
73+
case "bool":
74+
_, ok = val.(bool)
75+
case "number":
76+
_, ok = val.(float64)
77+
}
78+
if !ok {
79+
panic(fmt.Sprintf("unexpected type %q for %v", typ, val))
80+
}
81+
}
82+
83+
// override panic
84+
func panic(v interface{}) {
85+
fmt.Println(v)
86+
os.Exit(1)
87+
}

testdata/webhook/executable.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
id: executable
2+
exec: ["go", "run", "executable.go"]
3+
form:
4+
- id: username # Required
5+
label: Username # Required, the label for the input
6+
type: text # Required, the type of input (text, number, bool, secret)
7+
required: true # Optional, defaults to false
8+
- id: favorite-number
9+
label: Favorite Number
10+
type: number
11+
default: 12 # Optional, a default value
12+
- id: pineapple
13+
label: Pineapple on Pizza
14+
type: bool
15+
- id: secret
16+
label: Passphrase
17+
type: secret

testdata/webhook/http.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
id: http
2+
http: "http://localhost:4665"
3+
form:
4+
- id: username # Required
5+
label: Username # Required, the label for the input
6+
type: text # Required, the type of input (text, number, bool, secret)
7+
required: true # Optional, defaults to false
8+
- id: favorite-number
9+
label: Favorite Number
10+
type: number
11+
default: 12 # Optional, a default value
12+
- id: pineapple
13+
label: Pineapple on Pizza
14+
type: bool
15+
- id: secret
16+
label: Passphrase
17+
type: secret

0 commit comments

Comments
 (0)