Skip to content

Commit

Permalink
Merge pull request #779 from tchssk/swagger-extensions
Browse files Browse the repository at this point in the history
Support swagger extensions
  • Loading branch information
Raphaël Simon committed Sep 19, 2016
2 parents 1dc8ee4 + 9b6a2bb commit 61b3b6e
Show file tree
Hide file tree
Showing 8 changed files with 333 additions and 83 deletions.
90 changes: 72 additions & 18 deletions design/apidsl/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,48 +125,102 @@ func Routing(routes ...*design.RouteDefinition) {
}

// GET creates a route using the GET HTTP method.
func GET(path string) *design.RouteDefinition {
return &design.RouteDefinition{Verb: "GET", Path: path}
func GET(path string, dsl ...func()) *design.RouteDefinition {
route := &design.RouteDefinition{Verb: "GET", Path: path}
if len(dsl) != 0 {
if !dslengine.Execute(dsl[0], route) {
return nil
}
}
return route
}

// HEAD creates a route using the HEAD HTTP method.
func HEAD(path string) *design.RouteDefinition {
return &design.RouteDefinition{Verb: "HEAD", Path: path}
func HEAD(path string, dsl ...func()) *design.RouteDefinition {
route := &design.RouteDefinition{Verb: "HEAD", Path: path}
if len(dsl) != 0 {
if !dslengine.Execute(dsl[0], route) {
return nil
}
}
return route
}

// POST creates a route using the POST HTTP method.
func POST(path string) *design.RouteDefinition {
return &design.RouteDefinition{Verb: "POST", Path: path}
func POST(path string, dsl ...func()) *design.RouteDefinition {
route := &design.RouteDefinition{Verb: "POST", Path: path}
if len(dsl) != 0 {
if !dslengine.Execute(dsl[0], route) {
return nil
}
}
return route
}

// PUT creates a route using the PUT HTTP method.
func PUT(path string) *design.RouteDefinition {
return &design.RouteDefinition{Verb: "PUT", Path: path}
func PUT(path string, dsl ...func()) *design.RouteDefinition {
route := &design.RouteDefinition{Verb: "PUT", Path: path}
if len(dsl) != 0 {
if !dslengine.Execute(dsl[0], route) {
return nil
}
}
return route
}

// DELETE creates a route using the DELETE HTTP method.
func DELETE(path string) *design.RouteDefinition {
return &design.RouteDefinition{Verb: "DELETE", Path: path}
func DELETE(path string, dsl ...func()) *design.RouteDefinition {
route := &design.RouteDefinition{Verb: "DELETE", Path: path}
if len(dsl) != 0 {
if !dslengine.Execute(dsl[0], route) {
return nil
}
}
return route
}

// OPTIONS creates a route using the OPTIONS HTTP method.
func OPTIONS(path string) *design.RouteDefinition {
return &design.RouteDefinition{Verb: "OPTIONS", Path: path}
func OPTIONS(path string, dsl ...func()) *design.RouteDefinition {
route := &design.RouteDefinition{Verb: "OPTIONS", Path: path}
if len(dsl) != 0 {
if !dslengine.Execute(dsl[0], route) {
return nil
}
}
return route
}

// TRACE creates a route using the TRACE HTTP method.
func TRACE(path string) *design.RouteDefinition {
return &design.RouteDefinition{Verb: "TRACE", Path: path}
func TRACE(path string, dsl ...func()) *design.RouteDefinition {
route := &design.RouteDefinition{Verb: "TRACE", Path: path}
if len(dsl) != 0 {
if !dslengine.Execute(dsl[0], route) {
return nil
}
}
return route
}

// CONNECT creates a route using the CONNECT HTTP method.
func CONNECT(path string) *design.RouteDefinition {
return &design.RouteDefinition{Verb: "CONNECT", Path: path}
func CONNECT(path string, dsl ...func()) *design.RouteDefinition {
route := &design.RouteDefinition{Verb: "CONNECT", Path: path}
if len(dsl) != 0 {
if !dslengine.Execute(dsl[0], route) {
return nil
}
}
return route
}

// PATCH creates a route using the PATCH HTTP method.
func PATCH(path string) *design.RouteDefinition {
return &design.RouteDefinition{Verb: "PATCH", Path: path}
func PATCH(path string, dsl ...func()) *design.RouteDefinition {
route := &design.RouteDefinition{Verb: "PATCH", Path: path}
if len(dsl) != 0 {
if !dslengine.Execute(dsl[0], route) {
return nil
}
}
return route
}

// Headers implements the DSL for describing HTTP headers. The DSL syntax is identical to the one
Expand Down
21 changes: 21 additions & 0 deletions design/apidsl/action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,27 @@ var _ = Describe("Action", func() {
})
})

Context("with a metadata", func() {
BeforeEach(func() {
metadatadsl := func() { Metadata("swagger:extension:x-get", `{"foo":"bar"}`) }
route = GET("/:id", metadatadsl)
name = "foo"
})

It("produces a valid action definition with the route with the metadata", func() {
Ω(dslengine.Errors).ShouldNot(HaveOccurred())
Ω(action).ShouldNot(BeNil())
Ω(action.Name).Should(Equal(name))
Ω(action.Validate()).ShouldNot(HaveOccurred())
Ω(action.Routes).ShouldNot(BeNil())
Ω(action.Routes).Should(HaveLen(1))
Ω(action.Routes[0]).Should(Equal(route))
Ω(action.Routes[0].Metadata).ShouldNot(BeNil())
Ω(action.Routes[0].Metadata).Should(Equal(
dslengine.MetadataDefinition{"swagger:extension:x-get": []string{`{"foo":"bar"}`}},
))
})
})
})

Context("with a string payload", func() {
Expand Down
72 changes: 33 additions & 39 deletions design/apidsl/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ import (
//
// Metadata("swagger:summary", "Short summary of what action does")
//
// `swagger:extension:xxx`: sets the Swagger extensions xxx.
// Applicable to
// api as within the info and tag object,
// resource as within the paths object,
// action as within the path-item object,
// route as within the operation object,
// param as within the parameter object,
// response as within the response object
// and security as within the security-scheme object.
// See https://github.com/OAI/OpenAPI-Specification/blob/master/guidelines/EXTENSIONS.md.
//
// Metadata("swagger:extension:x-api", `{"foo":"bar"}`)
//
// The special key names listed above may be used as follows:
//
// var Account = Type("Account", func() {
Expand All @@ -49,55 +62,36 @@ import (
// })
//
func Metadata(name string, value ...string) {
appendMetadata := func(metadata dslengine.MetadataDefinition, name string, value ...string) dslengine.MetadataDefinition {
if metadata == nil {
metadata = make(map[string][]string)
}
metadata[name] = append(metadata[name], value...)
return metadata
}

switch def := dslengine.CurrentDefinition().(type) {
case design.ContainerDefinition:
att := def.Attribute()
if att.Metadata == nil {
att.Metadata = make(map[string][]string)
}
att.Metadata[name] = append(att.Metadata[name], value...)
att.Metadata = appendMetadata(att.Metadata, name, value...)
case *design.AttributeDefinition:
if def.Metadata == nil {
def.Metadata = make(map[string][]string)
}
def.Metadata[name] = append(def.Metadata[name], value...)

def.Metadata = appendMetadata(def.Metadata, name, value...)
case *design.MediaTypeDefinition:
if def.Metadata == nil {
def.Metadata = make(map[string][]string)
}
def.Metadata[name] = append(def.Metadata[name], value...)

def.Metadata = appendMetadata(def.Metadata, name, value...)
case *design.ActionDefinition:
if def.Metadata == nil {
def.Metadata = make(map[string][]string)
}
def.Metadata[name] = append(def.Metadata[name], value...)

def.Metadata = appendMetadata(def.Metadata, name, value...)
case *design.FileServerDefinition:
if def.Metadata == nil {
def.Metadata = make(map[string][]string)
}
def.Metadata[name] = append(def.Metadata[name], value...)

def.Metadata = appendMetadata(def.Metadata, name, value...)
case *design.ResourceDefinition:
if def.Metadata == nil {
def.Metadata = make(map[string][]string)
}
def.Metadata[name] = append(def.Metadata[name], value...)

def.Metadata = appendMetadata(def.Metadata, name, value...)
case *design.ResponseDefinition:
if def.Metadata == nil {
def.Metadata = make(map[string][]string)
}
def.Metadata[name] = append(def.Metadata[name], value...)

def.Metadata = appendMetadata(def.Metadata, name, value...)
case *design.APIDefinition:
if def.Metadata == nil {
def.Metadata = make(map[string][]string)
}
def.Metadata[name] = append(def.Metadata[name], value...)

def.Metadata = appendMetadata(def.Metadata, name, value...)
case *design.RouteDefinition:
def.Metadata = appendMetadata(def.Metadata, name, value...)
case *design.SecurityDefinition:
def.Scheme.Metadata = appendMetadata(def.Scheme.Metadata, name, value...)
default:
dslengine.IncompatibleDSL()
}
Expand Down
15 changes: 15 additions & 0 deletions design/apidsl/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,21 @@ var _ = Describe("Metadata", func() {
JustBeforeEach(func() {
api = API("Example API", func() {
Metadata(metadataKey, metadataValue)
BasicAuthSecurity("password")
})

rd = Resource("Example Resource", func() {
Metadata(metadataKey, metadataValue)
Action("Example Action", func() {
Metadata(metadataKey, metadataValue)
Routing(
GET("/", func() {
Metadata(metadataKey, metadataValue)
}),
)
Security("password", func() {
Metadata(metadataKey, metadataValue)
})
})
Response("Example Response", func() {
Metadata(metadataKey, metadataValue)
Expand Down Expand Up @@ -56,6 +65,8 @@ var _ = Describe("Metadata", func() {
Ω(api.Metadata).To(Equal(expected))
Ω(rd.Metadata).To(Equal(expected))
Ω(rd.Actions["Example Action"].Metadata).To(Equal(expected))
Ω(rd.Actions["Example Action"].Routes[0].Metadata).To(Equal(expected))
Ω(rd.Actions["Example Action"].Security.Scheme.Metadata).To(Equal(expected))
Ω(rd.Responses["Example Response"].Metadata).To(Equal(expected))
Ω(mtd.Metadata).To(Equal(expected))

Expand All @@ -75,6 +86,8 @@ var _ = Describe("Metadata", func() {
Ω(api.Metadata).To(Equal(expected))
Ω(rd.Metadata).To(Equal(expected))
Ω(rd.Actions["Example Action"].Metadata).To(Equal(expected))
Ω(rd.Actions["Example Action"].Routes[0].Metadata).To(Equal(expected))
Ω(rd.Actions["Example Action"].Security.Scheme.Metadata).To(Equal(expected))
Ω(rd.Responses["Example Response"].Metadata).To(Equal(expected))
Ω(mtd.Metadata).To(Equal(expected))

Expand All @@ -94,6 +107,8 @@ var _ = Describe("Metadata", func() {
Ω(api.Metadata).To(Equal(expected))
Ω(rd.Metadata).To(Equal(expected))
Ω(rd.Actions["Example Action"].Metadata).To(Equal(expected))
Ω(rd.Actions["Example Action"].Routes[0].Metadata).To(Equal(expected))
Ω(rd.Actions["Example Action"].Security.Scheme.Metadata).To(Equal(expected))
Ω(rd.Responses["Example Response"].Metadata).To(Equal(expected))
Ω(mtd.Metadata).To(Equal(expected))

Expand Down
2 changes: 2 additions & 0 deletions design/definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,8 @@ type (
Path string
// Parent is the action this route applies to.
Parent *ActionDefinition
// Metadata is a list of key/value pairs
Metadata dslengine.MetadataDefinition
}

// AttributeDefinition defines a JSON object member with optional description, default
Expand Down
4 changes: 4 additions & 0 deletions design/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package design
import (
"fmt"
"net/url"

"github.com/goadesign/goa/dslengine"
)

// SecuritySchemeKind is a type of security scheme, according to the
Expand Down Expand Up @@ -67,6 +69,8 @@ type SecuritySchemeDefinition struct {
TokenURL string `json:"token_url,omitempty"`
// AuthorizationURL holds URL for retrieving authorization codes with oauth2
AuthorizationURL string `json:"authorization_url,omitempty"`
// Metadata is a list of key/value pairs
Metadata dslengine.MetadataDefinition
}

// DSL returns the DSL function
Expand Down
Loading

0 comments on commit 61b3b6e

Please sign in to comment.