rails-swagger 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/rails/swagger/controller.rb +30 -0
- data/lib/rails/swagger/engine.rb +149 -0
- data/lib/rails/swagger/router.rb +178 -0
- data/lib/rails/swagger.rb +9 -0
- metadata +103 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0ca0079ec6e56d59984482c58ba8f88848f0c4cf
|
4
|
+
data.tar.gz: 74419e6fdc5516c8f86b52791c56c7acce3b0904
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9513137bdc27e6caed663bf6a6efe819ae089be425d9b9d1eecac25a1fc00b26f37d3d84bc70aec51fa4fea51fc536cd8c763765703b82307c68f9c9ebfe6951
|
7
|
+
data.tar.gz: f047a592844a8cf8b44fee3ee267ac09916674295b92de1325ca30382184e0d303c74d9bfc4c15582d9cf3f903c9b1d8e9e012e8d8c1ac05bc684ee9bed6c1af
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Rails
|
2
|
+
module Swagger
|
3
|
+
module Controller
|
4
|
+
|
5
|
+
# Injects swagger-related code into the controller when included
|
6
|
+
def self.included base
|
7
|
+
|
8
|
+
# Add controller hooks
|
9
|
+
base.class_eval do
|
10
|
+
before_action :validate_swagger_params
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the swagger spec definition for the endpoint serving
|
14
|
+
# the current request.
|
15
|
+
def swagger_endpoint
|
16
|
+
key = "#{params[:controller]}##{params[:action]}"
|
17
|
+
endpoint = rails_swagger_engine.endpoints[key]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Validates request parameters against the Swagger API spec
|
21
|
+
# associated with this controller.
|
22
|
+
def validate_swagger_params
|
23
|
+
#puts swagger_endpoint.inspect.white
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module Rails
|
2
|
+
module Swagger
|
3
|
+
|
4
|
+
# Defines the Swagger spec file formats that are parsable by this gem.
|
5
|
+
# Currently only the JSON format is supported.
|
6
|
+
ALLOWED_FORMATS = [".json"].freeze
|
7
|
+
|
8
|
+
# Defines a base class from which Swagger API engines can be created.
|
9
|
+
# Uses namespace isolation to ensure routes don't conflict with any
|
10
|
+
# pre-existing routes from the main rails application.
|
11
|
+
class Engine < Rails::Engine
|
12
|
+
isolate_namespace Rails::Swagger
|
13
|
+
end
|
14
|
+
|
15
|
+
# Helper method to create a new engine based on a module namespace
|
16
|
+
# prefix and Swagger spec file. The engine ceated will be a subclass of
|
17
|
+
# Rails::Swagger::Engine, which itself inherits from Rails::Engine.
|
18
|
+
def self.Engine base_module, file
|
19
|
+
|
20
|
+
# Convert the module prefix into a constant if passed in as a string
|
21
|
+
base_module = Object.const_get base_module if String === base_module
|
22
|
+
|
23
|
+
# Ensure the Swagger spec file is in an acceptable format
|
24
|
+
ext = File.extname(file)
|
25
|
+
unless ALLOWED_FORMATS.include? ext
|
26
|
+
raise "Swagger files must end with #{ALLOWED_FORMATS.join(' or ')}. File given: #{file}"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Attempt to read and parse the Swagger spec file
|
30
|
+
document = File.read file
|
31
|
+
case File.extname file
|
32
|
+
when ".json"
|
33
|
+
begin
|
34
|
+
require 'json'
|
35
|
+
document = JSON.parse document
|
36
|
+
rescue JSON::ParserError
|
37
|
+
raise $!, "Problem parsing swagger spec file \"#{file}\": #{$!.message.lines.first.strip}", $@
|
38
|
+
end
|
39
|
+
else
|
40
|
+
raise "Swagger files must end with #{ALLOWED_FORMATS.join(' or ')}. File given: #{file}"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Verify that the swagger version is supported
|
44
|
+
unless document["swagger"] == "2.0"
|
45
|
+
raise "Unsupported swagger version: #{document["swagger"]}. #{self} supports only version 2.0"
|
46
|
+
end
|
47
|
+
|
48
|
+
# # Parse the swagger schema
|
49
|
+
# schema = nil
|
50
|
+
# begin
|
51
|
+
# schema = JSchema.build document
|
52
|
+
# rescue JSchema::UnknownError, JSchema::InvalidSchema => e
|
53
|
+
# raise $!, "Problem parsing swagger spec file \"#{file}\": #{e.message}", $@
|
54
|
+
# end
|
55
|
+
|
56
|
+
# Builds a routing tree based on the swagger spec file.
|
57
|
+
# We'll add each endpoint to the routing tree and additionally
|
58
|
+
# store it in an array to be used below.
|
59
|
+
router = Router.new
|
60
|
+
endpoints = []
|
61
|
+
document["paths"].each do |url, actions|
|
62
|
+
actions.each do |verb, definition|
|
63
|
+
route = Endpoint.new(verb.downcase.to_sym, url, definition)
|
64
|
+
router << route
|
65
|
+
endpoints << route
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Creates the engine that will be used to actually route the
|
70
|
+
# contents of the swagger spec file. The engine will eventually be
|
71
|
+
# attached to the base module (argument to this current method).
|
72
|
+
#
|
73
|
+
# Exposes `::router` and `::endpoints` methods to allow other parts
|
74
|
+
# of the code to tie requests back to their spec file definitions.
|
75
|
+
engine = Class.new Engine do
|
76
|
+
|
77
|
+
@router = router
|
78
|
+
@endpoints = Hash.new
|
79
|
+
@schema = document
|
80
|
+
|
81
|
+
class << self
|
82
|
+
def router
|
83
|
+
@router
|
84
|
+
end
|
85
|
+
def endpoints
|
86
|
+
@endpoints
|
87
|
+
end
|
88
|
+
def schema
|
89
|
+
@schema
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Adds routes to the engine by passing the Mapper to the top
|
94
|
+
# of the routing tree. `self` inside the block refers to an
|
95
|
+
# instance of `ActionDispatch::Routing::Mapper`.
|
96
|
+
self.routes.draw do
|
97
|
+
scope module: base_module.name.underscore, format: false do
|
98
|
+
router.draw self
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
# Assign the engine as a class on the base module
|
105
|
+
base_module.const_set :Engine, engine
|
106
|
+
|
107
|
+
# Creates a hash that maps routes back to their swagger spec file
|
108
|
+
# equivalents. This is accomplished by mocking a request for each
|
109
|
+
# swagger spec file endpoint and determining which controller and
|
110
|
+
# action the request is routed to. Swagger spec file definitions
|
111
|
+
# are then attached to that controller/action pair.
|
112
|
+
endpoints.each do |route|
|
113
|
+
|
114
|
+
# Mocks a request using the route's URL
|
115
|
+
url = ::ActionDispatch::Journey::Router::Utils.normalize_path route.path
|
116
|
+
env = ::Rack::MockRequest.env_for url, method: route[:method].upcase
|
117
|
+
req = ::ActionDispatch::Request.new env
|
118
|
+
|
119
|
+
# Maps the swagger spec endpoint to the destination controller
|
120
|
+
# action by routing the request.
|
121
|
+
mapped = engine.routes.router.recognize(req){}.first[1]
|
122
|
+
key = "#{mapped[:controller]}##{mapped[:action]}"
|
123
|
+
engine.endpoints[key] = route
|
124
|
+
|
125
|
+
end
|
126
|
+
engine.endpoints.freeze
|
127
|
+
|
128
|
+
# Defines a helper module on the base module that can be used to
|
129
|
+
# properly generate swagger-aware controllers. Any controllers
|
130
|
+
# referenced from a swagger spec file should include this module.
|
131
|
+
mod = Module.new do
|
132
|
+
@base = base_module
|
133
|
+
def self.included controller
|
134
|
+
base_module = @base
|
135
|
+
controller.include Controller
|
136
|
+
define_method :rails_swagger_engine do
|
137
|
+
base_module.const_get :Engine
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
base_module.const_set :SwaggerController, mod
|
142
|
+
|
143
|
+
# Returns the new engine
|
144
|
+
base_module.const_get :Engine
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
module Rails
|
2
|
+
module Swagger
|
3
|
+
|
4
|
+
Endpoint = Struct.new(:method, :url, :definition, :_path) do
|
5
|
+
def initialize *opts
|
6
|
+
super
|
7
|
+
self[:_path] = self.path
|
8
|
+
end
|
9
|
+
def path
|
10
|
+
self[:url].gsub /\{(.+)\}/, ':\\1'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
RESOURCE_ROUTES = {
|
15
|
+
get: :index,
|
16
|
+
post: :create
|
17
|
+
}.freeze
|
18
|
+
PARAM_ROUTES = {
|
19
|
+
get: :show,
|
20
|
+
patch: :update,
|
21
|
+
put: :update,
|
22
|
+
delete: :destroy
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
class Router
|
26
|
+
|
27
|
+
attr_accessor :endpoints
|
28
|
+
|
29
|
+
def initialize prefix = [], parent = nil
|
30
|
+
@parent = parent
|
31
|
+
@prefix = prefix
|
32
|
+
@endpoints = []
|
33
|
+
@subroutes = Hash.new do |hash, k|
|
34
|
+
hash[k] = Router.new(@prefix + [k], self)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Adds an individual endpoint to the routing tree
|
39
|
+
def << route
|
40
|
+
raise "Argument must be an Endpoint" unless Endpoint === route
|
41
|
+
base, *subroute = route[:_path].split '/' # Split out first element
|
42
|
+
if subroute.count == 0
|
43
|
+
route[:_path] = ""
|
44
|
+
@endpoints << route
|
45
|
+
else
|
46
|
+
route[:_path] = subroute.join '/'
|
47
|
+
self[subroute[0]] << route
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns a specific branch of the routing tree
|
52
|
+
def [] path
|
53
|
+
@subroutes[path]
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the routing path
|
57
|
+
def path
|
58
|
+
"/" + @prefix.join("/")
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the mode used for collecting routes
|
62
|
+
def route_mode
|
63
|
+
mode = :resource
|
64
|
+
mode = :namespace if @endpoints.count == 0
|
65
|
+
mode = :action if @subroutes.count == 0 && @parent && @parent.route_mode == :resource
|
66
|
+
mode
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns the mode used for actions in this router
|
70
|
+
def action_mode
|
71
|
+
if /^:/ === @prefix[-2]
|
72
|
+
:member
|
73
|
+
elsif /^:/ === @prefix[-1]
|
74
|
+
:param
|
75
|
+
else
|
76
|
+
:collection
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Determines the action for a specific route
|
81
|
+
def action_for route
|
82
|
+
raise "Argument must be an Endpoint" unless Endpoint === route
|
83
|
+
action = @prefix[-1]
|
84
|
+
action = PARAM_ROUTES[route[:method]] if self.action_mode == :param
|
85
|
+
action = RESOURCE_ROUTES[route[:method]] if self.route_mode == :resource && self.action_mode == :collection
|
86
|
+
action
|
87
|
+
end
|
88
|
+
|
89
|
+
# Draws the routes for this router
|
90
|
+
def draw map
|
91
|
+
case self.route_mode
|
92
|
+
when :resource
|
93
|
+
|
94
|
+
# Find collection-level resource actions
|
95
|
+
actions = @endpoints.map{ |route| self.action_for route }.select{ |action| Symbol === action }
|
96
|
+
|
97
|
+
# Find parameter-level resource actions
|
98
|
+
@subroutes.select{ |k, subroute| /^:/ === k}.values.each do |subroute|
|
99
|
+
actions += subroute.endpoints.map{ |route| subroute.action_for route }.select{ |action| Symbol === action }
|
100
|
+
end
|
101
|
+
|
102
|
+
map.resources @prefix.last.to_sym, only: actions do
|
103
|
+
draw_actions! map
|
104
|
+
draw_subroutes! map
|
105
|
+
end
|
106
|
+
|
107
|
+
when :namespace
|
108
|
+
if @prefix.join("/").blank?
|
109
|
+
draw_subroutes! map
|
110
|
+
else
|
111
|
+
map.namespace @prefix.last do
|
112
|
+
draw_subroutes! map
|
113
|
+
end
|
114
|
+
end
|
115
|
+
when :action
|
116
|
+
draw_actions! map
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
def routing_tree
|
122
|
+
|
123
|
+
puts self.path + " - #{self.route_mode}"
|
124
|
+
@endpoints.each do |route|
|
125
|
+
puts "\t#{route[:method].to_s.upcase} to ##{self.action_for route} (#{self.action_mode})"
|
126
|
+
end
|
127
|
+
@subroutes.each do |k, subroute| subroute.routing_tree end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
# Outputs the routing tree in text format
|
132
|
+
def to_s
|
133
|
+
|
134
|
+
output = ""
|
135
|
+
|
136
|
+
path = "/" + @prefix.join('/')
|
137
|
+
@endpoints.each do |route|
|
138
|
+
output += "#{route[:method].to_s.upcase} #{path}\n"
|
139
|
+
end
|
140
|
+
@subroutes.each do |k, subroute|
|
141
|
+
output += subroute.to_s
|
142
|
+
end
|
143
|
+
|
144
|
+
output
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
protected
|
149
|
+
|
150
|
+
def draw_actions! map
|
151
|
+
indent = "\t" * @prefix.count
|
152
|
+
endpoint = @prefix.last
|
153
|
+
@endpoints.each do |route|
|
154
|
+
|
155
|
+
params = Hash.new
|
156
|
+
params[:via] = route[:method]
|
157
|
+
params[:on] = self.action_mode unless self.action_mode == :param
|
158
|
+
params[:action] = self.action_for route
|
159
|
+
|
160
|
+
# These are handled in the resource
|
161
|
+
next if Symbol === params[:action]
|
162
|
+
|
163
|
+
# Add this individual route
|
164
|
+
map.match endpoint, params
|
165
|
+
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def draw_subroutes! map
|
170
|
+
@subroutes.values.each do |subroute|
|
171
|
+
subroute.draw map
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails-swagger
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kenaniah Cerny
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: jschema
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- kenaniah@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- lib/rails/swagger.rb
|
77
|
+
- lib/rails/swagger/controller.rb
|
78
|
+
- lib/rails/swagger/engine.rb
|
79
|
+
- lib/rails/swagger/router.rb
|
80
|
+
homepage: https://github.com/kenaniah/rails-swagger
|
81
|
+
licenses: []
|
82
|
+
metadata: {}
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 2.6.8
|
100
|
+
signing_key:
|
101
|
+
specification_version: 4
|
102
|
+
summary: Turns Swagger API schemas into mountable Rails engines
|
103
|
+
test_files: []
|