Skip to content

Commit e08f099

Browse files
committed
fix: restore yaml parser
Signed-off-by: Thomas Bétrancourt <thomas@betrancourt.net>
1 parent 34082a3 commit e08f099

File tree

6 files changed

+343
-166
lines changed

6 files changed

+343
-166
lines changed

api/api_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,61 @@ func TestPasswordInput(t *testing.T) {
290290
tester.Run()
291291
}
292292

293+
func TestResolutionEdit(t *testing.T) {
294+
tester := iffy.NewTester(t, hdl)
295+
296+
dbp, err := zesty.NewDBProvider(utask.DBName)
297+
if err != nil {
298+
t.Fatal(err)
299+
}
300+
301+
tmpl := dummyTemplate()
302+
303+
_, err = tasktemplate.LoadFromName(dbp, tmpl.Name)
304+
if err != nil {
305+
if !errors.IsNotFound(err) {
306+
t.Fatal(err)
307+
}
308+
if err := dbp.DB().Insert(&tmpl); err != nil {
309+
t.Fatal(err)
310+
}
311+
}
312+
313+
tester.AddCall("getTemplate", http.MethodGet, "/template/"+tmpl.Name, "").
314+
Headers(regularHeaders).
315+
Checkers(
316+
iffy.ExpectStatus(200),
317+
)
318+
319+
tester.AddCall("newTask", http.MethodPost, "/task", `{"template_name":"{{.getTemplate.name}}","input":{"id":"foo"}}`).
320+
Headers(regularHeaders).
321+
Checkers(iffy.ExpectStatus(201))
322+
323+
tester.AddCall("jsonUpdate", http.MethodPut, "/task/{{.newTask.id}}", `{"input":{"id":"bar-json"}}`).
324+
Headers(regularHeaders).
325+
Checkers(iffy.ExpectStatus(200))
326+
327+
tester.AddCall("getAfterJson", http.MethodGet, "/task/{{.newTask.id}}", "").
328+
Headers(adminHeaders).
329+
Checkers(
330+
iffy.ExpectStatus(200),
331+
iffy.ExpectJSONBranch("input", "id", "bar-json"),
332+
)
333+
334+
tester.AddCall("yamlUpdate", http.MethodPut, "/task/{{.newTask.id}}", "input:\n id: bar-yaml").
335+
Headers(regularHeaders).
336+
Checkers(iffy.ExpectStatus(200))
337+
338+
tester.AddCall("getAfterYaml", http.MethodGet, "/task/{{.newTask.id}}", "").
339+
Headers(adminHeaders).
340+
Checkers(
341+
iffy.ExpectStatus(200),
342+
iffy.ExpectJSONBranch("input", "id", "bar-yaml"),
343+
)
344+
345+
tester.Run()
346+
}
347+
293348
func TestStartOver(t *testing.T) {
294349
tester := iffy.NewTester(t, hdl)
295350

api/bind.go

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,94 @@
11
package api
22

33
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"io/ioutil"
8+
"net/http"
49
"reflect"
510

11+
"github.com/ghodss/yaml"
612
"github.com/gin-gonic/gin"
7-
"github.com/loopfz/gadgeto/tonic"
813
)
914

10-
// bodyBindHook is a wrapper around the default binding hook of tonic.
15+
const (
16+
// default max body bytes: 256KB
17+
// this can be overridden via configuration
18+
defaultMaxBodyBytes = 256 * 1024
19+
20+
// absolute upper limit for configuration max body bytes: 10MB
21+
upperLimitMaxBodyBytes = 10 * 1024 * 1024
22+
23+
// absolute lower limit for configuration max body bytes: 1KB
24+
lowerLimitMaxBodyBytes = 1024
25+
)
26+
27+
var yamlBind = yamlBinding{}
28+
29+
type yamlBinding struct{}
30+
31+
func (yamlBinding) Name() string { return "yamlBinding" }
32+
func (yamlBinding) Bind(req *http.Request, obj interface{}) error {
33+
bodyBytes, err := ioutil.ReadAll(req.Body)
34+
if err != nil {
35+
return err
36+
}
37+
defer req.Body.Close()
38+
return yaml.Unmarshal(bodyBytes, obj, jsonNumberOpt)
39+
}
40+
41+
// defaultBindingHook is a wrapper around the yaml binding.
1142
// It adds the possibility to bind a specific field in an object rather than
1243
// unconditionally binding the whole object.
13-
func bodyBindHook(c *gin.Context, v interface{}) error {
14-
val := reflect.ValueOf(v)
15-
typ := reflect.TypeOf(v).Elem()
16-
17-
for i := 0; i < typ.NumField(); i++ {
18-
ft := typ.Field(i)
19-
if _, ok := ft.Tag.Lookup("body"); !ok {
20-
continue
21-
}
22-
flt := ft.Type
23-
var fv reflect.Value
24-
if flt.Kind() == reflect.Map {
25-
fv = reflect.New(flt)
26-
} else {
27-
fv = reflect.New(flt.Elem())
44+
func defaultBindingHook(maxBodyBytes int64) func(*gin.Context, interface{}) error {
45+
if maxBodyBytes == 0 {
46+
maxBodyBytes = defaultMaxBodyBytes
47+
} else if maxBodyBytes > upperLimitMaxBodyBytes {
48+
maxBodyBytes = upperLimitMaxBodyBytes
49+
} else if maxBodyBytes < lowerLimitMaxBodyBytes {
50+
maxBodyBytes = lowerLimitMaxBodyBytes
51+
}
52+
53+
return func(c *gin.Context, v interface{}) error {
54+
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, maxBodyBytes)
55+
if c.Request.ContentLength == 0 || c.Request.Method == http.MethodGet {
56+
return nil
2857
}
29-
if err := tonic.DefaultBindingHook(c, fv.Interface()); err != nil {
30-
return err
58+
59+
val := reflect.ValueOf(v)
60+
typ := reflect.TypeOf(v).Elem()
61+
62+
for i := 0; i < typ.NumField(); i++ {
63+
ft := typ.Field(i)
64+
if _, ok := ft.Tag.Lookup("body"); !ok {
65+
continue
66+
}
67+
flt := ft.Type
68+
var fv reflect.Value
69+
if flt.Kind() == reflect.Map {
70+
fv = reflect.New(flt)
71+
} else {
72+
fv = reflect.New(flt.Elem())
73+
}
74+
if err := c.ShouldBindWith(fv.Interface(), yamlBind); err != nil && err != io.EOF {
75+
return fmt.Errorf("error parsing request body: %s", err.Error())
76+
}
77+
if flt.Kind() == reflect.Map {
78+
val.Elem().Field(i).Set(fv.Elem())
79+
} else {
80+
val.Elem().Field(i).Set(fv)
81+
}
3182
}
32-
if flt.Kind() == reflect.Map {
33-
val.Elem().Field(i).Set(fv.Elem())
34-
} else {
35-
val.Elem().Field(i).Set(fv)
83+
84+
if err := c.ShouldBindWith(v, yamlBind); err != nil && err != io.EOF {
85+
return fmt.Errorf("error parsing request body: %s", err.Error())
3686
}
87+
return nil
3788
}
89+
}
3890

39-
return tonic.DefaultBindingHook(c, v)
91+
func jsonNumberOpt(dec *json.Decoder) *json.Decoder {
92+
dec.UseNumber()
93+
return dec
4094
}

api/server.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,7 @@ func (s *Server) build(ctx context.Context) {
228228
router.Use(ajaxHeadersMiddleware, auditLogsMiddleware)
229229

230230
tonic.SetErrorHook(jujerr.ErrHook)
231-
tonic.SetBindHook(yamlBindHook(s.maxBodyBytes))
232-
tonic.SetBindHook(bodyBindHook)
231+
tonic.SetBindHook(defaultBindingHook(s.maxBodyBytes))
233232
tonic.SetRenderHook(yamljsonRenderHook, "application/json")
234233

235234
authRoutes := router.Group("/", "x-misc", "Misc authenticated routes", s.authMiddleware)

api/yamlbinding.go

Lines changed: 0 additions & 63 deletions
This file was deleted.

pkg/plugins/builtin/callback/callback.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ func exec(stepName string, config interface{}, ctx interface{}) (interface{}, in
8080
return map[string]interface{}{
8181
"id": cb.PublicID,
8282
"url": buildUrl(cb),
83+
"token": cb.Secret,
8384
"schema": cb.Schema,
8485
}, nil, nil
8586

0 commit comments

Comments
 (0)