github.com/josephspurrier/go-swagger@v0.2.1-0.20221129144919-1f672a142a00/codescan/spec.go (about)

     1  package codescan
     2  
     3  import (
     4  	"go/ast"
     5  
     6  	"github.com/go-openapi/spec"
     7  )
     8  
     9  func newSpecBuilder(input *spec.Swagger, sc *scanCtx, scanModels bool) *specBuilder {
    10  	if input == nil {
    11  		input = new(spec.Swagger)
    12  		input.Swagger = "2.0"
    13  	}
    14  
    15  	if input.Paths == nil {
    16  		input.Paths = new(spec.Paths)
    17  	}
    18  	if input.Definitions == nil {
    19  		input.Definitions = make(map[string]spec.Schema)
    20  	}
    21  	if input.Responses == nil {
    22  		input.Responses = make(map[string]spec.Response)
    23  	}
    24  	if input.Extensions == nil {
    25  		input.Extensions = make(spec.Extensions)
    26  	}
    27  
    28  	return &specBuilder{
    29  		ctx:         sc,
    30  		input:       input,
    31  		scanModels:  scanModels,
    32  		operations:  collectOperationsFromInput(input),
    33  		definitions: input.Definitions,
    34  		responses:   input.Responses,
    35  	}
    36  }
    37  
    38  type specBuilder struct {
    39  	scanModels  bool
    40  	input       *spec.Swagger
    41  	ctx         *scanCtx
    42  	discovered  []*entityDecl
    43  	definitions map[string]spec.Schema
    44  	responses   map[string]spec.Response
    45  	operations  map[string]*spec.Operation
    46  }
    47  
    48  func (s *specBuilder) Build() (*spec.Swagger, error) {
    49  	if err := s.buildModels(); err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	if err := s.buildParameters(); err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	if err := s.buildRespones(); err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	// build definitions dictionary
    62  	if err := s.buildDiscovered(); err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	if err := s.buildRoutes(); err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	if err := s.buildOperations(); err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	if err := s.buildMeta(); err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	if s.input.Swagger == "" {
    79  		s.input.Swagger = "2.0"
    80  	}
    81  
    82  	return s.input, nil
    83  }
    84  
    85  func (s *specBuilder) buildDiscovered() error {
    86  	// loop over discovered until all the items are in definitions
    87  	keepGoing := len(s.discovered) > 0
    88  	for keepGoing {
    89  		var queue []*entityDecl
    90  		for _, d := range s.discovered {
    91  			nm, _ := d.Names()
    92  			if _, ok := s.definitions[nm]; !ok {
    93  				queue = append(queue, d)
    94  			}
    95  		}
    96  		s.discovered = nil
    97  		for _, sd := range queue {
    98  			if err := s.buildDiscoveredSchema(sd); err != nil {
    99  				return err
   100  			}
   101  		}
   102  		keepGoing = len(s.discovered) > 0
   103  	}
   104  
   105  	return nil
   106  }
   107  
   108  func (s *specBuilder) buildDiscoveredSchema(decl *entityDecl) error {
   109  	sb := &schemaBuilder{
   110  		ctx:        s.ctx,
   111  		decl:       decl,
   112  		discovered: s.discovered,
   113  	}
   114  	if err := sb.Build(s.definitions); err != nil {
   115  		return err
   116  	}
   117  	s.discovered = append(s.discovered, sb.postDecls...)
   118  	return nil
   119  }
   120  
   121  func (s *specBuilder) buildMeta() error {
   122  	// build swagger object
   123  	for _, decl := range s.ctx.app.Meta {
   124  		if err := newMetaParser(s.input).Parse(decl.Comments); err != nil {
   125  			return err
   126  		}
   127  	}
   128  	return nil
   129  }
   130  
   131  func (s *specBuilder) buildOperations() error {
   132  	for _, pp := range s.ctx.app.Operations {
   133  		ob := &operationsBuilder{
   134  			operations: s.operations,
   135  			ctx:        s.ctx,
   136  			path:       pp,
   137  		}
   138  		if err := ob.Build(s.input.Paths); err != nil {
   139  			return err
   140  		}
   141  	}
   142  	return nil
   143  }
   144  
   145  func (s *specBuilder) buildRoutes() error {
   146  	// build paths dictionary
   147  	for _, pp := range s.ctx.app.Routes {
   148  		rb := &routesBuilder{
   149  			ctx:         s.ctx,
   150  			route:       pp,
   151  			responses:   s.responses,
   152  			operations:  s.operations,
   153  			definitions: s.definitions,
   154  		}
   155  		if err := rb.Build(s.input.Paths); err != nil {
   156  			return err
   157  		}
   158  	}
   159  
   160  	return nil
   161  }
   162  
   163  func (s *specBuilder) buildRespones() error {
   164  	// build responses dictionary
   165  	for _, decl := range s.ctx.app.Responses {
   166  		rb := &responseBuilder{
   167  			ctx:  s.ctx,
   168  			decl: decl,
   169  		}
   170  		if err := rb.Build(s.responses); err != nil {
   171  			return err
   172  		}
   173  		s.discovered = append(s.discovered, rb.postDecls...)
   174  	}
   175  	return nil
   176  }
   177  
   178  func (s *specBuilder) buildParameters() error {
   179  	// build parameters dictionary
   180  	for _, decl := range s.ctx.app.Parameters {
   181  		pb := &parameterBuilder{
   182  			ctx:  s.ctx,
   183  			decl: decl,
   184  		}
   185  		if err := pb.Build(s.operations); err != nil {
   186  			return err
   187  		}
   188  		s.discovered = append(s.discovered, pb.postDecls...)
   189  	}
   190  	return nil
   191  }
   192  
   193  func (s *specBuilder) buildModels() error {
   194  	// build models dictionary
   195  	if !s.scanModels {
   196  		return nil
   197  	}
   198  
   199  	for _, decl := range s.ctx.app.Models {
   200  		if err := s.buildDiscoveredSchema(decl); err != nil {
   201  			return err
   202  		}
   203  	}
   204  
   205  	return s.joinExtraModels()
   206  }
   207  
   208  func (s *specBuilder) joinExtraModels() error {
   209  	tmp := make(map[*ast.Ident]*entityDecl, len(s.ctx.app.ExtraModels))
   210  	for k, v := range s.ctx.app.ExtraModels {
   211  		tmp[k] = v
   212  		s.ctx.app.Models[k] = v
   213  		delete(s.ctx.app.ExtraModels, k)
   214  	}
   215  
   216  	// process extra models and see if there is any reference to a new extra one
   217  	for _, decl := range tmp {
   218  		if err := s.buildDiscoveredSchema(decl); err != nil {
   219  			return err
   220  		}
   221  	}
   222  
   223  	if len(s.ctx.app.ExtraModels) > 0 {
   224  		return s.joinExtraModels()
   225  	}
   226  
   227  	return nil
   228  }
   229  
   230  func collectOperationsFromInput(input *spec.Swagger) map[string]*spec.Operation {
   231  	operations := make(map[string]*spec.Operation)
   232  	if input != nil && input.Paths != nil {
   233  		for _, pth := range input.Paths.Paths {
   234  			if pth.Get != nil {
   235  				operations[pth.Get.ID] = pth.Get
   236  			}
   237  			if pth.Post != nil {
   238  				operations[pth.Post.ID] = pth.Post
   239  			}
   240  			if pth.Put != nil {
   241  				operations[pth.Put.ID] = pth.Put
   242  			}
   243  			if pth.Patch != nil {
   244  				operations[pth.Patch.ID] = pth.Patch
   245  			}
   246  			if pth.Delete != nil {
   247  				operations[pth.Delete.ID] = pth.Delete
   248  			}
   249  			if pth.Head != nil {
   250  				operations[pth.Head.ID] = pth.Head
   251  			}
   252  			if pth.Options != nil {
   253  				operations[pth.Options.ID] = pth.Options
   254  			}
   255  		}
   256  	}
   257  	return operations
   258  }