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  }