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  }