1// Copyright 2015 go-swagger maintainers 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 15package spec 16 17import ( 18 "bytes" 19 "encoding/gob" 20 "encoding/json" 21 "net/http" 22 "os" 23 "path/filepath" 24 25 "github.com/go-openapi/jsonreference" 26) 27 28// Refable is a struct for things that accept a $ref property 29type Refable struct { 30 Ref Ref 31} 32 33// MarshalJSON marshals the ref to json 34func (r Refable) MarshalJSON() ([]byte, error) { 35 return r.Ref.MarshalJSON() 36} 37 38// UnmarshalJSON unmarshalss the ref from json 39func (r *Refable) UnmarshalJSON(d []byte) error { 40 return json.Unmarshal(d, &r.Ref) 41} 42 43// Ref represents a json reference that is potentially resolved 44type Ref struct { 45 jsonreference.Ref 46} 47 48// RemoteURI gets the remote uri part of the ref 49func (r *Ref) RemoteURI() string { 50 if r.String() == "" { 51 return r.String() 52 } 53 54 u := *r.GetURL() 55 u.Fragment = "" 56 return u.String() 57} 58 59// IsValidURI returns true when the url the ref points to can be found 60func (r *Ref) IsValidURI(basepaths ...string) bool { 61 if r.String() == "" { 62 return true 63 } 64 65 v := r.RemoteURI() 66 if v == "" { 67 return true 68 } 69 70 if r.HasFullURL { 71 rr, err := http.Get(v) 72 if err != nil { 73 return false 74 } 75 76 return rr.StatusCode/100 == 2 77 } 78 79 if !(r.HasFileScheme || r.HasFullFilePath || r.HasURLPathOnly) { 80 return false 81 } 82 83 // check for local file 84 pth := v 85 if r.HasURLPathOnly { 86 base := "." 87 if len(basepaths) > 0 { 88 base = filepath.Dir(filepath.Join(basepaths...)) 89 } 90 p, e := filepath.Abs(filepath.ToSlash(filepath.Join(base, pth))) 91 if e != nil { 92 return false 93 } 94 pth = p 95 } 96 97 fi, err := os.Stat(filepath.ToSlash(pth)) 98 if err != nil { 99 return false 100 } 101 102 return !fi.IsDir() 103} 104 105// Inherits creates a new reference from a parent and a child 106// If the child cannot inherit from the parent, an error is returned 107func (r *Ref) Inherits(child Ref) (*Ref, error) { 108 ref, err := r.Ref.Inherits(child.Ref) 109 if err != nil { 110 return nil, err 111 } 112 return &Ref{Ref: *ref}, nil 113} 114 115// NewRef creates a new instance of a ref object 116// returns an error when the reference uri is an invalid uri 117func NewRef(refURI string) (Ref, error) { 118 ref, err := jsonreference.New(refURI) 119 if err != nil { 120 return Ref{}, err 121 } 122 return Ref{Ref: ref}, nil 123} 124 125// MustCreateRef creates a ref object but panics when refURI is invalid. 126// Use the NewRef method for a version that returns an error. 127func MustCreateRef(refURI string) Ref { 128 return Ref{Ref: jsonreference.MustCreateRef(refURI)} 129} 130 131// MarshalJSON marshals this ref into a JSON object 132func (r Ref) MarshalJSON() ([]byte, error) { 133 str := r.String() 134 if str == "" { 135 if r.IsRoot() { 136 return []byte(`{"$ref":""}`), nil 137 } 138 return []byte("{}"), nil 139 } 140 v := map[string]interface{}{"$ref": str} 141 return json.Marshal(v) 142} 143 144// UnmarshalJSON unmarshals this ref from a JSON object 145func (r *Ref) UnmarshalJSON(d []byte) error { 146 var v map[string]interface{} 147 if err := json.Unmarshal(d, &v); err != nil { 148 return err 149 } 150 return r.fromMap(v) 151} 152 153// GobEncode provides a safe gob encoder for Ref 154func (r Ref) GobEncode() ([]byte, error) { 155 var b bytes.Buffer 156 raw, err := r.MarshalJSON() 157 if err != nil { 158 return nil, err 159 } 160 err = gob.NewEncoder(&b).Encode(raw) 161 return b.Bytes(), err 162} 163 164// GobDecode provides a safe gob decoder for Ref 165func (r *Ref) GobDecode(b []byte) error { 166 var raw []byte 167 buf := bytes.NewBuffer(b) 168 err := gob.NewDecoder(buf).Decode(&raw) 169 if err != nil { 170 return err 171 } 172 return json.Unmarshal(raw, r) 173} 174 175func (r *Ref) fromMap(v map[string]interface{}) error { 176 if v == nil { 177 return nil 178 } 179 180 if vv, ok := v["$ref"]; ok { 181 if str, ok := vv.(string); ok { 182 ref, err := jsonreference.New(str) 183 if err != nil { 184 return err 185 } 186 *r = Ref{Ref: ref} 187 } 188 } 189 190 return nil 191} 192