123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- /*
- Copyright 2016 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package openapi
- import (
- "fmt"
- "net/http"
- "testing"
- "github.com/emicklei/go-restful"
- "github.com/emicklei/go-restful/swagger"
- "github.com/go-openapi/spec"
- "github.com/stretchr/testify/assert"
- "sort"
- )
- // setUp is a convenience function for setting up for (most) tests.
- func setUp(t *testing.T, fullMethods bool) (openAPI, *assert.Assertions) {
- assert := assert.New(t)
- config := Config{
- SwaggerConfig: getSwaggerConfig(fullMethods),
- Info: &spec.Info{
- InfoProps: spec.InfoProps{
- Title: "TestAPI",
- Description: "Test API",
- },
- },
- }
- return openAPI{config: &config}, assert
- }
- func noOp(request *restful.Request, response *restful.Response) {}
- type TestInput struct {
- Name string `json:"name,omitempty"`
- ID int `json:"id,omitempty"`
- Tags []string `json:"tags,omitempty"`
- }
- type TestOutput struct {
- Name string `json:"name,omitempty"`
- Count int `json:"count,omitempty"`
- }
- func (t TestInput) SwaggerDoc() map[string]string {
- return map[string]string{
- "": "Test input",
- "name": "Name of the input",
- "id": "ID of the input",
- }
- }
- func (t TestOutput) SwaggerDoc() map[string]string {
- return map[string]string{
- "": "Test output",
- "name": "Name of the output",
- "count": "Number of outputs",
- }
- }
- func getTestRoute(ws *restful.WebService, method string, additionalParams bool) *restful.RouteBuilder {
- ret := ws.Method(method).
- Path("/test/{path:*}").
- Doc(fmt.Sprintf("%s test input", method)).
- Operation(fmt.Sprintf("%sTestInput", method)).
- Produces(restful.MIME_JSON).
- Consumes(restful.MIME_JSON).
- Param(ws.PathParameter("path", "path to the resource").DataType("string")).
- Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
- Reads(TestInput{}).
- Returns(200, "OK", TestOutput{}).
- Writes(TestOutput{}).
- To(noOp)
- if additionalParams {
- ret.Param(ws.HeaderParameter("hparam", "a test head parameter").DataType("integer"))
- ret.Param(ws.FormParameter("fparam", "a test form parameter").DataType("number"))
- }
- return ret
- }
- func getSwaggerConfig(fullMethods bool) *swagger.Config {
- mux := http.NewServeMux()
- container := restful.NewContainer()
- container.ServeMux = mux
- ws := new(restful.WebService)
- ws.Path("/foo")
- ws.Route(getTestRoute(ws, "get", true))
- if fullMethods {
- ws.Route(getTestRoute(ws, "post", false)).
- Route(getTestRoute(ws, "put", false)).
- Route(getTestRoute(ws, "head", false)).
- Route(getTestRoute(ws, "patch", false)).
- Route(getTestRoute(ws, "options", false)).
- Route(getTestRoute(ws, "delete", false))
- }
- ws.Path("/bar")
- ws.Route(getTestRoute(ws, "get", true))
- if fullMethods {
- ws.Route(getTestRoute(ws, "post", false)).
- Route(getTestRoute(ws, "put", false)).
- Route(getTestRoute(ws, "head", false)).
- Route(getTestRoute(ws, "patch", false)).
- Route(getTestRoute(ws, "options", false)).
- Route(getTestRoute(ws, "delete", false))
- }
- container.Add(ws)
- return &swagger.Config{
- WebServicesUrl: "https://test-server",
- WebServices: container.RegisteredWebServices(),
- }
- }
- func getTestOperation(method string) *spec.Operation {
- return &spec.Operation{
- OperationProps: spec.OperationProps{
- Description: fmt.Sprintf("%s test input", method),
- Consumes: []string{"application/json"},
- Produces: []string{"application/json"},
- Schemes: []string{"https"},
- Parameters: []spec.Parameter{},
- Responses: getTestResponses(),
- },
- }
- }
- func getTestPathItem(allMethods bool) spec.PathItem {
- ret := spec.PathItem{
- PathItemProps: spec.PathItemProps{
- Get: getTestOperation("get"),
- Parameters: getTestCommonParameters(),
- },
- }
- ret.Get.Parameters = getAdditionalTestParameters()
- if allMethods {
- ret.PathItemProps.Put = getTestOperation("put")
- ret.PathItemProps.Post = getTestOperation("post")
- ret.PathItemProps.Head = getTestOperation("head")
- ret.PathItemProps.Patch = getTestOperation("patch")
- ret.PathItemProps.Delete = getTestOperation("delete")
- ret.PathItemProps.Options = getTestOperation("options")
- }
- return ret
- }
- func getRefSchema(ref string) *spec.Schema {
- return &spec.Schema{
- SchemaProps: spec.SchemaProps{
- Ref: spec.MustCreateRef(ref),
- },
- }
- }
- func getTestResponses() *spec.Responses {
- ret := spec.Responses{
- ResponsesProps: spec.ResponsesProps{
- StatusCodeResponses: map[int]spec.Response{},
- },
- }
- ret.StatusCodeResponses[200] = spec.Response{
- ResponseProps: spec.ResponseProps{
- Description: "OK",
- Schema: getRefSchema("#/definitions/openapi.TestOutput"),
- },
- }
- return &ret
- }
- func getTestCommonParameters() []spec.Parameter {
- ret := make([]spec.Parameter, 3)
- ret[0] = spec.Parameter{
- ParamProps: spec.ParamProps{
- Name: "body",
- In: "body",
- Required: true,
- Schema: getRefSchema("#/definitions/openapi.TestInput"),
- },
- }
- ret[1] = spec.Parameter{
- SimpleSchema: spec.SimpleSchema{
- Type: "string",
- },
- ParamProps: spec.ParamProps{
- Description: "path to the resource",
- Name: "path",
- In: "path",
- Required: true,
- },
- CommonValidations: spec.CommonValidations{
- UniqueItems: true,
- },
- }
- ret[2] = spec.Parameter{
- SimpleSchema: spec.SimpleSchema{
- Type: "string",
- },
- ParamProps: spec.ParamProps{
- Description: "If 'true', then the output is pretty printed.",
- Name: "pretty",
- In: "query",
- },
- CommonValidations: spec.CommonValidations{
- UniqueItems: true,
- },
- }
- return ret
- }
- func getAdditionalTestParameters() []spec.Parameter {
- ret := make([]spec.Parameter, 2)
- ret[0] = spec.Parameter{
- ParamProps: spec.ParamProps{
- Name: "fparam",
- Description: "a test form parameter",
- In: "form",
- },
- SimpleSchema: spec.SimpleSchema{
- Type: "number",
- },
- CommonValidations: spec.CommonValidations{
- UniqueItems: true,
- },
- }
- ret[1] = spec.Parameter{
- SimpleSchema: spec.SimpleSchema{
- Type: "integer",
- },
- ParamProps: spec.ParamProps{
- Description: "a test head parameter",
- Name: "hparam",
- In: "header",
- },
- CommonValidations: spec.CommonValidations{
- UniqueItems: true,
- },
- }
- return ret
- }
- type Parameters []spec.Parameter
- func (s Parameters) Len() int { return len(s) }
- func (s Parameters) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
- type ByName struct {
- Parameters
- }
- func (s ByName) Less(i, j int) bool {
- return s.Parameters[i].Name < s.Parameters[j].Name
- }
- // TODO(mehdy): Consider sort parameters in actual spec generation for more predictable spec generation
- func sortParameters(s *spec.Swagger) *spec.Swagger {
- for k, p := range s.Paths.Paths {
- sort.Sort(ByName{p.Parameters})
- sort.Sort(ByName{p.Get.Parameters})
- sort.Sort(ByName{p.Put.Parameters})
- sort.Sort(ByName{p.Post.Parameters})
- sort.Sort(ByName{p.Head.Parameters})
- sort.Sort(ByName{p.Delete.Parameters})
- sort.Sort(ByName{p.Options.Parameters})
- sort.Sort(ByName{p.Patch.Parameters})
- s.Paths.Paths[k] = p // Unnecessary?! Magic!!!
- }
- return s
- }
- func getTestInputDefinition() spec.Schema {
- return spec.Schema{
- SchemaProps: spec.SchemaProps{
- Description: "Test input",
- Required: []string{},
- Properties: map[string]spec.Schema{
- "id": {
- SchemaProps: spec.SchemaProps{
- Description: "ID of the input",
- Type: spec.StringOrArray{"integer"},
- Format: "int32",
- Enum: []interface{}{},
- },
- },
- "name": {
- SchemaProps: spec.SchemaProps{
- Description: "Name of the input",
- Type: spec.StringOrArray{"string"},
- Enum: []interface{}{},
- },
- },
- "tags": {
- SchemaProps: spec.SchemaProps{
- Type: spec.StringOrArray{"array"},
- Enum: []interface{}{},
- Items: &spec.SchemaOrArray{
- Schema: &spec.Schema{
- SchemaProps: spec.SchemaProps{
- Type: spec.StringOrArray{"string"},
- },
- },
- },
- },
- },
- },
- },
- }
- }
- func getTestOutputDefinition() spec.Schema {
- return spec.Schema{
- SchemaProps: spec.SchemaProps{
- Description: "Test output",
- Required: []string{},
- Properties: map[string]spec.Schema{
- "count": {
- SchemaProps: spec.SchemaProps{
- Description: "Number of outputs",
- Type: spec.StringOrArray{"integer"},
- Format: "int32",
- Enum: []interface{}{},
- },
- },
- "name": {
- SchemaProps: spec.SchemaProps{
- Description: "Name of the output",
- Type: spec.StringOrArray{"string"},
- Enum: []interface{}{},
- },
- },
- },
- },
- }
- }
- func TestBuildSwaggerSpec(t *testing.T) {
- o, assert := setUp(t, true)
- expected := &spec.Swagger{
- SwaggerProps: spec.SwaggerProps{
- Info: &spec.Info{
- InfoProps: spec.InfoProps{
- Title: "TestAPI",
- Description: "Test API",
- },
- },
- Swagger: "2.0",
- Paths: &spec.Paths{
- Paths: map[string]spec.PathItem{
- "/foo/test/{path}": getTestPathItem(true),
- "/bar/test/{path}": getTestPathItem(true),
- },
- },
- Definitions: spec.Definitions{
- "openapi.TestInput": getTestInputDefinition(),
- "openapi.TestOutput": getTestOutputDefinition(),
- },
- },
- }
- err := o.buildSwaggerSpec()
- if assert.NoError(err) {
- sortParameters(expected)
- sortParameters(o.swagger)
- assert.Equal(expected, o.swagger)
- }
- }
- func TestBuildSwaggerSpecTwice(t *testing.T) {
- o, assert := setUp(t, true)
- err := o.buildSwaggerSpec()
- if assert.NoError(err) {
- assert.Error(o.buildSwaggerSpec(), "Swagger spec is already built. Duplicate call to buildSwaggerSpec is not allowed.")
- }
- }
- func TestBuildDefinitions(t *testing.T) {
- o, assert := setUp(t, true)
- expected := spec.Definitions{
- "openapi.TestInput": getTestInputDefinition(),
- "openapi.TestOutput": getTestOutputDefinition(),
- }
- def, err := o.buildDefinitions()
- if assert.NoError(err) {
- assert.Equal(expected, def)
- }
- }
- func TestBuildProtocolList(t *testing.T) {
- assert := assert.New(t)
- o := openAPI{config: &Config{SwaggerConfig: &swagger.Config{WebServicesUrl: "https://something"}}}
- p, err := o.buildProtocolList()
- if assert.NoError(err) {
- assert.Equal([]string{"https"}, p)
- }
- o = openAPI{config: &Config{SwaggerConfig: &swagger.Config{WebServicesUrl: "http://something"}}}
- p, err = o.buildProtocolList()
- if assert.NoError(err) {
- assert.Equal([]string{"http"}, p)
- }
- o = openAPI{config: &Config{SwaggerConfig: &swagger.Config{WebServicesUrl: "something"}}}
- p, err = o.buildProtocolList()
- if assert.NoError(err) {
- assert.Equal([]string{"http"}, p)
- }
- }
|