rails-swagger 0.0.1
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/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: []
|