...

Source file src/github.com/xeipuuv/gojsonschema/schemaLoader.go

Documentation: github.com/xeipuuv/gojsonschema

     1  // Copyright 2018 johandorland ( https://github.com/johandorland )
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //   http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package gojsonschema
    16  
    17  import (
    18  	"bytes"
    19  	"errors"
    20  
    21  	"github.com/xeipuuv/gojsonreference"
    22  )
    23  
    24  // SchemaLoader is used to load schemas
    25  type SchemaLoader struct {
    26  	pool       *schemaPool
    27  	AutoDetect bool
    28  	Validate   bool
    29  	Draft      Draft
    30  }
    31  
    32  // NewSchemaLoader creates a new NewSchemaLoader
    33  func NewSchemaLoader() *SchemaLoader {
    34  
    35  	ps := &SchemaLoader{
    36  		pool: &schemaPool{
    37  			schemaPoolDocuments: make(map[string]*schemaPoolDocument),
    38  		},
    39  		AutoDetect: true,
    40  		Validate:   false,
    41  		Draft:      Hybrid,
    42  	}
    43  	ps.pool.autoDetect = &ps.AutoDetect
    44  
    45  	return ps
    46  }
    47  
    48  func (sl *SchemaLoader) validateMetaschema(documentNode interface{}) error {
    49  
    50  	var (
    51  		schema string
    52  		err    error
    53  	)
    54  	if sl.AutoDetect {
    55  		schema, _, err = parseSchemaURL(documentNode)
    56  		if err != nil {
    57  			return err
    58  		}
    59  	}
    60  
    61  	// If no explicit "$schema" is used, use the default metaschema associated with the draft used
    62  	if schema == "" {
    63  		if sl.Draft == Hybrid {
    64  			return nil
    65  		}
    66  		schema = drafts.GetSchemaURL(sl.Draft)
    67  	}
    68  
    69  	//Disable validation when loading the metaschema to prevent an infinite recursive loop
    70  	sl.Validate = false
    71  
    72  	metaSchema, err := sl.Compile(NewReferenceLoader(schema))
    73  
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	sl.Validate = true
    79  
    80  	result := metaSchema.validateDocument(documentNode)
    81  
    82  	if !result.Valid() {
    83  		var res bytes.Buffer
    84  		for _, err := range result.Errors() {
    85  			res.WriteString(err.String())
    86  			res.WriteString("\n")
    87  		}
    88  		return errors.New(res.String())
    89  	}
    90  
    91  	return nil
    92  }
    93  
    94  // AddSchemas adds an arbritrary amount of schemas to the schema cache. As this function does not require
    95  // an explicit URL, every schema should contain an $id, so that it can be referenced by the main schema
    96  func (sl *SchemaLoader) AddSchemas(loaders ...JSONLoader) error {
    97  	emptyRef, _ := gojsonreference.NewJsonReference("")
    98  
    99  	for _, loader := range loaders {
   100  		doc, err := loader.LoadJSON()
   101  
   102  		if err != nil {
   103  			return err
   104  		}
   105  
   106  		if sl.Validate {
   107  			if err := sl.validateMetaschema(doc); err != nil {
   108  				return err
   109  			}
   110  		}
   111  
   112  		// Directly use the Recursive function, so that it get only added to the schema pool by $id
   113  		// and not by the ref of the document as it's empty
   114  		if err = sl.pool.parseReferences(doc, emptyRef, false); err != nil {
   115  			return err
   116  		}
   117  	}
   118  
   119  	return nil
   120  }
   121  
   122  //AddSchema adds a schema under the provided URL to the schema cache
   123  func (sl *SchemaLoader) AddSchema(url string, loader JSONLoader) error {
   124  
   125  	ref, err := gojsonreference.NewJsonReference(url)
   126  
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	doc, err := loader.LoadJSON()
   132  
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	if sl.Validate {
   138  		if err := sl.validateMetaschema(doc); err != nil {
   139  			return err
   140  		}
   141  	}
   142  
   143  	return sl.pool.parseReferences(doc, ref, true)
   144  }
   145  
   146  // Compile loads and compiles a schema
   147  func (sl *SchemaLoader) Compile(rootSchema JSONLoader) (*Schema, error) {
   148  
   149  	ref, err := rootSchema.JsonReference()
   150  
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	d := Schema{}
   156  	d.pool = sl.pool
   157  	d.pool.jsonLoaderFactory = rootSchema.LoaderFactory()
   158  	d.documentReference = ref
   159  	d.referencePool = newSchemaReferencePool()
   160  
   161  	var doc interface{}
   162  	if ref.String() != "" {
   163  		// Get document from schema pool
   164  		spd, err := d.pool.GetDocument(d.documentReference)
   165  		if err != nil {
   166  			return nil, err
   167  		}
   168  		doc = spd.Document
   169  	} else {
   170  		// Load JSON directly
   171  		doc, err = rootSchema.LoadJSON()
   172  		if err != nil {
   173  			return nil, err
   174  		}
   175  		// References need only be parsed if loading JSON directly
   176  		//  as pool.GetDocument already does this for us if loading by reference
   177  		err = sl.pool.parseReferences(doc, ref, true)
   178  		if err != nil {
   179  			return nil, err
   180  		}
   181  	}
   182  
   183  	if sl.Validate {
   184  		if err := sl.validateMetaschema(doc); err != nil {
   185  			return nil, err
   186  		}
   187  	}
   188  
   189  	draft := sl.Draft
   190  	if sl.AutoDetect {
   191  		_, detectedDraft, err := parseSchemaURL(doc)
   192  		if err != nil {
   193  			return nil, err
   194  		}
   195  		if detectedDraft != nil {
   196  			draft = *detectedDraft
   197  		}
   198  	}
   199  
   200  	err = d.parse(doc, draft)
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  
   205  	return &d, nil
   206  }
   207  

View as plain text