github.com/shogo82148/goa-v1@v1.6.2/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/shogo82148/goa-v1/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 preflight 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 respectively. 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 return r.Actions[name] 779 } 780 781 // URITemplate returns a URI template to this resource. 782 // The result is the empty string if the resource does not have a "show" action 783 // and does not define a different canonical action. 784 func (r *ResourceDefinition) URITemplate() string { 785 ca := r.CanonicalAction() 786 if ca == nil || len(ca.Routes) == 0 { 787 return "" 788 } 789 return ca.Routes[0].FullPath() 790 } 791 792 // FullPath computes the base path to the resource actions concatenating the API and parent resource 793 // base paths as needed. 794 func (r *ResourceDefinition) FullPath() string { 795 if strings.HasPrefix(r.BasePath, "//") { 796 return httppath.Clean(r.BasePath) 797 } 798 var basePath string 799 if p := r.Parent(); p != nil { 800 if ca := p.CanonicalAction(); ca != nil { 801 if routes := ca.Routes; len(routes) > 0 { 802 // Note: all these tests should be true at code generation time 803 // as DSL validation makes sure that parent resources have a 804 // canonical path. 805 basePath = path.Join(routes[0].FullPath()) 806 } 807 } 808 } else { 809 basePath = Design.BasePath 810 } 811 return httppath.Clean(path.Join(basePath, r.BasePath)) 812 } 813 814 // Parent returns the parent resource if any, nil otherwise. 815 func (r *ResourceDefinition) Parent() *ResourceDefinition { 816 if r.ParentName != "" { 817 if parent, ok := Design.Resources[r.ParentName]; ok { 818 return parent 819 } 820 } 821 return nil 822 } 823 824 // AllOrigins compute all CORS policies for the resource taking into account any API policy. 825 // The result is sorted alphabetically by policy origin. 826 func (r *ResourceDefinition) AllOrigins() []*CORSDefinition { 827 all := make(map[string]*CORSDefinition) 828 for n, o := range Design.Origins { 829 all[n] = o 830 } 831 for n, o := range r.Origins { 832 all[n] = o 833 } 834 names := make([]string, len(all)) 835 i := 0 836 for n := range all { 837 names[i] = n 838 i++ 839 } 840 sort.Strings(names) 841 cors := make([]*CORSDefinition, len(names)) 842 for i, n := range names { 843 cors[i] = all[n] 844 } 845 return cors 846 } 847 848 // PreflightPaths returns the paths that should handle OPTIONS requests. 849 func (r *ResourceDefinition) PreflightPaths() []string { 850 var paths []string 851 r.IterateActions(func(a *ActionDefinition) error { 852 for _, r := range a.Routes { 853 if r.Verb == "OPTIONS" { 854 continue 855 } 856 found := false 857 fp := r.FullPath() 858 for _, p := range paths { 859 if fp == p { 860 found = true 861 break 862 } 863 } 864 if !found { 865 paths = append(paths, fp) 866 } 867 } 868 return nil 869 }) 870 r.IterateFileServers(func(fs *FileServerDefinition) error { 871 found := false 872 fp := fs.RequestPath 873 for _, p := range paths { 874 if fp == p { 875 found = true 876 break 877 } 878 } 879 if !found { 880 paths = append(paths, fp) 881 } 882 return nil 883 }) 884 return paths 885 } 886 887 // DSL returns the initialization DSL. 888 func (r *ResourceDefinition) DSL() func() { 889 return r.DSLFunc 890 } 891 892 // Finalize is run post DSL execution. It merges response definitions, creates implicit action 893 // parameters, initializes querystring parameters, sets path parameters as non zero attributes 894 // and sets the fallbacks for security schemes. 895 func (r *ResourceDefinition) Finalize() { 896 meta := r.Metadata["swagger:generate"] 897 r.IterateFileServers(func(f *FileServerDefinition) error { 898 if meta != nil { 899 if _, ok := f.Metadata["swagger:generate"]; !ok { 900 f.Metadata["swagger:generate"] = meta 901 } 902 } 903 f.Finalize() 904 return nil 905 }) 906 r.IterateActions(func(a *ActionDefinition) error { 907 if meta != nil { 908 if _, ok := a.Metadata["swagger:generate"]; !ok { 909 a.Metadata["swagger:generate"] = meta 910 } 911 } 912 a.Finalize() 913 return nil 914 }) 915 } 916 917 // UserTypes returns all the user types used by the resource action payloads and parameters. 918 func (r *ResourceDefinition) UserTypes() map[string]*UserTypeDefinition { 919 types := make(map[string]*UserTypeDefinition) 920 for _, a := range r.Actions { 921 for n, ut := range a.UserTypes() { 922 types[n] = ut 923 } 924 } 925 if len(types) == 0 { 926 return nil 927 } 928 return types 929 } 930 931 // byParent makes it possible to sort resources - parents first the children. 932 type byParent []*ResourceDefinition 933 934 func (p byParent) Len() int { return len(p) } 935 func (p byParent) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 936 func (p byParent) Less(i, j int) bool { 937 for k := 0; k < i; k++ { 938 // We need to inspect _all_ previous fields to see if they are a parent. Sort doesn't do this. 939 if p[i].Name == p[k].ParentName { 940 return true 941 } 942 } 943 944 return false 945 } 946 947 // Context returns the generic definition name used in error messages. 948 func (cors *CORSDefinition) Context() string { 949 return fmt.Sprintf("CORS policy for resource %s origin %s", cors.Parent.Context(), cors.Origin) 950 } 951 952 // Context returns the generic definition name used in error messages. 953 func (enc *EncodingDefinition) Context() string { 954 return fmt.Sprintf("encoding for %s", strings.Join(enc.MIMETypes, ", ")) 955 } 956 957 // Context returns the generic definition name used in error messages. 958 func (a *AttributeDefinition) Context() string { 959 return "" 960 } 961 962 // AllRequired returns the list of all required fields from the underlying object. 963 // An attribute type can be itself an attribute (e.g. a MediaTypeDefinition or a UserTypeDefinition) 964 // This happens when the DSL uses references for example. So traverse the hierarchy and collect 965 // all the required validations. 966 func (a *AttributeDefinition) AllRequired() (required []string) { 967 if a == nil || a.Validation == nil { 968 return 969 } 970 required = a.Validation.Required 971 if ds, ok := a.Type.(DataStructure); ok { 972 required = append(required, ds.Definition().AllRequired()...) 973 } 974 return 975 } 976 977 // IsRequired returns true if the given string matches the name of a required 978 // attribute, false otherwise. 979 func (a *AttributeDefinition) IsRequired(attName string) bool { 980 for _, name := range a.AllRequired() { 981 if name == attName { 982 return true 983 } 984 } 985 return false 986 } 987 988 // HasDefaultValue returns true if the given attribute has a default value. 989 func (a *AttributeDefinition) HasDefaultValue(attName string) bool { 990 if a.Type.IsObject() { 991 att := a.Type.ToObject()[attName] 992 return att.DefaultValue != nil 993 } 994 return false 995 } 996 997 // SetDefault sets the default for the attribute. It also converts HashVal 998 // and ArrayVal to map and slice respectively. 999 func (a *AttributeDefinition) SetDefault(def interface{}) { 1000 switch actual := def.(type) { 1001 case HashVal: 1002 a.DefaultValue = actual.ToMap() 1003 case ArrayVal: 1004 a.DefaultValue = actual.ToSlice() 1005 default: 1006 a.DefaultValue = actual 1007 } 1008 } 1009 1010 // AddValues adds the Enum values to the attribute's validation definition. 1011 // It also performs any conversion needed for HashVal and ArrayVal types. 1012 func (a *AttributeDefinition) AddValues(values []interface{}) { 1013 if a.Validation == nil { 1014 a.Validation = &dslengine.ValidationDefinition{} 1015 } 1016 a.Validation.Values = make([]interface{}, len(values)) 1017 for i, v := range values { 1018 switch actual := v.(type) { 1019 case HashVal: 1020 a.Validation.Values[i] = actual.ToMap() 1021 case ArrayVal: 1022 a.Validation.Values[i] = actual.ToSlice() 1023 default: 1024 a.Validation.Values[i] = actual 1025 } 1026 } 1027 } 1028 1029 // AllNonZero returns the complete list of all non-zero attribute name. 1030 func (a *AttributeDefinition) AllNonZero() []string { 1031 nzs := make([]string, len(a.NonZeroAttributes)) 1032 i := 0 1033 for n := range a.NonZeroAttributes { 1034 nzs[i] = n 1035 i++ 1036 } 1037 return nzs 1038 } 1039 1040 // IsNonZero returns true if the given string matches the name of a non-zero 1041 // attribute, false otherwise. 1042 func (a *AttributeDefinition) IsNonZero(attName string) bool { 1043 return a.NonZeroAttributes[attName] 1044 } 1045 1046 // IsPrimitivePointer returns true if the field generated for the given attribute should be a 1047 // pointer to a primitive type. The target attribute must be an object. 1048 func (a *AttributeDefinition) IsPrimitivePointer(attName string) bool { 1049 if !a.Type.IsObject() { 1050 panic("checking pointer field on non-object") // bug 1051 } 1052 att := a.Type.ToObject()[attName] 1053 if att == nil { 1054 return false 1055 } 1056 if att.Type.IsPrimitive() { 1057 return (!a.IsRequired(attName) && !a.HasDefaultValue(attName) && !a.IsNonZero(attName) && !a.IsInterface(attName)) || a.IsFile(attName) 1058 } 1059 return false 1060 } 1061 1062 // IsInterface returns true if the field generated for the given attribute has 1063 // an interface type that should not be referenced as a "*interface{}" pointer. 1064 // The target attribute must be an object. 1065 func (a *AttributeDefinition) IsInterface(attName string) bool { 1066 if !a.Type.IsObject() { 1067 panic("checking pointer field on non-object") // bug 1068 } 1069 att := a.Type.ToObject()[attName] 1070 if att == nil { 1071 return false 1072 } 1073 return att.Type.Kind() == AnyKind 1074 } 1075 1076 // IsFile returns true if the attribute is of type File or if any its children attributes (if any) is. 1077 func (a *AttributeDefinition) IsFile(attName string) bool { 1078 if !a.Type.IsObject() { 1079 panic("checking pointer field on non-object") // bug 1080 } 1081 att := a.Type.ToObject()[attName] 1082 if att == nil { 1083 return false 1084 } 1085 return att.Type.Kind() == FileKind 1086 } 1087 1088 // SetExample sets the custom example. SetExample also handles the case when the user doesn't 1089 // want any example or any auto-generated example. 1090 func (a *AttributeDefinition) SetExample(example interface{}) bool { 1091 if example == nil { 1092 a.Example = "-" // set it to something else than nil so we know not to generate one 1093 return true 1094 } 1095 if a.Type == nil || a.Type.IsCompatible(example) { 1096 a.Example = example 1097 return true 1098 } 1099 return false 1100 } 1101 1102 // GenerateExample returns the value of the Example field if not nil. Otherwise it traverses the 1103 // attribute type and recursively generates an example. The result is saved in the Example field. 1104 func (a *AttributeDefinition) GenerateExample(rand *RandomGenerator, seen []string) interface{} { 1105 if a.Example != nil { 1106 return a.Example 1107 } 1108 if Design.NoExamples { 1109 return nil 1110 } 1111 1112 // Avoid infinite loops 1113 var key string 1114 if mt, ok := a.Type.(*MediaTypeDefinition); ok { 1115 key = mt.Identifier 1116 } else if ut, ok := a.Type.(*UserTypeDefinition); ok { 1117 key = ut.TypeName 1118 } 1119 if key != "" { 1120 count := 0 1121 for _, k := range seen { 1122 if k == key { 1123 count++ 1124 } 1125 } 1126 if count > 1 { 1127 // Only go a couple of levels deep 1128 return nil 1129 } 1130 seen = append(seen, key) 1131 } 1132 1133 switch { 1134 case a.Type.IsArray(): 1135 a.Example = a.arrayExample(rand, seen) 1136 1137 case a.Type.IsHash(): 1138 a.Example = a.hashExample(rand, seen) 1139 1140 case a.Type.IsObject(): 1141 a.Example = a.objectExample(rand, seen) 1142 1143 default: 1144 a.Example = newExampleGenerator(a, rand).Generate(seen) 1145 } 1146 1147 return a.Example 1148 } 1149 1150 // SetReadOnly sets the attribute's ReadOnly field as true. 1151 func (a *AttributeDefinition) SetReadOnly() { 1152 if a.Metadata == nil { 1153 a.Metadata = map[string][]string{} 1154 } 1155 a.Metadata["swagger:read-only"] = nil 1156 } 1157 1158 // IsReadOnly returns true if attribute is read-only (set using SetReadOnly() method) 1159 func (a *AttributeDefinition) IsReadOnly() bool { 1160 if _, readOnlyMetadataIsPresent := a.Metadata["swagger:read-only"]; readOnlyMetadataIsPresent { 1161 return true 1162 } 1163 return false 1164 } 1165 1166 func (a *AttributeDefinition) arrayExample(rand *RandomGenerator, seen []string) interface{} { 1167 ary := a.Type.ToArray() 1168 ln := newExampleGenerator(a, rand).ExampleLength() 1169 var res []interface{} 1170 for i := 0; i < ln; i++ { 1171 ex := ary.ElemType.GenerateExample(rand, seen) 1172 if ex != nil { 1173 res = append(res, ex) 1174 } 1175 } 1176 if len(res) == 0 { 1177 return nil 1178 } 1179 return ary.MakeSlice(res) 1180 } 1181 1182 func (a *AttributeDefinition) hashExample(rand *RandomGenerator, seen []string) interface{} { 1183 h := a.Type.ToHash() 1184 ln := newExampleGenerator(a, rand).ExampleLength() 1185 res := make(map[interface{}]interface{}) 1186 for i := 0; i < ln; i++ { 1187 k := h.KeyType.GenerateExample(rand, seen) 1188 v := h.ElemType.GenerateExample(rand, seen) 1189 if k != nil && v != nil { 1190 res[k] = v 1191 } 1192 } 1193 if len(res) == 0 { 1194 return nil 1195 } 1196 return h.MakeMap(res) 1197 } 1198 1199 func (a *AttributeDefinition) objectExample(rand *RandomGenerator, seen []string) interface{} { 1200 // project media types 1201 actual := a 1202 if mt, ok := a.Type.(*MediaTypeDefinition); ok { 1203 v := a.View 1204 if v == "" { 1205 v = DefaultView 1206 } 1207 projected, _, err := mt.Project(v) 1208 if err != nil { 1209 panic(err) // bug 1210 } 1211 actual = projected.AttributeDefinition 1212 } 1213 1214 // ensure fixed ordering so random values are computed with consistent seeds 1215 aObj := actual.Type.ToObject() 1216 keys := make([]string, len(aObj)) 1217 i := 0 1218 for n := range aObj { 1219 keys[i] = n 1220 i++ 1221 } 1222 sort.Strings(keys) 1223 1224 res := make(map[string]interface{}) 1225 for _, n := range keys { 1226 att := aObj[n] 1227 if ex := att.GenerateExample(rand, seen); ex != nil { 1228 res[n] = ex 1229 } 1230 } 1231 if len(res) > 0 { 1232 a.Example = res 1233 } 1234 1235 return a.Example 1236 } 1237 1238 // Merge merges the argument attributes into the target and returns the target overriding existing 1239 // attributes with identical names. 1240 // This only applies to attributes of type Object and Merge panics if the 1241 // argument or the target is not of type Object. 1242 func (a *AttributeDefinition) Merge(other *AttributeDefinition) *AttributeDefinition { 1243 if other == nil { 1244 return a 1245 } 1246 if a == nil { 1247 return other 1248 } 1249 left := a.Type.(Object) 1250 right := other.Type.(Object) 1251 if left == nil || right == nil { 1252 panic("cannot merge non object attributes") // bug 1253 } 1254 for n, v := range right { 1255 left[n] = v 1256 } 1257 if other.Validation != nil && len(other.Validation.Required) > 0 { 1258 if a.Validation == nil { 1259 a.Validation = &dslengine.ValidationDefinition{} 1260 } 1261 a.Validation.Required = append(a.Validation.Required, other.Validation.Required...) 1262 } 1263 return a 1264 } 1265 1266 // Inherit merges the properties of existing target type attributes with the argument's. 1267 // The algorithm is recursive so that child attributes are also merged. 1268 func (a *AttributeDefinition) Inherit(parent *AttributeDefinition, seen ...map[*AttributeDefinition]struct{}) { 1269 if !a.shouldInherit(parent) { 1270 return 1271 } 1272 1273 a.inheritValidations(parent) 1274 a.inheritRecursive(parent, seen...) 1275 } 1276 1277 // DSL returns the initialization DSL. 1278 func (a *AttributeDefinition) DSL() func() { 1279 return a.DSLFunc 1280 } 1281 1282 func (a *AttributeDefinition) inheritRecursive(parent *AttributeDefinition, seen ...map[*AttributeDefinition]struct{}) { 1283 // prevent infinite recursions 1284 var s map[*AttributeDefinition]struct{} 1285 if len(seen) > 0 { 1286 s = seen[0] 1287 if _, ok := s[parent]; ok { 1288 return 1289 } 1290 } else { 1291 s = make(map[*AttributeDefinition]struct{}) 1292 } 1293 s[parent] = struct{}{} 1294 1295 if !a.shouldInherit(parent) { 1296 return 1297 } 1298 1299 for n, att := range a.Type.ToObject() { 1300 if patt, ok := parent.Type.ToObject()[n]; ok { 1301 if att.Description == "" { 1302 att.Description = patt.Description 1303 } 1304 att.inheritValidations(patt) 1305 if att.DefaultValue == nil { 1306 att.DefaultValue = patt.DefaultValue 1307 } 1308 if att.View == "" { 1309 att.View = patt.View 1310 } 1311 if att.Type == nil { 1312 att.Type = patt.Type 1313 } else if att.shouldInherit(patt) { 1314 for _, att := range att.Type.ToObject() { 1315 att.Inherit(patt.Type.ToObject()[n], s) 1316 } 1317 } 1318 if att.Example == nil { 1319 att.Example = patt.Example 1320 } 1321 if patt.Metadata != nil { 1322 if att.Metadata == nil { 1323 att.Metadata = patt.Metadata 1324 } else { 1325 // Copy all key/value pairs from parent to child that DO NOT exist in child; existing ones will remain with the same value 1326 for k, v := range patt.Metadata { 1327 if _, keyMetadataIsPresent := att.Metadata[k]; !keyMetadataIsPresent { 1328 att.Metadata[k] = v 1329 } 1330 } 1331 } 1332 } 1333 } 1334 } 1335 } 1336 1337 func (a *AttributeDefinition) inheritValidations(parent *AttributeDefinition) { 1338 if parent.Validation == nil { 1339 return 1340 } 1341 if a.Validation == nil { 1342 a.Validation = &dslengine.ValidationDefinition{} 1343 } 1344 a.Validation.AddRequired(parent.Validation.Required) 1345 } 1346 1347 func (a *AttributeDefinition) shouldInherit(parent *AttributeDefinition) bool { 1348 return a != nil && a.Type.ToObject() != nil && 1349 parent != nil && parent.Type.ToObject() != nil 1350 } 1351 1352 // Context returns the generic definition name used in error messages. 1353 func (c *ContactDefinition) Context() string { 1354 if c.Name != "" { 1355 return fmt.Sprintf("contact %s", c.Name) 1356 } 1357 return "unnamed contact" 1358 } 1359 1360 // Context returns the generic definition name used in error messages. 1361 func (l *LicenseDefinition) Context() string { 1362 if l.Name != "" { 1363 return fmt.Sprintf("license %s", l.Name) 1364 } 1365 return "unnamed license" 1366 } 1367 1368 // Context returns the generic definition name used in error messages. 1369 func (d *DocsDefinition) Context() string { 1370 return fmt.Sprintf("documentation for %s", Design.Name) 1371 } 1372 1373 // Context returns the generic definition name used in error messages. 1374 func (t *UserTypeDefinition) Context() string { 1375 if t.TypeName != "" { 1376 return fmt.Sprintf("type %#v", t.TypeName) 1377 } 1378 return "unnamed type" 1379 } 1380 1381 // DSL returns the initialization DSL. 1382 func (t *UserTypeDefinition) DSL() func() { 1383 return t.DSLFunc 1384 } 1385 1386 // Context returns the generic definition name used in error messages. 1387 func (r *ResponseDefinition) Context() string { 1388 var prefix, suffix string 1389 if r.Name != "" { 1390 prefix = fmt.Sprintf("response %#v", r.Name) 1391 } else { 1392 prefix = "unnamed response" 1393 } 1394 if r.Parent != nil { 1395 suffix = fmt.Sprintf(" of %s", r.Parent.Context()) 1396 } 1397 return prefix + suffix 1398 } 1399 1400 // Finalize sets the response media type from its type if the type is a media type and no media 1401 // type is already specified. 1402 func (r *ResponseDefinition) Finalize() { 1403 if r.Type == nil { 1404 return 1405 } 1406 if r.MediaType != "" && r.MediaType != "text/plain" { 1407 return 1408 } 1409 mt, ok := r.Type.(*MediaTypeDefinition) 1410 if !ok { 1411 return 1412 } 1413 r.MediaType = mt.Identifier 1414 } 1415 1416 // Dup returns a copy of the response definition. 1417 func (r *ResponseDefinition) Dup() *ResponseDefinition { 1418 res := ResponseDefinition{ 1419 Name: r.Name, 1420 Status: r.Status, 1421 Description: r.Description, 1422 MediaType: r.MediaType, 1423 ViewName: r.ViewName, 1424 } 1425 if r.Headers != nil { 1426 res.Headers = DupAtt(r.Headers) 1427 } 1428 return &res 1429 } 1430 1431 // Merge merges other into target. Only the fields of target that are not already set are merged. 1432 func (r *ResponseDefinition) Merge(other *ResponseDefinition) { 1433 if other == nil { 1434 return 1435 } 1436 if r.Name == "" { 1437 r.Name = other.Name 1438 } 1439 if r.Status == 0 { 1440 r.Status = other.Status 1441 } 1442 if r.Description == "" { 1443 r.Description = other.Description 1444 } 1445 if r.MediaType == "" { 1446 r.MediaType = other.MediaType 1447 r.ViewName = other.ViewName 1448 } 1449 if other.Headers != nil { 1450 otherHeaders := other.Headers.Type.ToObject() 1451 if len(otherHeaders) > 0 { 1452 if r.Headers == nil { 1453 r.Headers = &AttributeDefinition{Type: Object{}} 1454 } 1455 headers := r.Headers.Type.ToObject() 1456 for n, h := range otherHeaders { 1457 if _, ok := headers[n]; !ok { 1458 headers[n] = h 1459 } 1460 } 1461 } 1462 } 1463 } 1464 1465 // Context returns the generic definition name used in error messages. 1466 func (r *ResponseTemplateDefinition) Context() string { 1467 if r.Name != "" { 1468 return fmt.Sprintf("response template %#v", r.Name) 1469 } 1470 return "unnamed response template" 1471 } 1472 1473 // Context returns the generic definition name used in error messages. 1474 func (a *ActionDefinition) Context() string { 1475 var prefix, suffix string 1476 if a.Name != "" { 1477 suffix = fmt.Sprintf("action %#v", a.Name) 1478 } else { 1479 suffix = "unnamed action" 1480 } 1481 if a.Parent != nil { 1482 prefix = a.Parent.Context() + " " 1483 } 1484 return prefix + suffix 1485 } 1486 1487 // PathParams returns the path parameters of the action across all its routes. 1488 func (a *ActionDefinition) PathParams() *AttributeDefinition { 1489 obj := make(Object) 1490 allParams := a.AllParams().Type.ToObject() 1491 for _, r := range a.Routes { 1492 for _, p := range r.Params() { 1493 if _, ok := obj[p]; !ok { 1494 obj[p] = allParams[p] 1495 } 1496 } 1497 } 1498 return &AttributeDefinition{Type: obj} 1499 } 1500 1501 // AllParams returns the path and query string parameters of the action across all its routes. 1502 func (a *ActionDefinition) AllParams() *AttributeDefinition { 1503 var res *AttributeDefinition 1504 if a.Params != nil { 1505 res = DupAtt(a.Params) 1506 } else { 1507 res = &AttributeDefinition{Type: Object{}} 1508 } 1509 if a.HasAbsoluteRoutes() { 1510 return res 1511 } 1512 res = res.Merge(a.Parent.Params) 1513 if p := a.Parent.Parent(); p != nil { 1514 res = res.Merge(p.CanonicalAction().PathParams()) 1515 } else { 1516 res = res.Merge(a.Parent.PathParams()) 1517 } 1518 return res.Merge(Design.Params) 1519 } 1520 1521 // HasAbsoluteRoutes returns true if all the action routes are absolute. 1522 func (a *ActionDefinition) HasAbsoluteRoutes() bool { 1523 for _, r := range a.Routes { 1524 if !r.IsAbsolute() { 1525 return false 1526 } 1527 } 1528 return true 1529 } 1530 1531 // CanonicalScheme returns the preferred scheme for making requests. Favor secure schemes. 1532 func (a *ActionDefinition) CanonicalScheme() string { 1533 if a.WebSocket() { 1534 for _, s := range a.EffectiveSchemes() { 1535 if s == "wss" { 1536 return s 1537 } 1538 } 1539 return "ws" 1540 } 1541 for _, s := range a.EffectiveSchemes() { 1542 if s == "https" { 1543 return s 1544 } 1545 } 1546 return "http" 1547 } 1548 1549 // EffectiveSchemes return the URL schemes that apply to the action. Looks recursively into action 1550 // resource, parent resources and API. 1551 func (a *ActionDefinition) EffectiveSchemes() []string { 1552 // Compute the schemes 1553 schemes := a.Schemes 1554 if len(schemes) == 0 { 1555 res := a.Parent 1556 schemes = res.Schemes 1557 parent := res.Parent() 1558 for len(schemes) == 0 && parent != nil { 1559 schemes = parent.Schemes 1560 parent = parent.Parent() 1561 } 1562 if len(schemes) == 0 { 1563 schemes = Design.Schemes 1564 } 1565 } 1566 return schemes 1567 } 1568 1569 // WebSocket returns true if the action scheme is "ws" or "wss" or both (directly or inherited 1570 // from the resource or API) 1571 func (a *ActionDefinition) WebSocket() bool { 1572 schemes := a.EffectiveSchemes() 1573 if len(schemes) == 0 { 1574 return false 1575 } 1576 for _, s := range schemes { 1577 if s != "ws" && s != "wss" { 1578 return false 1579 } 1580 } 1581 return true 1582 } 1583 1584 // Finalize inherits security scheme and action responses from parent and top level design. 1585 func (a *ActionDefinition) Finalize() { 1586 // Inherit security scheme 1587 if a.Security == nil { 1588 a.Security = a.Parent.Security // ResourceDefinition 1589 if a.Security == nil { 1590 a.Security = Design.Security 1591 } 1592 } 1593 1594 if a.Security != nil && a.Security.Scheme.Kind == NoSecurityKind { 1595 a.Security = nil 1596 } 1597 1598 if a.Payload != nil { 1599 a.Payload.Finalize() 1600 } 1601 1602 a.mergeResponses() 1603 a.initImplicitParams() 1604 a.initQueryParams() 1605 } 1606 1607 // UserTypes returns all the user types used by the action payload and parameters. 1608 func (a *ActionDefinition) UserTypes() map[string]*UserTypeDefinition { 1609 types := make(map[string]*UserTypeDefinition) 1610 allp := a.AllParams().Type.ToObject() 1611 if a.Payload != nil { 1612 allp["__payload__"] = &AttributeDefinition{Type: a.Payload} 1613 } 1614 for n, ut := range UserTypes(allp) { 1615 types[n] = ut 1616 } 1617 for _, r := range a.Responses { 1618 if mt := Design.MediaTypeWithIdentifier(r.MediaType); mt != nil { 1619 types[mt.TypeName] = mt.UserTypeDefinition 1620 for n, ut := range UserTypes(mt.UserTypeDefinition) { 1621 types[n] = ut 1622 } 1623 } 1624 } 1625 if len(types) == 0 { 1626 return nil 1627 } 1628 return types 1629 } 1630 1631 // IterateHeaders iterates over the resource-level and action-level headers, 1632 // calling the given iterator passing in each response sorted in alphabetical order. 1633 // Iteration stops if an iterator returns an error and in this case IterateHeaders returns that 1634 // error. 1635 func (a *ActionDefinition) IterateHeaders(it HeaderIterator) error { 1636 mergedHeaders := a.Parent.Headers.Merge(a.Headers) 1637 1638 isRequired := func(name string) bool { 1639 // header required in either the Resource or Action scope? 1640 return a.Parent.Headers.IsRequired(name) || a.Headers.IsRequired(name) 1641 } 1642 1643 return iterateHeaders(mergedHeaders, isRequired, it) 1644 } 1645 1646 // IterateResponses calls the given iterator passing in each response sorted in alphabetical order. 1647 // Iteration stops if an iterator returns an error and in this case IterateResponses returns that 1648 // error. 1649 func (a *ActionDefinition) IterateResponses(it ResponseIterator) error { 1650 names := make([]string, len(a.Responses)) 1651 i := 0 1652 for n := range a.Responses { 1653 names[i] = n 1654 i++ 1655 } 1656 sort.Strings(names) 1657 for _, n := range names { 1658 if err := it(a.Responses[n]); err != nil { 1659 return err 1660 } 1661 } 1662 return nil 1663 } 1664 1665 // mergeResponses merges the parent resource and design responses. 1666 func (a *ActionDefinition) mergeResponses() { 1667 for name, resp := range a.Parent.Responses { 1668 if _, ok := a.Responses[name]; !ok { 1669 if a.Responses == nil { 1670 a.Responses = make(map[string]*ResponseDefinition) 1671 } 1672 a.Responses[name] = resp.Dup() 1673 } 1674 } 1675 for name, resp := range a.Responses { 1676 resp.Finalize() 1677 if pr, ok := a.Parent.Responses[name]; ok { 1678 resp.Merge(pr) 1679 } 1680 if ar, ok := Design.Responses[name]; ok { 1681 resp.Merge(ar) 1682 } 1683 if dr, ok := Design.DefaultResponses[name]; ok { 1684 resp.Merge(dr) 1685 } 1686 } 1687 } 1688 1689 // initImplicitParams creates params for path segments that don't have one. 1690 func (a *ActionDefinition) initImplicitParams() { 1691 for _, ro := range a.Routes { 1692 for _, wc := range ro.Params() { 1693 found := false 1694 search := func(params *AttributeDefinition) { 1695 if params == nil { 1696 return 1697 } 1698 att, ok := params.Type.ToObject()[wc] 1699 if ok { 1700 if a.Params == nil { 1701 a.Params = &AttributeDefinition{Type: Object{}} 1702 } 1703 a.Params.Type.ToObject()[wc] = att 1704 found = true 1705 } 1706 } 1707 search(a.Params) 1708 parent := a.Parent 1709 for !found && parent != nil { 1710 bp := parent.Params 1711 parent = parent.Parent() 1712 search(bp) 1713 } 1714 if found { 1715 continue 1716 } 1717 search(Design.Params) 1718 if found { 1719 continue 1720 } 1721 if a.Params == nil { 1722 a.Params = &AttributeDefinition{Type: Object{}} 1723 } 1724 a.Params.Type.ToObject()[wc] = &AttributeDefinition{Type: String} 1725 } 1726 } 1727 } 1728 1729 // initQueryParams extract the query parameters from the action params. 1730 func (a *ActionDefinition) initQueryParams() { 1731 // 3. Compute QueryParams from Params and set all path params as non zero attributes 1732 if params := a.AllParams(); params != nil { 1733 queryParams := DupAtt(params) 1734 queryParams.Type = Dup(queryParams.Type) 1735 if a.Params == nil { 1736 a.Params = &AttributeDefinition{Type: Object{}} 1737 } 1738 a.Params.NonZeroAttributes = make(map[string]bool) 1739 for _, route := range a.Routes { 1740 pnames := route.Params() 1741 for _, pname := range pnames { 1742 a.Params.NonZeroAttributes[pname] = true 1743 delete(queryParams.Type.ToObject(), pname) 1744 if queryParams.Validation != nil { 1745 req := queryParams.Validation.Required 1746 for i, n := range req { 1747 if n == pname { 1748 queryParams.Validation.Required = append(req[:i], req[i+1:]...) 1749 break 1750 } 1751 } 1752 } 1753 } 1754 } 1755 a.QueryParams = queryParams 1756 } 1757 } 1758 1759 // Context returns the generic definition name used in error messages. 1760 func (f *FileServerDefinition) Context() string { 1761 suffix := fmt.Sprintf("file server %s", f.FilePath) 1762 var prefix string 1763 if f.Parent != nil { 1764 prefix = f.Parent.Context() + " " 1765 } 1766 return prefix + suffix 1767 } 1768 1769 // Finalize inherits security scheme from parent and top level design. 1770 func (f *FileServerDefinition) Finalize() { 1771 // Make sure request path starts with a "/" so codegen can rely on it. 1772 if !strings.HasPrefix(f.RequestPath, "/") { 1773 f.RequestPath = "/" + f.RequestPath 1774 } 1775 // Inherit security 1776 if f.Security == nil { 1777 f.Security = f.Parent.Security // ResourceDefinition 1778 if f.Security == nil { 1779 f.Security = Design.Security 1780 } 1781 } 1782 if f.Security != nil && f.Security.Scheme.Kind == NoSecurityKind { 1783 f.Security = nil 1784 } 1785 } 1786 1787 // IsDir returns true if the file server serves a directory, false otherwise. 1788 func (f *FileServerDefinition) IsDir() bool { 1789 return WildcardRegex.MatchString(f.RequestPath) 1790 } 1791 1792 // ByFilePath makes FileServerDefinition sortable for code generators. 1793 type ByFilePath []*FileServerDefinition 1794 1795 func (b ByFilePath) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 1796 func (b ByFilePath) Len() int { return len(b) } 1797 func (b ByFilePath) Less(i, j int) bool { return b[i].FilePath < b[j].FilePath } 1798 1799 // Context returns the generic definition name used in error messages. 1800 func (l *LinkDefinition) Context() string { 1801 var prefix, suffix string 1802 if l.Name != "" { 1803 prefix = fmt.Sprintf("link %#v", l.Name) 1804 } else { 1805 prefix = "unnamed link" 1806 } 1807 if l.Parent != nil { 1808 suffix = fmt.Sprintf(" of %s", l.Parent.Context()) 1809 } 1810 return prefix + suffix 1811 } 1812 1813 // Attribute returns the linked attribute. 1814 func (l *LinkDefinition) Attribute() *AttributeDefinition { 1815 p := l.Parent.ToObject() 1816 if p == nil { 1817 return nil 1818 } 1819 return p[l.Name] 1820 } 1821 1822 // MediaType returns the media type of the linked attribute. 1823 func (l *LinkDefinition) MediaType() *MediaTypeDefinition { 1824 att := l.Attribute() 1825 mt, _ := att.Type.(*MediaTypeDefinition) 1826 return mt 1827 } 1828 1829 // Context returns the generic definition name used in error messages. 1830 func (v *ViewDefinition) Context() string { 1831 var prefix, suffix string 1832 if v.Name != "" { 1833 prefix = fmt.Sprintf("view %#v", v.Name) 1834 } else { 1835 prefix = "unnamed view" 1836 } 1837 if v.Parent != nil { 1838 suffix = fmt.Sprintf(" of %s", v.Parent.Context()) 1839 } 1840 return prefix + suffix 1841 } 1842 1843 // Context returns the generic definition name used in error messages. 1844 func (r *RouteDefinition) Context() string { 1845 return fmt.Sprintf(`route %s "%s" of %s`, r.Verb, r.Path, r.Parent.Context()) 1846 } 1847 1848 // Params returns the route parameters. 1849 // For example for the route "GET /foo/:fooID" Params returns []string{"fooID"}. 1850 func (r *RouteDefinition) Params() []string { 1851 return ExtractWildcards(r.FullPath()) 1852 } 1853 1854 // FullPath returns the action full path computed by concatenating the API and resource base paths 1855 // with the action specific path. 1856 func (r *RouteDefinition) FullPath() string { 1857 if r.IsAbsolute() { 1858 return httppath.Clean(r.Path[1:]) 1859 } 1860 var base string 1861 if r.Parent != nil && r.Parent.Parent != nil { 1862 base = r.Parent.Parent.FullPath() 1863 } 1864 1865 joinedPath := path.Join(base, r.Path) 1866 if strings.HasSuffix(r.Path, "/") { 1867 //add slash removed by Join back again (it may be important for routing) 1868 joinedPath += "/" 1869 } 1870 1871 return httppath.Clean(joinedPath) 1872 } 1873 1874 // IsAbsolute returns true if the action path should not be concatenated to the resource and API 1875 // base paths. 1876 func (r *RouteDefinition) IsAbsolute() bool { 1877 return strings.HasPrefix(r.Path, "//") 1878 } 1879 1880 func iterateHeaders(headers *AttributeDefinition, isRequired func(name string) bool, it HeaderIterator) error { 1881 if headers == nil || !headers.Type.IsObject() { 1882 return nil 1883 } 1884 headersMap := headers.Type.ToObject() 1885 names := make([]string, len(headersMap)) 1886 i := 0 1887 for n := range headersMap { 1888 names[i] = n 1889 i++ 1890 } 1891 sort.Strings(names) 1892 for _, n := range names { 1893 header := headersMap[n] 1894 if err := it(n, isRequired(n), header); err != nil { 1895 return err 1896 } 1897 } 1898 return nil 1899 }