normalizer.go 4.7 KB

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