From 1d82d7353e3287487ac9d9f2e78d56b09c39c57a Mon Sep 17 00:00:00 2001 From: Andrew Marcuse Date: Mon, 11 Jul 2022 00:28:07 -0400 Subject: [PATCH] JSON schema checking (first working version) --- go.mod | 3 ++ go.sum | 7 ++++ internal/command/json.go | 70 +++++++++++++++++++++++++++++++++++++--- testdata/json.txt | 41 +++++++++++++++++++++++ 4 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 testdata/json.txt diff --git a/go.mod b/go.mod index dca6e2a..de46b69 100644 --- a/go.mod +++ b/go.mod @@ -45,6 +45,9 @@ require ( github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/stretchr/testify v1.7.1 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect golang.org/x/text v0.3.7 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/go.sum b/go.sum index bb5f4d0..8c5aa51 100644 --- a/go.sum +++ b/go.sum @@ -88,9 +88,16 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/zyxar/image2ascii v0.0.0-20180912034614-460a04e371ae h1:EiqxsQwk1eimsz+ncJrsMMMwnkYTGiVOrLe5lGxL9cs= github.com/zyxar/image2ascii v0.0.0-20180912034614-460a04e371ae/go.mod h1:Md4Hcw0pmYWDCo1o/fHeOC2Gdhc6oDRwLim8V+SMvI0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/internal/command/json.go b/internal/command/json.go index 514f5b1..63f34e9 100644 --- a/internal/command/json.go +++ b/internal/command/json.go @@ -2,24 +2,37 @@ package command import ( "bytes" + "fmt" + "net/url" + "os" + "path/filepath" "github.com/antchfx/jsonquery" "github.com/fileformat/badger/internal/shared" "github.com/spf13/cobra" + "github.com/xeipuuv/gojsonschema" +) + +var ( + jsonSchemaLocation string + jsonSchema *gojsonschema.Schema ) // jsonCmd represents the json command var jsonCmd = &cobra.Command{ - Args: cobra.MinimumNArgs(1), - Use: "json [options] files...", - Short: "Validate JSON files", - Long: `Check that your JSON files are valid`, - RunE: shared.MakeFileCommand(jsonCheck), + Args: cobra.MinimumNArgs(1), + Use: "json [options] files...", + Short: "Validate JSON files", + Long: `Check that your JSON files are valid`, + PreRunE: jsonInit, + RunE: shared.MakeFileCommand(jsonCheck), } func AddJsonCommand(rootCmd *cobra.Command) { rootCmd.AddCommand(jsonCmd) + jsonCmd.Flags().StringVar(&jsonSchemaLocation, "schema", "", "JSON Schema to validate against") //LATER: link to docs about embedded ones + //LATER: whitespace: canonical/none/any //LATER: schema (https://github.com/xeipuuv/gojsonschema) } @@ -42,4 +55,51 @@ func jsonCheck(f *shared.FileContext) { }) return } + + if jsonSchema != nil { + result, validateErr := jsonSchema.Validate(gojsonschema.NewStringLoader(string(data))) + if validateErr != nil { + f.RecordResult("jsonSchemaRun", false, map[string]interface{}{ + "error": validateErr.Error(), + }) + } else { + f.RecordResult("jsonSchemaValidate", result.Valid(), map[string]interface{}{ + "errors": result.Errors(), + }) + } + } + +} + +func jsonInit(cmd *cobra.Command, args []string) error { + + if jsonSchemaLocation == "" { + return nil + } + + // work with local file urls + jsonUrl, urlParseErr := url.Parse(jsonSchemaLocation) + if urlParseErr != nil { + return urlParseErr + } + + // allow relative local file schemas + if jsonUrl.Scheme == "" { + jsonUrl.Scheme = "file" + jsonPath, pathErr := filepath.Abs(jsonUrl.Path) + if pathErr != nil { + return pathErr + } + jsonUrl.Path = jsonPath + newLocation := jsonUrl.String() + if shared.Debug { + fmt.Fprintf(os.Stderr, "DEBUG: canonicalizing schema path from '%s' to '%s'\n", jsonSchemaLocation, newLocation) + } + jsonSchemaLocation = newLocation + } + + jsonSchemaLoader := gojsonschema.NewReferenceLoader(jsonSchemaLocation) + var schemaErr error + jsonSchema, schemaErr = gojsonschema.NewSchema(jsonSchemaLoader) + return schemaErr } diff --git a/testdata/json.txt b/testdata/json.txt new file mode 100644 index 0000000..91852d1 --- /dev/null +++ b/testdata/json.txt @@ -0,0 +1,41 @@ +exec badger json string.json + +! exec badger json toosmall.json + +exec badger json object.json +exec badger json something.json +! exec badger json trailingcomma.json + +exec badger json --schema=object.schema.json object.json --show-tests=all --debug +exec badger json --schema=object.schema.json --show-tests=all string.json --debug + + +-- toosmall.json -- +{ + +-- string.json -- +"" + +-- object.json -- +{} + +-- something.json -- +{ + "ab": true, + "cd": false +} + +-- trailingcomma.json -- +{ + "ab": true, + "cd": false, +} + +-- object.schema.json -- +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.com/product.schema.json", + "title": "Product", + "description": "A product in the catalog", + "type": "object" +} \ No newline at end of file