github.com/ManabuSeki/goa-v1@v1.4.3/goagen/gen_app/writers.go (about) 1 package genapp 2 3 import ( 4 "fmt" 5 "net/http" 6 "regexp" 7 "strings" 8 "text/template" 9 10 "sort" 11 12 "github.com/goadesign/goa/design" 13 "github.com/goadesign/goa/goagen/codegen" 14 ) 15 16 // WildcardRegex is the regex used to capture path parameters. 17 var WildcardRegex = regexp.MustCompile("(?:[^/]*/:([^/]+))+") 18 19 type ( 20 // ContextsWriter generate codes for a goa application contexts. 21 ContextsWriter struct { 22 *codegen.SourceFile 23 CtxTmpl *template.Template 24 CtxNewTmpl *template.Template 25 CtxRespTmpl *template.Template 26 PayloadTmpl *template.Template 27 Finalizer *codegen.Finalizer 28 Validator *codegen.Validator 29 } 30 31 // ControllersWriter generate code for a goa application handlers. 32 // Handlers receive a HTTP request, create the action context, call the action code and send the 33 // resulting HTTP response. 34 ControllersWriter struct { 35 *codegen.SourceFile 36 CtrlTmpl *template.Template 37 MountTmpl *template.Template 38 handleCORST *template.Template 39 Finalizer *codegen.Finalizer 40 Validator *codegen.Validator 41 } 42 43 // SecurityWriter generate code for action-level security handlers. 44 SecurityWriter struct { 45 *codegen.SourceFile 46 SecurityTmpl *template.Template 47 } 48 49 // ResourcesWriter generate code for a goa application resources. 50 // Resources are data structures initialized by the application handlers and passed to controller 51 // actions. 52 ResourcesWriter struct { 53 *codegen.SourceFile 54 ResourceTmpl *template.Template 55 } 56 57 // MediaTypesWriter generate code for a goa application media types. 58 // Media types are data structures used to render the response bodies. 59 MediaTypesWriter struct { 60 *codegen.SourceFile 61 MediaTypeTmpl *template.Template 62 Validator *codegen.Validator 63 } 64 65 // UserTypesWriter generate code for a goa application user types. 66 // User types are data structures defined in the DSL with "Type". 67 UserTypesWriter struct { 68 *codegen.SourceFile 69 UserTypeTmpl *template.Template 70 Finalizer *codegen.Finalizer 71 Validator *codegen.Validator 72 } 73 74 // ContextTemplateData contains all the information used by the template to render the context 75 // code for an action. 76 ContextTemplateData struct { 77 Name string // e.g. "ListBottleContext" 78 ResourceName string // e.g. "bottles" 79 ActionName string // e.g. "list" 80 Params *design.AttributeDefinition 81 Payload *design.UserTypeDefinition 82 Headers *design.AttributeDefinition 83 Routes []*design.RouteDefinition 84 Responses map[string]*design.ResponseDefinition 85 API *design.APIDefinition 86 DefaultPkg string 87 Security *design.SecurityDefinition 88 } 89 90 // ControllerTemplateData contains the information required to generate an action handler. 91 ControllerTemplateData struct { 92 API *design.APIDefinition // API definition 93 Resource string // Lower case plural resource name, e.g. "bottles" 94 Actions []map[string]interface{} // Array of actions, each action has keys "Name", "DesignName", "Routes", "Context" and "Unmarshal" 95 FileServers []*design.FileServerDefinition // File servers 96 Encoders []*EncoderTemplateData // Encoder data 97 Decoders []*EncoderTemplateData // Decoder data 98 Origins []*design.CORSDefinition // CORS policies 99 PreflightPaths []string 100 } 101 102 // ResourceData contains the information required to generate the resource GoGenerator 103 ResourceData struct { 104 Name string // Name of resource 105 Identifier string // Identifier of resource media type 106 Description string // Description of resource 107 Type *design.MediaTypeDefinition // Type of resource media type 108 CanonicalTemplate string // CanonicalFormat represents the resource canonical path in the form of a fmt.Sprintf format. 109 CanonicalParams []string // CanonicalParams is the list of parameter names that appear in the resource canonical path in order. 110 } 111 112 // EncoderTemplateData contains the data needed to render the registration code for a single 113 // encoder or decoder package. 114 EncoderTemplateData struct { 115 // PackagePath is the Go package path to the package implmenting the encoder/decoder. 116 PackagePath string 117 // PackageName is the name of the Go package implementing the encoder/decoder. 118 PackageName string 119 // Function is the name of the package function implementing the decoder/encoder factory. 120 Function string 121 // MIMETypes is the list of supported MIME types. 122 MIMETypes []string 123 // Default is true if this encoder/decoder should be set as the default. 124 Default bool 125 } 126 ) 127 128 // IsPathParam returns true if the given parameter name corresponds to a path parameter for all 129 // the context action routes. Such parameter is required but does not need to be validated as 130 // httptreemux takes care of that. 131 func (c *ContextTemplateData) IsPathParam(param string) bool { 132 params := c.Params 133 pp := false 134 if params.Type.IsObject() { 135 for _, r := range c.Routes { 136 pp = false 137 for _, p := range r.Params() { 138 if p == param { 139 pp = true 140 break 141 } 142 } 143 if !pp { 144 break 145 } 146 } 147 } 148 return pp 149 } 150 151 // HasParamAndHeader returns true if the generated struct field name for the given header name 152 // matches the generated struct field name of a param in c.Params. 153 func (c *ContextTemplateData) HasParamAndHeader(name string) bool { 154 if c.Params == nil || c.Headers == nil { 155 return false 156 } 157 158 headerAtt := c.Headers.Type.ToObject()[name] 159 headerName := codegen.GoifyAtt(headerAtt, name, true) 160 for paramName, paramAtt := range c.Params.Type.ToObject() { 161 paramName = codegen.GoifyAtt(paramAtt, paramName, true) 162 if headerName == paramName { 163 return true 164 } 165 } 166 return false 167 } 168 169 // MustValidate returns true if code that checks for the presence of the given param must be 170 // generated. 171 func (c *ContextTemplateData) MustValidate(name string) bool { 172 return c.Params.IsRequired(name) && !c.IsPathParam(name) 173 } 174 175 // IterateResponses iterates through the responses sorted by status code. 176 func (c *ContextTemplateData) IterateResponses(it func(*design.ResponseDefinition) error) error { 177 m := make(map[int]*design.ResponseDefinition, len(c.Responses)) 178 var s []int 179 for _, resp := range c.Responses { 180 status := resp.Status 181 m[status] = resp 182 s = append(s, status) 183 } 184 sort.Ints(s) 185 for _, status := range s { 186 if err := it(m[status]); err != nil { 187 return err 188 } 189 } 190 return nil 191 } 192 193 // NewContextsWriter returns a contexts code writer. 194 // Contexts provide the glue between the underlying request data and the user controller. 195 func NewContextsWriter(filename string) (*ContextsWriter, error) { 196 file, err := codegen.SourceFileFor(filename) 197 if err != nil { 198 return nil, err 199 } 200 return &ContextsWriter{ 201 SourceFile: file, 202 Finalizer: codegen.NewFinalizer(), 203 Validator: codegen.NewValidator(), 204 }, nil 205 } 206 207 // Execute writes the code for the context types to the writer. 208 func (w *ContextsWriter) Execute(data *ContextTemplateData) error { 209 if err := w.ExecuteTemplate("context", ctxT, nil, data); err != nil { 210 return err 211 } 212 fn := template.FuncMap{ 213 "newCoerceData": newCoerceData, 214 "arrayAttribute": arrayAttribute, 215 "printVal": codegen.PrintVal, 216 "canonicalHeaderKey": http.CanonicalHeaderKey, 217 "isPathParam": data.IsPathParam, 218 "valueTypeOf": valueTypeOf, 219 "fromString": fromString, 220 } 221 if err := w.ExecuteTemplate("new", ctxNewT, fn, data); err != nil { 222 return err 223 } 224 if data.Payload != nil { 225 found := false 226 for _, t := range design.Design.Types { 227 if t.TypeName == data.Payload.TypeName { 228 found = true 229 break 230 } 231 } 232 if !found { 233 fn := template.FuncMap{ 234 "finalizeCode": w.Finalizer.Code, 235 "validationCode": w.Validator.Code, 236 } 237 if err := w.ExecuteTemplate("payload", payloadT, fn, data); err != nil { 238 return err 239 } 240 } 241 } 242 return data.IterateResponses(func(resp *design.ResponseDefinition) error { 243 respData := map[string]interface{}{ 244 "Context": data, 245 "Response": resp, 246 } 247 var mt *design.MediaTypeDefinition 248 if resp.Type != nil { 249 var ok bool 250 if mt, ok = resp.Type.(*design.MediaTypeDefinition); !ok { 251 respData["Type"] = resp.Type 252 respData["ContentType"] = resp.MediaType 253 return w.ExecuteTemplate("response", ctxTRespT, nil, respData) 254 } 255 } else { 256 mt = design.Design.MediaTypeWithIdentifier(resp.MediaType) 257 } 258 if mt != nil { 259 var views []string 260 if resp.ViewName != "" { 261 views = []string{resp.ViewName} 262 } else { 263 views = make([]string, len(mt.Views)) 264 i := 0 265 for name := range mt.Views { 266 views[i] = name 267 i++ 268 } 269 sort.Strings(views) 270 } 271 for _, view := range views { 272 projected, _, err := mt.Project(view) 273 if err != nil { 274 return err 275 } 276 respData["Projected"] = projected 277 respData["ViewName"] = view 278 respData["MediaType"] = mt 279 respData["ContentType"] = mt.ContentType 280 if view == "default" { 281 respData["RespName"] = codegen.Goify(resp.Name, true) 282 } else { 283 base := fmt.Sprintf("%s%s", resp.Name, strings.Title(view)) 284 respData["RespName"] = codegen.Goify(base, true) 285 } 286 if err := w.ExecuteTemplate("response", ctxMTRespT, fn, respData); err != nil { 287 return err 288 } 289 } 290 return nil 291 } 292 return w.ExecuteTemplate("response", ctxNoMTRespT, nil, respData) 293 }) 294 } 295 296 // NewControllersWriter returns a handlers code writer. 297 // Handlers provide the glue between the underlying request data and the user controller. 298 func NewControllersWriter(filename string) (*ControllersWriter, error) { 299 file, err := codegen.SourceFileFor(filename) 300 if err != nil { 301 return nil, err 302 } 303 return &ControllersWriter{ 304 SourceFile: file, 305 Finalizer: codegen.NewFinalizer(), 306 Validator: codegen.NewValidator(), 307 }, nil 308 } 309 310 // WriteInitService writes the initService function 311 func (w *ControllersWriter) WriteInitService(encoders, decoders []*EncoderTemplateData) error { 312 ctx := map[string]interface{}{ 313 "API": design.Design, 314 "Encoders": encoders, 315 "Decoders": decoders, 316 } 317 return w.ExecuteTemplate("service", serviceT, nil, ctx) 318 } 319 320 // Execute writes the handlers GoGenerator 321 func (w *ControllersWriter) Execute(data []*ControllerTemplateData) error { 322 if len(data) == 0 { 323 return nil 324 } 325 for _, d := range data { 326 if err := w.ExecuteTemplate("controller", ctrlT, nil, d); err != nil { 327 return err 328 } 329 if err := w.ExecuteTemplate("mount", mountT, nil, d); err != nil { 330 return err 331 } 332 if len(d.Origins) > 0 { 333 if err := w.ExecuteTemplate("handleCORS", handleCORST, nil, d); err != nil { 334 return err 335 } 336 } 337 fn := template.FuncMap{ 338 "newCoerceData": newCoerceData, 339 "finalizeCode": w.Finalizer.Code, 340 "arrayAttribute": arrayAttribute, 341 "validationCode": w.Validator.Code, 342 "valueTypeOf": valueTypeOf, 343 "fromString": fromString, 344 } 345 if err := w.ExecuteTemplate("unmarshal", unmarshalT, fn, d); err != nil { 346 return err 347 } 348 } 349 return nil 350 } 351 352 // NewSecurityWriter returns a security functionality code writer. 353 // Those functionalities are there to support action-middleware related to security. 354 func NewSecurityWriter(filename string) (*SecurityWriter, error) { 355 file, err := codegen.SourceFileFor(filename) 356 if err != nil { 357 return nil, err 358 } 359 return &SecurityWriter{SourceFile: file}, nil 360 } 361 362 // Execute adds the different security schemes and middleware supporting functions. 363 func (w *SecurityWriter) Execute(schemes []*design.SecuritySchemeDefinition) error { 364 return w.ExecuteTemplate("security_schemes", securitySchemesT, nil, schemes) 365 } 366 367 // NewResourcesWriter returns a contexts code writer. 368 // Resources provide the glue between the underlying request data and the user controller. 369 func NewResourcesWriter(filename string) (*ResourcesWriter, error) { 370 file, err := codegen.SourceFileFor(filename) 371 if err != nil { 372 return nil, err 373 } 374 return &ResourcesWriter{SourceFile: file}, nil 375 } 376 377 // Execute writes the code for the context types to the writer. 378 func (w *ResourcesWriter) Execute(data *ResourceData) error { 379 return w.ExecuteTemplate("resource", resourceT, nil, data) 380 } 381 382 // NewMediaTypesWriter returns a contexts code writer. 383 // Media types contain the data used to render response bodies. 384 func NewMediaTypesWriter(filename string) (*MediaTypesWriter, error) { 385 file, err := codegen.SourceFileFor(filename) 386 if err != nil { 387 return nil, err 388 } 389 return &MediaTypesWriter{SourceFile: file, Validator: codegen.NewValidator()}, nil 390 } 391 392 // Execute writes the code for the context types to the writer. 393 func (w *MediaTypesWriter) Execute(mt *design.MediaTypeDefinition) error { 394 var ( 395 mLinks *design.UserTypeDefinition 396 fn = template.FuncMap{"validationCode": w.Validator.Code} 397 ) 398 err := mt.IterateViews(func(view *design.ViewDefinition) error { 399 p, links, err := mt.Project(view.Name) 400 if mLinks == nil { 401 mLinks = links 402 } 403 if err != nil { 404 return err 405 } 406 return w.ExecuteTemplate("mediatype", mediaTypeT, fn, p) 407 }) 408 if err != nil { 409 return err 410 } 411 if mLinks != nil { 412 if err := w.ExecuteTemplate("mediatypelink", mediaTypeLinkT, fn, mLinks); err != nil { 413 return err 414 } 415 } 416 return nil 417 } 418 419 // NewUserTypesWriter returns a contexts code writer. 420 // User types contain custom data structured defined in the DSL with "Type". 421 func NewUserTypesWriter(filename string) (*UserTypesWriter, error) { 422 file, err := codegen.SourceFileFor(filename) 423 if err != nil { 424 return nil, err 425 } 426 return &UserTypesWriter{ 427 SourceFile: file, 428 Finalizer: codegen.NewFinalizer(), 429 Validator: codegen.NewValidator(), 430 }, nil 431 } 432 433 // Execute writes the code for the context types to the writer. 434 func (w *UserTypesWriter) Execute(t *design.UserTypeDefinition) error { 435 fn := template.FuncMap{ 436 "finalizeCode": w.Finalizer.Code, 437 "validationCode": w.Validator.Code, 438 } 439 return w.ExecuteTemplate("types", userTypeT, fn, t) 440 } 441 442 // newCoerceData is a helper function that creates a map that can be given to the "Coerce" template. 443 func newCoerceData(name string, att *design.AttributeDefinition, pointer bool, pkg string, depth int) map[string]interface{} { 444 return map[string]interface{}{ 445 "Name": name, 446 "VarName": codegen.Goify(name, false), 447 "Pointer": pointer, 448 "Attribute": att, 449 "Pkg": pkg, 450 "Depth": depth, 451 } 452 } 453 454 // arrayAttribute returns the array element attribute definition. 455 func arrayAttribute(a *design.AttributeDefinition) *design.AttributeDefinition { 456 return a.Type.(*design.Array).ElemType 457 } 458 459 func hashAttribute(a *design.AttributeDefinition) (*design.AttributeDefinition, *design.AttributeDefinition) { 460 hash := a.Type.(*design.Hash) 461 return hash.KeyType, hash.ElemType 462 } 463 464 // valueTypeOf returns the golang type definition string from attribute definition 465 func valueTypeOf(prefix string, att *design.AttributeDefinition) string { 466 switch att.Type.Kind() { 467 case design.BooleanKind: 468 return prefix + "bool" 469 case design.IntegerKind: 470 return prefix + "int" 471 case design.NumberKind: 472 return prefix + "float" 473 case design.StringKind: 474 return prefix + "string" 475 case design.ArrayKind: 476 return valueTypeOf(prefix+"[]", arrayAttribute(att)) 477 case design.HashKind: 478 key, elm := hashAttribute(att) 479 return valueTypeOf(prefix+"map["+valueTypeOf("", key)+"]", elm) 480 } 481 return prefix + "interface{}" 482 } 483 484 // fromString returns the gocode expression to convert string typed varName value to go-type defined in the attribute 485 func fromString(att *design.AttributeDefinition, varName string) string { 486 switch att.Type.Kind() { 487 case design.BooleanKind: 488 return "strconv.ParseBool(" + varName + ")" 489 case design.IntegerKind: 490 return "strconv.Atoi(" + varName + ")" 491 case design.NumberKind: 492 return "strconv.ParseFloat(" + varName + ")" 493 case design.StringKind: 494 return varName + ", (error)(nil)" 495 case design.ArrayKind: 496 case design.HashKind: 497 return valueTypeOf("", att) + "{}, (error)(nil)" 498 } 499 return "(" + valueTypeOf("", att) + ")(nil), (error)(nil)" 500 } 501 502 const ( 503 // ctxT generates the code for the context data type. 504 // template input: *ContextTemplateData 505 ctxT = `// {{ .Name }} provides the {{ .ResourceName }} {{ .ActionName }} action context. 506 type {{ .Name }} struct { 507 context.Context 508 *goa.ResponseData 509 *goa.RequestData 510 {{ if .Headers }}{{ range $name, $att := .Headers.Type.ToObject }}{{ if not ($.HasParamAndHeader $name) }}{{/* 511 */}} {{ goifyatt $att $name true }} {{ if and $att.Type.IsPrimitive ($.Headers.IsPrimitivePointer $name) }}*{{ end }}{{ gotyperef .Type nil 0 false }} 512 {{ end }}{{ end }}{{ end }}{{ if .Params }}{{ range $name, $att := .Params.Type.ToObject }}{{/* 513 */}} {{ goifyatt $att $name true }} {{ if and $att.Type.IsPrimitive ($.Params.IsPrimitivePointer $name) }}*{{ end }}{{ gotyperef .Type nil 0 false }} 514 {{ end }}{{ end }}{{ if .Payload }} Payload {{ gotyperef .Payload nil 0 false }} 515 {{ end }}} 516 ` 517 // coerceT generates the code that coerces the generic deserialized 518 // data to the actual type. 519 // template input: map[string]interface{} as returned by newCoerceData 520 coerceT = `{{ if eq .Attribute.Type.Kind 1 }}{{/* 521 522 */}}{{/* BooleanType */}}{{/* 523 */}}{{ $varName := or (and (not .Pointer) .VarName) tempvar }}{{/* 524 */}}{{ tabs .Depth }}if {{ .VarName }}, err2 := strconv.ParseBool(raw{{ goify .Name true }}); err2 == nil { 525 {{ if .Pointer }}{{ tabs .Depth }} {{ $varName }} := &{{ .VarName }} 526 {{ end }}{{ tabs .Depth }} {{ .Pkg }} = {{ $varName }} 527 {{ tabs .Depth }}} else { 528 {{ tabs .Depth }} err = goa.MergeErrors(err, goa.InvalidParamTypeError("{{ .Name }}", raw{{ goify .Name true }}, "boolean")) 529 {{ tabs .Depth }}} 530 {{ else if eq .Attribute.Type.Kind 2 }}{{/* 531 532 */}}{{/* IntegerType */}}{{/* 533 */}}{{ $tmp := tempvar }}{{/* 534 */}}{{ tabs .Depth }}if {{ .VarName }}, err2 := strconv.Atoi(raw{{ goify .Name true }}); err2 == nil { 535 {{ if .Pointer }}{{ $tmp2 := tempvar }}{{ tabs .Depth }} {{ $tmp2 }} := {{ .VarName }} 536 {{ tabs .Depth }} {{ $tmp }} := &{{ $tmp2 }} 537 {{ tabs .Depth }} {{ .Pkg }} = {{ $tmp }} 538 {{ else }}{{ tabs .Depth }} {{ .Pkg }} = {{ .VarName }} 539 {{ end }}{{ tabs .Depth }}} else { 540 {{ tabs .Depth }} err = goa.MergeErrors(err, goa.InvalidParamTypeError("{{ .Name }}", raw{{ goify .Name true }}, "integer")) 541 {{ tabs .Depth }}} 542 {{ else if eq .Attribute.Type.Kind 3 }}{{/* 543 544 */}}{{/* NumberType */}}{{/* 545 */}}{{ $varName := or (and (not .Pointer) .VarName) tempvar }}{{/* 546 */}}{{ tabs .Depth }}if {{ .VarName }}, err2 := strconv.ParseFloat(raw{{ goify .Name true }}, 64); err2 == nil { 547 {{ if .Pointer }}{{ tabs .Depth }} {{ $varName }} := &{{ .VarName }} 548 {{ end }}{{ tabs .Depth }} {{ .Pkg }} = {{ $varName }} 549 {{ tabs .Depth }}} else { 550 {{ tabs .Depth }} err = goa.MergeErrors(err, goa.InvalidParamTypeError("{{ .Name }}", raw{{ goify .Name true }}, "number")) 551 {{ tabs .Depth }}} 552 {{ else if eq .Attribute.Type.Kind 4 }}{{/* 553 554 */}}{{/* StringType */}}{{/* 555 */}}{{ tabs .Depth }}{{ .Pkg }} = {{ if .Pointer }}&{{ end }}raw{{ goify .Name true }} 556 {{ else if eq .Attribute.Type.Kind 5 }}{{/* 557 558 */}}{{/* DateTimeType */}}{{/* 559 */}}{{ $varName := or (and (not .Pointer) .VarName) tempvar }}{{/* 560 */}}{{ tabs .Depth }}if {{ .VarName }}, err2 := time.Parse(time.RFC3339, raw{{ goify .Name true }}); err2 == nil { 561 {{ if .Pointer }}{{ tabs .Depth }} {{ $varName }} := &{{ .VarName }} 562 {{ end }}{{ tabs .Depth }} {{ .Pkg }} = {{ $varName }} 563 {{ tabs .Depth }}} else { 564 {{ tabs .Depth }} err = goa.MergeErrors(err, goa.InvalidParamTypeError("{{ .Name }}", raw{{ goify .Name true }}, "datetime")) 565 {{ tabs .Depth }}} 566 {{ else if eq .Attribute.Type.Kind 6 }}{{/* 567 568 */}}{{/* UUIDType */}}{{/* 569 */}}{{ $varName := or (and (not .Pointer) .VarName) tempvar }}{{/* 570 */}}{{ tabs .Depth }}if {{ .VarName }}, err2 := uuid.FromString(raw{{ goify .Name true }}); err2 == nil { 571 {{ if .Pointer }}{{ tabs .Depth }} {{ $varName }} := &{{ .VarName }} 572 {{ end }}{{ tabs .Depth }} {{ .Pkg }} = {{ $varName }} 573 {{ tabs .Depth }}} else { 574 {{ tabs .Depth }} err = goa.MergeErrors(err, goa.InvalidParamTypeError("{{ .Name }}", raw{{ goify .Name true }}, "uuid")) 575 {{ tabs .Depth }}} 576 {{ else if eq .Attribute.Type.Kind 7 }}{{/* 577 578 */}}{{/* AnyType */}}{{/* 579 */}}{{ if .Pointer }}{{ $tmp := tempvar }}{{ tabs .Depth }}{{ $tmp }} := interface{}(raw{{ goify .Name true }}) 580 {{ tabs .Depth }}{{ .Pkg }} = &{{ $tmp }} 581 {{ else }}{{ tabs .Depth }}{{ .Pkg }} = raw{{ goify .Name true }}{{/* 582 */}}{{ end }} 583 {{ else if eq .Attribute.Type.Kind 8 }}{{/* 584 585 */}}{{/* ArrayType */}}{{/* 586 */}}{{ tabs .Depth }}tmp{{ goify .Name true }} := make({{ valueTypeOf "" .Attribute }}, len(raw{{ goify .Name true }})) 587 {{ tabs .Depth }}for i := 0; i < len(raw{{ goify .Name true }}); i++ { 588 {{ if eq (arrayAttribute .Attribute).Type.Kind 4 }}{{ tabs .Depth}} tmp := raw{{ goify .Name true }}[i]{{ else }}{{/* 589 */}}{{ tabs .Depth }} tmp, err2 := {{ fromString (arrayAttribute .Attribute) (printf "raw%s[i]" (goify .Name true)) }} 590 {{ tabs .Depth }} if err2 != nil { 591 {{ tabs .Depth }} err = goa.MergeErrors(err, goa.InvalidParamTypeError("{{ .Name }}", raw{{ goify .Name true }}, "{{ valueTypeOf "" .Attribute }}")) 592 {{ tabs .Depth }} break 593 {{ tabs .Depth }} }{{ end }} 594 {{ tabs .Depth}} tmp{{ goify .Name true }}[i] = tmp 595 {{ tabs .Depth}}} 596 {{ tabs .Depth }}{{ .Pkg }} = tmp{{ goify .Name true }}{{/* 597 */}} 598 {{ else if eq .Attribute.Type.Kind 13 }}{{/* 599 600 */}}{{/* FileType */}}{{/* 601 */}}{{ tabs .Depth }}if err2 == nil { 602 {{ tabs .Depth }} {{ .Pkg }} = {{ printf "raw%s" (goify .VarName true) }} 603 {{ tabs .Depth }}} else { 604 {{ tabs .Depth }} err = goa.MergeErrors(err, goa.InvalidParamTypeError("{{ .Name }}", "{{ .Name }}", "file")) 605 {{ tabs .Depth }}} 606 {{ end }}` 607 608 // ctxNewT generates the code for the context factory method. 609 // template input: *ContextTemplateData 610 ctxNewT = `{{ define "Coerce" }}` + coerceT + `{{ end }}` + ` 611 // New{{ goify .Name true }} parses the incoming request URL and body, performs validations and creates the 612 // context used by the {{ .ResourceName }} controller {{ .ActionName }} action. 613 func New{{ .Name }}(ctx context.Context, r *http.Request, service *goa.Service) (*{{ .Name }}, error) { 614 var err error 615 resp := goa.ContextResponse(ctx) 616 resp.Service = service 617 req := goa.ContextRequest(ctx) 618 req.Request = r 619 rctx := {{ .Name }}{Context: ctx, ResponseData: resp, RequestData: req}{{/* 620 */}} 621 {{ if .Headers }}{{ range $name, $att := .Headers.Type.ToObject }} header{{ goify $name true }} := req.Header["{{ canonicalHeaderKey $name }}"] 622 {{ $mustValidate := $.Headers.IsRequired $name }}{{ if $mustValidate }} if len(header{{ goify $name true }}) == 0 { 623 err = goa.MergeErrors(err, goa.MissingHeaderError("{{ $name }}")) 624 } else { 625 {{ else }} if len(header{{ goify $name true }}) > 0 { 626 {{ end }}{{/* if $mustValidate */}}{{ if $att.Type.IsArray }} req.Params["{{ $name }}"] = header{{ goify $name true }} 627 {{ if eq (arrayAttribute $att).Type.Kind 4 }} headers := header{{ goify $name true }} 628 {{ else }} headers := make({{ gotypedef $att 2 true false }}, len(header{{ goify $name true }})) 629 for i, raw{{ goify $name true}} := range header{{ goify $name true}} { 630 {{ template "Coerce" (newCoerceData $name (arrayAttribute $att) ($.Headers.IsPrimitivePointer $name) "headers[i]" 3) }}{{/* 631 */}} } 632 {{ end }} {{ printf "rctx.%s" (goifyatt $att $name true) }} = headers 633 {{ else }} raw{{ goify $name true}} := header{{ goify $name true}}[0] 634 req.Params["{{ $name }}"] = []string{raw{{ goify $name true }}} 635 {{ template "Coerce" (newCoerceData $name $att ($.Headers.IsPrimitivePointer $name) (printf "rctx.%s" (goifyatt $att $name true)) 2) }}{{ end }}{{/* 636 */}}{{ $validation := validationChecker $att ($.Headers.IsNonZero $name) ($.Headers.IsRequired $name) ($.Headers.HasDefaultValue $name) (printf "rctx.%s" (goifyatt $att $name true)) $name 2 false }}{{/* 637 */}}{{ if $validation }}{{ $validation }} 638 {{ end }} } 639 {{ end }}{{ end }}{{/* if .Headers }}{{/* 640 641 */}}{{ if .Params }}{{ range $name, $att := .Params.Type.ToObject }}{{/* 642 */}} param{{ goify $name true }} := req.Params["{{ $name }}"] 643 {{ $mustValidate := $.MustValidate $name }}{{ if $mustValidate }} if len(param{{ goify $name true }}) == 0 { 644 {{ if $.Params.HasDefaultValue $name }}{{printf "rctx.%s" (goifyatt $att $name true) }} = {{ printVal $att.Type $att.DefaultValue }}{{else}}{{/* 645 */}}err = goa.MergeErrors(err, goa.MissingParamError("{{ $name }}")){{end}} 646 } else { 647 {{ else }}{{ if $.Params.HasDefaultValue $name }} if len(param{{ goify $name true }}) == 0 { 648 {{printf "rctx.%s" (goifyatt $att $name true) }} = {{ printVal $att.Type $att.DefaultValue }} 649 } else { 650 {{ else }} if len(param{{ goify $name true }}) > 0 { 651 {{ end }}{{ end }}{{/* if $mustValidate */}}{{ if $att.Type.IsArray }}{{ if eq (arrayAttribute $att).Type.Kind 4 }} params := param{{ goify $name true }} 652 {{ else }} params := make({{ gotypedef $att 2 true false }}, len(param{{ goify $name true }})) 653 for i, raw{{ goify $name true}} := range param{{ goify $name true}} { 654 {{ template "Coerce" (newCoerceData $name (arrayAttribute $att) ($.Params.IsPrimitivePointer $name) "params[i]" 3) }}{{/* 655 */}} } 656 {{ end }} {{ printf "rctx.%s" (goifyatt $att $name true) }} = params 657 {{ else }} raw{{ goify $name true}} := param{{ goify $name true}}[0] 658 {{ template "Coerce" (newCoerceData $name $att ($.Params.IsPrimitivePointer $name) (printf "rctx.%s" (goifyatt $att $name true)) 2) }}{{ end }}{{/* 659 */}}{{ if $att.Type.IsArray }}{{ $validation := validationChecker (arrayAttribute $att) true true false "param" (printf "%s[0]" $name) 2 false }}{{/* 660 */}}{{ if $validation }}for _, param := range {{ printf "rctx.%s" (goifyatt $att $name true) }} { 661 {{ $validation }} 662 }{{ end }}{{/* 663 */}}{{ else }}{{ $validation := validationChecker $att ($.Params.IsNonZero $name) ($.Params.IsRequired $name) ($.Params.HasDefaultValue $name) (printf "rctx.%s" (goifyatt $att $name true)) $name 2 false }}{{/* 664 */}}{{ if $validation }}{{ $validation }}{{ end }}{{ end }} } 665 {{ end }}{{ end }}{{/* if .Params */}} return &rctx, err 666 } 667 ` 668 669 // ctxMTRespT generates the response helpers for responses with media types. 670 // template input: map[string]interface{} 671 ctxMTRespT = `// {{ goify .RespName true }} sends a HTTP response with status code {{ .Response.Status }}. 672 func (ctx *{{ .Context.Name }}) {{ goify .RespName true }}(r {{ gotyperef .Projected .Projected.AllRequired 0 false }}) error { 673 if ctx.ResponseData.Header().Get("Content-Type") == "" { 674 ctx.ResponseData.Header().Set("Content-Type", "{{ .ContentType }}") 675 } 676 {{ if .Projected.Type.IsArray }} if r == nil { 677 r = {{ gotyperef .Projected .Projected.AllRequired 0 false }}{} 678 } 679 {{ end }} return ctx.ResponseData.Service.Send(ctx.Context, {{ .Response.Status }}, r) 680 } 681 ` 682 683 // ctxTRespT generates the response helpers for responses with overridden types. 684 // template input: map[string]interface{} 685 ctxTRespT = `// {{ goify .Response.Name true }} sends a HTTP response with status code {{ .Response.Status }}. 686 func (ctx *{{ .Context.Name }}) {{ goify .Response.Name true }}(r {{ gotyperef .Type nil 0 false }}) error { 687 if ctx.ResponseData.Header().Get("Content-Type") == "" { 688 ctx.ResponseData.Header().Set("Content-Type", "{{ .ContentType }}") 689 } 690 return ctx.ResponseData.Service.Send(ctx.Context, {{ .Response.Status }}, r) 691 } 692 ` 693 694 // ctxNoMTRespT generates the response helpers for responses with no known media type. 695 // template input: *ContextTemplateData 696 ctxNoMTRespT = ` 697 // {{ goify .Response.Name true }} sends a HTTP response with status code {{ .Response.Status }}. 698 func (ctx *{{ .Context.Name }}) {{ goify .Response.Name true }}({{ if .Response.MediaType }}resp []byte{{ end }}) error { 699 {{ if .Response.MediaType }} if ctx.ResponseData.Header().Get("Content-Type") == "" { 700 ctx.ResponseData.Header().Set("Content-Type", "{{ .Response.MediaType }}") 701 } 702 {{ end }} ctx.ResponseData.WriteHeader({{ .Response.Status }}){{ if .Response.MediaType }} 703 _, err := ctx.ResponseData.Write(resp) 704 return err{{ else }} 705 return nil{{ end }} 706 } 707 ` 708 709 // payloadT generates the payload type definition GoGenerator 710 // template input: *ContextTemplateData 711 payloadT = `{{ $payload := .Payload }}{{ if .Payload.IsObject }}// {{ gotypename .Payload nil 0 true }} is the {{ .ResourceName }} {{ .ActionName }} action payload.{{/* 712 */}}{{ $privateTypeName := gotypename .Payload nil 1 true }} 713 type {{ $privateTypeName }} {{ gotypedef .Payload 0 true true }} 714 715 {{ $assignment := finalizeCode .Payload.AttributeDefinition "payload" 1 }}{{ if $assignment }}// Finalize sets the default values defined in the design. 716 func (payload {{ gotyperef .Payload .Payload.AllRequired 0 true }}) Finalize() { 717 {{ $assignment }} 718 }{{ end }} 719 720 {{ $validation := validationCode .Payload.AttributeDefinition false false false "payload" "raw" 1 true }}{{ if $validation }}// Validate runs the validation rules defined in the design. 721 func (payload {{ gotyperef .Payload .Payload.AllRequired 0 true }}) Validate() (err error) { 722 {{ $validation }} 723 return 724 }{{ end }} 725 {{ $typeName := gotypename .Payload .Payload.AllRequired 1 false }} 726 // Publicize creates {{ $typeName }} from {{ $privateTypeName }} 727 func (payload {{ gotyperef .Payload .Payload.AllRequired 0 true }}) Publicize() {{ gotyperef .Payload .Payload.AllRequired 0 false }} { 728 var pub {{ $typeName }} 729 {{ recursivePublicizer .Payload.AttributeDefinition "payload" "pub" 1 }} 730 return &pub 731 }{{ end }} 732 733 // {{ gotypename .Payload nil 0 false }} is the {{ .ResourceName }} {{ .ActionName }} action payload. 734 type {{ gotypename .Payload nil 1 false }} {{ gotypedef .Payload 0 true false }} 735 736 {{ $validation := validationCode .Payload.AttributeDefinition false false false "payload" "raw" 1 false }}{{ if $validation }}// Validate runs the validation rules defined in the design. 737 func (payload {{ gotyperef .Payload .Payload.AllRequired 0 false }}) Validate() (err error) { 738 {{ $validation }} 739 return 740 }{{ end }} 741 ` 742 // ctrlT generates the controller interface for a given resource. 743 // template input: *ControllerTemplateData 744 ctrlT = `// {{ .Resource }}Controller is the controller interface for the {{ .Resource }} actions. 745 type {{ .Resource }}Controller interface { 746 goa.Muxer 747 {{ if .FileServers }} goa.FileServer 748 {{ end }}{{ range .Actions }} {{ .Name }}(*{{ .Context }}) error 749 {{ end }}} 750 ` 751 752 // serviceT generates the service initialization code. 753 // template input: *ControllerTemplateData 754 serviceT = ` 755 // initService sets up the service encoders, decoders and mux. 756 func initService(service *goa.Service) { 757 // Setup encoders and decoders 758 {{ range .Encoders }}{{/* 759 */}} service.Encoder.Register({{ .PackageName }}.{{ .Function }}, "{{ join .MIMETypes "\", \"" }}") 760 {{ end }}{{ range .Decoders }}{{/* 761 */}} service.Decoder.Register({{ .PackageName }}.{{ .Function }}, "{{ join .MIMETypes "\", \"" }}") 762 {{ end }} 763 764 // Setup default encoder and decoder 765 {{ range .Encoders }}{{ if .Default }}{{/* 766 */}} service.Encoder.Register({{ .PackageName }}.{{ .Function }}, "*/*") 767 {{ end }}{{ end }}{{ range .Decoders }}{{ if .Default }}{{/* 768 */}} service.Decoder.Register({{ .PackageName }}.{{ .Function }}, "*/*") 769 {{ end }}{{ end }}} 770 ` 771 772 // mountT generates the code for a resource "Mount" function. 773 // template input: *ControllerTemplateData 774 mountT = ` 775 // Mount{{ .Resource }}Controller "mounts" a {{ .Resource }} resource controller on the given service. 776 func Mount{{ .Resource }}Controller(service *goa.Service, ctrl {{ .Resource }}Controller) { 777 initService(service) 778 var h goa.Handler 779 {{ $res := .Resource }}{{ if .Origins }}{{ range .PreflightPaths }}{{/* 780 */}} service.Mux.Handle("OPTIONS", {{ printf "%q" . }}, ctrl.MuxHandler("preflight", handle{{ $res }}Origin(cors.HandlePreflight()), nil)) 781 {{ end }}{{ end }}{{ range .Actions }}{{ $action := . }} 782 h = func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 783 // Check if there was an error loading the request 784 if err := goa.ContextError(ctx); err != nil { 785 return err 786 } 787 // Build the context 788 rctx, err := New{{ .Context }}(ctx, req, service) 789 if err != nil { 790 return err 791 } 792 {{ if .Payload }} // Build the payload 793 if rawPayload := goa.ContextRequest(ctx).Payload; rawPayload != nil { 794 rctx.Payload = rawPayload.({{ gotyperef .Payload nil 1 false }}) 795 {{ if not .PayloadOptional }} } else { 796 return goa.MissingPayloadError() 797 {{ end }} } 798 {{ end }} return ctrl.{{ .Name }}(rctx) 799 } 800 {{ if .Security }} h = handleSecurity({{ printf "%q" .Security.Scheme.SchemeName }}, h{{ range .Security.Scopes }}, {{ printf "%q" . }}{{ end }}) 801 {{ end }}{{ if $.Origins }} h = handle{{ $res }}Origin(h) 802 {{ end }}{{ range .Routes }} service.Mux.Handle("{{ .Verb }}", {{ printf "%q" .FullPath }}, ctrl.MuxHandler({{ printf "%q" $action.DesignName }}, h, {{ if $action.Payload }}{{ $action.Unmarshal }}{{ else }}nil{{ end }})) 803 service.LogInfo("mount", "ctrl", {{ printf "%q" $res }}, "action", {{ printf "%q" $action.Name }}, "route", {{ printf "%q" (printf "%s %s" .Verb .FullPath) }}{{ with $action.Security }}, "security", {{ printf "%q" .Scheme.SchemeName }}{{ end }}) 804 {{ end }}{{ end }}{{ range .FileServers }} 805 h = ctrl.FileHandler({{ printf "%q" .RequestPath }}, {{ printf "%q" .FilePath }}) 806 {{ if .Security }} h = handleSecurity({{ printf "%q" .Security.Scheme.SchemeName }}, h{{ range .Security.Scopes }}, {{ printf "%q" . }}{{ end }}) 807 {{ end }}{{ if $.Origins }} h = handle{{ $res }}Origin(h) 808 {{ end }} service.Mux.Handle("GET", "{{ .RequestPath }}", ctrl.MuxHandler("serve", h, nil)) 809 service.LogInfo("mount", "ctrl", {{ printf "%q" $res }}, "files", {{ printf "%q" .FilePath }}, "route", {{ printf "%q" (printf "GET %s" .RequestPath) }}{{ with .Security }}, "security", {{ printf "%q" .Scheme.SchemeName }}{{ end }}) 810 {{ end }}} 811 ` 812 813 // handleCORST generates the code that checks whether a CORS request is authorized 814 // template input: *ControllerTemplateData 815 handleCORST = `// handle{{ .Resource }}Origin applies the CORS response headers corresponding to the origin. 816 func handle{{ .Resource }}Origin(h goa.Handler) goa.Handler { 817 {{ range $i, $policy := .Origins }}{{ if $policy.Regexp }} spec{{$i}} := regexp.MustCompile({{ printf "%q" $policy.Origin }}) 818 {{ end }}{{ end }} 819 return func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 820 origin := req.Header.Get("Origin") 821 if origin == "" { 822 // Not a CORS request 823 return h(ctx, rw, req) 824 } 825 {{ range $i, $policy := .Origins }} {{ if $policy.Regexp }}if cors.MatchOriginRegexp(origin, spec{{$i}}){{else}}if cors.MatchOrigin(origin, {{ printf "%q" $policy.Origin }}){{end}} { 826 ctx = goa.WithLogContext(ctx, "origin", origin) 827 rw.Header().Set("Access-Control-Allow-Origin", origin) 828 {{ if not (eq $policy.Origin "*") }} rw.Header().Set("Vary", "Origin") 829 {{ end }}{{ if $policy.Exposed }} rw.Header().Set("Access-Control-Expose-Headers", "{{ join $policy.Exposed ", " }}") 830 {{ end }}{{ if gt $policy.MaxAge 0 }} rw.Header().Set("Access-Control-Max-Age", "{{ $policy.MaxAge }}") 831 {{ end }} rw.Header().Set("Access-Control-Allow-Credentials", "{{ $policy.Credentials }}") 832 if acrm := req.Header.Get("Access-Control-Request-Method"); acrm != "" { 833 // We are handling a preflight request 834 {{ if $policy.Methods }} rw.Header().Set("Access-Control-Allow-Methods", "{{ join $policy.Methods ", " }}") 835 {{ end }}{{ if $policy.Headers }} rw.Header().Set("Access-Control-Allow-Headers", "{{ join $policy.Headers ", " }}") 836 {{ end }} } 837 return h(ctx, rw, req) 838 } 839 {{ end }} 840 return h(ctx, rw, req) 841 } 842 } 843 ` 844 845 // unmarshalT generates the code for an action payload unmarshal function. 846 // template input: *ControllerTemplateData 847 unmarshalT = `{{ define "Coerce" }}` + coerceT + `{{ end }}` + `{{ range .Actions }}{{ if .Payload }} 848 // {{ .Unmarshal }} unmarshals the request body into the context request data Payload field. 849 func {{ .Unmarshal }}(ctx context.Context, service *goa.Service, req *http.Request) error { 850 {{ if .PayloadMultipart}}var err error 851 var payload {{ gotypename .Payload nil 1 true }} 852 {{ $o := .Payload.ToObject }}{{ range $name, $att := $o -}} 853 {{ if eq $att.Type.Kind 13 }} _, raw{{ goify $name true }}, err2 := req.FormFile("{{ $name }}"){{ else if eq $att.Type.Kind 8 }}{{/* 854 */}} raw{{ goify $name true }} := req.Form["{{ $name }}[]"]{{ else }}{{/* 855 */}} raw{{ goify $name true }} := req.FormValue("{{ $name }}"){{ end }} 856 {{ template "Coerce" (newCoerceData $name $att true (printf "payload.%s" (goifyatt $att $name true)) 1) }}{{ end }}{{/* 857 */}} if err != nil { 858 return err 859 }{{ else if .Payload.IsObject }}payload := &{{ gotypename .Payload nil 1 true }}{} 860 if err := service.DecodeRequest(req, payload); err != nil { 861 return err 862 }{{ $assignment := finalizeCode .Payload.AttributeDefinition "payload" 1 }}{{ if $assignment }} 863 payload.Finalize(){{ end }}{{ else }}var payload {{ gotypename .Payload nil 1 false }} 864 if err := service.DecodeRequest(req, &payload); err != nil { 865 return err 866 }{{ end }}{{ $validation := validationCode .Payload.AttributeDefinition false false false "payload" "raw" 1 true }}{{ if $validation }} 867 if err := payload.Validate(); err != nil { 868 // Initialize payload with private data structure so it can be logged 869 goa.ContextRequest(ctx).Payload = payload 870 return err 871 }{{ end }} 872 goa.ContextRequest(ctx).Payload = payload{{ if .Payload.IsObject }}.Publicize(){{ end }} 873 return nil 874 } 875 {{ end }} 876 {{ end }}` 877 878 // resourceT generates the code for a resource. 879 // template input: *ResourceData 880 resourceT = `{{ if .CanonicalTemplate }}// {{ .Name }}Href returns the resource href. 881 func {{ .Name }}Href({{ if .CanonicalParams }}{{ join .CanonicalParams ", " }} interface{}{{ end }}) string { 882 {{ range $param := .CanonicalParams }} param{{$param}} := strings.TrimLeftFunc(fmt.Sprintf("%v", {{$param}}), func(r rune) bool { return r == '/' }) 883 {{ end }}{{ if .CanonicalParams }} return fmt.Sprintf("{{ .CanonicalTemplate }}", param{{ join .CanonicalParams ", param" }}) 884 {{ else }} return "{{ .CanonicalTemplate }}" 885 {{ end }}} 886 {{ end }}` 887 888 // mediaTypeT generates the code for a media type. 889 // template input: MediaTypeTemplateData 890 mediaTypeT = `// {{ gotypedesc . true }} 891 // 892 // Identifier: {{ .Identifier }}{{ $typeName := gotypename . .AllRequired 0 false }} 893 type {{ $typeName }} {{ gotypedef . 0 true false }} 894 895 {{ $validation := validationCode .AttributeDefinition false false false "mt" "response" 1 false }}{{ if $validation }}// Validate validates the {{$typeName}} media type instance. 896 func (mt {{ gotyperef . .AllRequired 0 false }}) Validate() (err error) { 897 {{ $validation }} 898 return 899 } 900 {{ end }} 901 ` 902 903 // mediaTypeLinkT generates the code for a media type link. 904 // template input: MediaTypeLinkTemplateData 905 mediaTypeLinkT = `// {{ gotypedesc . true }}{{ $typeName := gotypename . .AllRequired 0 false }} 906 type {{ $typeName }} {{ gotypedef . 0 true false }} 907 {{ $validation := validationCode .AttributeDefinition false false false "ut" "response" 1 false }}{{ if $validation }}// Validate validates the {{$typeName}} type instance. 908 func (ut {{ gotyperef . .AllRequired 0 false }}) Validate() (err error) { 909 {{ $validation }} 910 return 911 }{{ end }} 912 ` 913 914 // userTypeT generates the code for a user type. 915 // template input: UserTypeTemplateData 916 userTypeT = `// {{ gotypedesc . false }}{{ $privateTypeName := gotypename . .AllRequired 0 true }} 917 type {{ $privateTypeName }} {{ gotypedef . 0 true true }} 918 {{ $assignment := finalizeCode .AttributeDefinition "ut" 1 }}{{ if $assignment }}// Finalize sets the default values for {{$privateTypeName}} type instance. 919 func (ut {{ gotyperef . .AllRequired 0 true }}) Finalize() { 920 {{ $assignment }} 921 }{{ end }} 922 {{ $validation := validationCode .AttributeDefinition false false false "ut" "request" 1 true }}{{ if $validation }}// Validate validates the {{$privateTypeName}} type instance. 923 func (ut {{ gotyperef . .AllRequired 0 true }}) Validate() (err error) { 924 {{ $validation }} 925 return 926 }{{ end }} 927 {{ $typeName := gotypename . .AllRequired 0 false }} 928 // Publicize creates {{ $typeName }} from {{ $privateTypeName }} 929 func (ut {{ gotyperef . .AllRequired 0 true }}) Publicize() {{ gotyperef . .AllRequired 0 false }} { 930 var pub {{ gotypename . .AllRequired 0 false }} 931 {{ recursivePublicizer .AttributeDefinition "ut" "pub" 1 }} 932 return &pub 933 } 934 935 // {{ gotypedesc . true }} 936 type {{ $typeName }} {{ gotypedef . 0 true false }} 937 {{ $validation := validationCode .AttributeDefinition false false false "ut" "type" 1 false }}{{ if $validation }}// Validate validates the {{$typeName}} type instance. 938 func (ut {{ gotyperef . .AllRequired 0 false }}) Validate() (err error) { 939 {{ $validation }} 940 return 941 }{{ end }} 942 ` 943 944 // securitySchemesT generates the code for the security module. 945 // template input: []*design.SecuritySchemeDefinition 946 securitySchemesT = ` 947 type ( 948 // Private type used to store auth handler info in request context 949 authMiddlewareKey string 950 ) 951 952 {{ range . }} 953 {{ $funcName := printf "Use%sMiddleware" (goify .SchemeName true) }}// {{ $funcName }} mounts the {{ .SchemeName }} auth middleware onto the service. 954 func {{ $funcName }}(service *goa.Service, middleware goa.Middleware) { 955 service.Context = context.WithValue(service.Context, authMiddlewareKey({{ printf "%q" .SchemeName }}), middleware) 956 } 957 958 {{ $funcName := printf "New%sSecurity" (goify .SchemeName true) }}// {{ $funcName }} creates a {{ .SchemeName }} security definition. 959 func {{ $funcName }}() *goa.{{ .Context }} { 960 def := goa.{{ .Context }}{ 961 {{ if eq .Context "APIKeySecurity" }}{{/* 962 */}} In: {{ if eq .In "header" }}goa.LocHeader{{ else }}goa.LocQuery{{ end }}, 963 Name: {{ printf "%q" .Name }}, 964 {{ else if eq .Context "OAuth2Security" }}{{/* 965 */}} Flow: {{ printf "%q" .Flow }}, 966 TokenURL: {{ printf "%q" .TokenURL }}, 967 AuthorizationURL: {{ printf "%q" .AuthorizationURL }},{{ with .Scopes }} 968 Scopes: map[string]string{ 969 {{ range $k, $v := . }} {{ printf "%q" $k }}: {{ printf "%q" $v }}, 970 {{ end }}{{/* 971 */}} },{{ end }}{{/* 972 */}}{{ else if eq .Context "BasicAuthSecurity" }}{{/* 973 */}}{{ else if eq .Context "JWTSecurity" }}{{/* 974 */}} In: {{ if eq .In "header" }}goa.LocHeader{{ else }}goa.LocQuery{{ end }}, 975 Name: {{ printf "%q" .Name }}, 976 TokenURL: {{ printf "%q" .TokenURL }},{{ with .Scopes }} 977 Scopes: map[string]string{ 978 {{ range $k, $v := . }} {{ printf "%q" $k }}: {{ printf "%q" $v }}, 979 {{ end }}{{/* 980 */}} },{{ end }} 981 {{ end }}{{/* 982 */}} } 983 {{ if .Description }} def.Description = {{ printf "%q" .Description }} 984 {{ end }} return &def 985 } 986 987 {{ end }}// handleSecurity creates a handler that runs the auth middleware for the security scheme. 988 func handleSecurity(schemeName string, h goa.Handler, scopes ...string) goa.Handler { 989 return func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 990 scheme := ctx.Value(authMiddlewareKey(schemeName)) 991 am, ok := scheme.(goa.Middleware) 992 if !ok { 993 return goa.NoAuthMiddleware(schemeName) 994 } 995 ctx = goa.WithRequiredScopes(ctx, scopes) 996 return am(h)(ctx, rw, req) 997 } 998 } 999 ` 1000 )