openapi-rails 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +99 -0
- data/LICENSE.md +21 -0
- data/README.md +61 -0
- data/Rakefile +1 -0
- data/app/assets/fonts/openapi/DroidSans-Bold.ttf +0 -0
- data/app/assets/fonts/openapi/DroidSans.ttf +0 -0
- data/app/assets/images/openapi/collapse.gif +0 -0
- data/app/assets/images/openapi/expand.gif +0 -0
- data/app/assets/images/openapi/explorer_icons.png +0 -0
- data/app/assets/images/openapi/favicon-16x16.png +0 -0
- data/app/assets/images/openapi/favicon-32x32.png +0 -0
- data/app/assets/images/openapi/favicon.ico +0 -0
- data/app/assets/images/openapi/logo_small.png +0 -0
- data/app/assets/images/openapi/pet_store_api.png +0 -0
- data/app/assets/images/openapi/throbber.gif +0 -0
- data/app/assets/images/openapi/wordnik_api.png +0 -0
- data/app/assets/javascripts/openapi/application.coffee +58 -0
- data/app/assets/javascripts/openapi/lib/backbone-min.js +15 -0
- data/app/assets/javascripts/openapi/lib/es5-shim.js +2065 -0
- data/app/assets/javascripts/openapi/lib/handlebars-4.0.5.js +4608 -0
- data/app/assets/javascripts/openapi/lib/highlight.9.1.0.pack.js +2 -0
- data/app/assets/javascripts/openapi/lib/highlight.9.1.0.pack_extended.js +34 -0
- data/app/assets/javascripts/openapi/lib/jquery-1.8.0.min.js +2 -0
- data/app/assets/javascripts/openapi/lib/jquery.ba-bbq.min.js +18 -0
- data/app/assets/javascripts/openapi/lib/jquery.slideto.min.js +1 -0
- data/app/assets/javascripts/openapi/lib/jquery.wiggle.min.js +8 -0
- data/app/assets/javascripts/openapi/lib/js-yaml.min.js +3 -0
- data/app/assets/javascripts/openapi/lib/jsoneditor.min.js +11 -0
- data/app/assets/javascripts/openapi/lib/lodash.min.js +102 -0
- data/app/assets/javascripts/openapi/lib/marked.js +1272 -0
- data/app/assets/javascripts/openapi/lib/object-assign-pollyfill.js +23 -0
- data/app/assets/javascripts/openapi/lib/swagger-oauth.js +347 -0
- data/app/assets/javascripts/openapi/swagger-ui.js +24758 -0
- data/app/assets/stylesheets/openapi/application.scss +4 -0
- data/app/assets/stylesheets/openapi/print.scss +1367 -0
- data/app/assets/stylesheets/openapi/reset.scss +125 -0
- data/app/assets/stylesheets/openapi/screen.scss +1497 -0
- data/app/assets/stylesheets/openapi/typography.scss +14 -0
- data/app/controllers/openapi_controller.rb +24 -0
- data/app/views/openapi/index.html.erb +56 -0
- data/lib/generators/openapi/config_generator.rb +20 -0
- data/lib/generators/openapi/templates/base_controller.rb +6 -0
- data/lib/generators/openapi/templates/openapi.rb +21 -0
- data/lib/openapi-rails.rb +1 -0
- data/lib/openapi.rb +26 -0
- data/lib/openapi/configuration.rb +17 -0
- data/lib/openapi/engine.rb +32 -0
- data/lib/openapi/mongoid/crud_actions.rb +235 -0
- data/lib/openapi/mongoid/spec_builder.rb +451 -0
- data/lib/openapi/routes_parser.rb +50 -0
- data/lib/openapi/version.rb +3 -0
- data/lib/rails/routes.rb +20 -0
- data/lib/renderers/csv.rb +36 -0
- data/lib/swagger/blocks/items_node.rb +7 -0
- data/lib/swagger/blocks/property_node.rb +7 -0
- data/lib/swagger/blocks/schema_builder.rb +89 -0
- data/lib/swagger/blocks/schema_node.rb +7 -0
- data/openapi-rails.gemspec +35 -0
- metadata +204 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* Google Font's Droid Sans */
|
|
2
|
+
@font-face {
|
|
3
|
+
font-family: 'Droid Sans';
|
|
4
|
+
font-style: normal;
|
|
5
|
+
font-weight: 400;
|
|
6
|
+
src: local('Droid Sans'), local('DroidSans'), font-url('openapi/DroidSans.ttf'), format('truetype');
|
|
7
|
+
}
|
|
8
|
+
/* Google Font's Droid Sans Bold */
|
|
9
|
+
@font-face {
|
|
10
|
+
font-family: 'Droid Sans';
|
|
11
|
+
font-style: normal;
|
|
12
|
+
font-weight: 700;
|
|
13
|
+
src: local('Droid Sans Bold'), local('DroidSans-Bold'), font-url('openapi/DroidSans-Bold.ttf'), format('truetype');
|
|
14
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
class OpenapiController < ActionController::Base
|
|
2
|
+
respond_to :json
|
|
3
|
+
|
|
4
|
+
def index
|
|
5
|
+
@specs = Openapi.apis.map do |name, config|
|
|
6
|
+
title = config[:title]
|
|
7
|
+
spec_path = "#{config[:base_path]}/spec"
|
|
8
|
+
[title, spec_path]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
@default_specification_path = @specs.first ? @specs.first[1] : ''
|
|
12
|
+
|
|
13
|
+
render 'index', layout: false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def spec
|
|
17
|
+
name = params[:name] || :default
|
|
18
|
+
config = Openapi.apis[name]
|
|
19
|
+
controllers = config[:controllers]
|
|
20
|
+
json_schema = Swagger::Blocks.build_root_json(controllers)
|
|
21
|
+
|
|
22
|
+
render json: json_schema
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset='UTF-8'>
|
|
5
|
+
<title>OpenAPI</title>
|
|
6
|
+
|
|
7
|
+
<%= favicon_link_tag 'openapi/favicon-32x32.png', rel: 'icon',
|
|
8
|
+
type: 'image/png',
|
|
9
|
+
sizes: '32x32' %>
|
|
10
|
+
|
|
11
|
+
<%= favicon_link_tag 'openapi/favicon-16x16.png', rel: 'icon',
|
|
12
|
+
type: 'image/png',
|
|
13
|
+
sizes: '16x16' %>
|
|
14
|
+
|
|
15
|
+
<%= stylesheet_link_tag 'openapi/application' %>
|
|
16
|
+
<%= stylesheet_link_tag 'openapi/reset', media: 'print' %>
|
|
17
|
+
<%= stylesheet_link_tag 'openapi/print', media: 'print' %>
|
|
18
|
+
<%= javascript_include_tag 'openapi/application' %>
|
|
19
|
+
|
|
20
|
+
<%= csrf_meta_tags %>
|
|
21
|
+
</head>
|
|
22
|
+
|
|
23
|
+
<body class='swagger-section'>
|
|
24
|
+
<div id='header'>
|
|
25
|
+
<div class='swagger-ui-wrap'>
|
|
26
|
+
<%= link_to openapi_path, id: 'logo' do %>
|
|
27
|
+
<%= image_tag 'openapi/logo_small.png', alt: 'Swagger',
|
|
28
|
+
class: 'logo__img',
|
|
29
|
+
height: 30,
|
|
30
|
+
width: 30 %>
|
|
31
|
+
<span class='logo__title'>OpenAPI</span></a>
|
|
32
|
+
<% end %>
|
|
33
|
+
|
|
34
|
+
<form id='api_selector'>
|
|
35
|
+
<input id='input_baseUrl'
|
|
36
|
+
name='baseUrl'
|
|
37
|
+
type='hidden'
|
|
38
|
+
value='<%= @default_specification_path %>' />
|
|
39
|
+
|
|
40
|
+
<% if @specs.size > 1 %>
|
|
41
|
+
<div class='input'>
|
|
42
|
+
<select id='input_spec'>
|
|
43
|
+
<% @specs.each do |s| %>
|
|
44
|
+
<option value='<%= s[1] %>'><%= s[0]%></option>
|
|
45
|
+
<% end %>
|
|
46
|
+
</select>
|
|
47
|
+
</div>
|
|
48
|
+
<% end %>
|
|
49
|
+
</form>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div id='message-bar' class='swagger-ui-wrap' data-sw-translate> </div>
|
|
54
|
+
<div id='swagger-ui-container' class='swagger-ui-wrap'></div>
|
|
55
|
+
</body>
|
|
56
|
+
</html>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Openapi
|
|
3
|
+
class ConfigGenerator < Rails::Generators::Base
|
|
4
|
+
desc 'Creates OpenAPI initialization file and Api::BaseController'
|
|
5
|
+
|
|
6
|
+
def self.source_root
|
|
7
|
+
File.expand_path('../templates', __FILE__)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def create_config_file
|
|
11
|
+
dest = File.join('config/initializers', 'openapi.rb')
|
|
12
|
+
template 'openapi.rb', dest
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def create_base_controller_file
|
|
16
|
+
dest = File.join('app/controllers/api', 'base_controller.rb')
|
|
17
|
+
template 'base_controller.rb', dest
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Openapi.configure do |config|
|
|
2
|
+
config.apis = {
|
|
3
|
+
default: {
|
|
4
|
+
title: 'Default',
|
|
5
|
+
description: '',
|
|
6
|
+
version: '1.0',
|
|
7
|
+
base_path: '/api',
|
|
8
|
+
controllers: []
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module BSON
|
|
14
|
+
class ObjectId
|
|
15
|
+
def as_json(*args)
|
|
16
|
+
to_s
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
alias :to_json :as_json
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'openapi'
|
data/lib/openapi.rb
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'bson'
|
|
2
|
+
require 'yajl'
|
|
3
|
+
require 'yajl/json_gem'
|
|
4
|
+
require 'swagger/blocks'
|
|
5
|
+
require 'rails/routes'
|
|
6
|
+
|
|
7
|
+
require 'has_scope'
|
|
8
|
+
require 'responders'
|
|
9
|
+
require 'renderers/csv'
|
|
10
|
+
require 'kaminari'
|
|
11
|
+
require 'openapi/mongoid/crud_actions'
|
|
12
|
+
|
|
13
|
+
require 'swagger/blocks/schema_builder'
|
|
14
|
+
require 'swagger/blocks/schema_node'
|
|
15
|
+
require 'swagger/blocks/property_node'
|
|
16
|
+
require 'swagger/blocks/items_node'
|
|
17
|
+
require 'openapi/mongoid/spec_builder'
|
|
18
|
+
|
|
19
|
+
require 'openapi/configuration'
|
|
20
|
+
require 'openapi/routes_parser'
|
|
21
|
+
require 'openapi/engine'
|
|
22
|
+
require 'openapi/version'
|
|
23
|
+
|
|
24
|
+
module Openapi
|
|
25
|
+
extend Configuration
|
|
26
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Openapi
|
|
2
|
+
class Engine < ::Rails::Engine
|
|
3
|
+
initializer 'openapi.assets.precompile', group: :all do |app|
|
|
4
|
+
app.config.assets.precompile += %w(openapi/print.css
|
|
5
|
+
openapi/reset.css
|
|
6
|
+
openapi/logo_small.png
|
|
7
|
+
openapi/favicon-32x32.png
|
|
8
|
+
openapi/favicon-16x16.png)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class Railtie < Rails::Railtie
|
|
13
|
+
initializer 'openapi.builders', after: :load_config_initializers do
|
|
14
|
+
Rails.application.reload_routes!
|
|
15
|
+
|
|
16
|
+
Openapi.apis.each do |name, config|
|
|
17
|
+
base_path = config[:base_path] || '/api'
|
|
18
|
+
|
|
19
|
+
config[:controllers].each do |controller|
|
|
20
|
+
controller.build_openapi_specification(base_path: base_path)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
name = name.to_s.titleize.remove(' ')
|
|
24
|
+
root_klass_name = "#{name}SwaggerRootController"
|
|
25
|
+
klass = Object.const_set root_klass_name, Class.new(SwaggerRoot)
|
|
26
|
+
klass.build_specification(config, config[:controllers])
|
|
27
|
+
|
|
28
|
+
config[:controllers].push klass
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
module Openapi
|
|
2
|
+
module Mongoid
|
|
3
|
+
module CrudActions
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
respond_to :json
|
|
8
|
+
respond_to :csv, only: %w(index)
|
|
9
|
+
|
|
10
|
+
class_attribute :resource_class
|
|
11
|
+
class_attribute :per_page
|
|
12
|
+
|
|
13
|
+
## Actions
|
|
14
|
+
|
|
15
|
+
def index
|
|
16
|
+
@chain = default_scope
|
|
17
|
+
|
|
18
|
+
apply_scopes_to_chain!
|
|
19
|
+
search_filter_chain!
|
|
20
|
+
paginate_chain!
|
|
21
|
+
set_index_headers!
|
|
22
|
+
|
|
23
|
+
respond_to do |format|
|
|
24
|
+
format.json { render json: @chain.as_json(json_config) }
|
|
25
|
+
format.csv { render csv: @chain }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def show
|
|
30
|
+
@object = find_object
|
|
31
|
+
set_object_version!
|
|
32
|
+
render json: @object.as_json(json_config)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def create
|
|
36
|
+
@object = build_object
|
|
37
|
+
|
|
38
|
+
if @object.save
|
|
39
|
+
render json: @object.as_json(json_config), status: :created
|
|
40
|
+
|
|
41
|
+
else
|
|
42
|
+
log_errors @object.errors
|
|
43
|
+
render json: @object.errors, status: :unprocessable_entity
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def update
|
|
49
|
+
@object = find_object
|
|
50
|
+
if @object.update_attributes(resource_params)
|
|
51
|
+
render json: @object.as_json(json_config)
|
|
52
|
+
|
|
53
|
+
else
|
|
54
|
+
log_errors @object.errors
|
|
55
|
+
render json: @object.errors, status: :unprocessable_entity
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def destroy
|
|
61
|
+
@object = find_object
|
|
62
|
+
|
|
63
|
+
if @object.destroy
|
|
64
|
+
render nothing: true, status: :no_content
|
|
65
|
+
|
|
66
|
+
else
|
|
67
|
+
log_errors @object.errors
|
|
68
|
+
render json: @object.errors, status: :unprocessable_entity
|
|
69
|
+
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def response_config
|
|
74
|
+
config = {}
|
|
75
|
+
fields = params[:fields]
|
|
76
|
+
methods = params[:methods]
|
|
77
|
+
|
|
78
|
+
if fields
|
|
79
|
+
only_fields = fields.split(',').select do |field_name|
|
|
80
|
+
resource_class.fields.has_key?(field_name)
|
|
81
|
+
end
|
|
82
|
+
config[:only] = only_fields unless only_fields.empty?
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
if methods
|
|
86
|
+
include_methods = methods.split(',').select do |method_name|
|
|
87
|
+
method = method_name.to_sym
|
|
88
|
+
resource_class.instance_methods(false).include?(method)
|
|
89
|
+
end
|
|
90
|
+
config[:methods] = include_methods unless include_methods.empty?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
config
|
|
94
|
+
end
|
|
95
|
+
alias csv_config response_config
|
|
96
|
+
alias json_config response_config
|
|
97
|
+
|
|
98
|
+
## Helpers
|
|
99
|
+
|
|
100
|
+
def log_errors(errors)
|
|
101
|
+
if Rails.env.development?
|
|
102
|
+
logger.info "Errors:\n #{errors.to_h}"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def resource_class
|
|
107
|
+
@resource_class ||= self.class.resource_class
|
|
108
|
+
@resource_class ||= self.class.
|
|
109
|
+
to_s.
|
|
110
|
+
split('::').
|
|
111
|
+
last.
|
|
112
|
+
sub(/Controller$/, '').
|
|
113
|
+
singularize.
|
|
114
|
+
constantize
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def default_scope
|
|
118
|
+
resource_class
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def find_object
|
|
122
|
+
resource_class.find(params[:id])
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def build_object
|
|
126
|
+
resource_class.new(resource_params)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def support_version?
|
|
130
|
+
@object.respond_to?(:undo, true)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def set_object_version!
|
|
134
|
+
version = params[:version]
|
|
135
|
+
if version && support_version? && version.to_i != @object.version
|
|
136
|
+
@object.undo(nil, from: version.to_i + 1, to: @object.version)
|
|
137
|
+
@object.version = version
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def apply_scopes_to_chain!
|
|
142
|
+
@chain = apply_scopes(@chain)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def support_search?
|
|
146
|
+
@chain.respond_to?(:search, true)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def search_filter_chain!
|
|
150
|
+
query = params[:search]
|
|
151
|
+
if query && support_search?
|
|
152
|
+
normalized_query = query.to_s.downcase
|
|
153
|
+
@chain = @chain.search(normalized_query, match: :all)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def page
|
|
158
|
+
@page ||= params[:page]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def per_page
|
|
162
|
+
@per_page ||= (params[:perPage] || self.class.per_page || 50)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def paginate_chain!
|
|
166
|
+
@chain = begin
|
|
167
|
+
if page
|
|
168
|
+
@chain.page(page).per(per_page)
|
|
169
|
+
else
|
|
170
|
+
@chain.all
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def set_index_headers!
|
|
176
|
+
total_objects = page ? @chain.total_count : @chain.size
|
|
177
|
+
response.headers['X-Total-Count'] = total_objects
|
|
178
|
+
|
|
179
|
+
if page and !@chain.last_page?
|
|
180
|
+
url = request.url
|
|
181
|
+
url.gsub!("page=#{page}", "page=#{@chain.next_page}")
|
|
182
|
+
next_page = "<#{url}>; rel=\"next\""
|
|
183
|
+
response.headers['Link'] = next_page
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def resource_params
|
|
188
|
+
permitted_params
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# NOTE: here we permit all parameters for ease of development,
|
|
192
|
+
# before release this method should be overriden to allow only
|
|
193
|
+
# permitted parameters.
|
|
194
|
+
def permitted_params
|
|
195
|
+
logger.warn "#{self}: please override `permitted_params` method."
|
|
196
|
+
params.require(resource_request_name).permit!
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def resource_request_name
|
|
200
|
+
resource_class.
|
|
201
|
+
to_s.
|
|
202
|
+
underscore.
|
|
203
|
+
gsub('/', '_').
|
|
204
|
+
gsub('::', '_')
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
class_methods do
|
|
209
|
+
def index_json_config(hash)
|
|
210
|
+
self.index_json_config = hash
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def create_json_config(hash)
|
|
214
|
+
self.create_json_config = hash
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def show_json_config(hash)
|
|
218
|
+
self.show_json_config = hash
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def update_json_config(hash)
|
|
222
|
+
self.update_json_config = hash
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def resource_class(name)
|
|
226
|
+
self.resource_class = name
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def per_page(number)
|
|
230
|
+
self.per_page = number
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|