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