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