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