Skip to content

Commit

Permalink
big restructuring of project layout
Browse files Browse the repository at this point in the history
  • Loading branch information
casualjim committed Jan 2, 2015
0 parents commit cb9ba12
Show file tree
Hide file tree
Showing 38 changed files with 3,449 additions and 0 deletions.
151 changes: 151 additions & 0 deletions analyzer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package spec

import (
"strings"

"github.com/casualjim/go-swagger/util"
)

// type operationRef struct {
// operation *Operation
// parameter *Parameter
// }

// specAnalyzer takes a swagger spec object and turns it into a registry
// with a bunch of utility methods to act on the information in the spec
type specAnalyzer struct {
spec *Swagger
consumes map[string]struct{}
produces map[string]struct{}
authSchemes map[string]struct{}
operations map[string]map[string]*Operation
}

func (s *specAnalyzer) initialize() {
for _, c := range s.spec.Consumes {
s.consumes[c] = struct{}{}
}
for _, c := range s.spec.Produces {
s.produces[c] = struct{}{}
}
for path, pathItem := range s.AllPaths() {
s.analyzeOperations(path, &pathItem)
}
}

func (s *specAnalyzer) analyzeOperations(path string, op *PathItem) {
s.analyzeOperation("GET", path, op.Get)
s.analyzeOperation("PUT", path, op.Put)
s.analyzeOperation("POST", path, op.Post)
s.analyzeOperation("PATCH", path, op.Patch)
s.analyzeOperation("DELETE", path, op.Delete)
s.analyzeOperation("HEAD", path, op.Head)
s.analyzeOperation("OPTIONS", path, op.Options)
}

func (s *specAnalyzer) analyzeOperation(method, path string, op *Operation) {
if op != nil {
for _, c := range op.Consumes {
s.consumes[c] = struct{}{}
}
for _, c := range op.Produces {
s.produces[c] = struct{}{}
}
if _, ok := s.operations[method]; !ok {
s.operations[method] = make(map[string]*Operation)
}
s.operations[method][path] = op
}
}

// ConsumesFor gets the mediatypes for the operation
func (s *specAnalyzer) ConsumesFor(operation *Operation) []string {
cons := make(map[string]struct{})
for k := range s.consumes {
cons[k] = struct{}{}
}
for _, c := range operation.Consumes {
cons[c] = struct{}{}
}
return s.structMapKeys(cons)
}

// ProducesFor gets the mediatypes for the operation
func (s *specAnalyzer) ProducesFor(operation *Operation) []string {
prod := make(map[string]struct{})
for k := range s.produces {
prod[k] = struct{}{}
}
for _, c := range operation.Produces {
prod[c] = struct{}{}
}
return s.structMapKeys(prod)
}

func fieldNameFromParam(param *Parameter) string {
if nm, ok := param.Extensions.GetString("go-name"); ok {
return nm
}
return util.ToGoName(param.Name)
}

func (s *specAnalyzer) paramsAsMap(parameters []Parameter, res map[string]Parameter) {
for _, param := range parameters {
res[fieldNameFromParam(&param)] = param
}
}

func (s *specAnalyzer) ParamsFor(method, path string) map[string]Parameter {
res := make(map[string]Parameter)
for _, param := range s.spec.Parameters {
res[fieldNameFromParam(&param)] = param
}
if pi, ok := s.spec.Paths.Paths[path]; ok {
s.paramsAsMap(pi.Parameters, res)
s.paramsAsMap(s.operations[strings.ToUpper(method)][path].Parameters, res)
}
return res
}

func (s *specAnalyzer) OperationFor(method, path string) (*Operation, bool) {
if mp, ok := s.operations[strings.ToUpper(method)]; ok {
op, fn := mp[path]
return op, fn
}
return nil, false
}

func (s *specAnalyzer) Operations() map[string]map[string]*Operation {
return s.operations
}

func (s *specAnalyzer) structMapKeys(mp map[string]struct{}) []string {
var result []string
for k := range mp {
result = append(result, k)
}
return result
}

// AllPaths returns all the paths in the swagger spec
func (s *specAnalyzer) AllPaths() map[string]PathItem {
return s.spec.Paths.Paths
}

func (s *specAnalyzer) OperationIDs() []string {
var result []string
for _, v := range s.operations {
for _, vv := range v {
result = append(result, vv.ID)
}
}
return result
}

func (s *specAnalyzer) RequiredConsumes() []string {
return s.structMapKeys(s.consumes)
}

func (s *specAnalyzer) RequiredProduces() []string {
return s.structMapKeys(s.produces)
}
97 changes: 97 additions & 0 deletions analyzer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package spec

import (
"sort"
"testing"

"github.com/stretchr/testify/assert"
)

func newAnalyzer(spec *Swagger) *specAnalyzer {
a := &specAnalyzer{
spec: spec,
consumes: make(map[string]struct{}),
produces: make(map[string]struct{}),
authSchemes: make(map[string]struct{}),
operations: make(map[string]map[string]*Operation),
}
a.initialize()
return a
}

func TestAnalyzer(t *testing.T) {
formatParam := QueryParam()
formatParam.Name = "format"
formatParam.Type = "string"

limitParam := QueryParam()
limitParam.Name = "limit"
limitParam.Type = "integer"
limitParam.Format = "int32"
limitParam.Extensions = Extensions(map[string]interface{}{})
limitParam.Extensions.Add("go-name", "Limit")

skipParam := QueryParam()
skipParam.Name = "skip"
skipParam.Type = "integer"
skipParam.Format = "int32"
pi := PathItem{}
pi.Parameters = []Parameter{*limitParam}

op := &Operation{}
op.Consumes = []string{"application/x-yaml"}
op.Produces = []string{"application/x-yaml"}
op.ID = "someOperation"
op.Parameters = []Parameter{*skipParam}
pi.Get = op

spec := &Swagger{
Consumes: []string{"application/json"},
Produces: []string{"application/json"},
Parameters: map[string]Parameter{"format": *formatParam},
Paths: &Paths{
Paths: map[string]PathItem{
"/": pi,
},
},
}
analyzer := newAnalyzer(spec)

assert.Len(t, analyzer.consumes, 2)
assert.Len(t, analyzer.produces, 2)
assert.Len(t, analyzer.operations, 1)
assert.Equal(t, analyzer.operations["GET"]["/"], spec.Paths.Paths["/"].Get)

expected := []string{"application/json", "application/x-yaml"}
sort.Sort(sort.StringSlice(expected))
consumes := analyzer.ConsumesFor(spec.Paths.Paths["/"].Get)
sort.Sort(sort.StringSlice(consumes))
assert.Equal(t, expected, consumes)

produces := analyzer.ProducesFor(spec.Paths.Paths["/"].Get)
sort.Sort(sort.StringSlice(produces))
assert.Equal(t, expected, produces)

parameters := analyzer.ParamsFor("GET", "/")
assert.Len(t, parameters, 3)

operations := analyzer.OperationIDs()
assert.Len(t, operations, 1)

producers := analyzer.RequiredProduces()
assert.Len(t, producers, 2)
consumers := analyzer.RequiredConsumes()
assert.Len(t, consumers, 2)

ops := analyzer.Operations()
assert.Len(t, ops, 1)
assert.Len(t, ops["GET"], 1)

op, ok := analyzer.OperationFor("get", "/")
assert.True(t, ok)
assert.NotNil(t, op)

op, ok = analyzer.OperationFor("delete", "/")
assert.False(t, ok)
assert.Nil(t, op)
}
123 changes: 123 additions & 0 deletions auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package spec

import (
"testing"

. "github.com/smartystreets/goconvey/convey"
)

func TestAuthSerialization(t *testing.T) {
Convey("Auth should", t, func() {
Convey("serialize", func() {
Convey("basic auth security scheme", func() {
auth := BasicAuth()
So(auth, ShouldSerializeJSON, `{"type":"basic"}`)
})

Convey("header key model", func() {
auth := APIKeyAuth("api-key", "header")
So(auth, ShouldSerializeJSON, `{"type":"apiKey","name":"api-key","in":"header"}`)
})

Convey("oauth2 implicit flow model", func() {
auth := OAuth2Implicit("http://foo.com/authorization")
So(auth, ShouldSerializeJSON, `{"type":"oauth2","flow":"implicit","authorizationUrl":"http://foo.com/authorization"}`)
})

Convey("oauth2 password flow model", func() {
auth := OAuth2Password("http://foo.com/token")
So(auth, ShouldSerializeJSON, `{"type":"oauth2","flow":"password","tokenUrl":"http://foo.com/token"}`)
})

Convey("oauth2 application flow model", func() {
auth := OAuth2Application("http://foo.com/token")
So(auth, ShouldSerializeJSON, `{"type":"oauth2","flow":"application","tokenUrl":"http://foo.com/token"}`)
})

Convey("oauth2 access code flow model", func() {
auth := OAuth2AccessToken("http://foo.com/authorization", "http://foo.com/token")
So(auth, ShouldSerializeJSON, `{"type":"oauth2","flow":"accessCode","authorizationUrl":"http://foo.com/authorization","tokenUrl":"http://foo.com/token"}`)
})

Convey("oauth2 implicit flow model with scopes", func() {
auth := OAuth2Implicit("http://foo.com/authorization")
auth.AddScope("email", "read your email")
So(auth, ShouldSerializeJSON, `{"type":"oauth2","flow":"implicit","authorizationUrl":"http://foo.com/authorization","scopes":{"email":"read your email"}}`)
})

Convey("oauth2 password flow model with scopes", func() {
auth := OAuth2Password("http://foo.com/token")
auth.AddScope("email", "read your email")
So(auth, ShouldSerializeJSON, `{"type":"oauth2","flow":"password","tokenUrl":"http://foo.com/token","scopes":{"email":"read your email"}}`)
})

Convey("oauth2 application flow model with scopes", func() {
auth := OAuth2Application("http://foo.com/token")
auth.AddScope("email", "read your email")
So(auth, ShouldSerializeJSON, `{"type":"oauth2","flow":"application","tokenUrl":"http://foo.com/token","scopes":{"email":"read your email"}}`)
})

Convey("oauth2 access code flow model with scopes", func() {
auth := OAuth2AccessToken("http://foo.com/authorization", "http://foo.com/token")
auth.AddScope("email", "read your email")
So(auth, ShouldSerializeJSON, `{"type":"oauth2","flow":"accessCode","authorizationUrl":"http://foo.com/authorization","tokenUrl":"http://foo.com/token","scopes":{"email":"read your email"}}`)
})
})

Convey("deserialize", func() {
Convey("basic auth security scheme", func() {
auth := BasicAuth()
So(`{"type":"basic"}`, ShouldParseJSON, auth)
})

Convey("header key model", func() {
auth := APIKeyAuth("api-key", "header")
So(`{"in":"header","name":"api-key","type":"apiKey"}`, ShouldParseJSON, auth)
})

Convey("oauth2 implicit flow model", func() {
auth := OAuth2Implicit("http://foo.com/authorization")
So(`{"authorizationUrl":"http://foo.com/authorization","flow":"implicit","type":"oauth2"}`, ShouldParseJSON, auth)
})

Convey("oauth2 password flow model", func() {
auth := OAuth2Password("http://foo.com/token")
So(`{"flow":"password","tokenUrl":"http://foo.com/token","type":"oauth2"}`, ShouldParseJSON, auth)
})

Convey("oauth2 application flow model", func() {
auth := OAuth2Application("http://foo.com/token")
So(`{"flow":"application","tokenUrl":"http://foo.com/token","type":"oauth2"}`, ShouldParseJSON, auth)
})

Convey("oauth2 access code flow model", func() {
auth := OAuth2AccessToken("http://foo.com/authorization", "http://foo.com/token")
So(`{"authorizationUrl":"http://foo.com/authorization","flow":"accessCode","tokenUrl":"http://foo.com/token","type":"oauth2"}`, ShouldParseJSON, auth)
})

Convey("oauth2 implicit flow model with scopes", func() {
auth := OAuth2Implicit("http://foo.com/authorization")
auth.AddScope("email", "read your email")
So(`{"authorizationUrl":"http://foo.com/authorization","flow":"implicit","scopes":{"email":"read your email"},"type":"oauth2"}`, ShouldParseJSON, auth)
})

Convey("oauth2 password flow model with scopes", func() {
auth := OAuth2Password("http://foo.com/token")
auth.AddScope("email", "read your email")
So(`{"flow":"password","scopes":{"email":"read your email"},"tokenUrl":"http://foo.com/token","type":"oauth2"}`, ShouldParseJSON, auth)
})

Convey("oauth2 application flow model with scopes", func() {
auth := OAuth2Application("http://foo.com/token")
auth.AddScope("email", "read your email")
So(`{"flow":"application","scopes":{"email":"read your email"},"tokenUrl":"http://foo.com/token","type":"oauth2"}`, ShouldParseJSON, auth)
})

Convey("oauth2 access code flow model with scopes", func() {
auth := OAuth2AccessToken("http://foo.com/authorization", "http://foo.com/token")
auth.AddScope("email", "read your email")
So(`{"authorizationUrl":"http://foo.com/authorization","flow":"accessCode","scopes":{"email":"read your email"},"tokenUrl":"http://foo.com/token","type":"oauth2"}`, ShouldParseJSON, auth)
})
})
})
}
10 changes: 10 additions & 0 deletions contact_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package spec

// ContactInfo contact information for the exposed API.
//
// For more information: http://goo.gl/8us55a#contactObject
type ContactInfo struct {
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
Email string `json:"email,omitempty"`
}
Loading

0 comments on commit cb9ba12

Please sign in to comment.