Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

openapi v3 formatter #48

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ To create the Swagger files use the rake task:
bundle exec rake swagger
```

For OpenAPI use:

```
bundle exec rake openapi
```


Now you can use Swagger UI or the renderer of your choice to display the
formatted documentation. [swagger_engine](https://github.com/batdevis/swagger_engine)
works pretty well and supports multiple documents.
Expand Down
6 changes: 6 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Example of standalone swagger UI with assets loaded from CDN

How to run:

1. `cd examples && ruby -run -e httpd . -p 4000`
2. Visit http://localhost:4000/``
22 changes: 22 additions & 0 deletions examples/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
Example API
</title>
<link href="https://unpkg.com/swagger-ui-dist@3/swagger-ui.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
<script src="https://unpkg.com/swagger-ui-dist@3/swagger-ui-standalone-preset.js"></script>
<script>
SwaggerUIBundle({
url: './swagger.json',
dom_id: '#swagger-ui',
deepLinking: true
});
</script>
</body>
</html>
106 changes: 106 additions & 0 deletions examples/swagger.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
{
"swagger": "2.0",
"info": {
"title": "API V1",
"version": "v1"
},
"paths": {
"/posts": {
"get": {
"responses": {
"200": {
"description": "successful"
}
},
"tags": [
"context_tag",
"path_tag",
"operation_tag"
],
"summary": "fetch list",
"produces": [
"application/json"
]
},
"post": {
"responses": {
"201": {
"description": "successfully created",
"content": {
"application/json": {
"schema": {
"example": {
"id": 1,
"title": "asdf",
"body": "blah",
"created_at": "2019-05-27T09:54:55.606Z",
"updated_at": "2019-05-27T09:54:55.606Z"
}
}
}
}
}
},
"requestBody": {
"required": null,
"content": {
"application/json": {
"schema": {
"foo": "bar"
},
"examples": {
}
}
}
},
"parameters": [

],
"tags": [
"context_tag",
"path_tag"
],
"summary": "create",
"produces": [
"application/json"
]
}
},
"/posts/{post_id}": {
"parameters": [
{
"in": "path",
"name": "post_id",
"required": true
}
],
"get": {
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"example": {
"id": 1,
"title": null,
"body": null,
"created_at": "2019-05-27T09:54:55.611Z",
"updated_at": "2019-05-27T09:54:55.611Z"
}
}
}
},
"description": "success"
}
},
"tags": [
"context_tag"
],
"summary": "fetch item",
"produces": [
"application/json"
]
}
}
}
}
1 change: 1 addition & 0 deletions lib/rspec/rails/swagger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require 'rspec/rails/swagger/configuration'
require 'rspec/rails/swagger/document'
require 'rspec/rails/swagger/formatter'
require 'rspec/rails/swagger/formatter_open_api'
require 'rspec/rails/swagger/helpers'
require 'rspec/rails/swagger/response_formatters'
require 'rspec/rails/swagger/request_builder'
Expand Down
90 changes: 90 additions & 0 deletions lib/rspec/rails/swagger/formatter_open_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
RSpec::Support.require_rspec_core "formatters/base_text_formatter"
RSpec::Support.require_rspec_core "formatters/console_codes"

require_relative 'formatter'

module RSpec
module Rails
module Swagger
class FormatterOpenApi < RSpec::Rails::Swagger::Formatter
RSpec::Core::Formatters.register self, :example_group_started,
:example_passed, :example_pending, :example_failed, :example_finished,
:close

def response_for(operation, swagger_response)
status = swagger_response[:status_code]

content_type = operation[:consumes] && operation[:consumes][0] || 'application/json'
operation.delete(:consumes)

operation[:responses][status] ||= {}
operation[:responses][status].tap do |response|
prepare_response_contents(response, swagger_response, content_type)
end
end

def prepare_response_contents(response, swagger_response, content_type)
if swagger_response[:examples]
schema = swagger_response[:schema] || {}
response[:content] ||= {}
swagger_response[:examples].each_pair do |format, resp|
formatted = ResponseFormatters[format].call(resp)
response[:content][format] ||= {schema: schema.merge(example: formatted)}
end
elsif swagger_response[:schema]
response[:content] = {content_type => {schema: swagger_response[:schema]}}
end

response.merge!(swagger_response.slice(:description, :headers))
end

def path_item_for(document, swagger_path_item)
name = swagger_path_item[:path]

document[:paths] ||= {}
document[:paths][name] ||= {}
if swagger_path_item[:parameters]
apply_params(document[:paths][name], swagger_path_item[:parameters].dup)
end
document[:paths][name]
end

def operation_for(path, swagger_operation)
method = swagger_operation[:method]

path[method] ||= {responses: {}}
path[method].tap do |operation|
if swagger_operation[:parameters]
apply_params(operation, swagger_operation[:parameters].dup)
end
operation.merge!(swagger_operation.slice(
:tags, :summary, :description, :externalDocs, :operationId,
:consumes, :produces, :schemes, :deprecated, :security
))
end
end

def apply_params(object, parameters)
body = parameters.delete('body&body')
if body
object[:requestBody] = {
required: body[:required],
content: {
'application/json' => {
schema: body[:schema],
examples: body[:examples] || {}
}
}
}
end

object[:parameters] = parameters.values.map do |param|
param.slice(:in, :name, :required, :schema, :description, :style,
:explode, :allowEmptyValue, :example, :examples, :deprecated)
end
end

end
end
end
end
5 changes: 5 additions & 0 deletions lib/rspec/rails/swagger/tasks/swagger.rake
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@ RSpec::Core::RakeTask.new(:swagger) do |t|
t.verbose = false
t.rspec_opts = "-f RSpec::Rails::Swagger::Formatter --order defined -t swagger_object"
end

RSpec::Core::RakeTask.new(:openapi) do |t|
t.verbose = false
t.rspec_opts = "-f RSpec::Rails::Swagger::FormatterOpenApi --order defined -t swagger_object"
end
1 change: 1 addition & 0 deletions scripts/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ set -x -e
bundle exec rspec
# Duplicating the body of the rake task. Need to figure out how to call it directly.
bundle exec rspec -f RSpec::Rails::Swagger::Formatter --order defined -t swagger_object
bundle exec rspec -f RSpec::Rails::Swagger::FormatterOpenApi --order defined -t swagger_object
Loading