github.com/emreu/go-swagger@v0.22.1/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  }