github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/data/json_validator.go (about) 1 // Copyright © 2021 Kaleido, Inc. 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package data 18 19 import ( 20 "context" 21 "strings" 22 23 "github.com/kaleido-io/firefly/internal/i18n" 24 "github.com/kaleido-io/firefly/internal/log" 25 "github.com/kaleido-io/firefly/pkg/fftypes" 26 "github.com/xeipuuv/gojsonschema" 27 ) 28 29 type jsonValidator struct { 30 id *fftypes.UUID 31 size int64 32 ns string 33 datatype *fftypes.DatatypeRef 34 schema *gojsonschema.Schema 35 } 36 37 func newJSONValidator(ctx context.Context, ns string, datatype *fftypes.Datatype) (*jsonValidator, error) { 38 jv := &jsonValidator{ 39 id: datatype.ID, 40 ns: ns, 41 datatype: &fftypes.DatatypeRef{ 42 Name: datatype.Name, 43 Version: datatype.Version, 44 }, 45 } 46 47 schemaBytes := []byte(datatype.Value) 48 sl := gojsonschema.NewBytesLoader(schemaBytes) 49 schema, err := gojsonschema.NewSchema(sl) 50 if err != nil { 51 return nil, i18n.WrapError(ctx, err, i18n.MsgSchemaLoadFailed, jv.datatype) 52 } 53 jv.schema = schema 54 jv.size = int64(len(schemaBytes)) 55 56 log.L(ctx).Debugf("Found JSON schema validator for json:%s:%s: %v", jv.ns, datatype, jv.id) 57 return jv, nil 58 } 59 60 func (jv *jsonValidator) Validate(ctx context.Context, data *fftypes.Data) error { 61 return jv.ValidateValue(ctx, data.Value, data.Hash) 62 } 63 64 func (jv *jsonValidator) ValidateValue(ctx context.Context, value fftypes.Byteable, expectedHash *fftypes.Bytes32) error { 65 if value == nil { 66 return i18n.NewError(ctx, i18n.MsgDataValueIsNull) 67 } 68 69 if expectedHash != nil { 70 hash := value.Hash() 71 if *hash != *expectedHash { 72 return i18n.NewError(ctx, i18n.MsgDataInvalidHash, hash, expectedHash) 73 } 74 } 75 76 err := jv.validateBytes(ctx, []byte(value)) 77 if err != nil { 78 log.L(ctx).Warnf("JSON schema %s [%v] validation failed: %s", jv.datatype, jv.id, err) 79 } 80 return err 81 } 82 83 func (jv *jsonValidator) validateBytes(ctx context.Context, b []byte) error { 84 res, err := jv.schema.Validate(gojsonschema.NewBytesLoader(b)) 85 if err != nil { 86 return i18n.WrapError(ctx, err, i18n.MsgDataCannotBeValidated) 87 } 88 if !res.Valid() { 89 errStrings := make([]string, len(res.Errors())) 90 for i, e := range res.Errors() { 91 errStrings[i] = e.String() 92 } 93 errors := strings.Join(errStrings, ",") 94 return i18n.NewError(ctx, i18n.MsgJSONDataInvalidPerSchema, jv.datatype, errors) 95 } 96 return nil 97 } 98 99 func (jv *jsonValidator) Size() int64 { 100 return jv.size 101 }