reader.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // Copyright 2017 Google Inc. All Rights Reserved.
  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 compiler
  15. import (
  16. "fmt"
  17. "gopkg.in/yaml.v2"
  18. "io/ioutil"
  19. "log"
  20. "net/http"
  21. "net/url"
  22. "path/filepath"
  23. "strings"
  24. )
  25. var fileCache map[string][]byte
  26. var infoCache map[string]interface{}
  27. var count int64
  28. var verboseReader = false
  29. func initializeFileCache() {
  30. if fileCache == nil {
  31. fileCache = make(map[string][]byte, 0)
  32. }
  33. }
  34. func initializeInfoCache() {
  35. if infoCache == nil {
  36. infoCache = make(map[string]interface{}, 0)
  37. }
  38. }
  39. // FetchFile gets a specified file from the local filesystem or a remote location.
  40. func FetchFile(fileurl string) ([]byte, error) {
  41. initializeFileCache()
  42. bytes, ok := fileCache[fileurl]
  43. if ok {
  44. if verboseReader {
  45. log.Printf("Cache hit %s", fileurl)
  46. }
  47. return bytes, nil
  48. }
  49. log.Printf("Fetching %s", fileurl)
  50. response, err := http.Get(fileurl)
  51. if err != nil {
  52. return nil, err
  53. }
  54. defer response.Body.Close()
  55. bytes, err = ioutil.ReadAll(response.Body)
  56. if err == nil {
  57. fileCache[fileurl] = bytes
  58. }
  59. return bytes, err
  60. }
  61. // ReadBytesForFile reads the bytes of a file.
  62. func ReadBytesForFile(filename string) ([]byte, error) {
  63. // is the filename a url?
  64. fileurl, _ := url.Parse(filename)
  65. if fileurl.Scheme != "" {
  66. // yes, fetch it
  67. bytes, err := FetchFile(filename)
  68. if err != nil {
  69. return nil, err
  70. }
  71. return bytes, nil
  72. }
  73. // no, it's a local filename
  74. bytes, err := ioutil.ReadFile(filename)
  75. if err != nil {
  76. return nil, err
  77. }
  78. return bytes, nil
  79. }
  80. // ReadInfoFromBytes unmarshals a file as a yaml.MapSlice.
  81. func ReadInfoFromBytes(filename string, bytes []byte) (interface{}, error) {
  82. initializeInfoCache()
  83. cachedInfo, ok := infoCache[filename]
  84. if ok {
  85. if verboseReader {
  86. log.Printf("Cache hit info for file %s", filename)
  87. }
  88. return cachedInfo, nil
  89. }
  90. if verboseReader {
  91. log.Printf("Reading info for file %s", filename)
  92. }
  93. var info yaml.MapSlice
  94. err := yaml.Unmarshal(bytes, &info)
  95. if err != nil {
  96. return nil, err
  97. }
  98. infoCache[filename] = info
  99. return info, nil
  100. }
  101. // ReadInfoForRef reads a file and return the fragment needed to resolve a $ref.
  102. func ReadInfoForRef(basefile string, ref string) (interface{}, error) {
  103. initializeInfoCache()
  104. {
  105. info, ok := infoCache[ref]
  106. if ok {
  107. if verboseReader {
  108. log.Printf("Cache hit for ref %s#%s", basefile, ref)
  109. }
  110. return info, nil
  111. }
  112. }
  113. if verboseReader {
  114. log.Printf("Reading info for ref %s#%s", basefile, ref)
  115. }
  116. count = count + 1
  117. basedir, _ := filepath.Split(basefile)
  118. parts := strings.Split(ref, "#")
  119. var filename string
  120. if parts[0] != "" {
  121. filename = basedir + parts[0]
  122. } else {
  123. filename = basefile
  124. }
  125. bytes, err := ReadBytesForFile(filename)
  126. if err != nil {
  127. return nil, err
  128. }
  129. info, err := ReadInfoFromBytes(filename, bytes)
  130. if err != nil {
  131. log.Printf("File error: %v\n", err)
  132. } else {
  133. if len(parts) > 1 {
  134. path := strings.Split(parts[1], "/")
  135. for i, key := range path {
  136. if i > 0 {
  137. m, ok := info.(yaml.MapSlice)
  138. if ok {
  139. found := false
  140. for _, section := range m {
  141. if section.Key == key {
  142. info = section.Value
  143. found = true
  144. }
  145. }
  146. if !found {
  147. infoCache[ref] = nil
  148. return nil, NewError(nil, fmt.Sprintf("could not resolve %s", ref))
  149. }
  150. }
  151. }
  152. }
  153. }
  154. }
  155. infoCache[ref] = info
  156. return info, nil
  157. }