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 "" 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 //nolint:noctx,gosec 72 rr, err := http.Get(v) 73 if err != nil { 74 return false 75 } 76 defer rr.Body.Close() 77 78 return rr.StatusCode/100 == 2 79 } 80 81 if !(r.HasFileScheme || r.HasFullFilePath || r.HasURLPathOnly) { 82 return false 83 } 84 85 // check for local file 86 pth := v 87 if r.HasURLPathOnly { 88 base := "." 89 if len(basepaths) > 0 { 90 base = filepath.Dir(filepath.Join(basepaths...)) 91 } 92 p, e := filepath.Abs(filepath.ToSlash(filepath.Join(base, pth))) 93 if e != nil { 94 return false 95 } 96 pth = p 97 } 98 99 fi, err := os.Stat(filepath.ToSlash(pth)) 100 if err != nil { 101 return false 102 } 103 104 return !fi.IsDir() 105} 106 107// Inherits creates a new reference from a parent and a child 108// If the child cannot inherit from the parent, an error is returned 109func (r *Ref) Inherits(child Ref) (*Ref, error) { 110 ref, err := r.Ref.Inherits(child.Ref) 111 if err != nil { 112 return nil, err 113 } 114 return &Ref{Ref: *ref}, nil 115} 116 117// NewRef creates a new instance of a ref object 118// returns an error when the reference uri is an invalid uri 119func NewRef(refURI string) (Ref, error) { 120 ref, err := jsonreference.New(refURI) 121 if err != nil { 122 return Ref{}, err 123 } 124 return Ref{Ref: ref}, nil 125} 126 127// MustCreateRef creates a ref object but panics when refURI is invalid. 128// Use the NewRef method for a version that returns an error. 129func MustCreateRef(refURI string) Ref { 130 return Ref{Ref: jsonreference.MustCreateRef(refURI)} 131} 132 133// MarshalJSON marshals this ref into a JSON object 134func (r Ref) MarshalJSON() ([]byte, error) { 135 str := r.String() 136 if str == "" { 137 if r.IsRoot() { 138 return []byte(`{"$ref":""}`), nil 139 } 140 return []byte("{}"), nil 141 } 142 v := map[string]interface{}{"$ref": str} 143 return json.Marshal(v) 144} 145 146// UnmarshalJSON unmarshals this ref from a JSON object 147func (r *Ref) UnmarshalJSON(d []byte) error { 148 var v map[string]interface{} 149 if err := json.Unmarshal(d, &v); err != nil { 150 return err 151 } 152 return r.fromMap(v) 153} 154 155// GobEncode provides a safe gob encoder for Ref 156func (r Ref) GobEncode() ([]byte, error) { 157 var b bytes.Buffer 158 raw, err := r.MarshalJSON() 159 if err != nil { 160 return nil, err 161 } 162 err = gob.NewEncoder(&b).Encode(raw) 163 return b.Bytes(), err 164} 165 166// GobDecode provides a safe gob decoder for Ref 167func (r *Ref) GobDecode(b []byte) error { 168 var raw []byte 169 buf := bytes.NewBuffer(b) 170 err := gob.NewDecoder(buf).Decode(&raw) 171 if err != nil { 172 return err 173 } 174 return json.Unmarshal(raw, r) 175} 176 177func (r *Ref) fromMap(v map[string]interface{}) error { 178 if v == nil { 179 return nil 180 } 181 182 if vv, ok := v["$ref"]; ok { 183 if str, ok := vv.(string); ok { 184 ref, err := jsonreference.New(str) 185 if err != nil { 186 return err 187 } 188 *r = Ref{Ref: ref} 189 } 190 } 191 192 return nil 193} 194