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