Skip to content

Commit

Permalink
feat: change jsonschema library, better error messages 2020 draft (#127)
Browse files Browse the repository at this point in the history
* feat: change jsonschema library, better error messages 2020 draft

* fix test
  • Loading branch information
jaredallard authored Aug 10, 2022
1 parent 19c8c8d commit fa803f7
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 31 deletions.
6 changes: 2 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ require (
github.com/davecgh/go-spew v1.1.1
github.com/google/go-cmp v0.5.8
github.com/google/go-github/v43 v43.0.0
github.com/xeipuuv/gojsonschema v1.2.0
github.com/santhosh-tekuri/jsonschema/v5 v5.0.0
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
gopkg.in/yaml.v3 v3.0.1
)
Expand Down Expand Up @@ -102,8 +102,6 @@ require (
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xanzy/ssh-agent v0.3.1 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yuin/goldmark v1.4.4 // indirect
github.com/yuin/goldmark-emoji v1.0.1 // indirect
github.com/zalando/go-keyring v0.2.1 // indirect
Expand All @@ -113,7 +111,7 @@ require (
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea // indirect
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368 // indirect
google.golang.org/grpc v1.42.0 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/alexcesaro/statsd.v2 v2.0.0 // indirect
Expand Down
19 changes: 5 additions & 14 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 47 additions & 11 deletions internal/codegen/tpl_stencil.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package codegen
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"os"
Expand All @@ -16,10 +17,11 @@ import (

"github.com/davecgh/go-spew/spew"
"github.com/getoutreach/stencil/internal/dotnotation"
"github.com/getoutreach/stencil/pkg/configuration"
"github.com/go-git/go-billy/v5/osfs"
"github.com/pkg/errors"
"github.com/santhosh-tekuri/jsonschema/v5"
"github.com/sirupsen/logrus"
"github.com/xeipuuv/gojsonschema"
)

// TplStencil contains the global functions available to a template for
Expand Down Expand Up @@ -167,7 +169,7 @@ func (s *TplStencil) Arg(pth string) (interface{}, error) {
v = []interface{}{}
case "boolean", "bool":
v = false
case "integer", "int":
case "integer", "int", "number":
v = 0
case "string":
v = ""
Expand All @@ -178,20 +180,54 @@ func (s *TplStencil) Arg(pth string) (interface{}, error) {

// validate the data
if arg.Schema != nil {
schema := gojsonschema.NewGoLoader(arg.Schema)
document := gojsonschema.NewGoLoader(v)
result, err := gojsonschema.Validate(schema, document)
if err != nil {
return nil, errors.Wrapf(err, "module %q argument %q validation failed", s.t.Module.Name, pth)
if err := s.validateArg(pth, &arg, v); err != nil {
return nil, err
}
}

return v, nil
}

// validateArg validates an argument against the schema
func (s *TplStencil) validateArg(pth string, arg *configuration.Argument, v interface{}) error {
schemaBuf := new(bytes.Buffer)
if err := json.NewEncoder(schemaBuf).Encode(arg.Schema); err != nil {
return errors.Wrap(err, "failed to encode schema into JSON")
}

jsc := jsonschema.NewCompiler()
jsc.Draft = jsonschema.Draft2020

schemaURL := "manifest.yaml/arguments/" + pth
if err := jsc.AddResource(schemaURL, schemaBuf); err != nil {
return errors.Wrapf(err, "failed to add argument '%s' json schema to compiler", pth)
}

schema, err := jsc.Compile(schemaURL)
if err != nil {
return errors.Wrapf(err, "failed to compile argument '%s' schema", pth)
}

// return the invalid response
if !result.Valid() {
return nil, fmt.Errorf("module %q argument %q validation failed: %s", s.t.Module.Name, pth, result.Errors())
if err := schema.Validate(v); err != nil {
var validationError *jsonschema.ValidationError
if errors.As(err, &validationError) {
// If there's only one error, return it directly, otherwise
// return the full list of errors.
errs := validationError.DetailedOutput().Errors
out := ""
if len(errs) == 1 {
out = errs[0].Error
} else {
out = fmt.Sprintf("%#v", validationError.DetailedOutput().Errors)
}

return fmt.Errorf("module %q argument %q validation failed: %s", s.t.Module.Name, pth, out)
}

return errors.Wrapf(err, "module %q argument %q validation failed", s.t.Module.Name, pth)
}

return v, nil
return nil
}

// Deprecated: Use Arg instead.
Expand Down
4 changes: 2 additions & 2 deletions internal/codegen/tpl_stencil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func TestTplStencil_Arg(t *testing.T) {
fields: fakeTemplate(t, map[string]interface{}{
"hello": map[string]interface{}{
"world": map[string]interface{}{
"abc": []string{"def"},
"abc": []interface{}{"def"},
},
},
}, map[string]configuration.Argument{
Expand All @@ -162,7 +162,7 @@ func TestTplStencil_Arg(t *testing.T) {
args: args{
pth: "hello",
},
want: map[string]interface{}{"world": map[string]interface{}{"abc": []string{"def"}}},
want: map[string]interface{}{"world": map[string]interface{}{"abc": []interface{}{"def"}}},
wantErr: false,
},
{
Expand Down

0 comments on commit fa803f7

Please sign in to comment.