github.com/rzurga/go-swagger@v0.28.1-0.20211109195225-5d1f453ffa3a/scan/meta.go (about) 1 // +build !go1.11 2 3 // Copyright 2015 go-swagger maintainers 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package scan 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "net/mail" 23 "regexp" 24 "strings" 25 26 "github.com/go-openapi/spec" 27 ) 28 29 func metaTOSSetter(meta *spec.Info) func([]string) { 30 return func(lines []string) { 31 meta.TermsOfService = joinDropLast(lines) 32 } 33 } 34 35 func metaConsumesSetter(meta *spec.Swagger) func([]string) { 36 return func(consumes []string) { meta.Consumes = consumes } 37 } 38 39 func metaProducesSetter(meta *spec.Swagger) func([]string) { 40 return func(produces []string) { meta.Produces = produces } 41 } 42 43 func metaSchemeSetter(meta *spec.Swagger) func([]string) { 44 return func(schemes []string) { meta.Schemes = schemes } 45 } 46 47 func metaSecuritySetter(meta *spec.Swagger) func([]map[string][]string) { 48 return func(secDefs []map[string][]string) { meta.Security = secDefs } 49 } 50 51 func metaSecurityDefinitionsSetter(meta *spec.Swagger) func(json.RawMessage) error { 52 return func(jsonValue json.RawMessage) error { 53 var jsonData spec.SecurityDefinitions 54 err := json.Unmarshal(jsonValue, &jsonData) 55 if err != nil { 56 return err 57 } 58 meta.SecurityDefinitions = jsonData 59 return nil 60 } 61 } 62 63 func metaVendorExtensibleSetter(meta *spec.Swagger) func(json.RawMessage) error { 64 return func(jsonValue json.RawMessage) error { 65 var jsonData spec.Extensions 66 err := json.Unmarshal(jsonValue, &jsonData) 67 if err != nil { 68 return err 69 } 70 for k := range jsonData { 71 if !rxAllowedExtensions.MatchString(k) { 72 return fmt.Errorf("invalid schema extension name, should start from `x-`: %s", k) 73 } 74 } 75 meta.Extensions = jsonData 76 return nil 77 } 78 } 79 80 func infoVendorExtensibleSetter(meta *spec.Swagger) func(json.RawMessage) error { 81 return func(jsonValue json.RawMessage) error { 82 var jsonData spec.Extensions 83 err := json.Unmarshal(jsonValue, &jsonData) 84 if err != nil { 85 return err 86 } 87 for k := range jsonData { 88 if !rxAllowedExtensions.MatchString(k) { 89 return fmt.Errorf("invalid schema extension name, should start from `x-`: %s", k) 90 } 91 } 92 meta.Info.Extensions = jsonData 93 return nil 94 } 95 } 96 97 func newMetaParser(swspec *spec.Swagger) *sectionedParser { 98 sp := new(sectionedParser) 99 if swspec.Info == nil { 100 swspec.Info = new(spec.Info) 101 } 102 info := swspec.Info 103 sp.setTitle = func(lines []string) { 104 tosave := joinDropLast(lines) 105 if len(tosave) > 0 { 106 tosave = rxStripTitleComments.ReplaceAllString(tosave, "") 107 } 108 info.Title = tosave 109 } 110 sp.setDescription = func(lines []string) { info.Description = joinDropLast(lines) } 111 sp.taggers = []tagParser{ 112 newMultiLineTagParser("TOS", newMultilineDropEmptyParser(rxTOS, metaTOSSetter(info)), false), 113 newMultiLineTagParser("Consumes", newMultilineDropEmptyParser(rxConsumes, metaConsumesSetter(swspec)), false), 114 newMultiLineTagParser("Produces", newMultilineDropEmptyParser(rxProduces, metaProducesSetter(swspec)), false), 115 newSingleLineTagParser("Schemes", newSetSchemes(metaSchemeSetter(swspec))), 116 newMultiLineTagParser("Security", newSetSecurity(rxSecuritySchemes, metaSecuritySetter(swspec)), false), 117 newMultiLineTagParser("SecurityDefinitions", newYamlParser(rxSecurity, metaSecurityDefinitionsSetter(swspec)), true), 118 newSingleLineTagParser("Version", &setMetaSingle{swspec, rxVersion, setInfoVersion}), 119 newSingleLineTagParser("Host", &setMetaSingle{swspec, rxHost, setSwaggerHost}), 120 newSingleLineTagParser("BasePath", &setMetaSingle{swspec, rxBasePath, setSwaggerBasePath}), 121 newSingleLineTagParser("Contact", &setMetaSingle{swspec, rxContact, setInfoContact}), 122 newSingleLineTagParser("License", &setMetaSingle{swspec, rxLicense, setInfoLicense}), 123 newMultiLineTagParser("YAMLInfoExtensionsBlock", newYamlParser(rxInfoExtensions, infoVendorExtensibleSetter(swspec)), true), 124 newMultiLineTagParser("YAMLExtensionsBlock", newYamlParser(rxExtensions, metaVendorExtensibleSetter(swspec)), true), 125 } 126 return sp 127 } 128 129 type setMetaSingle struct { 130 spec *spec.Swagger 131 rx *regexp.Regexp 132 set func(spec *spec.Swagger, lines []string) error 133 } 134 135 func (s *setMetaSingle) Matches(line string) bool { 136 return s.rx.MatchString(line) 137 } 138 139 func (s *setMetaSingle) Parse(lines []string) error { 140 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 141 return nil 142 } 143 matches := s.rx.FindStringSubmatch(lines[0]) 144 if len(matches) > 1 && len(matches[1]) > 0 { 145 return s.set(s.spec, []string{matches[1]}) 146 } 147 return nil 148 } 149 150 func setSwaggerHost(swspec *spec.Swagger, lines []string) error { 151 lns := lines 152 if len(lns) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 153 lns = []string{"localhost"} 154 } 155 swspec.Host = lns[0] 156 return nil 157 } 158 159 func setSwaggerBasePath(swspec *spec.Swagger, lines []string) error { 160 var ln string 161 if len(lines) > 0 { 162 ln = lines[0] 163 } 164 swspec.BasePath = ln 165 return nil 166 } 167 168 func setInfoVersion(swspec *spec.Swagger, lines []string) error { 169 if len(lines) == 0 { 170 return nil 171 } 172 info := safeInfo(swspec) 173 info.Version = strings.TrimSpace(lines[0]) 174 return nil 175 } 176 177 func setInfoContact(swspec *spec.Swagger, lines []string) error { 178 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 179 return nil 180 } 181 contact, err := parseContactInfo(lines[0]) 182 if err != nil { 183 return err 184 } 185 info := safeInfo(swspec) 186 info.Contact = contact 187 return nil 188 } 189 190 func parseContactInfo(line string) (*spec.ContactInfo, error) { 191 nameEmail, url := splitURL(line) 192 var name, email string 193 if len(nameEmail) > 0 { 194 addr, err := mail.ParseAddress(nameEmail) 195 if err != nil { 196 return nil, err 197 } 198 name, email = addr.Name, addr.Address 199 } 200 return &spec.ContactInfo{ 201 URL: url, 202 Name: name, 203 Email: email, 204 }, nil 205 } 206 207 func setInfoLicense(swspec *spec.Swagger, lines []string) error { 208 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 209 return nil 210 } 211 info := safeInfo(swspec) 212 line := lines[0] 213 name, url := splitURL(line) 214 info.License = &spec.License{ 215 Name: name, 216 URL: url, 217 } 218 return nil 219 } 220 221 func safeInfo(swspec *spec.Swagger) *spec.Info { 222 if swspec.Info == nil { 223 swspec.Info = new(spec.Info) 224 } 225 return swspec.Info 226 } 227 228 // httpFTPScheme matches http://, https://, ws://, wss:// 229 var httpFTPScheme = regexp.MustCompile("(?:(?:ht|f)tp|ws)s?://") 230 231 func splitURL(line string) (notURL, url string) { 232 str := strings.TrimSpace(line) 233 parts := httpFTPScheme.FindStringIndex(str) 234 if len(parts) == 0 { 235 if len(str) > 0 { 236 notURL = str 237 } 238 return 239 } 240 if len(parts) > 0 { 241 notURL = strings.TrimSpace(str[:parts[0]]) 242 url = strings.TrimSpace(str[parts[0]:]) 243 } 244 return 245 }