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  }