github.com/kaptinlin/jsonschema@v0.4.6/unevaluatedProperties.go (about)

     1  package jsonschema
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  )
     7  
     8  // EvaluateUnevaluatedProperties checks if the unevaluated properties of the data object conform to the unevaluatedProperties schema specified in the schema.
     9  // This is in accordance with the JSON Schema Draft 2020-12 specification which dictates that:
    10  // - The value of "unevaluatedProperties" must be a valid JSON Schema.
    11  // - This keyword's behavior is contingent on the annotations from "properties", "patternProperties", and "additionalProperties".
    12  // - Validation applies only to properties that do not appear in the annotations resulting from the aforementioned keywords.
    13  // - The validation succeeds if such properties conform to the "unevaluatedProperties" schema.
    14  // - The annotations influence the evaluation order, meaning all related properties and applicators must be processed first.
    15  //
    16  // Reference: https://json-schema.org/draft/2020-12/json-schema-core#name-unevaluatedproperties
    17  func evaluateUnevaluatedProperties(schema *Schema, data interface{}, evaluatedProps map[string]bool, _ map[int]bool, dynamicScope *DynamicScope) ([]*EvaluationResult, *EvaluationError) {
    18  	if schema.UnevaluatedProperties == nil {
    19  		return nil, nil // If "unevaluatedProperties" is not defined, all properties are considered evaluated.
    20  	}
    21  
    22  	invalid_properties := []string{}
    23  	results := []*EvaluationResult{}
    24  
    25  	object, ok := data.(map[string]interface{})
    26  	if !ok {
    27  		return nil, nil // If data is not an object, then skip the unevaluatedProperties validation.
    28  	}
    29  
    30  	// Loop through all properties of the object to find unevaluated properties.
    31  	for propName, propValue := range object {
    32  		if _, evaluated := evaluatedProps[propName]; !evaluated {
    33  			// If property has not been evaluated, validate it against the "unevaluatedProperties" schema.
    34  			result, _, _ := schema.UnevaluatedProperties.evaluate(propValue, dynamicScope)
    35  			if result != nil {
    36  				//nolint:errcheck
    37  				result.SetEvaluationPath("/unevaluatedProperties").
    38  					SetSchemaLocation(schema.GetSchemaLocation("/unevaluatedProperties")).
    39  					SetInstanceLocation(fmt.Sprintf("/%s", propName))
    40  
    41  				results = append(results, result)
    42  
    43  				if !result.IsValid() {
    44  					invalid_properties = append(invalid_properties, propName)
    45  				}
    46  			}
    47  			evaluatedProps[propName] = true
    48  		}
    49  	}
    50  
    51  	if len(invalid_properties) == 1 {
    52  		return results, NewEvaluationError("properties", "unevaluated_property_mismatch", "Property {property} does not match the unevaluatedProperties schema", map[string]interface{}{
    53  			"property": fmt.Sprintf("'%s'", invalid_properties[0]),
    54  		})
    55  	} else if len(invalid_properties) > 1 {
    56  		quotedProperties := make([]string, len(invalid_properties))
    57  		for i, prop := range invalid_properties {
    58  			quotedProperties[i] = fmt.Sprintf("'%s'", prop)
    59  		}
    60  		return results, NewEvaluationError("properties", "unevaluated_properties_mismatch", "Properties {properties} do not match the unevaluatedProperties schema", map[string]interface{}{
    61  			"properties": strings.Join(quotedProperties, ", "),
    62  		})
    63  	}
    64  
    65  	return results, nil
    66  }