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