diff --git a/cmd/openapi/transform.go b/cmd/openapi/transform.go index 205ca867..4cd3b891 100644 --- a/cmd/openapi/transform.go +++ b/cmd/openapi/transform.go @@ -14,7 +14,7 @@ import ( var transformCmd = &model.CommandGroup{ Usage: "transform", Short: "Transform an OpenAPI spec using a well-defined function", - Commands: []model.Command{removeUnusedCmd, filterOperationsCmd, cleanupCmd, formatCmd}, + Commands: []model.Command{removeUnusedCmd, filterOperationsCmd, cleanupCmd, formatCmd, convertSwaggerCmd}, } type basicFlagsI struct { @@ -44,6 +44,18 @@ var removeUnusedCmd = &model.ExecutableCommand[basicFlagsI]{ Flags: basicFlags, } +type convertSwaggerFlags struct { + Schema string `json:"schema"` + Out string `json:"out"` +} + +var convertSwaggerCmd = &model.ExecutableCommand[convertSwaggerFlags]{ + Usage: "convert-swagger", + Short: "Given a Swagger 2.0 file, convert it to an OpenAPI 3.x file", + Run: runConvertSwagger, + Flags: basicFlags, +} + type filterOperationsFlags struct { Schema string `json:"schema"` Out string `json:"out"` @@ -95,6 +107,16 @@ func runRemoveUnused(ctx context.Context, flags basicFlagsI) error { return transform.RemoveUnused(ctx, flags.Schema, yamlOut, out) } +func runConvertSwagger(ctx context.Context, flags convertSwaggerFlags) error { + out, yamlOut, err := setupOutput(ctx, flags.Out) + defer out.Close() + if err != nil { + return err + } + + return transform.ConvertSwagger(ctx, flags.Schema, yamlOut, out) +} + func runFilterOperations(ctx context.Context, flags filterOperationsFlags) error { out, yamlOut, err := setupOutput(ctx, flags.Out) defer out.Close() diff --git a/go.mod b/go.mod index 0033adcc..0c76eaea 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,9 @@ require ( github.com/charmbracelet/huh v0.5.1 github.com/dustin/go-humanize v1.0.1 github.com/ericlagergren/decimal v0.0.0-20240411145413-00de7ca16731 + github.com/getkin/kin-openapi v0.128.0 github.com/inkeep/ai-api-go v0.3.1 + github.com/invopop/yaml v0.3.1 github.com/pb33f/openapi-changes v0.0.61 github.com/samber/lo v1.47.0 github.com/speakeasy-api/openapi v0.1.3 @@ -123,6 +125,8 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.1 // indirect @@ -182,6 +186,7 @@ require ( github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect @@ -194,6 +199,7 @@ require ( github.com/pb33f/doctor v0.0.12 // indirect github.com/pb33f/libopenapi-validator v0.1.0 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect diff --git a/go.sum b/go.sum index d082f011..cb898fa0 100644 --- a/go.sum +++ b/go.sum @@ -206,6 +206,8 @@ github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAY github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg= github.com/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlLgiA= github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk= +github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4= +github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -229,6 +231,10 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -242,8 +248,8 @@ github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QX github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q= github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= -github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -331,6 +337,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inkeep/ai-api-go v0.3.1 h1:fBo2ARhhCzsbZ9g1HflGxJOBuCRVttXjtZtRyzqHAf8= github.com/inkeep/ai-api-go v0.3.1/go.mod h1:LygQ9ask/6nRx7e7LOVb7fNS6Eph7a4RqBgjQhw22yw= +github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso= +github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -430,6 +438,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= @@ -476,6 +486,8 @@ github.com/pb33f/openapi-changes v0.0.61 h1:GGkvKfbZ5B6a1GmqfsxiUgyQekDC0RuLFw8M github.com/pb33f/openapi-changes v0.0.61/go.mod h1:slYdcUuZeZDjOqfh1nVgO99Jbax70+SXAe1EgdBFU90= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= diff --git a/integration/resources/converted.yaml b/integration/resources/converted.yaml new file mode 100644 index 00000000..fced5581 --- /dev/null +++ b/integration/resources/converted.yaml @@ -0,0 +1,697 @@ +components: + schemas: + ApiResponse: + properties: + code: + format: int32 + type: integer + message: + type: string + type: + type: string + type: object + Category: + properties: + id: + format: int64 + type: integer + name: + type: string + type: object + xml: + name: Category + Order: + properties: + complete: + type: boolean + id: + format: int64 + type: integer + petId: + format: int64 + type: integer + quantity: + format: int32 + type: integer + shipDate: + format: date-time + type: string + status: + description: Order Status + enum: + - placed + - approved + - delivered + type: string + type: object + xml: + name: Order + Pet: + properties: + category: + $ref: '#/components/schemas/Category' + id: + format: int64 + type: integer + name: + example: doggie + type: string + photoUrls: + items: + type: string + xml: + name: photoUrl + type: array + xml: + wrapped: true + status: + description: pet status in the store + enum: + - available + - pending + - sold + type: string + tags: + items: + $ref: '#/components/schemas/Tag' + type: array + xml: + wrapped: true + required: + - name + - photoUrls + type: object + xml: + name: Pet + Tag: + properties: + id: + format: int64 + type: integer + name: + type: string + type: object + xml: + name: Tag + User: + properties: + email: + type: string + firstName: + type: string + id: + format: int64 + type: integer + lastName: + type: string + password: + type: string + phone: + type: string + userStatus: + description: User Status + format: int32 + type: integer + username: + type: string + type: object + xml: + name: User + securitySchemes: + api_key: + in: header + name: api_key + type: apiKey + petstore_auth: + flows: + implicit: + authorizationUrl: https://petstore.swagger.io/oauth/authorize + scopes: + read:pets: read your pets + write:pets: modify pets in your account + type: oauth2 +externalDocs: + description: Find out more about Swagger + url: http://swagger.io +info: + contact: + email: apiteam@swagger.io + description: 'This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.' + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + termsOfService: http://swagger.io/terms/ + title: Swagger Petstore + version: 1.0.7 +openapi: 3.0.3 +paths: + /pet: + post: + operationId: addPet + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: Pet object that needs to be added to the store + required: true + x-originalParamName: body + responses: + "405": + description: Invalid input + security: + - petstore_auth: + - write:pets + - read:pets + summary: Add a new pet to the store + tags: + - pet + put: + operationId: updatePet + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: Pet object that needs to be added to the store + required: true + x-originalParamName: body + responses: + "400": + description: Invalid ID supplied + "404": + description: Pet not found + "405": + description: Validation exception + security: + - petstore_auth: + - write:pets + - read:pets + summary: Update an existing pet + tags: + - pet + /pet/{petId}: + delete: + operationId: deletePet + parameters: + - in: header + name: api_key + schema: + type: string + - description: Pet id to delete + in: path + name: petId + required: true + schema: + format: int64 + type: integer + responses: + "400": + description: Invalid ID supplied + "404": + description: Pet not found + security: + - petstore_auth: + - write:pets + - read:pets + summary: Deletes a pet + tags: + - pet + get: + description: Returns a single pet + operationId: getPetById + parameters: + - description: ID of pet to return + in: path + name: petId + required: true + schema: + format: int64 + type: integer + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: successful operation + "400": + description: Invalid ID supplied + "404": + description: Pet not found + security: + - api_key: [] + summary: Find pet by ID + tags: + - pet + post: + operationId: updatePetWithForm + parameters: + - description: ID of pet that needs to be updated + in: path + name: petId + required: true + schema: + format: int64 + type: integer + requestBody: + content: + application/x-www-form-urlencoded: + schema: + properties: + name: + description: Updated name of the pet + type: string + x-formData-name: name + status: + description: Updated status of the pet + type: string + x-formData-name: status + type: object + responses: + "405": + description: Invalid input + security: + - petstore_auth: + - write:pets + - read:pets + summary: Updates a pet in the store with form data + tags: + - pet + /pet/{petId}/uploadImage: + post: + operationId: uploadFile + parameters: + - description: ID of pet to update + in: path + name: petId + required: true + schema: + format: int64 + type: integer + requestBody: + content: + multipart/form-data: + schema: + properties: + additionalMetadata: + description: Additional data to pass to server + type: string + x-formData-name: additionalMetadata + file: + description: file to upload + format: binary + type: string + x-formData-name: file + type: object + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + description: successful operation + security: + - petstore_auth: + - write:pets + - read:pets + summary: uploads an image + tags: + - pet + /pet/findByStatus: + get: + description: Multiple status values can be provided with comma separated strings + operationId: findPetsByStatus + parameters: + - description: Status values that need to be considered for filter + in: query + name: status + required: true + schema: + items: + default: available + enum: + - available + - pending + - sold + type: string + type: array + responses: + "200": + content: + application/json: + schema: + items: + $ref: '#/components/schemas/Pet' + type: array + application/xml: + schema: + items: + $ref: '#/components/schemas/Pet' + type: array + description: successful operation + "400": + description: Invalid status value + security: + - petstore_auth: + - write:pets + - read:pets + summary: Finds Pets by status + tags: + - pet + /pet/findByTags: + get: + deprecated: true + description: Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. + operationId: findPetsByTags + parameters: + - description: Tags to filter by + in: query + name: tags + required: true + schema: + items: + type: string + type: array + responses: + "200": + content: + application/json: + schema: + items: + $ref: '#/components/schemas/Pet' + type: array + application/xml: + schema: + items: + $ref: '#/components/schemas/Pet' + type: array + description: successful operation + "400": + description: Invalid tag value + security: + - petstore_auth: + - write:pets + - read:pets + summary: Finds Pets by tags + tags: + - pet + /store/inventory: + get: + description: Returns a map of status codes to quantities + operationId: getInventory + responses: + "200": + content: + application/json: + schema: + additionalProperties: + format: int32 + type: integer + type: object + description: successful operation + security: + - api_key: [] + summary: Returns pet inventories by status + tags: + - store + /store/order: + post: + operationId: placeOrder + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + description: order placed for purchasing the pet + required: true + x-originalParamName: body + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + application/xml: + schema: + $ref: '#/components/schemas/Order' + description: successful operation + "400": + description: Invalid Order + summary: Place an order for a pet + tags: + - store + /store/order/{orderId}: + delete: + description: For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors + operationId: deleteOrder + parameters: + - description: ID of the order that needs to be deleted + in: path + name: orderId + required: true + schema: + format: int64 + minimum: 1 + type: integer + responses: + "400": + description: Invalid ID supplied + "404": + description: Order not found + summary: Delete purchase order by ID + tags: + - store + get: + description: For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions + operationId: getOrderById + parameters: + - description: ID of pet that needs to be fetched + in: path + name: orderId + required: true + schema: + format: int64 + maximum: 10 + minimum: 1 + type: integer + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + application/xml: + schema: + $ref: '#/components/schemas/Order' + description: successful operation + "400": + description: Invalid ID supplied + "404": + description: Order not found + summary: Find purchase order by ID + tags: + - store + /user: + post: + description: This can only be done by the logged in user. + operationId: createUser + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + description: Created user object + required: true + x-originalParamName: body + responses: + default: + description: successful operation + summary: Create user + tags: + - user + /user/{username}: + delete: + description: This can only be done by the logged in user. + operationId: deleteUser + parameters: + - description: The name that needs to be deleted + in: path + name: username + required: true + schema: + type: string + responses: + "400": + description: Invalid username supplied + "404": + description: User not found + summary: Delete user + tags: + - user + get: + operationId: getUserByName + parameters: + - description: 'The name that needs to be fetched. Use user1 for testing. ' + in: path + name: username + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + description: successful operation + "400": + description: Invalid username supplied + "404": + description: User not found + summary: Get user by user name + tags: + - user + put: + description: This can only be done by the logged in user. + operationId: updateUser + parameters: + - description: name that need to be updated + in: path + name: username + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + description: Updated user object + required: true + x-originalParamName: body + responses: + "400": + description: Invalid user supplied + "404": + description: User not found + summary: Updated user + tags: + - user + /user/createWithArray: + post: + operationId: createUsersWithArrayInput + requestBody: + content: + application/json: + schema: + items: + $ref: '#/components/schemas/User' + type: array + description: List of user object + required: true + x-originalParamName: body + responses: + default: + description: successful operation + summary: Creates list of users with given input array + tags: + - user + /user/createWithList: + post: + operationId: createUsersWithListInput + requestBody: + content: + application/json: + schema: + items: + $ref: '#/components/schemas/User' + type: array + description: List of user object + required: true + x-originalParamName: body + responses: + default: + description: successful operation + summary: Creates list of users with given input array + tags: + - user + /user/login: + get: + operationId: loginUser + parameters: + - description: The user name for login + in: query + name: username + required: true + schema: + type: string + - description: The password for login in clear text + in: query + name: password + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + type: string + application/xml: + schema: + type: string + description: successful operation + headers: + X-Expires-After: + description: date in UTC when token expires + schema: + format: date-time + type: string + X-Rate-Limit: + description: calls per hour allowed by the user + schema: + format: int32 + type: integer + "400": + description: Invalid username/password supplied + summary: Logs user into the system + tags: + - user + /user/logout: + get: + operationId: logoutUser + responses: + default: + description: successful operation + summary: Logs out current logged in user session + tags: + - user +servers: + - url: https://petstore.swagger.io/v2 + - url: http://petstore.swagger.io/v2 +tags: + - description: Everything about your Pets + externalDocs: + description: Find out more + url: http://swagger.io + name: pet + - description: Access to Petstore orders + name: store + - description: Operations about user + externalDocs: + description: Find out more about our store + url: http://swagger.io + name: user diff --git a/integration/resources/swagger.json b/integration/resources/swagger.json new file mode 100644 index 00000000..817ecb02 --- /dev/null +++ b/integration/resources/swagger.json @@ -0,0 +1,1054 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.7", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "https", + "http" + ], + "paths": { + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "get": { + "tags": [ + "pet" + ], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Pet" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [] + } + ] + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10, + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + }, + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + } + }, + "schema": { + "type": "string" + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + }, + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "https://petstore.swagger.io/oauth/authorize", + "flow": "implicit", + "scopes": { + "read:pets": "read your pets", + "write:pets": "modify pets in your account" + } + } + }, + "definitions": { + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "wrapped": true + }, + "items": { + "type": "string", + "xml": { + "name": "photoUrl" + } + } + }, + "tags": { + "type": "array", + "xml": { + "wrapped": true + }, + "items": { + "xml": { + "name": "tag" + }, + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean" + } + }, + "xml": { + "name": "Order" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/integration/resources/swagger.yaml b/integration/resources/swagger.yaml new file mode 100644 index 00000000..5366307c --- /dev/null +++ b/integration/resources/swagger.yaml @@ -0,0 +1,717 @@ +--- +swagger: '2.0' +info: + description: 'This is a sample server Petstore server. You can find out more about + Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For + this sample, you can use the api key `special-key` to test the authorization filters.' + version: 1.0.7 + title: Swagger Petstore + termsOfService: http://swagger.io/terms/ + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html +host: petstore.swagger.io +basePath: "/v2" +tags: +- name: pet + description: Everything about your Pets + externalDocs: + description: Find out more + url: http://swagger.io +- name: store + description: Access to Petstore orders +- name: user + description: Operations about user + externalDocs: + description: Find out more about our store + url: http://swagger.io +schemes: +- https +- http +paths: + "/pet/{petId}/uploadImage": + post: + tags: + - pet + summary: uploads an image + description: '' + operationId: uploadFile + consumes: + - multipart/form-data + produces: + - application/json + parameters: + - name: petId + in: path + description: ID of pet to update + required: true + type: integer + format: int64 + - name: additionalMetadata + in: formData + description: Additional data to pass to server + required: false + type: string + - name: file + in: formData + description: file to upload + required: false + type: file + responses: + '200': + description: successful operation + schema: + "$ref": "#/definitions/ApiResponse" + security: + - petstore_auth: + - write:pets + - read:pets + "/pet": + post: + tags: + - pet + summary: Add a new pet to the store + description: '' + operationId: addPet + consumes: + - application/json + - application/xml + produces: + - application/json + - application/xml + parameters: + - in: body + name: body + description: Pet object that needs to be added to the store + required: true + schema: + "$ref": "#/definitions/Pet" + responses: + '405': + description: Invalid input + security: + - petstore_auth: + - write:pets + - read:pets + put: + tags: + - pet + summary: Update an existing pet + description: '' + operationId: updatePet + consumes: + - application/json + - application/xml + produces: + - application/json + - application/xml + parameters: + - in: body + name: body + description: Pet object that needs to be added to the store + required: true + schema: + "$ref": "#/definitions/Pet" + responses: + '400': + description: Invalid ID supplied + '404': + description: Pet not found + '405': + description: Validation exception + security: + - petstore_auth: + - write:pets + - read:pets + "/pet/findByStatus": + get: + tags: + - pet + summary: Finds Pets by status + description: Multiple status values can be provided with comma separated strings + operationId: findPetsByStatus + produces: + - application/json + - application/xml + parameters: + - name: status + in: query + description: Status values that need to be considered for filter + required: true + type: array + items: + type: string + enum: + - available + - pending + - sold + default: available + collectionFormat: multi + responses: + '200': + description: successful operation + schema: + type: array + items: + "$ref": "#/definitions/Pet" + '400': + description: Invalid status value + security: + - petstore_auth: + - write:pets + - read:pets + "/pet/findByTags": + get: + tags: + - pet + summary: Finds Pets by tags + description: Multiple tags can be provided with comma separated strings. Use + tag1, tag2, tag3 for testing. + operationId: findPetsByTags + produces: + - application/json + - application/xml + parameters: + - name: tags + in: query + description: Tags to filter by + required: true + type: array + items: + type: string + collectionFormat: multi + responses: + '200': + description: successful operation + schema: + type: array + items: + "$ref": "#/definitions/Pet" + '400': + description: Invalid tag value + security: + - petstore_auth: + - write:pets + - read:pets + deprecated: true + "/pet/{petId}": + get: + tags: + - pet + summary: Find pet by ID + description: Returns a single pet + operationId: getPetById + produces: + - application/json + - application/xml + parameters: + - name: petId + in: path + description: ID of pet to return + required: true + type: integer + format: int64 + responses: + '200': + description: successful operation + schema: + "$ref": "#/definitions/Pet" + '400': + description: Invalid ID supplied + '404': + description: Pet not found + security: + - api_key: [] + post: + tags: + - pet + summary: Updates a pet in the store with form data + description: '' + operationId: updatePetWithForm + consumes: + - application/x-www-form-urlencoded + produces: + - application/json + - application/xml + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + type: integer + format: int64 + - name: name + in: formData + description: Updated name of the pet + required: false + type: string + - name: status + in: formData + description: Updated status of the pet + required: false + type: string + responses: + '405': + description: Invalid input + security: + - petstore_auth: + - write:pets + - read:pets + delete: + tags: + - pet + summary: Deletes a pet + description: '' + operationId: deletePet + produces: + - application/json + - application/xml + parameters: + - name: api_key + in: header + required: false + type: string + - name: petId + in: path + description: Pet id to delete + required: true + type: integer + format: int64 + responses: + '400': + description: Invalid ID supplied + '404': + description: Pet not found + security: + - petstore_auth: + - write:pets + - read:pets + "/store/inventory": + get: + tags: + - store + summary: Returns pet inventories by status + description: Returns a map of status codes to quantities + operationId: getInventory + produces: + - application/json + parameters: [] + responses: + '200': + description: successful operation + schema: + type: object + additionalProperties: + type: integer + format: int32 + security: + - api_key: [] + "/store/order": + post: + tags: + - store + summary: Place an order for a pet + description: '' + operationId: placeOrder + consumes: + - application/json + produces: + - application/json + - application/xml + parameters: + - in: body + name: body + description: order placed for purchasing the pet + required: true + schema: + "$ref": "#/definitions/Order" + responses: + '200': + description: successful operation + schema: + "$ref": "#/definitions/Order" + '400': + description: Invalid Order + "/store/order/{orderId}": + get: + tags: + - store + summary: Find purchase order by ID + description: For valid response try integer IDs with value >= 1 and <= 10. Other + values will generated exceptions + operationId: getOrderById + produces: + - application/json + - application/xml + parameters: + - name: orderId + in: path + description: ID of pet that needs to be fetched + required: true + type: integer + maximum: 10 + minimum: 1 + format: int64 + responses: + '200': + description: successful operation + schema: + "$ref": "#/definitions/Order" + '400': + description: Invalid ID supplied + '404': + description: Order not found + delete: + tags: + - store + summary: Delete purchase order by ID + description: For valid response try integer IDs with positive integer value. + Negative or non-integer values will generate API errors + operationId: deleteOrder + produces: + - application/json + - application/xml + parameters: + - name: orderId + in: path + description: ID of the order that needs to be deleted + required: true + type: integer + minimum: 1 + format: int64 + responses: + '400': + description: Invalid ID supplied + '404': + description: Order not found + "/user/createWithList": + post: + tags: + - user + summary: Creates list of users with given input array + description: '' + operationId: createUsersWithListInput + consumes: + - application/json + produces: + - application/json + - application/xml + parameters: + - in: body + name: body + description: List of user object + required: true + schema: + type: array + items: + "$ref": "#/definitions/User" + responses: + default: + description: successful operation + "/user/{username}": + get: + tags: + - user + summary: Get user by user name + description: '' + operationId: getUserByName + produces: + - application/json + - application/xml + parameters: + - name: username + in: path + description: 'The name that needs to be fetched. Use user1 for testing. ' + required: true + type: string + responses: + '200': + description: successful operation + schema: + "$ref": "#/definitions/User" + '400': + description: Invalid username supplied + '404': + description: User not found + put: + tags: + - user + summary: Updated user + description: This can only be done by the logged in user. + operationId: updateUser + consumes: + - application/json + produces: + - application/json + - application/xml + parameters: + - name: username + in: path + description: name that need to be updated + required: true + type: string + - in: body + name: body + description: Updated user object + required: true + schema: + "$ref": "#/definitions/User" + responses: + '400': + description: Invalid user supplied + '404': + description: User not found + delete: + tags: + - user + summary: Delete user + description: This can only be done by the logged in user. + operationId: deleteUser + produces: + - application/json + - application/xml + parameters: + - name: username + in: path + description: The name that needs to be deleted + required: true + type: string + responses: + '400': + description: Invalid username supplied + '404': + description: User not found + "/user/login": + get: + tags: + - user + summary: Logs user into the system + description: '' + operationId: loginUser + produces: + - application/json + - application/xml + parameters: + - name: username + in: query + description: The user name for login + required: true + type: string + - name: password + in: query + description: The password for login in clear text + required: true + type: string + responses: + '200': + description: successful operation + headers: + X-Expires-After: + type: string + format: date-time + description: date in UTC when token expires + X-Rate-Limit: + type: integer + format: int32 + description: calls per hour allowed by the user + schema: + type: string + '400': + description: Invalid username/password supplied + "/user/logout": + get: + tags: + - user + summary: Logs out current logged in user session + description: '' + operationId: logoutUser + produces: + - application/json + - application/xml + parameters: [] + responses: + default: + description: successful operation + "/user/createWithArray": + post: + tags: + - user + summary: Creates list of users with given input array + description: '' + operationId: createUsersWithArrayInput + consumes: + - application/json + produces: + - application/json + - application/xml + parameters: + - in: body + name: body + description: List of user object + required: true + schema: + type: array + items: + "$ref": "#/definitions/User" + responses: + default: + description: successful operation + "/user": + post: + tags: + - user + summary: Create user + description: This can only be done by the logged in user. + operationId: createUser + consumes: + - application/json + produces: + - application/json + - application/xml + parameters: + - in: body + name: body + description: Created user object + required: true + schema: + "$ref": "#/definitions/User" + responses: + default: + description: successful operation +securityDefinitions: + api_key: + type: apiKey + name: api_key + in: header + petstore_auth: + type: oauth2 + authorizationUrl: https://petstore.swagger.io/oauth/authorize + flow: implicit + scopes: + read:pets: read your pets + write:pets: modify pets in your account +definitions: + ApiResponse: + type: object + properties: + code: + type: integer + format: int32 + type: + type: string + message: + type: string + Category: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Category + Pet: + type: object + required: + - name + - photoUrls + properties: + id: + type: integer + format: int64 + category: + "$ref": "#/definitions/Category" + name: + type: string + example: doggie + photoUrls: + type: array + xml: + wrapped: true + items: + type: string + xml: + name: photoUrl + tags: + type: array + xml: + wrapped: true + items: + xml: + name: tag + "$ref": "#/definitions/Tag" + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: Pet + Tag: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Tag + Order: + type: object + properties: + id: + type: integer + format: int64 + petId: + type: integer + format: int64 + quantity: + type: integer + format: int32 + shipDate: + type: string + format: date-time + status: + type: string + description: Order Status + enum: + - placed + - approved + - delivered + complete: + type: boolean + xml: + name: Order + User: + type: object + properties: + id: + type: integer + format: int64 + username: + type: string + firstName: + type: string + lastName: + type: string + email: + type: string + password: + type: string + phone: + type: string + userStatus: + type: integer + format: int32 + description: User Status + xml: + name: User +externalDocs: + description: Find out more about Swagger + url: http://swagger.io diff --git a/internal/transform/convertSwagger.go b/internal/transform/convertSwagger.go new file mode 100644 index 00000000..da71e33c --- /dev/null +++ b/internal/transform/convertSwagger.go @@ -0,0 +1,52 @@ +package transform + +import ( + "context" + "encoding/json" + "io" + "os" + "strings" + + "github.com/getkin/kin-openapi/openapi2" + "github.com/getkin/kin-openapi/openapi2conv" + "github.com/invopop/yaml" +) + +func ConvertSwagger(ctx context.Context, schemaPath string, yamlOut bool, w io.Writer) error { + schemaBytes, err := os.ReadFile(schemaPath) + if err != nil { + return err + } + + var swaggerDoc openapi2.T + if strings.Contains(schemaPath, ".json") { + err = json.Unmarshal(schemaBytes, &swaggerDoc) + } else { + err = yaml.Unmarshal(schemaBytes, &swaggerDoc) + } + if err != nil { + return err + } + + openAPIV3Spec, err := openapi2conv.ToV3(&swaggerDoc) + if err != nil { + return err + } + + var outBytes []byte + if yamlOut { + outBytes, err = yaml.Marshal(openAPIV3Spec) + } else { + outBytes, err = json.Marshal(openAPIV3Spec) + } + if err != nil { + return err + } + + _, err = w.Write(outBytes) + if err != nil { + return err + } + + return nil +} diff --git a/internal/transform/convertSwagger_test.go b/internal/transform/convertSwagger_test.go new file mode 100644 index 00000000..79de00c8 --- /dev/null +++ b/internal/transform/convertSwagger_test.go @@ -0,0 +1,59 @@ +package transform + +import ( + "bytes" + "context" + "os" + "testing" + + "github.com/pb33f/libopenapi" + "github.com/stretchr/testify/require" +) + +func TestConvertSwaggerYAML(t *testing.T) { + // Create a buffer to store the filtered spec + var buf bytes.Buffer + + // Call ConvertSwagger + err := ConvertSwagger(context.Background(), "../../integration/resources/swagger.yaml", true, &buf) + require.NoError(t, err) + + // Parse the converted spec + filteredDoc, err := libopenapi.NewDocument(buf.Bytes()) + require.NoError(t, err) + + // Validate the OpenAPI v3 model + model, errors := filteredDoc.BuildV3Model() + require.Empty(t, errors) + + // Validate that the model exists + require.NotNil(t, model) + + converted, err := os.ReadFile("../../integration/resources/converted.yaml") + require.NoError(t, err) + + // Normalize and compare as bytes + normalizedConverted := bytes.ReplaceAll(converted, []byte("\r\n"), []byte("\n")) + normalizedBuf := bytes.ReplaceAll(buf.Bytes(), []byte("\r\n"), []byte("\n")) + require.Equal(t, normalizedConverted, normalizedBuf) +} + +func TestConvertSwaggerJSON(t *testing.T) { + // Create a buffer to store the filtered spec + var buf bytes.Buffer + + // Call ConvertSwagger + err := ConvertSwagger(context.Background(), "../../integration/resources/swagger.json", false, &buf) + require.NoError(t, err) + + // Parse the converted spec + filteredDoc, err := libopenapi.NewDocument(buf.Bytes()) + require.NoError(t, err) + + // Validate the OpenAPI v3 model + model, errors := filteredDoc.BuildV3Model() + require.Empty(t, errors) + + // Validate that the model exists + require.NotNil(t, model) +}