github.com/emreu/go-swagger@v0.22.1/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  		URL:   url,
   205  		Name:  name,
   206  		Email: email,
   207  	}, nil
   208  }
   209  
   210  func setInfoLicense(swspec *spec.Swagger, lines []string) error {
   211  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   212  		return nil
   213  	}
   214  	info := safeInfo(swspec)
   215  	line := lines[0]
   216  	name, url := splitURL(line)
   217  	info.License = &spec.License{
   218  		Name: name,
   219  		URL:  url,
   220  	}
   221  	return nil
   222  }
   223  
   224  func safeInfo(swspec *spec.Swagger) *spec.Info {
   225  	if swspec.Info == nil {
   226  		swspec.Info = new(spec.Info)
   227  	}
   228  	return swspec.Info
   229  }
   230  
   231  // httpFTPScheme matches http://, https://, ws://, wss://
   232  var httpFTPScheme = regexp.MustCompile("(?:(?:ht|f)tp|ws)s?://")
   233  
   234  func splitURL(line string) (notURL, url string) {
   235  	str := strings.TrimSpace(line)
   236  	parts := httpFTPScheme.FindStringIndex(str)
   237  	if len(parts) == 0 {
   238  		if len(str) > 0 {
   239  			notURL = str
   240  		}
   241  		return
   242  	}
   243  	if len(parts) > 0 {
   244  		notURL = strings.TrimSpace(str[:parts[0]])
   245  		url = strings.TrimSpace(str[parts[0]:])
   246  	}
   247  	return
   248  }