pkg/util/jsonschema/jsonschema.go
// Copyright © 2023 Horizoncd.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. package jsonschema import ( "encoding/json" "fmt" herrors "github.com/horizoncd/horizon/core/errors" perror "github.com/horizoncd/horizon/pkg/errors" v5jsonschema "github.com/santhosh-tekuri/jsonschema/v5") var ( unevaluatedProperties = "unevaluatedProperties" properties = "properties") // Validate json by jsonschema.// schema and document support 2 types: string, map[string]interface{}Function `Validate` has 6 return statements (exceeds 4 allowed).func Validate(schema, document interface{}, setUnevaluatedPropertiesToFalse bool) error { // change schema type to Golang map var schemaMap map[string]interface{} switch schema := schema.(type) { case string: err := json.Unmarshal([]byte(schema), &schemaMap) if err != nil { return perror.Wrap(herrors.ErrParamInvalid, fmt.Sprintf("json unmarshal error, schema: %s, error: %s", schema, err.Error())) } case map[string]interface{}: schemaMap = schema default: return perror.Wrap(herrors.ErrParamInvalid, fmt.Sprintf("unsported type: %T for schema", schema)) } // add "unevaluatedProperties": false if setUnevaluatedPropertiesToFalse { addUnevaluatedPropertiesField(schemaMap) } var v interface{} switch document := document.(type) { case string: if err := json.Unmarshal([]byte(document), &v); err != nil { return perror.Wrap(herrors.ErrParamInvalid, fmt.Sprintf("json unmarshal error, document: %s, error: %s", document, err.Error())) } case map[string]interface{}: v = document default: return perror.Wrap(herrors.ErrParamInvalid, fmt.Sprintf("unsported type: %T for document", document)) } schemaStr, err := json.Marshal(schemaMap) if err != nil { return perror.Wrap(herrors.ErrParamInvalid, fmt.Sprintf("json marshal error, document: %s, error: %s", document, err.Error())) } sch, err := v5jsonschema.CompileString("schema.json", string(schemaStr)) if err != nil { return perror.Wrap(herrors.ErrParamInvalid, fmt.Sprintf("jsonschema compilestring error, schema: %s, error: %s", schemaStr, err.Error())) } if err = sch.Validate(v); err != nil { return perror.Wrap(herrors.ErrParamInvalid, err.Error()) } return nil} // addUnevaluatedPropertiesField add "unevaluatedProperties": false to the jsonschema// which means no additional properties will be allowed.func addUnevaluatedPropertiesField(m map[string]interface{}) map[string]interface{} { _, propertiesExist := m[properties] _, unevaluatedPropertiesExist := m[unevaluatedProperties] // ignore when schema has already set unevaluatedProperties field if propertiesExist && !unevaluatedPropertiesExist { m[unevaluatedProperties] = false } for _, v := range m { v1, ok := v.(map[string]interface{}) if ok { addUnevaluatedPropertiesField(v1) } } return m}