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