diff --git a/openapi2conv/issue187_test.go b/openapi2conv/issue187_test.go new file mode 100644 index 000000000..0fccf9da8 --- /dev/null +++ b/openapi2conv/issue187_test.go @@ -0,0 +1,100 @@ +package openapi2conv + +import ( + "context" + "encoding/json" + "testing" + + "github.com/getkin/kin-openapi/openapi2" + "github.com/getkin/kin-openapi/openapi3" + "github.com/stretchr/testify/require" +) + +func v2v3(spec2 []byte) (doc3 *openapi3.Swagger, err error) { + var doc2 openapi2.Swagger + if err = json.Unmarshal(spec2, &doc2); err != nil { + return + } + doc3, err = ToV3Swagger(&doc2) + return +} + +func TestIssue187(t *testing.T) { + spec := ` +{ + "swagger": "2.0", + "info": { + "description": "Test Golang Application", + "version": "1.0", + "title": "Test", + "contact": { + "name": "Test", + "email": "test@test.com" + } + }, + + "paths": { + "/me": { + "get": { + "description": "", + "operationId": "someTest", + "summary": "Some test", + "tags": ["probe"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "successful operation", + "schema": {"$ref": "#/definitions/model.ProductSearchAttributeRequest"} + } + } + } + } + }, + + "host": "", + "basePath": "/test", + "definitions": { + "model.ProductSearchAttributeRequest": { + "type": "object", + "properties": { + "filterField": { + "type": "string" + }, + "filterKey": { + "type": "string" + }, + "type": { + "type": "string" + }, + "values": { + "$ref": "#/definitions/model.ProductSearchAttributeValueRequest" + } + }, + "title": "model.ProductSearchAttributeRequest" + }, + "model.ProductSearchAttributeValueRequest": { + "type": "object", + "properties": { + "imageUrl": { + "type": "string" + }, + "text": { + "type": "string" + } + }, + "title": "model.ProductSearchAttributeValueRequest" + } + } +} +` + doc3, err := v2v3([]byte(spec)) + require.NoError(t, err) + + spec3, err := json.Marshal(doc3) + require.NoError(t, err) + const expected = `{"components":{"schemas":{"model.ProductSearchAttributeRequest":{"properties":{"filterField":{"type":"string"},"filterKey":{"type":"string"},"type":{"type":"string"},"values":{"$ref":"#/components/schemas/model.ProductSearchAttributeValueRequest"}},"title":"model.ProductSearchAttributeRequest","type":"object"},"model.ProductSearchAttributeValueRequest":{"properties":{"imageUrl":{"type":"string"},"text":{"type":"string"}},"title":"model.ProductSearchAttributeValueRequest","type":"object"}}},"info":{"contact":{"email":"test@test.com","name":"Test"},"description":"Test Golang Application","title":"Test","version":"1.0"},"openapi":"3.0.2","paths":{"/me":{"get":{"operationId":"someTest","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/model.ProductSearchAttributeRequest"}}},"description":"successful operation"}},"summary":"Some test","tags":["probe"]}}}}` + require.Equal(t, string(spec3), expected) + + err = doc3.Validate(context.Background()) + require.NoError(t, err) +} diff --git a/openapi2conv/openapi2_conv.go b/openapi2conv/openapi2_conv.go index dada63704..408e3f5b0 100644 --- a/openapi2conv/openapi2_conv.go +++ b/openapi2conv/openapi2_conv.go @@ -23,13 +23,11 @@ func ToV3Swagger(swagger *openapi2.Swagger) (*openapi3.Swagger, error) { ExtensionProps: swagger.ExtensionProps, ExternalDocs: swagger.ExternalDocs, } - host := swagger.Host - if len(host) > 0 { + + if host := swagger.Host; len(host) > 0 { schemes := swagger.Schemes if len(schemes) == 0 { - schemes = []string{ - "https://", - } + schemes = []string{"https://"} } basePath := swagger.BasePath for _, scheme := range schemes { @@ -38,11 +36,10 @@ func ToV3Swagger(swagger *openapi2.Swagger) (*openapi3.Swagger, error) { Host: host, Path: basePath, } - result.AddServer(&openapi3.Server{ - URL: u.String(), - }) + result.AddServer(&openapi3.Server{URL: u.String()}) } } + if paths := swagger.Paths; paths != nil { resultPaths := make(map[string]*openapi3.PathItem, len(paths)) for path, pathItem := range paths { @@ -54,6 +51,7 @@ func ToV3Swagger(swagger *openapi2.Swagger) (*openapi3.Swagger, error) { } result.Paths = resultPaths } + if parameters := swagger.Parameters; parameters != nil { result.Components.Parameters = make(map[string]*openapi3.ParameterRef) result.Components.RequestBodies = make(map[string]*openapi3.RequestBodyRef) @@ -70,6 +68,7 @@ func ToV3Swagger(swagger *openapi2.Swagger) (*openapi3.Swagger, error) { } } } + if responses := swagger.Responses; responses != nil { result.Components.Responses = make(map[string]*openapi3.ResponseRef, len(responses)) for k, response := range responses { @@ -80,7 +79,9 @@ func ToV3Swagger(swagger *openapi2.Swagger) (*openapi3.Swagger, error) { result.Components.Responses[k] = r } } + result.Components.Schemas = ToV3Schemas(swagger.Definitions) + if m := swagger.SecurityDefinitions; m != nil { resultSecuritySchemes := make(map[string]*openapi3.SecuritySchemeRef) for k, v := range m { @@ -92,7 +93,15 @@ func ToV3Swagger(swagger *openapi2.Swagger) (*openapi3.Swagger, error) { } result.Components.SecuritySchemes = resultSecuritySchemes } + result.Security = ToV3SecurityRequirements(swagger.Security) + + { + sl := openapi3.NewSwaggerLoader() + if err := sl.ResolveRefsIn(result, nil); err != nil { + return nil, err + } + } return result, nil } @@ -508,7 +517,7 @@ func FromV3SchemaRef(schema *openapi3.SchemaRef) *openapi3.SchemaRef { return schema } if schema.Value.Items != nil { - schema.Value.Items = FromV3SchemaRef((schema.Value.Items)) + schema.Value.Items = FromV3SchemaRef(schema.Value.Items) } for k, v := range schema.Value.Properties { schema.Value.Properties[k] = FromV3SchemaRef(v) @@ -814,7 +823,7 @@ var attemptedBodyParameterNames = []string{ } func stripNonCustomExtensions(extensions map[string]interface{}) { - for extName, _ := range extensions { + for extName := range extensions { if !strings.HasPrefix(extName, "x-") { delete(extensions, extName) } diff --git a/openapi3/components.go b/openapi3/components.go index 78b66aa31..a8ebd7926 100644 --- a/openapi3/components.go +++ b/openapi3/components.go @@ -92,7 +92,7 @@ func (components *Components) Validate(c context.Context) (err error) { return } -const identifierPattern = `^[a-zA-Z0-9.\-_]+$` +const identifierPattern = `^[a-zA-Z0-9._-]+$` var identifierRegExp = regexp.MustCompile(identifierPattern) @@ -100,5 +100,5 @@ func ValidateIdentifier(value string) error { if identifierRegExp.MatchString(value) { return nil } - return fmt.Errorf("Identifier '%s' is not supported by OpenAPI version 3 standard (regexp: '%s')", value, identifierPattern) + return fmt.Errorf("identifier %q is not supported by OpenAPIv3 standard (regexp: %q)", value, identifierPattern) }