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 "fmt" 19 "net/url" 20 "os" 21 "path" 22 "path/filepath" 23 "strings" 24) 25 26// normalize absolute path for cache. 27// on Windows, drive letters should be converted to lower as scheme in net/url.URL 28func normalizeAbsPath(path string) string { 29 u, err := url.Parse(path) 30 if err != nil { 31 debugLog("normalize absolute path failed: %s", err) 32 return path 33 } 34 return u.String() 35} 36 37// base or refPath could be a file path or a URL 38// given a base absolute path and a ref path, return the absolute path of refPath 39// 1) if refPath is absolute, return it 40// 2) if refPath is relative, join it with basePath keeping the scheme, hosts, and ports if exists 41// base could be a directory or a full file path 42func normalizePaths(refPath, base string) string { 43 refURL, _ := url.Parse(refPath) 44 if path.IsAbs(refURL.Path) || filepath.IsAbs(refPath) { 45 // refPath is actually absolute 46 if refURL.Host != "" { 47 return refPath 48 } 49 parts := strings.Split(refPath, "#") 50 result := filepath.FromSlash(parts[0]) 51 if len(parts) == 2 { 52 result += "#" + parts[1] 53 } 54 return result 55 } 56 57 // relative refPath 58 baseURL, _ := url.Parse(base) 59 if !strings.HasPrefix(refPath, "#") { 60 // combining paths 61 if baseURL.Host != "" { 62 baseURL.Path = path.Join(path.Dir(baseURL.Path), refURL.Path) 63 } else { // base is a file 64 newBase := fmt.Sprintf("%s#%s", filepath.Join(filepath.Dir(base), filepath.FromSlash(refURL.Path)), refURL.Fragment) 65 return newBase 66 } 67 68 } 69 // copying fragment from ref to base 70 baseURL.Fragment = refURL.Fragment 71 return baseURL.String() 72} 73 74// denormalizePaths returns to simplest notation on file $ref, 75// i.e. strips the absolute path and sets a path relative to the base path. 76// 77// This is currently used when we rewrite ref after a circular ref has been detected 78func denormalizeFileRef(ref *Ref, relativeBase, originalRelativeBase string) *Ref { 79 debugLog("denormalizeFileRef for: %s", ref.String()) 80 81 if ref.String() == "" || ref.IsRoot() || ref.HasFragmentOnly { 82 return ref 83 } 84 // strip relativeBase from URI 85 relativeBaseURL, _ := url.Parse(relativeBase) 86 relativeBaseURL.Fragment = "" 87 88 if relativeBaseURL.IsAbs() && strings.HasPrefix(ref.String(), relativeBase) { 89 // this should work for absolute URI (e.g. http://...): we have an exact match, just trim prefix 90 r, _ := NewRef(strings.TrimPrefix(ref.String(), relativeBase)) 91 return &r 92 } 93 94 if relativeBaseURL.IsAbs() { 95 // other absolute URL get unchanged (i.e. with a non-empty scheme) 96 return ref 97 } 98 99 // for relative file URIs: 100 originalRelativeBaseURL, _ := url.Parse(originalRelativeBase) 101 originalRelativeBaseURL.Fragment = "" 102 if strings.HasPrefix(ref.String(), originalRelativeBaseURL.String()) { 103 // the resulting ref is in the expanded spec: return a local ref 104 r, _ := NewRef(strings.TrimPrefix(ref.String(), originalRelativeBaseURL.String())) 105 return &r 106 } 107 108 // check if we may set a relative path, considering the original base path for this spec. 109 // Example: 110 // spec is located at /mypath/spec.json 111 // my normalized ref points to: /mypath/item.json#/target 112 // expected result: item.json#/target 113 parts := strings.Split(ref.String(), "#") 114 relativePath, err := filepath.Rel(path.Dir(originalRelativeBaseURL.String()), parts[0]) 115 if err != nil { 116 // there is no common ancestor (e.g. different drives on windows) 117 // leaves the ref unchanged 118 return ref 119 } 120 if len(parts) == 2 { 121 relativePath += "#" + parts[1] 122 } 123 r, _ := NewRef(relativePath) 124 return &r 125} 126 127// relativeBase could be an ABSOLUTE file path or an ABSOLUTE URL 128func normalizeFileRef(ref *Ref, relativeBase string) *Ref { 129 // This is important for when the reference is pointing to the root schema 130 if ref.String() == "" { 131 r, _ := NewRef(relativeBase) 132 return &r 133 } 134 135 debugLog("normalizing %s against %s", ref.String(), relativeBase) 136 137 s := normalizePaths(ref.String(), relativeBase) 138 r, _ := NewRef(s) 139 return &r 140} 141 142// absPath returns the absolute path of a file 143func absPath(fname string) (string, error) { 144 if strings.HasPrefix(fname, "http") { 145 return fname, nil 146 } 147 if filepath.IsAbs(fname) { 148 return fname, nil 149 } 150 wd, err := os.Getwd() 151 return filepath.Join(wd, fname), err 152} 153