99 "encoding/json"
1010 "fmt"
1111 "github.com/qri-io/jsonpointer"
12+ // "io/ioutil"
13+ "net/http"
1214 "net/url"
1315)
1416
@@ -73,9 +75,7 @@ func (rs *RootSchema) UnmarshalJSON(data []byte) error {
7375 if err := walkJSON (sch , func (elem JSONPather ) error {
7476 if sch , ok := elem .(* Schema ); ok {
7577 if sch .Ref != "" {
76- // fmt.Println(sch.Ref, ids[sch.Ref])
7778 if ids [sch .Ref ] != nil {
78- fmt .Println ("using id:" , sch .Ref )
7979 sch .ref = ids [sch .Ref ]
8080 return nil
8181 }
@@ -107,16 +107,68 @@ func (rs *RootSchema) UnmarshalJSON(data []byte) error {
107107 return nil
108108}
109109
110- func (rs * RootSchema ) ValdiateBytes (data []byte ) error {
110+ // FetchRemoteReferences grabs any url-based schema references that cannot
111+ // be locally resolved via network requests
112+ func (rs * RootSchema ) FetchRemoteReferences () error {
113+ sch := & rs .Schema
114+
115+ // collect IDs for internal referencing:
116+ refs := map [string ]* Schema {}
117+ if err := walkJSON (sch , func (elem JSONPather ) error {
118+ if sch , ok := elem .(* Schema ); ok {
119+ ref := sch .Ref
120+ if ref != "" {
121+ if refs [ref ] == nil {
122+ if u , err := url .Parse (ref ); err == nil {
123+ if res , err := http .Get (u .String ()); err == nil {
124+ s := & RootSchema {}
125+ if err := json .NewDecoder (res .Body ).Decode (s ); err != nil {
126+ return err
127+ }
128+ refs [ref ] = & s .Schema
129+ sch .ref = refs [ref ]
130+ }
131+ }
132+ }
133+ }
134+ }
135+ return nil
136+ }); err != nil {
137+ return err
138+ }
139+
140+ // pass a pointer to the schema component in here (instead of the RootSchema struct)
141+ // to ensure root is evaluated for references
142+ if err := walkJSON (sch , func (elem JSONPather ) error {
143+ if sch , ok := elem .(* Schema ); ok {
144+ if sch .Ref != "" && refs [sch .Ref ] != nil {
145+ if refs [sch .Ref ] != nil {
146+ fmt .Println ("using remote ref:" , sch .Ref )
147+ sch .ref = refs [sch .Ref ]
148+ }
149+ return nil
150+ }
151+ }
152+ return nil
153+ }); err != nil {
154+ return err
155+ }
156+
157+ rs .Schema = * sch
158+ return nil
159+ }
160+
161+ // ValidateBytes performs schema validation against a slice of json byte data
162+ func (rs * RootSchema ) ValidateBytes (data []byte ) error {
111163 var doc interface {}
112164 if err := json .Unmarshal (data , & doc ); err != nil {
113165 return err
114166 }
115167 return rs .Validate (doc )
116168}
117169
118- func (s * RootSchema ) evalJSONValidatorPointer (ptr jsonpointer.Pointer ) (res interface {}, err error ) {
119- res = s
170+ func (rs * RootSchema ) evalJSONValidatorPointer (ptr jsonpointer.Pointer ) (res interface {}, err error ) {
171+ res = rs
120172 for _ , token := range ptr {
121173 if adr , ok := res .(JSONPather ); ok {
122174 res = adr .JSONProp (token )
@@ -164,8 +216,6 @@ const (
164216type Schema struct {
165217 // internal tracking for true/false/{...} schemas
166218 schemaType schemaType
167- // reference to root for ref parsing
168- root * RootSchema
169219 // The "$id" keyword defines a URI for the schema,
170220 // and the base URI that other URI references within the schema are resolved against.
171221 // A subschema's "$id" is resolved against the base URI of its parent schema.
@@ -237,6 +287,11 @@ type Schema struct {
237287 // might get stuck in an infinite recursive loop trying to validate the instance.
238288 // Schemas SHOULD NOT make use of infinite recursive nesting like this; the behavior is undefined.
239289 Ref string `json:"$ref,omitempty"`
290+ // Format functions as both an annotation (Section 3.3) and as an assertion (Section 3.2).
291+ // While no special effort is required to implement it as an annotation conveying semantic meaning,
292+ // implementing validation is non-trivial.
293+ Format string `json:"format,omitempty"`
294+
240295 ref Validator
241296
242297 // Definitions provides a standardized location for schema authors to inline re-usable JSON Schemas
@@ -261,6 +316,7 @@ type _schema struct {
261316 Comment string `json:"comment,omitempty"`
262317 Ref string `json:"$ref,omitempty"`
263318 Definitions map [string ]* Schema `json:"definitions,omitempty"`
319+ Format string `json:"format,omitempty"`
264320}
265321
266322// JSONProp implements the JSONPather for Schema
@@ -286,6 +342,8 @@ func (s Schema) JSONProp(name string) interface{} {
286342 return s .Ref
287343 case "definitions" :
288344 return s .Definitions
345+ case "format" :
346+ return s .Format
289347 default :
290348 prop := s .Validators [name ]
291349 if prop == nil && s .extraDefinitions [name ] != nil {
@@ -295,7 +353,7 @@ func (s Schema) JSONProp(name string) interface{} {
295353 }
296354}
297355
298- // JSONChildren implements the JSONPather for Schema
356+ // JSONChildren implements the JSONContainer interface for Schema
299357func (s * Schema ) JSONChildren () (ch map [string ]JSONPather ) {
300358 ch = map [string ]JSONPather {}
301359
@@ -350,6 +408,7 @@ func (s *Schema) UnmarshalJSON(data []byte) error {
350408 Comment : _s .Comment ,
351409 Ref : _s .Ref ,
352410 Definitions : _s .Definitions ,
411+ Format : _s .Format ,
353412 Validators : map [string ]Validator {},
354413 }
355414
@@ -377,7 +436,7 @@ func (s *Schema) UnmarshalJSON(data []byte) error {
377436 // props to validator factory functions
378437 switch prop {
379438 // skip any already-parsed props
380- case "$id" , "title" , "description" , "default" , "examples" , "readOnly" , "writeOnly" , "comment" , "$ref" , "definitions" :
439+ case "$schema" , "$ id" , "title" , "description" , "default" , "examples" , "readOnly" , "writeOnly" , "comment" , "$ref" , "definitions" , "format " :
381440 continue
382441 case "type" :
383442 val = new (Type )
@@ -461,6 +520,17 @@ func (s *Schema) UnmarshalJSON(data []byte) error {
461520 sch .Validators [prop ] = val
462521 }
463522
523+ if sch .Validators ["if" ] != nil {
524+ if ite , ok := sch .Validators ["if" ].(* If ); ok {
525+ if s , ok := sch .Validators ["then" ].(* Then ); ok {
526+ ite .Then = s
527+ }
528+ if s , ok := sch .Validators ["else" ].(* Else ); ok {
529+ ite .Else = s
530+ }
531+ }
532+ }
533+
464534 // TODO - replace all these assertions with methods on Schema that return proper types
465535 if sch .Validators ["items" ] != nil && sch .Validators ["additionalItems" ] != nil && ! sch .Validators ["items" ].(* Items ).single {
466536 sch .Validators ["additionalItems" ].(* AdditionalItems ).startIndex = len (sch .Validators ["items" ].(* Items ).Schemas )
@@ -482,6 +552,9 @@ func (s *Schema) Validate(data interface{}) error {
482552 return s .ref .Validate (data )
483553 }
484554
555+ // TODO - so far all default.json tests pass when no use of "default" is made.
556+ // Is this correct?
557+
485558 for _ , v := range s .Validators {
486559 if err := v .Validate (data ); err != nil {
487560 return err
@@ -490,12 +563,16 @@ func (s *Schema) Validate(data interface{}) error {
490563 return nil
491564}
492565
566+ // Definitions implements a map of schemas while also satsfying the JSON
567+ // traversal methods
493568type Definitions map [string ]* Schema
494569
570+ // JSONProp implements the JSONPather for Definitions
495571func (d Definitions ) JSONProp (name string ) interface {} {
496572 return d [name ]
497573}
498574
575+ // JSONChildren implements the JSONContainer interface for Definitions
499576func (d Definitions ) JSONChildren () (r map [string ]JSONPather ) {
500577 r = map [string ]JSONPather {}
501578 // fmt.Println("getting children for definitions:", d)
0 commit comments