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 := ¶meterBuilder{ 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 }