github.com/jamescostian/go-swagger@v0.30.4-0.20221130163922-68364d6b567b/scan/routes.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  	"fmt"
    22  	"go/ast"
    23  
    24  	"github.com/go-openapi/spec"
    25  
    26  	"golang.org/x/tools/go/loader"
    27  )
    28  
    29  func opConsumesSetter(op *spec.Operation) func([]string) {
    30  	return func(consumes []string) { op.Consumes = consumes }
    31  }
    32  
    33  func opProducesSetter(op *spec.Operation) func([]string) {
    34  	return func(produces []string) { op.Produces = produces }
    35  }
    36  
    37  func opSchemeSetter(op *spec.Operation) func([]string) {
    38  	return func(schemes []string) { op.Schemes = schemes }
    39  }
    40  
    41  func opSecurityDefsSetter(op *spec.Operation) func([]map[string][]string) {
    42  	return func(securityDefs []map[string][]string) { op.Security = securityDefs }
    43  }
    44  
    45  func opResponsesSetter(op *spec.Operation) func(*spec.Response, map[int]spec.Response) {
    46  	return func(def *spec.Response, scr map[int]spec.Response) {
    47  		if op.Responses == nil {
    48  			op.Responses = new(spec.Responses)
    49  		}
    50  		op.Responses.Default = def
    51  		op.Responses.StatusCodeResponses = scr
    52  	}
    53  }
    54  
    55  func opParamSetter(op *spec.Operation) func([]*spec.Parameter) {
    56  	return func(params []*spec.Parameter) {
    57  		for _, v := range params {
    58  			op.AddParam(v)
    59  		}
    60  	}
    61  }
    62  
    63  func newRoutesParser(prog *loader.Program) *routesParser {
    64  	return &routesParser{
    65  		program: prog,
    66  	}
    67  }
    68  
    69  type routesParser struct {
    70  	program     *loader.Program
    71  	definitions map[string]spec.Schema
    72  	operations  map[string]*spec.Operation
    73  	responses   map[string]spec.Response
    74  	parameters  []*spec.Parameter
    75  }
    76  
    77  var routeVendorExtensibleParser = vendorExtensibleParser{
    78  	setExtensions: func(ext spec.Extensions, dest interface{}) {
    79  		dest.(*spec.Operation).Extensions = ext
    80  	},
    81  }
    82  
    83  func (rp *routesParser) Parse(gofile *ast.File, target interface{}, includeTags map[string]bool, excludeTags map[string]bool) error {
    84  	tgt := target.(*spec.Paths)
    85  	for _, comsec := range gofile.Comments {
    86  		content := parsePathAnnotation(rxRoute, comsec.List)
    87  
    88  		if content.Method == "" {
    89  			continue // it's not, next!
    90  		}
    91  
    92  		if !shouldAcceptTag(content.Tags, includeTags, excludeTags) {
    93  			if Debug {
    94  				fmt.Printf("route %s %s is ignored due to tag rules\n", content.Method, content.Path)
    95  			}
    96  			continue
    97  		}
    98  
    99  		pthObj := tgt.Paths[content.Path]
   100  		op := setPathOperation(
   101  			content.Method, content.ID,
   102  			&pthObj, rp.operations[content.ID])
   103  
   104  		op.Tags = content.Tags
   105  
   106  		sp := new(sectionedParser)
   107  		sp.setTitle = func(lines []string) { op.Summary = joinDropLast(lines) }
   108  		sp.setDescription = func(lines []string) { op.Description = joinDropLast(lines) }
   109  		sr := newSetResponses(rp.definitions, rp.responses, opResponsesSetter(op))
   110  		spa := newSetParams(rp.parameters, opParamSetter(op))
   111  		sp.taggers = []tagParser{
   112  			newMultiLineTagParser("Consumes", newMultilineDropEmptyParser(rxConsumes, opConsumesSetter(op)), false),
   113  			newMultiLineTagParser("Produces", newMultilineDropEmptyParser(rxProduces, opProducesSetter(op)), false),
   114  			newSingleLineTagParser("Schemes", newSetSchemes(opSchemeSetter(op))),
   115  			newMultiLineTagParser("Security", newSetSecurity(rxSecuritySchemes, opSecurityDefsSetter(op)), false),
   116  			newMultiLineTagParser("Parameters", spa, false),
   117  			newMultiLineTagParser("Responses", sr, false),
   118  			newMultiLineTagParser("YAMLExtensionsBlock", newYamlParser(rxExtensions, routeVendorExtensibleParser.ParseInto(op)), true),
   119  		}
   120  		if err := sp.Parse(content.Remaining); err != nil {
   121  			return fmt.Errorf("operation (%s): %v", op.ID, err)
   122  		}
   123  
   124  		if tgt.Paths == nil {
   125  			tgt.Paths = make(map[string]spec.PathItem)
   126  		}
   127  		tgt.Paths[content.Path] = pthObj
   128  	}
   129  
   130  	return nil
   131  }
   132  
   133  func shouldAcceptTag(tags []string, includeTags map[string]bool, excludeTags map[string]bool) bool {
   134  	for _, tag := range tags {
   135  		if len(includeTags) > 0 {
   136  			if includeTags[tag] {
   137  				return true
   138  			}
   139  		} else if len(excludeTags) > 0 {
   140  			if excludeTags[tag] {
   141  				return false
   142  			}
   143  		}
   144  	}
   145  	return len(includeTags) <= 0
   146  }