diff --git a/bundle/definition/validation_test.go b/bundle/definition/validation_test.go index 724679b0..a032f1a1 100644 --- a/bundle/definition/validation_test.go +++ b/bundle/definition/validation_test.go @@ -42,7 +42,7 @@ func TestObjectValidationValid(t *testing.T) { assert.NoError(t, err) } -func TestObjectValidationValid_CustomValidator(t *testing.T) { +func TestObjectValidationValid_CustomValidator_ContentEncoding_base64(t *testing.T) { s := `{ "type": "object", "properties" : { @@ -71,8 +71,43 @@ func TestObjectValidationValid_CustomValidator(t *testing.T) { File: "SGVsbG8gV29ybGQhCg==", } valErrors, err := definition.Validate(val) + assert.NoError(t, err) assert.Len(t, valErrors, 0, "expected no validation errors") + + invalidVal := struct { + File string `json:"file"` + }{ + File: "SGVsbG8gV29ybGQhCg===", + } + valErrors, err = definition.Validate(invalidVal) + assert.NoError(t, err) + assert.Len(t, valErrors, 1, "expected 1 validation error") + assert.Equal(t, "invalid base64 value: SGVsbG8gV29ybGQhCg===", valErrors[0].Error) +} + +func TestObjectValidationValid_CustomValidator_ContentEncoding_InvalidEncoding(t *testing.T) { + s := `{ + "type": "object", + "properties" : { + "file" : { + "type": "string", + "contentEncoding": "base65" + } + }, + "required" : ["file"] + }` + definition := new(Schema) + err := json.Unmarshal([]byte(s), definition) + + val := struct { + File string `json:"file"` + }{ + File: "SGVsbG8gV29ybGQhCg==", + } + valErrors, err := definition.Validate(val) assert.NoError(t, err) + assert.Len(t, valErrors, 1, "expected 1 validation error") + assert.Equal(t, "unsupported or invalid contentEncoding type of base65", valErrors[0].Error) } func TestObjectValidationInValidMinimum(t *testing.T) { diff --git a/bundle/definition/validators.go b/bundle/definition/validators.go index 18d990ce..d09f36ec 100644 --- a/bundle/definition/validators.go +++ b/bundle/definition/validators.go @@ -1,6 +1,9 @@ package definition import ( + "encoding/base64" + "fmt" + "github.com/qri-io/jsonschema" ) @@ -13,9 +16,22 @@ func NewContentEncoding() jsonschema.Validator { } // Validate implements the Validator interface for ContentEncoding -// Currently, this is a no-op and is only used to register with the jsonschema library -// that 'contentEncoding' is a valid property (as of writing, it isn't one of the defaults) -func (c ContentEncoding) Validate(propPath string, data interface{}, errs *[]jsonschema.ValError) {} +// which, as of writing, isn't included by default in the jsonschema library we consume +func (c ContentEncoding) Validate(propPath string, data interface{}, errs *[]jsonschema.ValError) { + if obj, ok := data.(string); ok { + switch c { + case "base64": + _, err := base64.StdEncoding.DecodeString(obj) + if err != nil { + jsonschema.AddError(errs, propPath, data, fmt.Sprintf("invalid %s value: %s", c, obj)) + } + // Add validation support for other encodings as needed + // See https://json-schema.org/latest/json-schema-validation.html#rfc.section.8.3 + default: + jsonschema.AddError(errs, propPath, data, fmt.Sprintf("unsupported or invalid contentEncoding type of %s", c)) + } + } +} // NewRootSchema returns a jsonschema.RootSchema with any needed custom // jsonschema.Validators pre-registered