From fbc65539cda193de83b5b5edbdaa1ffa03abd095 Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Tue, 20 Feb 2018 16:03:44 +0000 Subject: [PATCH] use file extensions to unmarshal data appropriately --- .travis.yml | 2 +- conflate.go | 44 ++++++-------- conflate_test.go | 13 +++- example/main.go | 19 ++++++ filedata.go | 96 +++++++++++++++++++++++++++++ filedata_test.go | 153 +++++++++++++++++++++++++++++++++++++++++++++++ loader.go | 49 ++++----------- loader_test.go | 58 +++--------------- marshal.go | 52 +++------------- marshal_test.go | 122 ++++++++++--------------------------- schema.go | 20 +++---- schema_test.go | 34 +++++------ 12 files changed, 384 insertions(+), 278 deletions(-) create mode 100644 filedata.go create mode 100644 filedata_test.go diff --git a/.travis.yml b/.travis.yml index 1209c56..da43b21 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ script: - test -z "$(gofmt -s -l . 2>&1 | grep -v vendor | tee /dev/stderr)" - golint -set_exit_status ./... - ineffassign ./ - - megacheck ./ + - megacheck -simple.exit-non-zero -unused.exit-non-zero ./ - go test -coverprofile .coverprofile - $GOPATH/bin/goveralls -v -coverprofile .coverprofile -service=travis-ci diff --git a/conflate.go b/conflate.go index 24d6991..a08557e 100644 --- a/conflate.go +++ b/conflate.go @@ -70,12 +70,12 @@ func (c *Conflate) AddURLs(urls ...url.URL) error { if err != nil { return err } - return c.MergeData(data...) + return c.mergeData(data...) } // AddGo recursively merges the given (json-serializable) golang objects into the Conflate instance func (c *Conflate) AddGo(objs ...interface{}) error { - data, err := marshalAll(jsonMarshal, objs...) + data, err := jsonMarshalAll(objs...) if err != nil { return err } @@ -84,32 +84,11 @@ func (c *Conflate) AddGo(objs ...interface{}) error { // AddData recursively merges the given data into the Conflate instance func (c *Conflate) AddData(data ...[]byte) error { - data, err := loadDataRecursive(nil, data...) + fdata, err := wrapFiledatas(data...) if err != nil { return err } - return c.MergeData(data...) -} - -// MergeData merges the given data into the Conflate instance -func (c *Conflate) MergeData(data ...[]byte) error { - doms, err := unmarshalAll(unmarshalAny, data...) - if err != nil { - return err - } - err = mergeTo(&c.data, doms...) - if err != nil { - return err - } - c.removeIncludes() - return nil -} - -func (c *Conflate) removeIncludes() { - m, ok := c.data.(map[string]interface{}) - if ok { - delete(m, "includes") - } + return c.addData(fdata...) } // SetSchemaFile loads a JSON v4 schema from the given path @@ -133,7 +112,7 @@ func (c *Conflate) SetSchemaURL(url url.URL) error { // SetSchemaData loads a JSON v4 schema from the given data func (c *Conflate) SetSchemaData(data []byte) error { var schema interface{} - err := jsonUnmarshal(data, &schema) + err := JSONUnmarshal(data, &schema) if err != nil { return wrapError(err, "Schema is not valid json") } @@ -187,3 +166,16 @@ func (c *Conflate) MarshalTOML() ([]byte, error) { func (c *Conflate) MarshalSchema() ([]byte, error) { return jsonMarshal(c.schema) } + +func (c *Conflate) addData(fdata ...filedata) error { + fdata, err := loadDataRecursive(nil, fdata...) + if err != nil { + return err + } + return c.mergeData(fdata...) +} + +func (c *Conflate) mergeData(fdata ...filedata) error { + doms := filedatas(fdata).objs() + return mergeTo(&c.data, doms...) +} diff --git a/conflate_test.go b/conflate_test.go index 8432fd6..4e016b7 100644 --- a/conflate_test.go +++ b/conflate_test.go @@ -333,9 +333,16 @@ func TestConflate_MarshalSchemaError(t *testing.T) { assert.Contains(t, err.Error(), "The data could not be marshalled") } -func TestConflate_MergeDataError(t *testing.T) { +func TestConflate_addDataError(t *testing.T) { c := New() - err := c.MergeData([]byte("{not valid")) + err := c.AddData([]byte(`{"includes": ["missing"]}`)) assert.NotNil(t, err) - assert.Contains(t, err.Error(), "Could not unmarshal data") + assert.Contains(t, err.Error(), "Failed to load url") +} + +func TestConflate_mergeDataError(t *testing.T) { + c := New() + err := c.AddData([]byte(`"x": {}`), []byte(`"x": []`)) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "Failed to merge") } diff --git a/example/main.go b/example/main.go index b76af16..dee6b1b 100644 --- a/example/main.go +++ b/example/main.go @@ -7,6 +7,25 @@ import ( "runtime" ) +func init() { + // define the unmarshallers for the given file extensions, blank extension is the global unmarshaller + conflate.Unmarshallers = conflate.UnmarshallerMap{ + ".json": {customJSONUnmarshal}, + ".jsn": {conflate.JSONUnmarshal}, + ".yaml": {conflate.YAMLUnmarshal}, + ".yml": {conflate.YAMLUnmarshal}, + ".toml": {conflate.TOMLUnmarshal}, + ".tml": {conflate.TOMLUnmarshal}, + "": {conflate.JSONUnmarshal, conflate.YAMLUnmarshal, conflate.TOMLUnmarshal}, + } +} + +// example of a custom unmarshaller for JSON +func customJSONUnmarshal(data []byte, out interface{}) error { + fmt.Println("Using custom JSON Unmarshaller") + return conflate.JSONUnmarshal(data, out) +} + func main() { _, thisFile, _, _ := runtime.Caller(0) thisDir := path.Dir(thisFile) diff --git a/filedata.go b/filedata.go new file mode 100644 index 0000000..2d22abd --- /dev/null +++ b/filedata.go @@ -0,0 +1,96 @@ +package conflate + +import ( + pkgurl "net/url" + "path/filepath" + "strings" +) + +type filedata struct { + url pkgurl.URL + bytes []byte + obj map[string]interface{} + includes []string +} + +type filedatas []filedata + +// UnmarshallerFunc defines the type of function used for unmarshalling data +type UnmarshallerFunc func([]byte, interface{}) error + +// UnmarshallerFuncs defines the type for a slice of UnmarshallerFunc +type UnmarshallerFuncs []UnmarshallerFunc + +// UnmarshallerMap defines the type of a map of string to UnmarshallerFuncs +type UnmarshallerMap map[string]UnmarshallerFuncs + +// Unmarshallers is a list of unmarshalling functions to be used for given file extensions. The unmarshaller slice for the blank file extension is used when no match is found. +var Unmarshallers = UnmarshallerMap{ + ".json": {JSONUnmarshal}, + ".jsn": {JSONUnmarshal}, + ".yaml": {YAMLUnmarshal}, + ".yml": {YAMLUnmarshal}, + ".toml": {TOMLUnmarshal}, + ".tml": {TOMLUnmarshal}, + "": {JSONUnmarshal, YAMLUnmarshal, TOMLUnmarshal}, +} + +func newFiledata(bytes []byte, url pkgurl.URL) (filedata, error) { + fd := filedata{bytes: bytes, url: url} + err := fd.unmarshal() + if err != nil { + return fd, err + } + err = fd.extractIncludes() + return fd, err +} + +func wrapFiledata(bytes []byte) (filedata, error) { + return newFiledata(bytes, emptyURL) +} + +func wrapFiledatas(bytes ...[]byte) (filedatas, error) { + var fds []filedata + for _, b := range bytes { + fd, err := wrapFiledata(b) + if err != nil { + return nil, err + } + fds = append(fds, fd) + } + return fds, nil +} + +func (fd *filedata) unmarshal() error { + ext := strings.ToLower(filepath.Ext(fd.url.Path)) + unmarshallers, ok := Unmarshallers[ext] + if !ok { + unmarshallers = Unmarshallers[""] + } + err := makeError("Could not unmarshal data") + for _, unmarshal := range unmarshallers { + uerr := unmarshal(fd.bytes, &fd.obj) + if uerr == nil { + return nil + } + err = wrapError(uerr, err.Error()) + } + return err +} + +func (fd *filedata) extractIncludes() error { + err := jsonMarshalUnmarshal(fd.obj["includes"], &fd.includes) + if err != nil { + return wrapError(err, "Could not extract includes") + } + delete(fd.obj, "includes") + return nil +} + +func (fds filedatas) objs() []interface{} { + var objs []interface{} + for _, fd := range fds { + objs = append(objs, fd.obj) + } + return objs +} diff --git a/filedata_test.go b/filedata_test.go new file mode 100644 index 0000000..0b769e8 --- /dev/null +++ b/filedata_test.go @@ -0,0 +1,153 @@ +package conflate + +import ( + "github.com/stretchr/testify/assert" + pkgurl "net/url" + "testing" +) + +func testFiledataNew(t *testing.T, data []byte, path string) (filedata, error) { + url, err := pkgurl.Parse(path) + assert.Nil(t, err) + return newFiledata(data, *url) +} + +func testFiledataNewAssert(t *testing.T, data []byte, path string) filedata { + fd, err := testFiledataNew(t, data, path) + assert.Nil(t, err) + return fd +} + +func TestFiledata_JSONAsAny(t *testing.T) { + fd, err := testFiledataNew(t, testMarshalJSON, "file") + assert.Nil(t, err) + assert.Equal(t, fd.obj, testMarshalData) +} + +func TestFiledata_JSONAsUnknown(t *testing.T) { + fd, err := testFiledataNew(t, testMarshalJSON, "file.unknown") + assert.Nil(t, err) + assert.Equal(t, fd.obj, testMarshalData) +} + +func TestFiledata_JSONAsJSON(t *testing.T) { + fd, err := testFiledataNew(t, testMarshalJSON, "file.json") + assert.Nil(t, err) + assert.Equal(t, fd.obj, testMarshalData) +} + +func TestFiledata_JSONAsJSN(t *testing.T) { + fd, err := testFiledataNew(t, testMarshalJSON, "file.jsn") + assert.Nil(t, err) + assert.Equal(t, fd.obj, testMarshalData) +} + +func TestFiledata_JSONAsTOML(t *testing.T) { + fd, err := testFiledataNew(t, testMarshalJSON, "file.toml") + assert.NotNil(t, err) + assert.Nil(t, fd.obj) +} + +func TestFiledata_YAMLAsAny(t *testing.T) { + fd, err := testFiledataNew(t, testMarshalYAML, "file") + assert.Nil(t, err) + assert.Equal(t, fd.obj, testMarshalData) +} + +func TestFiledata_YAMLAsUnknown(t *testing.T) { + fd, err := testFiledataNew(t, testMarshalYAML, "file.unknown") + assert.Nil(t, err) + assert.Equal(t, fd.obj, testMarshalData) +} + +func TestFiledata_YAMLAsYAML(t *testing.T) { + fd, err := testFiledataNew(t, testMarshalYAML, "file.yaml") + assert.Nil(t, err) + assert.Equal(t, fd.obj, testMarshalData) +} + +func TestFiledata_YAMLAsYML(t *testing.T) { + fd, err := testFiledataNew(t, testMarshalYAML, "file.yml") + assert.Nil(t, err) + assert.Equal(t, fd.obj, testMarshalData) +} + +func TestFiledata_YAMLAsTOML(t *testing.T) { + fd, err := testFiledataNew(t, testMarshalYAML, "file.toml") + assert.NotNil(t, err) + assert.Nil(t, fd.obj) +} + +func TestFiledata_TOMLAsAny(t *testing.T) { + fd, err := testFiledataNew(t, testMarshalTOML, "file") + assert.Nil(t, err) + assert.Equal(t, fd.obj, testMarshalData) +} + +func TestFiledata_TOMLAsUnknown(t *testing.T) { + fd, err := testFiledataNew(t, testMarshalTOML, "file.unknown") + assert.Nil(t, err) + assert.Equal(t, fd.obj, testMarshalData) +} + +func TestFiledata_TOMLAsTOML(t *testing.T) { + fd, err := testFiledataNew(t, testMarshalTOML, "file.toml") + assert.Nil(t, err) + assert.Equal(t, fd.obj, testMarshalData) +} + +func TestFiledata_TOMLAsTML(t *testing.T) { + fd, err := testFiledataNew(t, testMarshalTOML, "file.tml") + assert.Nil(t, err) + assert.Equal(t, fd.obj, testMarshalData) +} + +func TestFiledata_TOMLAsJSON(t *testing.T) { + fd, err := testFiledataNew(t, testMarshalTOML, "file.json") + assert.NotNil(t, err) + assert.Nil(t, fd.obj) +} + +func TestFiledata_NoIncludes(t *testing.T) { + fd, err := wrapFiledata([]byte(`{"x": 1}`)) + assert.Nil(t, err) + assert.Nil(t, fd.obj["includes"]) + assert.Equal(t, fd.obj, map[string]interface{}{"x": 1.0}) +} + +func TestFiledata_BlankIncludes(t *testing.T) { + fd, err := wrapFiledata([]byte(`{"includes":[], "x": 1}`)) + assert.Nil(t, err) + assert.Nil(t, fd.obj["includes"]) + assert.Equal(t, fd.obj, map[string]interface{}{"x": 1.0}) +} + +func TestFiledata_NullIncludes(t *testing.T) { + fd, err := wrapFiledata([]byte(`{"includes":null, "x": 1}`)) + assert.Nil(t, err) + assert.Nil(t, fd.obj["includes"]) + assert.Equal(t, fd.obj, map[string]interface{}{"x": 1.0}) +} + +func TestFiledata_Includes(t *testing.T) { + fd, err := wrapFiledata([]byte(`{"includes":["test1", "test2"], "x": 1}`)) + assert.Nil(t, err) + assert.Equal(t, fd.includes, []string{"test1", "test2"}) + assert.Nil(t, fd.obj["includes"]) + assert.Equal(t, fd.obj, map[string]interface{}{"x": 1.0}) +} + +func TestFiledata_IncludesError(t *testing.T) { + _, err := wrapFiledata([]byte(`{"includes": "not array"}`)) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "Could not extract includes") +} + +func TestFiledatas_Unmarshal(t *testing.T) { + fds := filedatas{ + testFiledataNewAssert(t, testMarshalJSON, "file.json"), + testFiledataNewAssert(t, testMarshalYAML, "file.yaml"), + testFiledataNewAssert(t, testMarshalTOML, "file.toml"), + } + assert.Equal(t, fds.objs(), []interface{}{testMarshalData, testMarshalData, testMarshalData}) +} diff --git a/loader.go b/loader.go index 8ceffea..2beb479 100644 --- a/loader.go +++ b/loader.go @@ -12,18 +12,6 @@ import ( var emptyURL = pkgurl.URL{} var getwd = os.Getwd -func loadURLs(urls ...pkgurl.URL) ([][]byte, error) { - var allData [][]byte - for _, url := range urls { - data, err := loadURL(url) - if err != nil { - return nil, err - } - allData = append(allData, data) - } - return allData, nil -} - func loadURL(url pkgurl.URL) ([]byte, error) { client := http.Client{Transport: newTransport()} resp, err := client.Get(url.String()) @@ -38,8 +26,8 @@ func loadURL(url pkgurl.URL) ([]byte, error) { return data, err } -func loadURLsRecursive(parentUrls []pkgurl.URL, urls ...pkgurl.URL) ([][]byte, error) { - var allData [][]byte +func loadURLsRecursive(parentUrls []pkgurl.URL, urls ...pkgurl.URL) (filedatas, error) { + var allData filedatas for _, url := range urls { data, err := loadURLRecursive(parentUrls, url) if err != nil { @@ -50,16 +38,20 @@ func loadURLsRecursive(parentUrls []pkgurl.URL, urls ...pkgurl.URL) ([][]byte, e return allData, nil } -func loadURLRecursive(parentUrls []pkgurl.URL, url pkgurl.URL) ([][]byte, error) { +func loadURLRecursive(parentUrls []pkgurl.URL, url pkgurl.URL) (filedatas, error) { data, err := loadURL(url) if err != nil { return nil, err } - return loadDatumRecursive(parentUrls, &url, data) + fdata, err := newFiledata(data, url) + if err != nil { + return nil, err + } + return loadDatumRecursive(parentUrls, &url, fdata) } -func loadDataRecursive(parentUrls []pkgurl.URL, data ...[]byte) ([][]byte, error) { - var allData [][]byte +func loadDataRecursive(parentUrls []pkgurl.URL, data ...filedata) (filedatas, error) { + var allData filedatas for _, datum := range data { childData, err := loadDatumRecursive(parentUrls, nil, datum) if err != nil { @@ -70,15 +62,11 @@ func loadDataRecursive(parentUrls []pkgurl.URL, data ...[]byte) ([][]byte, error return allData, nil } -func loadDatumRecursive(parentUrls []pkgurl.URL, url *pkgurl.URL, data []byte) ([][]byte, error) { +func loadDatumRecursive(parentUrls []pkgurl.URL, url *pkgurl.URL, data filedata) (filedatas, error) { if containsURL(url, parentUrls) { return nil, makeError("The url recursively includes itself (%v)", url) } - childPaths, err := extractIncludes(data) - if err != nil { - return nil, err - } - childUrls, err := toURLs(url, childPaths...) + childUrls, err := toURLs(url, data.includes...) if err != nil { return nil, err } @@ -91,7 +79,7 @@ func loadDatumRecursive(parentUrls []pkgurl.URL, url *pkgurl.URL, data []byte) ( if err != nil { return nil, err } - var allData [][]byte + var allData filedatas allData = append(allData, childData...) allData = append(allData, data) return allData, nil @@ -164,17 +152,6 @@ func toURL(rootURL *pkgurl.URL, path string) (pkgurl.URL, error) { return *url, nil } -func extractIncludes(data []byte) ([]string, error) { - out := struct { - Includes []string - }{} - err := unmarshalAny(data, &out) - if err != nil { - return nil, wrapError(err, "Could not extract includes") - } - return out.Includes, nil -} - func workingDir() (*pkgurl.URL, error) { rootPath, err := getwd() if err != nil { diff --git a/loader_test.go b/loader_test.go index 19fd50b..b3a720d 100644 --- a/loader_test.go +++ b/loader_test.go @@ -203,33 +203,12 @@ func TestLoadURL(t *testing.T) { func TestLoadURL_Relative(t *testing.T) { root, err := workingDir() assert.Nil(t, err) - test, err := toURL(root, "./testdata/valid_parent.json") + url, err := toURL(root, "./testdata/valid_parent.json") assert.Nil(t, err) - assert.NotNil(t, test) -} - -// -------- - -func TestLoadURLs_Error(t *testing.T) { - data, err := loadURLs(url.URL{}) - assert.NotNil(t, err) - assert.Nil(t, data) -} - -func TestLoadURLs(t *testing.T) { - shutdown := testServer() - defer shutdown() - testWaitForURL(t, "http://0.0.0.0:9999") - url1, err := url.Parse("http://0.0.0.0:9999/valid_parent.json") - assert.Nil(t, err) - root, err := workingDir() - assert.Nil(t, err) - url2, err := toURL(root, "./testdata/valid_parent.json") - assert.Nil(t, err) - data, err := loadURLs(*url1, url2) + data, err := loadURL(url) assert.Nil(t, err) - assert.NotNil(t, data) - assert.Equal(t, len(data), 2) + assert.NotNil(t, url) + assert.Contains(t, string(data), "parent") } // -------- @@ -242,29 +221,6 @@ func TestNewClient(t *testing.T) { // -------- -func TestExtractIncludes_Error(t *testing.T) { - data := []byte{1, 2, 3} - paths, err := extractIncludes(data) - assert.NotNil(t, err) - assert.Nil(t, paths) -} - -func TestExtractIncludes(t *testing.T) { - data := []byte(`{ "includes": [ "inc1", "inc2", "inc3"] }`) - paths, err := extractIncludes(data) - assert.Nil(t, err) - assert.Equal(t, []string{"inc1", "inc2", "inc3"}, paths) -} - -func TestExtractIncludes_NilData(t *testing.T) { - data := []byte{} - paths, err := extractIncludes(data) - assert.Nil(t, err) - assert.Nil(t, paths) -} - -// -------- - func TestLoadURLsRecursive_LoadError(t *testing.T) { data, err := loadURLsRecursive(nil, url.URL{}) assert.NotNil(t, err) @@ -280,7 +236,7 @@ func TestLoadURLsRecursive_IncludesError(t *testing.T) { assert.NotNil(t, url) data, err := loadURLsRecursive(nil, url) assert.NotNil(t, err) - assert.Contains(t, err.Error(), "Could not extract includes") + assert.Contains(t, err.Error(), "Could not unmarshal") assert.Nil(t, data) } @@ -333,4 +289,8 @@ func TestLoadURLsRecursive(t *testing.T) { data, err := loadURLsRecursive(nil, url) assert.Nil(t, err) assert.NotNil(t, data) + assert.Equal(t, 3, len(data)) + assert.Contains(t, data[0].url.String(), "valid_child.json") + assert.Contains(t, data[1].url.String(), "valid_sibling.json") + assert.Contains(t, data[2].url.String(), "valid_parent.json") } diff --git a/marshal.go b/marshal.go index 6710553..1359eed 100644 --- a/marshal.go +++ b/marshal.go @@ -7,10 +7,10 @@ import ( "github.com/ghodss/yaml" ) -func marshalAll(fMarshal func(interface{}) ([]byte, error), data ...interface{}) ([][]byte, error) { +func jsonMarshalAll(data ...interface{}) ([][]byte, error) { var outs [][]byte for i, datum := range data { - out, err := fMarshal(datum) + out, err := jsonMarshal(datum) if err != nil { return nil, wrapError(err, "Could not marshal data %v", i) } @@ -19,52 +19,16 @@ func marshalAll(fMarshal func(interface{}) ([]byte, error), data ...interface{}) return outs, nil } -func unmarshalAll(fUnmarshal func([]byte, interface{}) error, data ...[]byte) ([]interface{}, error) { - var outs []interface{} - for i, datum := range data { - var out interface{} - err := fUnmarshal(datum, &out) - if err != nil { - return nil, wrapError(err, "Could not unmarshal data %v", i) - } - outs = append(outs, out) - } - return outs, nil -} - -func unmarshalAny(data []byte, out interface{}) error { - errs := makeError("Could not unmarshal data") - - err := jsonUnmarshal(data, out) - if err == nil { - return nil - } - errs = wrapError(err, errs.Error()) - - err = tomlUnmarshal(data, out) - if err == nil { - return nil - } - errs = wrapError(err, errs.Error()) - - err = yamlUnmarshal(data, out) - if err == nil { - return nil - } - errs = wrapError(err, errs.Error()) - - return errs -} - func jsonMarshalUnmarshal(in interface{}, out interface{}) error { data, err := jsonMarshal(in) if err != nil { return err } - return jsonUnmarshal(data, out) + return JSONUnmarshal(data, out) } -func jsonUnmarshal(data []byte, out interface{}) error { +// JSONUnmarshal unmarshals the data as JSON +func JSONUnmarshal(data []byte, out interface{}) error { err := json.Unmarshal(data, out) if err != nil { return wrapError(err, "The data could not be unmarshalled as json") @@ -72,7 +36,8 @@ func jsonUnmarshal(data []byte, out interface{}) error { return nil } -func yamlUnmarshal(data []byte, out interface{}) error { +// YAMLUnmarshal unmarshals the data as YAML +func YAMLUnmarshal(data []byte, out interface{}) error { err := yaml.Unmarshal(data, out) if err != nil { return wrapError(err, "The data could not be unmarshalled as yaml") @@ -80,7 +45,8 @@ func yamlUnmarshal(data []byte, out interface{}) error { return nil } -func tomlUnmarshal(data []byte, out interface{}) error { +// TOMLUnmarshal unmarshals the data as TOML +func TOMLUnmarshal(data []byte, out interface{}) error { err := toml.Unmarshal(data, out) if err != nil { return wrapError(err, "The data could not be unmarshalled as toml") diff --git a/marshal_test.go b/marshal_test.go index 36ef7eb..3e91f86 100644 --- a/marshal_test.go +++ b/marshal_test.go @@ -3,109 +3,47 @@ package conflate import ( "errors" "github.com/stretchr/testify/assert" - "reflect" "testing" ) // -------- -func TestMarshalAll(t *testing.T) { - mockMarshal := func(obj interface{}) ([]byte, error) { - return []byte(obj.(string)), nil - } - data, err := marshalAll(mockMarshal, "a", "b", "c") +func TestJSONMarshalAll(t *testing.T) { + data, err := jsonMarshalAll("a", "b", "c") assert.Nil(t, err) assert.Equal(t, 3, len(data)) - assert.Equal(t, []byte("a"), data[0]) - assert.Equal(t, []byte("b"), data[1]) - assert.Equal(t, []byte("c"), data[2]) + assert.Equal(t, []byte("\"a\"\n"), data[0]) + assert.Equal(t, []byte("\"b\"\n"), data[1]) + assert.Equal(t, []byte("\"c\"\n"), data[2]) } -func TestMarshalAll_Error(t *testing.T) { +func TestJSONMarshalAll_Error(t *testing.T) { mockMarshal := func(obj interface{}) ([]byte, error) { return nil, errors.New("my error") } - data, err := marshalAll(mockMarshal, "a") + data, err := jsonMarshalAll(mockMarshal, "a") assert.NotNil(t, err) assert.Nil(t, data) - assert.Contains(t, err.Error(), "my error") -} - -// -------- - -func TestUnmarshalAll(t *testing.T) { - mockUnmarshal := func(data []byte, obj interface{}) error { - reflect.ValueOf(obj).Elem().Set(reflect.ValueOf(string(data))) - return nil - } - data, err := unmarshalAll(mockUnmarshal, []byte("1"), []byte("2"), []byte("3")) - assert.Nil(t, err) - assert.Equal(t, 3, len(data)) - assert.Equal(t, "1", data[0]) - assert.Equal(t, "2", data[1]) - assert.Equal(t, "3", data[2]) -} - -func TestUnmarshalAll_Error(t *testing.T) { - mockUnmarshal := func(data []byte, obj interface{}) error { - return errors.New("my error") - } - data, err := unmarshalAll(mockUnmarshal, []byte("1")) - assert.NotNil(t, err) - assert.Nil(t, data) - assert.Contains(t, err.Error(), "my error") -} - -// -------- - -func TestUnmarshal_Json(t *testing.T) { - var out interface{} - err := unmarshalAny(testMarshalJSON, &out) - assert.Nil(t, err) - assert.Equal(t, testMarshalData, out) -} - -func TestUnmarshal_Yaml(t *testing.T) { - var out interface{} - err := unmarshalAny(testMarshalYAML, &out) - assert.Nil(t, err) - assert.Equal(t, testMarshalData, out) -} - -func TestUnmarshal_Toml(t *testing.T) { - var out interface{} - err := unmarshalAny(testMarshalTOML, &out) - assert.Nil(t, err) - assert.Equal(t, testMarshalData, out) -} - -func TestUnmarshal_Unsupported(t *testing.T) { - var out interface{} - err := unmarshalAny(testMarshalInvalid, &out) - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "Could not unmarshal data") - assert.Contains(t, err.Error(), "could not be unmarshalled as json") - assert.Contains(t, err.Error(), "could not be unmarshalled as yaml") - assert.Contains(t, err.Error(), "could not be unmarshalled as toml") + assert.Contains(t, err.Error(), "The data could not be marshalled") } // -------- -func TestJsonMarshalUnmarshal(t *testing.T) { +func TestJSONMarshalUnmarshal(t *testing.T) { var out interface{} err := jsonMarshalUnmarshal(testMarshalData, &out) assert.Nil(t, err) assert.Equal(t, testMarshalData, out) } -func TestJsonMarshalUnmarshal_MarshalError(t *testing.T) { +func TestJSONMarshalUnmarshal_MarshalError(t *testing.T) { var out interface{} err := jsonMarshalUnmarshal(testMarshalDataInvalid, out) assert.NotNil(t, err) assert.Contains(t, err.Error(), "could not be marshalled to json") } -func TestJsonMarshalUnmarshal_UnmarshalError(t *testing.T) { +func TestJSONMarshalUnmarshal_UnmarshalError(t *testing.T) { err := jsonMarshalUnmarshal(testMarshalData, testMarshalDataInvalid) assert.NotNil(t, err) assert.Contains(t, err.Error(), "could not be unmarshalled as json") @@ -113,57 +51,57 @@ func TestJsonMarshalUnmarshal_UnmarshalError(t *testing.T) { // -------- -func TestJsonUnmarshal(t *testing.T) { +func TestJSONUnmarshal(t *testing.T) { var out interface{} - err := jsonUnmarshal(testMarshalJSON, &out) + err := JSONUnmarshal(testMarshalJSON, &out) assert.Nil(t, err) assert.Equal(t, testMarshalData, out) } -func TestJsonUnmarshal_Error(t *testing.T) { +func TestJSONUnmarshal_Error(t *testing.T) { var out interface{} - err := jsonUnmarshal(testMarshalInvalid, &out) + err := JSONUnmarshal(testMarshalInvalid, &out) assert.NotNil(t, err) assert.Contains(t, err.Error(), "could not be unmarshalled as json") } -func TestYamlUnmarshal(t *testing.T) { +func TestYAMLUnmarshal(t *testing.T) { var out interface{} - err := yamlUnmarshal(testMarshalYAML, &out) + err := YAMLUnmarshal(testMarshalYAML, &out) assert.Nil(t, err) assert.Equal(t, testMarshalData, out) } -func TestYamlUnmarshal_Error(t *testing.T) { +func TestYAMLUnmarshal_Error(t *testing.T) { var out interface{} - err := yamlUnmarshal(testMarshalInvalid, &out) + err := YAMLUnmarshal(testMarshalInvalid, &out) assert.NotNil(t, err) assert.Contains(t, err.Error(), "could not be unmarshalled as yaml") } -func TestTomlUnmarshal(t *testing.T) { +func TestTOMLUnmarshal(t *testing.T) { var out interface{} - err := tomlUnmarshal(testMarshalTOML, &out) + err := TOMLUnmarshal(testMarshalTOML, &out) assert.Nil(t, err) assert.Equal(t, testMarshalData, out) } -func TestTomlUnmarshal_Error(t *testing.T) { +func TestTOMLUnmarshal_Error(t *testing.T) { var out interface{} - err := tomlUnmarshal(testMarshalInvalid, &out) + err := TOMLUnmarshal(testMarshalInvalid, &out) assert.NotNil(t, err) assert.Contains(t, err.Error(), "could not be unmarshalled as toml") } // -------- -func TestJsonMarshal(t *testing.T) { +func TestJSONMarshal(t *testing.T) { out, err := jsonMarshal(testMarshalData) assert.Nil(t, err) assert.Equal(t, string(testMarshalJSON), string(out)) } -func TestJsonMarshal_Error(t *testing.T) { +func TestJSONMarshal_Error(t *testing.T) { out, err := jsonMarshal(testMarshalDataInvalid) assert.NotNil(t, err) assert.Nil(t, out) @@ -171,33 +109,33 @@ func TestJsonMarshal_Error(t *testing.T) { assert.Contains(t, err.Error(), "marshalled to json") } -func TestYamlMarshal(t *testing.T) { +func TestYAMLMarshal(t *testing.T) { out, err := yamlMarshal(testMarshalData) assert.Nil(t, err) assert.Equal(t, string(testMarshalYAML), string(out)) } -func TestYamlMarshal_Error(t *testing.T) { +func TestYAMLMarshal_Error(t *testing.T) { out, err := yamlMarshal(testMarshalDataInvalid) assert.NotNil(t, err) assert.Nil(t, out) assert.Contains(t, err.Error(), "marshalled to yaml") } -func TestTomlMarshal(t *testing.T) { +func TestTOMLMarshal(t *testing.T) { out, err := tomlMarshal(testMarshalData) assert.Nil(t, err) assert.Equal(t, string(testMarshalTOML), string(out)) } -func TestTomlMarshal_PanicError(t *testing.T) { +func TestTOMLMarshal_PanicError(t *testing.T) { out, err := tomlMarshal(testMarshalDataInvalid) assert.NotNil(t, err) assert.Nil(t, out) assert.Contains(t, err.Error(), "marshalled to toml") } -func TestTomlMarshal_Error(t *testing.T) { +func TestTOMLMarshal_Error(t *testing.T) { in := []interface{}{123, "123"} out, err := tomlMarshal(in) assert.NotNil(t, err) diff --git a/schema.go b/schema.go index 181dc0c..2057a65 100644 --- a/schema.go +++ b/schema.go @@ -10,7 +10,7 @@ var metaSchema interface{} func validateSchema(schema interface{}) error { if metaSchema == nil { - err := jsonUnmarshal(metaSchemaData, &metaSchema) + err := JSONUnmarshal(metaSchemaData, &metaSchema) if err != nil { return wrapError(err, "Could not load json meta-schema") } @@ -109,16 +109,14 @@ func applyDefaultsRecursive(ctx context, pData interface{}, schema interface{}) var schemaProps map[string]interface{} if props, ok := schemaNode["properties"]; ok { schemaProps = props.(map[string]interface{}) - if schemaProps != nil { - for name, schemaProp := range schemaProps { - dataProp := dataProps[name] - err := applyDefaultsRecursive(ctx.add(name), &dataProp, schemaProp) - if err != nil { - return wrapError(err, "Failed to apply defaults to object property") - } - if dataProp != nil { - dataProps[name] = dataProp - } + for name, schemaProp := range schemaProps { + dataProp := dataProps[name] + err := applyDefaultsRecursive(ctx.add(name), &dataProp, schemaProp) + if err != nil { + return wrapError(err, "Failed to apply defaults to object property") + } + if dataProp != nil { + dataProps[name] = dataProp } } } diff --git a/schema_test.go b/schema_test.go index 911f169..4ba6715 100644 --- a/schema_test.go +++ b/schema_test.go @@ -9,7 +9,7 @@ func TestValidateSchema(t *testing.T) { metaSchema = nil data := `{"title": "test"}` var schema interface{} - err := jsonUnmarshal([]byte(data), &schema) + err := JSONUnmarshal([]byte(data), &schema) assert.Nil(t, err) err = validateSchema(schema) assert.Nil(t, err) @@ -19,7 +19,7 @@ func TestValidateSchema(t *testing.T) { func TestValidateSchema_AnyOf(t *testing.T) { data := `{ "type": "object", "properties": { "test": { "anyOf": [ { "type": "integer" } ] } } }` var schema interface{} - err := jsonUnmarshal([]byte(data), &schema) + err := JSONUnmarshal([]byte(data), &schema) assert.Nil(t, err) err = validateSchema(schema) assert.Nil(t, err) @@ -41,9 +41,9 @@ func TestValidateSchema_Error(t *testing.T) { func TestValidate(t *testing.T) { var data interface{} var schema interface{} - err := jsonUnmarshal(testSchemaData, &data) + err := JSONUnmarshal(testSchemaData, &data) assert.Nil(t, err) - err = jsonUnmarshal(testSchema, &schema) + err = JSONUnmarshal(testSchema, &schema) assert.Nil(t, err) err = validate(data, schema) assert.Nil(t, err) @@ -52,7 +52,7 @@ func TestValidate(t *testing.T) { func TestValidate_ValidateError(t *testing.T) { var data interface{} var schema interface{} - err := jsonUnmarshal(testSchemaData, &data) + err := JSONUnmarshal(testSchemaData, &data) assert.Nil(t, err) err = validate(data, schema) assert.NotNil(t, err) @@ -63,11 +63,11 @@ func TestValidate_ValidateError(t *testing.T) { func TestValidate_NotValid(t *testing.T) { var data map[string]interface{} var schema map[string]interface{} - err := jsonUnmarshal(testSchemaData, &data) + err := JSONUnmarshal(testSchemaData, &data) assert.Nil(t, err) - err = jsonUnmarshal(testSchema, &schema) + err = JSONUnmarshal(testSchema, &schema) assert.Nil(t, err) - err = jsonUnmarshal(testSchema, &schema) + err = JSONUnmarshal(testSchema, &schema) assert.Nil(t, err) obj := data["obj"].(map[string]interface{}) obj["str"] = 123 @@ -81,9 +81,9 @@ func TestValidate_NotValid(t *testing.T) { func TestValidate_CustomFormatError(t *testing.T) { var data interface{} var schema map[string]interface{} - err := jsonUnmarshal(testSchemaData, &data) + err := JSONUnmarshal(testSchemaData, &data) assert.Nil(t, err) - err = jsonUnmarshal(testSchema, &schema) + err = JSONUnmarshal(testSchema, &schema) assert.Nil(t, err) props := schema["properties"].(map[string]interface{}) str := props["str"].(map[string]interface{}) @@ -378,9 +378,9 @@ func TestApplyDefaults_Empty(t *testing.T) { var data interface{} var defaults interface{} var schema interface{} - err := jsonUnmarshal(testSchemaDefaults, &defaults) + err := JSONUnmarshal(testSchemaDefaults, &defaults) assert.Nil(t, err) - err = jsonUnmarshal(testSchema, &schema) + err = JSONUnmarshal(testSchema, &schema) assert.Nil(t, err) err = applyDefaults(&data, schema) assert.Nil(t, err) @@ -391,11 +391,11 @@ func TestApplyDefaults_NoDefaults(t *testing.T) { var data interface{} var dataExpected interface{} var schema interface{} - err := jsonUnmarshal(testSchemaData, &data) + err := JSONUnmarshal(testSchemaData, &data) assert.Nil(t, err) - err = jsonUnmarshal(testSchemaData, &dataExpected) + err = JSONUnmarshal(testSchemaData, &dataExpected) assert.Nil(t, err) - err = jsonUnmarshal(testSchema, &schema) + err = JSONUnmarshal(testSchema, &schema) assert.Nil(t, err) err = applyDefaults(&data, schema) assert.Nil(t, err) @@ -405,9 +405,9 @@ func TestApplyDefaults_NoDefaults(t *testing.T) { func TestApplyDefaults_MissingIntFields(t *testing.T) { var data map[string]interface{} var schema interface{} - err := jsonUnmarshal(testSchemaData, &data) + err := JSONUnmarshal(testSchemaData, &data) assert.Nil(t, err) - err = jsonUnmarshal(testSchema, &schema) + err = JSONUnmarshal(testSchema, &schema) assert.Nil(t, err) delete(data, "int")