github.com/ccrossley/goa@v1.3.1/design/definitions.go (about) 1 package design 2 3 import ( 4 "errors" 5 "fmt" 6 "net/http" 7 "path" 8 "sort" 9 "strings" 10 11 "github.com/dimfeld/httppath" 12 "github.com/goadesign/goa/dslengine" 13 ) 14 15 type ( 16 // APIDefinition defines the global properties of the API. 17 APIDefinition struct { 18 // Name of API 19 Name string 20 // Title of API 21 Title string 22 // Description of API 23 Description string 24 // Version is the version of the API described by this design. 25 Version string 26 // Host is the default API hostname 27 Host string 28 // Schemes is the supported API URL schemes 29 Schemes []string 30 // BasePath is the common base path to all API endpoints 31 BasePath string 32 // Params define the common path parameters to all API endpoints 33 Params *AttributeDefinition 34 // Consumes lists the mime types supported by the API controllers 35 Consumes []*EncodingDefinition 36 // Produces lists the mime types generated by the API controllers 37 Produces []*EncodingDefinition 38 // Origins defines the CORS policies that apply to this API. 39 Origins map[string]*CORSDefinition 40 // TermsOfService describes or links to the API terms of service 41 TermsOfService string 42 // Contact provides the API users with contact information 43 Contact *ContactDefinition 44 // License describes the API license 45 License *LicenseDefinition 46 // Docs points to the API external documentation 47 Docs *DocsDefinition 48 // Resources is the set of exposed resources indexed by name 49 Resources map[string]*ResourceDefinition 50 // Types indexes the user defined types by name 51 Types map[string]*UserTypeDefinition 52 // MediaTypes indexes the API media types by canonical identifier 53 MediaTypes map[string]*MediaTypeDefinition 54 // Traits available to all API resources and actions indexed by name 55 Traits map[string]*dslengine.TraitDefinition 56 // Responses available to all API actions indexed by name 57 Responses map[string]*ResponseDefinition 58 // Response template factories available to all API actions indexed by name 59 ResponseTemplates map[string]*ResponseTemplateDefinition 60 // Built-in responses 61 DefaultResponses map[string]*ResponseDefinition 62 // Built-in response templates 63 DefaultResponseTemplates map[string]*ResponseTemplateDefinition 64 // DSLFunc contains the DSL used to create this definition if any 65 DSLFunc func() 66 // Metadata is a list of key/value pairs 67 Metadata dslengine.MetadataDefinition 68 // SecuritySchemes lists the available security schemes available 69 // to the API. 70 SecuritySchemes []*SecuritySchemeDefinition 71 // Security defines security requirements for all the 72 // resources and actions, unless overridden by Resource or 73 // Action-level Security() calls. 74 Security *SecurityDefinition 75 // NoExamples indicates whether to bypass automatic example generation. 76 NoExamples bool 77 78 // rand is the random generator used to generate examples. 79 rand *RandomGenerator 80 } 81 82 // ContactDefinition contains the API contact information. 83 ContactDefinition struct { 84 // Name of the contact person/organization 85 Name string `json:"name,omitempty"` 86 // Email address of the contact person/organization 87 Email string `json:"email,omitempty"` 88 // URL pointing to the contact information 89 URL string `json:"url,omitempty"` 90 } 91 92 // LicenseDefinition contains the license information for the API. 93 LicenseDefinition struct { 94 // Name of license used for the API 95 Name string `json:"name,omitempty"` 96 // URL to the license used for the API 97 URL string `json:"url,omitempty"` 98 } 99 100 // DocsDefinition points to external documentation. 101 DocsDefinition struct { 102 // Description of documentation. 103 Description string `json:"description,omitempty"` 104 // URL to documentation. 105 URL string `json:"url,omitempty"` 106 } 107 108 // ResourceDefinition describes a REST resource. 109 // It defines both a media type and a set of actions that can be executed through HTTP 110 // requests. 111 ResourceDefinition struct { 112 // Resource name 113 Name string 114 // Schemes is the supported API URL schemes 115 Schemes []string 116 // Common URL prefix to all resource action HTTP requests 117 BasePath string 118 // Path and query string parameters that apply to all actions. 119 Params *AttributeDefinition 120 // Name of parent resource if any 121 ParentName string 122 // Optional description 123 Description string 124 // Default media type, describes the resource attributes 125 MediaType string 126 // Default view name if default media type is MediaTypeDefinition 127 DefaultViewName string 128 // Exposed resource actions indexed by name 129 Actions map[string]*ActionDefinition 130 // FileServers is the list of static asset serving endpoints 131 FileServers []*FileServerDefinition 132 // Action with canonical resource path 133 CanonicalActionName string 134 // Map of response definitions that apply to all actions indexed by name. 135 Responses map[string]*ResponseDefinition 136 // Request headers that apply to all actions. 137 Headers *AttributeDefinition 138 // Origins defines the CORS policies that apply to this resource. 139 Origins map[string]*CORSDefinition 140 // DSLFunc contains the DSL used to create this definition if any. 141 DSLFunc func() 142 // metadata is a list of key/value pairs 143 Metadata dslengine.MetadataDefinition 144 // Security defines security requirements for the Resource, 145 // for actions that don't define one themselves. 146 Security *SecurityDefinition 147 } 148 149 // CORSDefinition contains the definition for a specific origin CORS policy. 150 CORSDefinition struct { 151 // Parent API or resource 152 Parent dslengine.Definition 153 // Origin 154 Origin string 155 // List of authorized headers, "*" authorizes all 156 Headers []string 157 // List of authorized HTTP methods 158 Methods []string 159 // List of headers exposed to clients 160 Exposed []string 161 // How long to cache a prefligh request response 162 MaxAge uint 163 // Sets Access-Control-Allow-Credentials header 164 Credentials bool 165 // Sets Whether the Origin string is a regular expression 166 Regexp bool 167 } 168 169 // EncodingDefinition defines an encoder supported by the API. 170 EncodingDefinition struct { 171 // MIMETypes is the set of possible MIME types for the content being encoded or decoded. 172 MIMETypes []string 173 // PackagePath is the path to the Go package that implements the encoder/decoder. 174 // The package must expose a `EncoderFactory` or `DecoderFactory` function 175 // that the generated code calls. The methods must return objects that implement 176 // the goa.EncoderFactory or goa.DecoderFactory interface respectively. 177 PackagePath string 178 // Function is the name of the Go function used to instantiate the encoder/decoder. 179 // Defaults to NewEncoder and NewDecoder respecitively. 180 Function string 181 // Encoder is true if the definition is for a encoder, false if it's for a decoder. 182 Encoder bool 183 } 184 185 // ResponseDefinition defines a HTTP response status and optional validation rules. 186 ResponseDefinition struct { 187 // Response name 188 Name string 189 // HTTP status 190 Status int 191 // Response description 192 Description string 193 // Response body type if any 194 Type DataType 195 // Response body media type if any 196 MediaType string 197 // Response view name if MediaType is MediaTypeDefinition 198 ViewName string 199 // Response header definitions 200 Headers *AttributeDefinition 201 // Parent action or resource 202 Parent dslengine.Definition 203 // Metadata is a list of key/value pairs 204 Metadata dslengine.MetadataDefinition 205 // Standard is true if the response definition comes from the goa default responses 206 Standard bool 207 } 208 209 // ResponseTemplateDefinition defines a response template. 210 // A response template is a function that takes an arbitrary number 211 // of strings and returns a response definition. 212 ResponseTemplateDefinition struct { 213 // Response template name 214 Name string 215 // Response template function 216 Template func(params ...string) *ResponseDefinition 217 } 218 219 // ActionDefinition defines a resource action. 220 // It defines both an HTTP endpoint and the shape of HTTP requests and responses made to 221 // that endpoint. 222 // The shape of requests is defined via "parameters", there are path parameters 223 // parameters and a payload parameter (request body). 224 // (i.e. portions of the URL that define parameter values), query string 225 ActionDefinition struct { 226 // Action name, e.g. "create" 227 Name string 228 // Action description, e.g. "Creates a task" 229 Description string 230 // Docs points to the API external documentation 231 Docs *DocsDefinition 232 // Parent resource 233 Parent *ResourceDefinition 234 // Specific action URL schemes 235 Schemes []string 236 // Action routes 237 Routes []*RouteDefinition 238 // Map of possible response definitions indexed by name 239 Responses map[string]*ResponseDefinition 240 // Path and query string parameters 241 Params *AttributeDefinition 242 // Query string parameters only 243 QueryParams *AttributeDefinition 244 // Payload blueprint (request body) if any 245 Payload *UserTypeDefinition 246 // PayloadOptional is true if the request payload is optional, false otherwise. 247 PayloadOptional bool 248 // Request headers that need to be made available to action 249 Headers *AttributeDefinition 250 // Metadata is a list of key/value pairs 251 Metadata dslengine.MetadataDefinition 252 // Security defines security requirements for the action 253 Security *SecurityDefinition 254 } 255 256 // FileServerDefinition defines an endpoint that servers static assets. 257 FileServerDefinition struct { 258 // Parent resource 259 Parent *ResourceDefinition 260 // Description for docs 261 Description string 262 // Docs points to the API external documentation 263 Docs *DocsDefinition 264 // FilePath is the file path to the static asset(s) 265 FilePath string 266 // RequestPath is the HTTP path that servers the assets. 267 RequestPath string 268 // Metadata is a list of key/value pairs 269 Metadata dslengine.MetadataDefinition 270 // Security defines security requirements for the file server. 271 Security *SecurityDefinition 272 } 273 274 // LinkDefinition defines a media type link, it specifies a URL to a related resource. 275 LinkDefinition struct { 276 // Link name 277 Name string 278 // View used to render link if not "link" 279 View string 280 // URITemplate is the RFC6570 URI template of the link Href. 281 URITemplate string 282 283 // Parent media Type 284 Parent *MediaTypeDefinition 285 } 286 287 // ViewDefinition defines which members and links to render when building a response. 288 // The view is a JSON object whose property names must match the names of the parent media 289 // type members. 290 // The members fields are inherited from the parent media type but may be overridden. 291 ViewDefinition struct { 292 // Set of properties included in view 293 *AttributeDefinition 294 // Name of view 295 Name string 296 // Parent media Type 297 Parent *MediaTypeDefinition 298 } 299 300 // RouteDefinition represents an action route. 301 RouteDefinition struct { 302 // Verb is the HTTP method, e.g. "GET", "POST", etc. 303 Verb string 304 // Path is the URL path e.g. "/tasks/:id" 305 Path string 306 // Parent is the action this route applies to. 307 Parent *ActionDefinition 308 // Metadata is a list of key/value pairs 309 Metadata dslengine.MetadataDefinition 310 } 311 312 // AttributeDefinition defines a JSON object member with optional description, default 313 // value and validations. 314 AttributeDefinition struct { 315 // Attribute type 316 Type DataType 317 // Attribute reference type if any 318 Reference DataType 319 // Optional description 320 Description string 321 // Optional validations 322 Validation *dslengine.ValidationDefinition 323 // Metadata is a list of key/value pairs 324 Metadata dslengine.MetadataDefinition 325 // Optional member default value 326 DefaultValue interface{} 327 // Optional member example value 328 Example interface{} 329 // Optional view used to render Attribute (only applies to media type attributes). 330 View string 331 // NonZeroAttributes lists the names of the child attributes that cannot have a 332 // zero value (and thus whose presence does not need to be validated). 333 NonZeroAttributes map[string]bool 334 // DSLFunc contains the initialization DSL. This is used for user types. 335 DSLFunc func() 336 } 337 338 // ContainerDefinition defines a generic container definition that contains attributes. 339 // This makes it possible for plugins to use attributes in their own data structures. 340 ContainerDefinition interface { 341 // Attribute returns the container definition embedded attribute. 342 Attribute() *AttributeDefinition 343 } 344 345 // ResourceIterator is the type of functions given to IterateResources. 346 ResourceIterator func(r *ResourceDefinition) error 347 348 // MediaTypeIterator is the type of functions given to IterateMediaTypes. 349 MediaTypeIterator func(m *MediaTypeDefinition) error 350 351 // UserTypeIterator is the type of functions given to IterateUserTypes. 352 UserTypeIterator func(m *UserTypeDefinition) error 353 354 // ActionIterator is the type of functions given to IterateActions. 355 ActionIterator func(a *ActionDefinition) error 356 357 // FileServerIterator is the type of functions given to IterateFileServers. 358 FileServerIterator func(f *FileServerDefinition) error 359 360 // HeaderIterator is the type of functions given to IterateHeaders. 361 HeaderIterator func(name string, isRequired bool, h *AttributeDefinition) error 362 363 // ResponseIterator is the type of functions given to IterateResponses. 364 ResponseIterator func(r *ResponseDefinition) error 365 ) 366 367 // NewAPIDefinition returns a new design with built-in response templates. 368 func NewAPIDefinition() *APIDefinition { 369 api := &APIDefinition{ 370 DefaultResponseTemplates: make(map[string]*ResponseTemplateDefinition), 371 DefaultResponses: make(map[string]*ResponseDefinition), 372 } 373 t := func(params ...string) *ResponseDefinition { 374 if len(params) < 1 { 375 dslengine.ReportError("expected media type as argument when invoking response template OK") 376 return nil 377 } 378 return &ResponseDefinition{ 379 Name: OK, 380 Status: 200, 381 MediaType: params[0], 382 } 383 } 384 api.DefaultResponseTemplates[OK] = &ResponseTemplateDefinition{ 385 Name: OK, 386 Template: t, 387 } 388 for _, p := range []struct { 389 status int 390 name string 391 }{ 392 {100, Continue}, 393 {101, SwitchingProtocols}, 394 {200, OK}, 395 {201, Created}, 396 {202, Accepted}, 397 {203, NonAuthoritativeInfo}, 398 {204, NoContent}, 399 {205, ResetContent}, 400 {206, PartialContent}, 401 {300, MultipleChoices}, 402 {301, MovedPermanently}, 403 {302, Found}, 404 {303, SeeOther}, 405 {304, NotModified}, 406 {305, UseProxy}, 407 {307, TemporaryRedirect}, 408 {400, BadRequest}, 409 {401, Unauthorized}, 410 {402, PaymentRequired}, 411 {403, Forbidden}, 412 {404, NotFound}, 413 {405, MethodNotAllowed}, 414 {406, NotAcceptable}, 415 {407, ProxyAuthRequired}, 416 {408, RequestTimeout}, 417 {409, Conflict}, 418 {410, Gone}, 419 {411, LengthRequired}, 420 {412, PreconditionFailed}, 421 {413, RequestEntityTooLarge}, 422 {414, RequestURITooLong}, 423 {415, UnsupportedMediaType}, 424 {416, RequestedRangeNotSatisfiable}, 425 {417, ExpectationFailed}, 426 {418, Teapot}, 427 {422, UnprocessableEntity}, 428 {500, InternalServerError}, 429 {501, NotImplemented}, 430 {502, BadGateway}, 431 {503, ServiceUnavailable}, 432 {504, GatewayTimeout}, 433 {505, HTTPVersionNotSupported}, 434 } { 435 api.DefaultResponses[p.name] = &ResponseDefinition{ 436 Name: p.name, 437 Description: http.StatusText(p.status), 438 Status: p.status, 439 } 440 } 441 return api 442 } 443 444 // DSLName is the name of the DSL as displayed to the user during execution. 445 func (a *APIDefinition) DSLName() string { 446 return "goa API" 447 } 448 449 // DependsOn returns the other roots this root depends on, nothing for APIDefinition. 450 func (a *APIDefinition) DependsOn() []dslengine.Root { 451 return nil 452 } 453 454 // IterateSets calls the given iterator possing in the API definition, user types, media types and 455 // finally resources. 456 func (a *APIDefinition) IterateSets(iterator dslengine.SetIterator) { 457 // First run the top level API DSL to initialize responses and 458 // response templates needed by resources. 459 iterator([]dslengine.Definition{a}) 460 461 // Then run the user type DSLs 462 typeAttributes := make([]dslengine.Definition, len(a.Types)) 463 i := 0 464 a.IterateUserTypes(func(u *UserTypeDefinition) error { 465 u.AttributeDefinition.DSLFunc = u.DSLFunc 466 typeAttributes[i] = u.AttributeDefinition 467 i++ 468 return nil 469 }) 470 iterator(typeAttributes) 471 472 // Then the media type DSLs 473 mediaTypes := make([]dslengine.Definition, len(a.MediaTypes)) 474 i = 0 475 a.IterateMediaTypes(func(mt *MediaTypeDefinition) error { 476 mediaTypes[i] = mt 477 i++ 478 return nil 479 }) 480 iterator(mediaTypes) 481 482 // Then, the Security schemes definitions 483 var securitySchemes []dslengine.Definition 484 for _, scheme := range a.SecuritySchemes { 485 securitySchemes = append(securitySchemes, dslengine.Definition(scheme)) 486 } 487 iterator(securitySchemes) 488 489 // And now that we have everything - the resources. The resource 490 // lifecycle handlers dispatch to their children elements, like Actions, 491 // etc.. We must process parent resources first to ensure that query 492 // string and path parameters are initialized by the time a child 493 // resource action parameters are categorized. 494 resources := make([]*ResourceDefinition, len(a.Resources)) 495 i = 0 496 a.IterateResources(func(res *ResourceDefinition) error { 497 resources[i] = res 498 i++ 499 return nil 500 }) 501 sort.Sort(byParent(resources)) 502 defs := make([]dslengine.Definition, len(resources)) 503 for i, r := range resources { 504 defs[i] = r 505 } 506 iterator(defs) 507 } 508 509 // Reset sets all the API definition fields to their zero value except the default responses and 510 // default response templates. 511 func (a *APIDefinition) Reset() { 512 n := NewAPIDefinition() 513 *a = *n 514 } 515 516 // Context returns the generic definition name used in error messages. 517 func (a *APIDefinition) Context() string { 518 if a.Name != "" { 519 return fmt.Sprintf("API %#v", a.Name) 520 } 521 return "unnamed API" 522 } 523 524 // PathParams returns the base path parameters of a. 525 func (a *APIDefinition) PathParams() *AttributeDefinition { 526 names := ExtractWildcards(a.BasePath) 527 obj := make(Object) 528 for _, n := range names { 529 obj[n] = a.Params.Type.ToObject()[n] 530 } 531 return &AttributeDefinition{Type: obj} 532 } 533 534 // IterateMediaTypes calls the given iterator passing in each media type sorted in alphabetical order. 535 // Iteration stops if an iterator returns an error and in this case IterateMediaTypes returns that 536 // error. 537 func (a *APIDefinition) IterateMediaTypes(it MediaTypeIterator) error { 538 names := make([]string, len(a.MediaTypes)) 539 i := 0 540 for n := range a.MediaTypes { 541 names[i] = n 542 i++ 543 } 544 sort.Strings(names) 545 for _, n := range names { 546 if err := it(a.MediaTypes[n]); err != nil { 547 return err 548 } 549 } 550 return nil 551 } 552 553 // IterateUserTypes calls the given iterator passing in each user type sorted in alphabetical order. 554 // Iteration stops if an iterator returns an error and in this case IterateUserTypes returns that 555 // error. 556 func (a *APIDefinition) IterateUserTypes(it UserTypeIterator) error { 557 names := make([]string, len(a.Types)) 558 i := 0 559 for n := range a.Types { 560 names[i] = n 561 i++ 562 } 563 sort.Strings(names) 564 for _, n := range names { 565 if err := it(a.Types[n]); err != nil { 566 return err 567 } 568 } 569 return nil 570 } 571 572 // IterateResponses calls the given iterator passing in each response sorted in alphabetical order. 573 // Iteration stops if an iterator returns an error and in this case IterateResponses returns that 574 // error. 575 func (a *APIDefinition) IterateResponses(it ResponseIterator) error { 576 names := make([]string, len(a.Responses)) 577 i := 0 578 for n := range a.Responses { 579 names[i] = n 580 i++ 581 } 582 sort.Strings(names) 583 for _, n := range names { 584 if err := it(a.Responses[n]); err != nil { 585 return err 586 } 587 } 588 return nil 589 } 590 591 // RandomGenerator is seeded after the API name. It's used to generate examples. 592 func (a *APIDefinition) RandomGenerator() *RandomGenerator { 593 if a.rand == nil { 594 a.rand = NewRandomGenerator(a.Name) 595 } 596 return a.rand 597 } 598 599 // MediaTypeWithIdentifier returns the media type with a matching 600 // media type identifier. Two media type identifiers match if their 601 // values sans suffix match. So for example "application/vnd.foo+xml", 602 // "application/vnd.foo+json" and "application/vnd.foo" all match. 603 func (a *APIDefinition) MediaTypeWithIdentifier(id string) *MediaTypeDefinition { 604 canonicalID := CanonicalIdentifier(id) 605 for _, mt := range a.MediaTypes { 606 if canonicalID == CanonicalIdentifier(mt.Identifier) { 607 return mt 608 } 609 } 610 return nil 611 } 612 613 // IterateResources calls the given iterator passing in each resource sorted in alphabetical order. 614 // Iteration stops if an iterator returns an error and in this case IterateResources returns that 615 // error. 616 func (a *APIDefinition) IterateResources(it ResourceIterator) error { 617 names := make([]string, len(a.Resources)) 618 i := 0 619 for n := range a.Resources { 620 names[i] = n 621 i++ 622 } 623 sort.Strings(names) 624 for _, n := range names { 625 if err := it(a.Resources[n]); err != nil { 626 return err 627 } 628 } 629 return nil 630 } 631 632 // DSL returns the initialization DSL. 633 func (a *APIDefinition) DSL() func() { 634 return a.DSLFunc 635 } 636 637 // Finalize sets the Consumes and Produces fields to the defaults if empty. 638 // Also it records built-in media types that are used by the user design. 639 func (a *APIDefinition) Finalize() { 640 if len(a.Consumes) == 0 { 641 a.Consumes = DefaultDecoders 642 } 643 if len(a.Produces) == 0 { 644 a.Produces = DefaultEncoders 645 } 646 a.IterateResources(func(r *ResourceDefinition) error { 647 returnsError := func(resp *ResponseDefinition) bool { 648 if resp.MediaType == ErrorMediaIdentifier { 649 if a.MediaTypes == nil { 650 a.MediaTypes = make(map[string]*MediaTypeDefinition) 651 } 652 a.MediaTypes[CanonicalIdentifier(ErrorMediaIdentifier)] = ErrorMedia 653 return true 654 } 655 return false 656 } 657 for _, resp := range a.Responses { 658 if returnsError(resp) { 659 return errors.New("done") 660 } 661 } 662 for _, resp := range r.Responses { 663 if returnsError(resp) { 664 return errors.New("done") 665 } 666 } 667 return r.IterateActions(func(action *ActionDefinition) error { 668 for _, resp := range action.Responses { 669 if returnsError(resp) { 670 return errors.New("done") 671 } 672 } 673 return nil 674 }) 675 }) 676 } 677 678 // NewResourceDefinition creates a resource definition but does not 679 // execute the DSL. 680 func NewResourceDefinition(name string, dsl func()) *ResourceDefinition { 681 return &ResourceDefinition{ 682 Name: name, 683 MediaType: "text/plain", 684 DSLFunc: dsl, 685 } 686 } 687 688 // Context returns the generic definition name used in error messages. 689 func (r *ResourceDefinition) Context() string { 690 if r.Name != "" { 691 return fmt.Sprintf("resource %#v", r.Name) 692 } 693 return "unnamed resource" 694 } 695 696 // PathParams returns the base path parameters of r. 697 func (r *ResourceDefinition) PathParams() *AttributeDefinition { 698 names := ExtractWildcards(r.BasePath) 699 obj := make(Object) 700 if r.Params != nil { 701 for _, n := range names { 702 if p, ok := r.Params.Type.ToObject()[n]; ok { 703 obj[n] = p 704 } 705 } 706 } 707 return &AttributeDefinition{Type: obj} 708 } 709 710 // IterateActions calls the given iterator passing in each resource action sorted in alphabetical order. 711 // Iteration stops if an iterator returns an error and in this case IterateActions returns that 712 // error. 713 func (r *ResourceDefinition) IterateActions(it ActionIterator) error { 714 names := make([]string, len(r.Actions)) 715 i := 0 716 for n := range r.Actions { 717 names[i] = n 718 i++ 719 } 720 sort.Strings(names) 721 for _, n := range names { 722 if err := it(r.Actions[n]); err != nil { 723 return err 724 } 725 } 726 return nil 727 } 728 729 // IterateFileServers calls the given iterator passing each resource file server sorted by file 730 // path. Iteration stops if an iterator returns an error and in this case IterateFileServers returns 731 // that error. 732 func (r *ResourceDefinition) IterateFileServers(it FileServerIterator) error { 733 sort.Sort(ByFilePath(r.FileServers)) 734 for _, f := range r.FileServers { 735 if err := it(f); err != nil { 736 return err 737 } 738 } 739 return nil 740 } 741 742 // IterateHeaders calls the given iterator passing in each response sorted in alphabetical order. 743 // Iteration stops if an iterator returns an error and in this case IterateHeaders returns that 744 // error. 745 func (r *ResourceDefinition) IterateHeaders(it HeaderIterator) error { 746 return iterateHeaders(r.Headers, r.Headers.IsRequired, it) 747 } 748 749 // CanonicalAction returns the canonical action of the resource if any. 750 // The canonical action is used to compute hrefs to resources. 751 func (r *ResourceDefinition) CanonicalAction() *ActionDefinition { 752 name := r.CanonicalActionName 753 if name == "" { 754 name = "show" 755 } 756 ca, _ := r.Actions[name] 757 return ca 758 } 759 760 // URITemplate returns a URI template to this resource. 761 // The result is the empty string if the resource does not have a "show" action 762 // and does not define a different canonical action. 763 func (r *ResourceDefinition) URITemplate() string { 764 ca := r.CanonicalAction() 765 if ca == nil || len(ca.Routes) == 0 { 766 return "" 767 } 768 return ca.Routes[0].FullPath() 769 } 770 771 // FullPath computes the base path to the resource actions concatenating the API and parent resource 772 // base paths as needed. 773 func (r *ResourceDefinition) FullPath() string { 774 if strings.HasPrefix(r.BasePath, "//") { 775 return httppath.Clean(r.BasePath) 776 } 777 var basePath string 778 if p := r.Parent(); p != nil { 779 if ca := p.CanonicalAction(); ca != nil { 780 if routes := ca.Routes; len(routes) > 0 { 781 // Note: all these tests should be true at code generation time 782 // as DSL validation makes sure that parent resources have a 783 // canonical path. 784 basePath = path.Join(routes[0].FullPath()) 785 } 786 } 787 } else { 788 basePath = Design.BasePath 789 } 790 return httppath.Clean(path.Join(basePath, r.BasePath)) 791 } 792 793 // Parent returns the parent resource if any, nil otherwise. 794 func (r *ResourceDefinition) Parent() *ResourceDefinition { 795 if r.ParentName != "" { 796 if parent, ok := Design.Resources[r.ParentName]; ok { 797 return parent 798 } 799 } 800 return nil 801 } 802 803 // AllOrigins compute all CORS policies for the resource taking into account any API policy. 804 // The result is sorted alphabetically by policy origin. 805 func (r *ResourceDefinition) AllOrigins() []*CORSDefinition { 806 all := make(map[string]*CORSDefinition) 807 for n, o := range Design.Origins { 808 all[n] = o 809 } 810 for n, o := range r.Origins { 811 all[n] = o 812 } 813 names := make([]string, len(all)) 814 i := 0 815 for n := range all { 816 names[i] = n 817 i++ 818 } 819 sort.Strings(names) 820 cors := make([]*CORSDefinition, len(names)) 821 for i, n := range names { 822 cors[i] = all[n] 823 } 824 return cors 825 } 826 827 // PreflightPaths returns the paths that should handle OPTIONS requests. 828 func (r *ResourceDefinition) PreflightPaths() []string { 829 var paths []string 830 r.IterateActions(func(a *ActionDefinition) error { 831 for _, r := range a.Routes { 832 if r.Verb == "OPTIONS" { 833 continue 834 } 835 found := false 836 fp := r.FullPath() 837 for _, p := range paths { 838 if fp == p { 839 found = true 840 break 841 } 842 } 843 if !found { 844 paths = append(paths, fp) 845 } 846 } 847 return nil 848 }) 849 r.IterateFileServers(func(fs *FileServerDefinition) error { 850 found := false 851 fp := fs.RequestPath 852 for _, p := range paths { 853 if fp == p { 854 found = true 855 break 856 } 857 } 858 if !found { 859 paths = append(paths, fp) 860 } 861 return nil 862 }) 863 return paths 864 } 865 866 // DSL returns the initialization DSL. 867 func (r *ResourceDefinition) DSL() func() { 868 return r.DSLFunc 869 } 870 871 // Finalize is run post DSL execution. It merges response definitions, creates implicit action 872 // parameters, initializes querystring parameters, sets path parameters as non zero attributes 873 // and sets the fallbacks for security schemes. 874 func (r *ResourceDefinition) Finalize() { 875 meta := r.Metadata["swagger:generate"] 876 r.IterateFileServers(func(f *FileServerDefinition) error { 877 if meta != nil { 878 if _, ok := f.Metadata["swagger:generate"]; !ok { 879 f.Metadata["swagger:generate"] = meta 880 } 881 } 882 f.Finalize() 883 return nil 884 }) 885 r.IterateActions(func(a *ActionDefinition) error { 886 if meta != nil { 887 if _, ok := a.Metadata["swagger:generate"]; !ok { 888 a.Metadata["swagger:generate"] = meta 889 } 890 } 891 a.Finalize() 892 return nil 893 }) 894 } 895 896 // UserTypes returns all the user types used by the resource action payloads and parameters. 897 func (r *ResourceDefinition) UserTypes() map[string]*UserTypeDefinition { 898 types := make(map[string]*UserTypeDefinition) 899 for _, a := range r.Actions { 900 for n, ut := range a.UserTypes() { 901 types[n] = ut 902 } 903 } 904 if len(types) == 0 { 905 return nil 906 } 907 return types 908 } 909 910 // byParent makes it possible to sort resources - parents first the children. 911 type byParent []*ResourceDefinition 912 913 func (p byParent) Len() int { return len(p) } 914 func (p byParent) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 915 func (p byParent) Less(i, j int) bool { 916 for k := 0; k < i; k++ { 917 // We need to inspect _all_ previous fields to see if they are a parent. Sort doesn't do this. 918 if p[i].Name == p[k].ParentName { 919 return true 920 } 921 } 922 923 return false 924 } 925 926 // Context returns the generic definition name used in error messages. 927 func (cors *CORSDefinition) Context() string { 928 return fmt.Sprintf("CORS policy for resource %s origin %s", cors.Parent.Context(), cors.Origin) 929 } 930 931 // Context returns the generic definition name used in error messages. 932 func (enc *EncodingDefinition) Context() string { 933 return fmt.Sprintf("encoding for %s", strings.Join(enc.MIMETypes, ", ")) 934 } 935 936 // Context returns the generic definition name used in error messages. 937 func (a *AttributeDefinition) Context() string { 938 return "" 939 } 940 941 // AllRequired returns the list of all required fields from the underlying object. 942 // An attribute type can be itself an attribute (e.g. a MediaTypeDefinition or a UserTypeDefinition) 943 // This happens when the DSL uses references for example. So traverse the hierarchy and collect 944 // all the required validations. 945 func (a *AttributeDefinition) AllRequired() (required []string) { 946 if a == nil || a.Validation == nil { 947 return 948 } 949 required = a.Validation.Required 950 if ds, ok := a.Type.(DataStructure); ok { 951 required = append(required, ds.Definition().AllRequired()...) 952 } 953 return 954 } 955 956 // IsRequired returns true if the given string matches the name of a required 957 // attribute, false otherwise. 958 func (a *AttributeDefinition) IsRequired(attName string) bool { 959 for _, name := range a.AllRequired() { 960 if name == attName { 961 return true 962 } 963 } 964 return false 965 } 966 967 // HasDefaultValue returns true if the given attribute has a default value. 968 func (a *AttributeDefinition) HasDefaultValue(attName string) bool { 969 if a.Type.IsObject() { 970 att := a.Type.ToObject()[attName] 971 return att.DefaultValue != nil 972 } 973 return false 974 } 975 976 // SetDefault sets the default for the attribute. It also converts HashVal 977 // and ArrayVal to map and slice respectively. 978 func (a *AttributeDefinition) SetDefault(def interface{}) { 979 switch actual := def.(type) { 980 case HashVal: 981 a.DefaultValue = actual.ToMap() 982 case ArrayVal: 983 a.DefaultValue = actual.ToSlice() 984 default: 985 a.DefaultValue = actual 986 } 987 } 988 989 // AddValues adds the Enum values to the attribute's validation definition. 990 // It also performs any conversion needed for HashVal and ArrayVal types. 991 func (a *AttributeDefinition) AddValues(values []interface{}) { 992 if a.Validation == nil { 993 a.Validation = &dslengine.ValidationDefinition{} 994 } 995 a.Validation.Values = make([]interface{}, len(values)) 996 for i, v := range values { 997 switch actual := v.(type) { 998 case HashVal: 999 a.Validation.Values[i] = actual.ToMap() 1000 case ArrayVal: 1001 a.Validation.Values[i] = actual.ToSlice() 1002 default: 1003 a.Validation.Values[i] = actual 1004 } 1005 } 1006 } 1007 1008 // AllNonZero returns the complete list of all non-zero attribute name. 1009 func (a *AttributeDefinition) AllNonZero() []string { 1010 nzs := make([]string, len(a.NonZeroAttributes)) 1011 i := 0 1012 for n := range a.NonZeroAttributes { 1013 nzs[i] = n 1014 i++ 1015 } 1016 return nzs 1017 } 1018 1019 // IsNonZero returns true if the given string matches the name of a non-zero 1020 // attribute, false otherwise. 1021 func (a *AttributeDefinition) IsNonZero(attName string) bool { 1022 return a.NonZeroAttributes[attName] 1023 } 1024 1025 // IsPrimitivePointer returns true if the field generated for the given attribute should be a 1026 // pointer to a primitive type. The target attribute must be an object. 1027 func (a *AttributeDefinition) IsPrimitivePointer(attName string) bool { 1028 if !a.Type.IsObject() { 1029 panic("checking pointer field on non-object") // bug 1030 } 1031 att := a.Type.ToObject()[attName] 1032 if att == nil { 1033 return false 1034 } 1035 if att.Type.IsPrimitive() { 1036 return !a.IsRequired(attName) && !a.HasDefaultValue(attName) && !a.IsNonZero(attName) 1037 } 1038 return false 1039 } 1040 1041 // SetExample sets the custom example. SetExample also handles the case when the user doesn't 1042 // want any example or any auto-generated example. 1043 func (a *AttributeDefinition) SetExample(example interface{}) bool { 1044 if example == nil { 1045 a.Example = "-" // set it to something else than nil so we know not to generate one 1046 return true 1047 } 1048 if a.Type == nil || a.Type.IsCompatible(example) { 1049 a.Example = example 1050 return true 1051 } 1052 return false 1053 } 1054 1055 // GenerateExample returns the value of the Example field if not nil. Otherwise it traverses the 1056 // attribute type and recursively generates an example. The result is saved in the Example field. 1057 func (a *AttributeDefinition) GenerateExample(rand *RandomGenerator, seen []string) interface{} { 1058 if a.Example != nil { 1059 return a.Example 1060 } 1061 if Design.NoExamples { 1062 return nil 1063 } 1064 1065 // Avoid infinite loops 1066 var key string 1067 if mt, ok := a.Type.(*MediaTypeDefinition); ok { 1068 key = mt.Identifier 1069 } else if ut, ok := a.Type.(*UserTypeDefinition); ok { 1070 key = ut.TypeName 1071 } 1072 if key != "" { 1073 count := 0 1074 for _, k := range seen { 1075 if k == key { 1076 count++ 1077 } 1078 } 1079 if count > 1 { 1080 // Only go a couple of levels deep 1081 return nil 1082 } 1083 seen = append(seen, key) 1084 } 1085 1086 switch { 1087 case a.Type.IsArray(): 1088 a.Example = a.arrayExample(rand, seen) 1089 1090 case a.Type.IsHash(): 1091 a.Example = a.hashExample(rand, seen) 1092 1093 case a.Type.IsObject(): 1094 a.Example = a.objectExample(rand, seen) 1095 1096 default: 1097 a.Example = newExampleGenerator(a, rand).Generate(seen) 1098 } 1099 1100 return a.Example 1101 } 1102 1103 func (a *AttributeDefinition) arrayExample(rand *RandomGenerator, seen []string) interface{} { 1104 ary := a.Type.ToArray() 1105 ln := newExampleGenerator(a, rand).ExampleLength() 1106 var res []interface{} 1107 for i := 0; i < ln; i++ { 1108 ex := ary.ElemType.GenerateExample(rand, seen) 1109 if ex != nil { 1110 res = append(res, ex) 1111 } 1112 } 1113 if len(res) == 0 { 1114 return nil 1115 } 1116 return ary.MakeSlice(res) 1117 } 1118 1119 func (a *AttributeDefinition) hashExample(rand *RandomGenerator, seen []string) interface{} { 1120 h := a.Type.ToHash() 1121 ln := newExampleGenerator(a, rand).ExampleLength() 1122 res := make(map[interface{}]interface{}) 1123 for i := 0; i < ln; i++ { 1124 k := h.KeyType.GenerateExample(rand, seen) 1125 v := h.ElemType.GenerateExample(rand, seen) 1126 if k != nil && v != nil { 1127 res[k] = v 1128 } 1129 } 1130 if len(res) == 0 { 1131 return nil 1132 } 1133 return h.MakeMap(res) 1134 } 1135 1136 func (a *AttributeDefinition) objectExample(rand *RandomGenerator, seen []string) interface{} { 1137 // project media types 1138 actual := a 1139 if mt, ok := a.Type.(*MediaTypeDefinition); ok { 1140 v := a.View 1141 if v == "" { 1142 v = DefaultView 1143 } 1144 projected, _, err := mt.Project(v) 1145 if err != nil { 1146 panic(err) // bug 1147 } 1148 actual = projected.AttributeDefinition 1149 } 1150 1151 // ensure fixed ordering so random values are computed with consistent seeds 1152 aObj := actual.Type.ToObject() 1153 keys := make([]string, len(aObj)) 1154 i := 0 1155 for n := range aObj { 1156 keys[i] = n 1157 i++ 1158 } 1159 sort.Strings(keys) 1160 1161 res := make(map[string]interface{}) 1162 for _, n := range keys { 1163 att := aObj[n] 1164 if ex := att.GenerateExample(rand, seen); ex != nil { 1165 res[n] = ex 1166 } 1167 } 1168 if len(res) > 0 { 1169 a.Example = res 1170 } 1171 1172 return a.Example 1173 } 1174 1175 // Merge merges the argument attributes into the target and returns the target overriding existing 1176 // attributes with identical names. 1177 // This only applies to attributes of type Object and Merge panics if the 1178 // argument or the target is not of type Object. 1179 func (a *AttributeDefinition) Merge(other *AttributeDefinition) *AttributeDefinition { 1180 if other == nil { 1181 return a 1182 } 1183 if a == nil { 1184 return other 1185 } 1186 left := a.Type.(Object) 1187 right := other.Type.(Object) 1188 if left == nil || right == nil { 1189 panic("cannot merge non object attributes") // bug 1190 } 1191 for n, v := range right { 1192 left[n] = v 1193 } 1194 if other.Validation != nil && len(other.Validation.Required) > 0 { 1195 if a.Validation == nil { 1196 a.Validation = &dslengine.ValidationDefinition{} 1197 } 1198 for _, r := range other.Validation.Required { 1199 a.Validation.Required = append(a.Validation.Required, r) 1200 } 1201 } 1202 return a 1203 } 1204 1205 // Inherit merges the properties of existing target type attributes with the argument's. 1206 // The algorithm is recursive so that child attributes are also merged. 1207 func (a *AttributeDefinition) Inherit(parent *AttributeDefinition, seen ...map[*AttributeDefinition]struct{}) { 1208 if !a.shouldInherit(parent) { 1209 return 1210 } 1211 1212 a.inheritValidations(parent) 1213 a.inheritRecursive(parent, seen...) 1214 } 1215 1216 // DSL returns the initialization DSL. 1217 func (a *AttributeDefinition) DSL() func() { 1218 return a.DSLFunc 1219 } 1220 1221 func (a *AttributeDefinition) inheritRecursive(parent *AttributeDefinition, seen ...map[*AttributeDefinition]struct{}) { 1222 // prevent infinite recursions 1223 var s map[*AttributeDefinition]struct{} 1224 if len(seen) > 0 { 1225 s = seen[0] 1226 if _, ok := s[parent]; ok { 1227 return 1228 } 1229 } else { 1230 s = make(map[*AttributeDefinition]struct{}) 1231 } 1232 s[parent] = struct{}{} 1233 1234 if !a.shouldInherit(parent) { 1235 return 1236 } 1237 1238 for n, att := range a.Type.ToObject() { 1239 if patt, ok := parent.Type.ToObject()[n]; ok { 1240 if att.Description == "" { 1241 att.Description = patt.Description 1242 } 1243 att.inheritValidations(patt) 1244 if att.DefaultValue == nil { 1245 att.DefaultValue = patt.DefaultValue 1246 } 1247 if att.View == "" { 1248 att.View = patt.View 1249 } 1250 if att.Type == nil { 1251 att.Type = patt.Type 1252 } else if att.shouldInherit(patt) { 1253 for _, att := range att.Type.ToObject() { 1254 att.Inherit(patt.Type.ToObject()[n], s) 1255 } 1256 } 1257 if att.Example == nil { 1258 att.Example = patt.Example 1259 } 1260 } 1261 } 1262 } 1263 1264 func (a *AttributeDefinition) inheritValidations(parent *AttributeDefinition) { 1265 if parent.Validation == nil { 1266 return 1267 } 1268 if a.Validation == nil { 1269 a.Validation = &dslengine.ValidationDefinition{} 1270 } 1271 a.Validation.AddRequired(parent.Validation.Required) 1272 } 1273 1274 func (a *AttributeDefinition) shouldInherit(parent *AttributeDefinition) bool { 1275 return a != nil && a.Type.ToObject() != nil && 1276 parent != nil && parent.Type.ToObject() != nil 1277 } 1278 1279 // Context returns the generic definition name used in error messages. 1280 func (c *ContactDefinition) Context() string { 1281 if c.Name != "" { 1282 return fmt.Sprintf("contact %s", c.Name) 1283 } 1284 return "unnamed contact" 1285 } 1286 1287 // Context returns the generic definition name used in error messages. 1288 func (l *LicenseDefinition) Context() string { 1289 if l.Name != "" { 1290 return fmt.Sprintf("license %s", l.Name) 1291 } 1292 return "unnamed license" 1293 } 1294 1295 // Context returns the generic definition name used in error messages. 1296 func (d *DocsDefinition) Context() string { 1297 return fmt.Sprintf("documentation for %s", Design.Name) 1298 } 1299 1300 // Context returns the generic definition name used in error messages. 1301 func (t *UserTypeDefinition) Context() string { 1302 if t.TypeName != "" { 1303 return fmt.Sprintf("type %#v", t.TypeName) 1304 } 1305 return "unnamed type" 1306 } 1307 1308 // DSL returns the initialization DSL. 1309 func (t *UserTypeDefinition) DSL() func() { 1310 return t.DSLFunc 1311 } 1312 1313 // Context returns the generic definition name used in error messages. 1314 func (r *ResponseDefinition) Context() string { 1315 var prefix, suffix string 1316 if r.Name != "" { 1317 prefix = fmt.Sprintf("response %#v", r.Name) 1318 } else { 1319 prefix = "unnamed response" 1320 } 1321 if r.Parent != nil { 1322 suffix = fmt.Sprintf(" of %s", r.Parent.Context()) 1323 } 1324 return prefix + suffix 1325 } 1326 1327 // Finalize sets the response media type from its type if the type is a media type and no media 1328 // type is already specified. 1329 func (r *ResponseDefinition) Finalize() { 1330 if r.Type == nil { 1331 return 1332 } 1333 if r.MediaType != "" && r.MediaType != "text/plain" { 1334 return 1335 } 1336 mt, ok := r.Type.(*MediaTypeDefinition) 1337 if !ok { 1338 return 1339 } 1340 r.MediaType = mt.Identifier 1341 } 1342 1343 // Dup returns a copy of the response definition. 1344 func (r *ResponseDefinition) Dup() *ResponseDefinition { 1345 res := ResponseDefinition{ 1346 Name: r.Name, 1347 Status: r.Status, 1348 Description: r.Description, 1349 MediaType: r.MediaType, 1350 ViewName: r.ViewName, 1351 } 1352 if r.Headers != nil { 1353 res.Headers = DupAtt(r.Headers) 1354 } 1355 return &res 1356 } 1357 1358 // Merge merges other into target. Only the fields of target that are not already set are merged. 1359 func (r *ResponseDefinition) Merge(other *ResponseDefinition) { 1360 if other == nil { 1361 return 1362 } 1363 if r.Name == "" { 1364 r.Name = other.Name 1365 } 1366 if r.Status == 0 { 1367 r.Status = other.Status 1368 } 1369 if r.Description == "" { 1370 r.Description = other.Description 1371 } 1372 if r.MediaType == "" { 1373 r.MediaType = other.MediaType 1374 r.ViewName = other.ViewName 1375 } 1376 if other.Headers != nil { 1377 otherHeaders := other.Headers.Type.ToObject() 1378 if len(otherHeaders) > 0 { 1379 if r.Headers == nil { 1380 r.Headers = &AttributeDefinition{Type: Object{}} 1381 } 1382 headers := r.Headers.Type.ToObject() 1383 for n, h := range otherHeaders { 1384 if _, ok := headers[n]; !ok { 1385 headers[n] = h 1386 } 1387 } 1388 } 1389 } 1390 } 1391 1392 // Context returns the generic definition name used in error messages. 1393 func (r *ResponseTemplateDefinition) Context() string { 1394 if r.Name != "" { 1395 return fmt.Sprintf("response template %#v", r.Name) 1396 } 1397 return "unnamed response template" 1398 } 1399 1400 // Context returns the generic definition name used in error messages. 1401 func (a *ActionDefinition) Context() string { 1402 var prefix, suffix string 1403 if a.Name != "" { 1404 suffix = fmt.Sprintf("action %#v", a.Name) 1405 } else { 1406 suffix = "unnamed action" 1407 } 1408 if a.Parent != nil { 1409 prefix = a.Parent.Context() + " " 1410 } 1411 return prefix + suffix 1412 } 1413 1414 // PathParams returns the path parameters of the action across all its routes. 1415 func (a *ActionDefinition) PathParams() *AttributeDefinition { 1416 obj := make(Object) 1417 allParams := a.AllParams().Type.ToObject() 1418 for _, r := range a.Routes { 1419 for _, p := range r.Params() { 1420 if _, ok := obj[p]; !ok { 1421 obj[p] = allParams[p] 1422 } 1423 } 1424 } 1425 return &AttributeDefinition{Type: obj} 1426 } 1427 1428 // AllParams returns the path and query string parameters of the action across all its routes. 1429 func (a *ActionDefinition) AllParams() *AttributeDefinition { 1430 var res *AttributeDefinition 1431 if a.Params != nil { 1432 res = DupAtt(a.Params) 1433 } else { 1434 res = &AttributeDefinition{Type: Object{}} 1435 } 1436 if a.HasAbsoluteRoutes() { 1437 return res 1438 } 1439 res = res.Merge(a.Parent.Params) 1440 if p := a.Parent.Parent(); p != nil { 1441 res = res.Merge(p.CanonicalAction().PathParams()) 1442 } else { 1443 res = res.Merge(a.Parent.PathParams()) 1444 res = res.Merge(Design.PathParams()) 1445 } 1446 return res 1447 } 1448 1449 // HasAbsoluteRoutes returns true if all the action routes are absolute. 1450 func (a *ActionDefinition) HasAbsoluteRoutes() bool { 1451 for _, r := range a.Routes { 1452 if !r.IsAbsolute() { 1453 return false 1454 } 1455 } 1456 return true 1457 } 1458 1459 // CanonicalScheme returns the preferred scheme for making requests. Favor secure schemes. 1460 func (a *ActionDefinition) CanonicalScheme() string { 1461 if a.WebSocket() { 1462 for _, s := range a.EffectiveSchemes() { 1463 if s == "wss" { 1464 return s 1465 } 1466 } 1467 return "ws" 1468 } 1469 for _, s := range a.EffectiveSchemes() { 1470 if s == "https" { 1471 return s 1472 } 1473 } 1474 return "http" 1475 } 1476 1477 // EffectiveSchemes return the URL schemes that apply to the action. Looks recursively into action 1478 // resource, parent resources and API. 1479 func (a *ActionDefinition) EffectiveSchemes() []string { 1480 // Compute the schemes 1481 schemes := a.Schemes 1482 if len(schemes) == 0 { 1483 res := a.Parent 1484 schemes = res.Schemes 1485 parent := res.Parent() 1486 for len(schemes) == 0 && parent != nil { 1487 schemes = parent.Schemes 1488 parent = parent.Parent() 1489 } 1490 if len(schemes) == 0 { 1491 schemes = Design.Schemes 1492 } 1493 } 1494 return schemes 1495 } 1496 1497 // WebSocket returns true if the action scheme is "ws" or "wss" or both (directly or inherited 1498 // from the resource or API) 1499 func (a *ActionDefinition) WebSocket() bool { 1500 schemes := a.EffectiveSchemes() 1501 if len(schemes) == 0 { 1502 return false 1503 } 1504 for _, s := range schemes { 1505 if s != "ws" && s != "wss" { 1506 return false 1507 } 1508 } 1509 return true 1510 } 1511 1512 // Finalize inherits security scheme and action responses from parent and top level design. 1513 func (a *ActionDefinition) Finalize() { 1514 // Inherit security scheme 1515 if a.Security == nil { 1516 a.Security = a.Parent.Security // ResourceDefinition 1517 if a.Security == nil { 1518 a.Security = Design.Security 1519 } 1520 } 1521 1522 if a.Security != nil && a.Security.Scheme.Kind == NoSecurityKind { 1523 a.Security = nil 1524 } 1525 1526 if a.Payload != nil { 1527 a.Payload.Finalize() 1528 } 1529 1530 a.mergeResponses() 1531 a.initImplicitParams() 1532 a.initQueryParams() 1533 } 1534 1535 // UserTypes returns all the user types used by the action payload and parameters. 1536 func (a *ActionDefinition) UserTypes() map[string]*UserTypeDefinition { 1537 types := make(map[string]*UserTypeDefinition) 1538 allp := a.AllParams().Type.ToObject() 1539 if a.Payload != nil { 1540 allp["__payload__"] = &AttributeDefinition{Type: a.Payload} 1541 } 1542 for n, ut := range UserTypes(allp) { 1543 types[n] = ut 1544 } 1545 for _, r := range a.Responses { 1546 if mt := Design.MediaTypeWithIdentifier(r.MediaType); mt != nil { 1547 types[mt.TypeName] = mt.UserTypeDefinition 1548 for n, ut := range UserTypes(mt.UserTypeDefinition) { 1549 types[n] = ut 1550 } 1551 } 1552 } 1553 if len(types) == 0 { 1554 return nil 1555 } 1556 return types 1557 } 1558 1559 // IterateHeaders iterates over the resource-level and action-level headers, 1560 // calling the given iterator passing in each response sorted in alphabetical order. 1561 // Iteration stops if an iterator returns an error and in this case IterateHeaders returns that 1562 // error. 1563 func (a *ActionDefinition) IterateHeaders(it HeaderIterator) error { 1564 mergedHeaders := a.Parent.Headers.Merge(a.Headers) 1565 1566 isRequired := func(name string) bool { 1567 // header required in either the Resource or Action scope? 1568 return a.Parent.Headers.IsRequired(name) || a.Headers.IsRequired(name) 1569 } 1570 1571 return iterateHeaders(mergedHeaders, isRequired, it) 1572 } 1573 1574 // IterateResponses calls the given iterator passing in each response sorted in alphabetical order. 1575 // Iteration stops if an iterator returns an error and in this case IterateResponses returns that 1576 // error. 1577 func (a *ActionDefinition) IterateResponses(it ResponseIterator) error { 1578 names := make([]string, len(a.Responses)) 1579 i := 0 1580 for n := range a.Responses { 1581 names[i] = n 1582 i++ 1583 } 1584 sort.Strings(names) 1585 for _, n := range names { 1586 if err := it(a.Responses[n]); err != nil { 1587 return err 1588 } 1589 } 1590 return nil 1591 } 1592 1593 // mergeResponses merges the parent resource and design responses. 1594 func (a *ActionDefinition) mergeResponses() { 1595 for name, resp := range a.Parent.Responses { 1596 if _, ok := a.Responses[name]; !ok { 1597 if a.Responses == nil { 1598 a.Responses = make(map[string]*ResponseDefinition) 1599 } 1600 a.Responses[name] = resp.Dup() 1601 } 1602 } 1603 for name, resp := range a.Responses { 1604 resp.Finalize() 1605 if pr, ok := a.Parent.Responses[name]; ok { 1606 resp.Merge(pr) 1607 } 1608 if ar, ok := Design.Responses[name]; ok { 1609 resp.Merge(ar) 1610 } 1611 if dr, ok := Design.DefaultResponses[name]; ok { 1612 resp.Merge(dr) 1613 } 1614 } 1615 } 1616 1617 // initImplicitParams creates params for path segments that don't have one. 1618 func (a *ActionDefinition) initImplicitParams() { 1619 for _, ro := range a.Routes { 1620 for _, wc := range ro.Params() { 1621 found := false 1622 search := func(params *AttributeDefinition) { 1623 if params == nil { 1624 return 1625 } 1626 att, ok := params.Type.ToObject()[wc] 1627 if ok { 1628 if a.Params == nil { 1629 a.Params = &AttributeDefinition{Type: Object{}} 1630 } 1631 a.Params.Type.ToObject()[wc] = att 1632 found = true 1633 } 1634 } 1635 search(a.Params) 1636 parent := a.Parent 1637 for !found && parent != nil { 1638 bp := parent.Params 1639 parent = parent.Parent() 1640 search(bp) 1641 } 1642 if found { 1643 continue 1644 } 1645 search(Design.Params) 1646 if found { 1647 continue 1648 } 1649 if a.Params == nil { 1650 a.Params = &AttributeDefinition{Type: Object{}} 1651 } 1652 a.Params.Type.ToObject()[wc] = &AttributeDefinition{Type: String} 1653 } 1654 } 1655 } 1656 1657 // initQueryParams extract the query parameters from the action params. 1658 func (a *ActionDefinition) initQueryParams() { 1659 // 3. Compute QueryParams from Params and set all path params as non zero attributes 1660 if params := a.AllParams(); params != nil { 1661 queryParams := DupAtt(params) 1662 queryParams.Type = Dup(queryParams.Type) 1663 if a.Params == nil { 1664 a.Params = &AttributeDefinition{Type: Object{}} 1665 } 1666 a.Params.NonZeroAttributes = make(map[string]bool) 1667 for _, route := range a.Routes { 1668 pnames := route.Params() 1669 for _, pname := range pnames { 1670 a.Params.NonZeroAttributes[pname] = true 1671 delete(queryParams.Type.ToObject(), pname) 1672 if queryParams.Validation != nil { 1673 req := queryParams.Validation.Required 1674 for i, n := range req { 1675 if n == pname { 1676 queryParams.Validation.Required = append(req[:i], req[i+1:]...) 1677 break 1678 } 1679 } 1680 } 1681 } 1682 } 1683 a.QueryParams = queryParams 1684 } 1685 } 1686 1687 // Context returns the generic definition name used in error messages. 1688 func (f *FileServerDefinition) Context() string { 1689 suffix := fmt.Sprintf("file server %s", f.FilePath) 1690 var prefix string 1691 if f.Parent != nil { 1692 prefix = f.Parent.Context() + " " 1693 } 1694 return prefix + suffix 1695 } 1696 1697 // Finalize inherits security scheme from parent and top level design. 1698 func (f *FileServerDefinition) Finalize() { 1699 // Make sure request path starts with a "/" so codegen can rely on it. 1700 if !strings.HasPrefix(f.RequestPath, "/") { 1701 f.RequestPath = "/" + f.RequestPath 1702 } 1703 // Inherit security 1704 if f.Security == nil { 1705 f.Security = f.Parent.Security // ResourceDefinition 1706 if f.Security == nil { 1707 f.Security = Design.Security 1708 } 1709 } 1710 if f.Security != nil && f.Security.Scheme.Kind == NoSecurityKind { 1711 f.Security = nil 1712 } 1713 } 1714 1715 // IsDir returns true if the file server serves a directory, false otherwise. 1716 func (f *FileServerDefinition) IsDir() bool { 1717 return WildcardRegex.MatchString(f.RequestPath) 1718 } 1719 1720 // ByFilePath makes FileServerDefinition sortable for code generators. 1721 type ByFilePath []*FileServerDefinition 1722 1723 func (b ByFilePath) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 1724 func (b ByFilePath) Len() int { return len(b) } 1725 func (b ByFilePath) Less(i, j int) bool { return b[i].FilePath < b[j].FilePath } 1726 1727 // Context returns the generic definition name used in error messages. 1728 func (l *LinkDefinition) Context() string { 1729 var prefix, suffix string 1730 if l.Name != "" { 1731 prefix = fmt.Sprintf("link %#v", l.Name) 1732 } else { 1733 prefix = "unnamed link" 1734 } 1735 if l.Parent != nil { 1736 suffix = fmt.Sprintf(" of %s", l.Parent.Context()) 1737 } 1738 return prefix + suffix 1739 } 1740 1741 // Attribute returns the linked attribute. 1742 func (l *LinkDefinition) Attribute() *AttributeDefinition { 1743 p := l.Parent.ToObject() 1744 if p == nil { 1745 return nil 1746 } 1747 att, _ := p[l.Name] 1748 1749 return att 1750 } 1751 1752 // MediaType returns the media type of the linked attribute. 1753 func (l *LinkDefinition) MediaType() *MediaTypeDefinition { 1754 att := l.Attribute() 1755 mt, _ := att.Type.(*MediaTypeDefinition) 1756 return mt 1757 } 1758 1759 // Context returns the generic definition name used in error messages. 1760 func (v *ViewDefinition) Context() string { 1761 var prefix, suffix string 1762 if v.Name != "" { 1763 prefix = fmt.Sprintf("view %#v", v.Name) 1764 } else { 1765 prefix = "unnamed view" 1766 } 1767 if v.Parent != nil { 1768 suffix = fmt.Sprintf(" of %s", v.Parent.Context()) 1769 } 1770 return prefix + suffix 1771 } 1772 1773 // Context returns the generic definition name used in error messages. 1774 func (r *RouteDefinition) Context() string { 1775 return fmt.Sprintf(`route %s "%s" of %s`, r.Verb, r.Path, r.Parent.Context()) 1776 } 1777 1778 // Params returns the route parameters. 1779 // For example for the route "GET /foo/:fooID" Params returns []string{"fooID"}. 1780 func (r *RouteDefinition) Params() []string { 1781 return ExtractWildcards(r.FullPath()) 1782 } 1783 1784 // FullPath returns the action full path computed by concatenating the API and resource base paths 1785 // with the action specific path. 1786 func (r *RouteDefinition) FullPath() string { 1787 if r.IsAbsolute() { 1788 return httppath.Clean(r.Path[1:]) 1789 } 1790 var base string 1791 if r.Parent != nil && r.Parent.Parent != nil { 1792 base = r.Parent.Parent.FullPath() 1793 } 1794 1795 joinedPath := path.Join(base, r.Path) 1796 if strings.HasSuffix(r.Path, "/") { 1797 //add slash removed by Join back again (it may be important for routing) 1798 joinedPath += "/" 1799 } 1800 1801 return httppath.Clean(joinedPath) 1802 } 1803 1804 // IsAbsolute returns true if the action path should not be concatenated to the resource and API 1805 // base paths. 1806 func (r *RouteDefinition) IsAbsolute() bool { 1807 return strings.HasPrefix(r.Path, "//") 1808 } 1809 1810 func iterateHeaders(headers *AttributeDefinition, isRequired func(name string) bool, it HeaderIterator) error { 1811 if headers == nil || !headers.Type.IsObject() { 1812 return nil 1813 } 1814 headersMap := headers.Type.ToObject() 1815 names := make([]string, len(headersMap)) 1816 i := 0 1817 for n := range headersMap { 1818 names[i] = n 1819 i++ 1820 } 1821 sort.Strings(names) 1822 for _, n := range names { 1823 header := headersMap[n] 1824 if err := it(n, isRequired(n), header); err != nil { 1825 return err 1826 } 1827 } 1828 return nil 1829 }