ruby-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/ruby-swagger.rb +7 -0
- data/lib/ruby-swagger/data/contact.rb +49 -0
- data/lib/ruby-swagger/data/definitions.rb +49 -0
- data/lib/ruby-swagger/data/document.rb +181 -0
- data/lib/ruby-swagger/data/example.rb +29 -0
- data/lib/ruby-swagger/data/external_documentation.rb +24 -0
- data/lib/ruby-swagger/data/header.rb +34 -0
- data/lib/ruby-swagger/data/headers.rb +48 -0
- data/lib/ruby-swagger/data/info.rb +56 -0
- data/lib/ruby-swagger/data/items.rb +45 -0
- data/lib/ruby-swagger/data/license.rb +51 -0
- data/lib/ruby-swagger/data/mime.rb +31 -0
- data/lib/ruby-swagger/data/operation.rb +82 -0
- data/lib/ruby-swagger/data/parameter.rb +88 -0
- data/lib/ruby-swagger/data/parameters.rb +53 -0
- data/lib/ruby-swagger/data/path.rb +115 -0
- data/lib/ruby-swagger/data/paths.rb +50 -0
- data/lib/ruby-swagger/data/reference.rb +30 -0
- data/lib/ruby-swagger/data/response.rb +25 -0
- data/lib/ruby-swagger/data/responses.rb +50 -0
- data/lib/ruby-swagger/data/schema.rb +68 -0
- data/lib/ruby-swagger/data/scopes.rb +44 -0
- data/lib/ruby-swagger/data/security_definitions.rb +49 -0
- data/lib/ruby-swagger/data/security_requirement.rb +35 -0
- data/lib/ruby-swagger/data/security_scheme.rb +67 -0
- data/lib/ruby-swagger/data/tag.rb +24 -0
- data/lib/ruby-swagger/data/url.rb +26 -0
- data/lib/ruby-swagger/data/xml_object.rb +15 -0
- data/lib/ruby-swagger/grape/grape.rb +2 -0
- data/lib/ruby-swagger/grape/grape_config.rb +160 -0
- data/lib/ruby-swagger/grape/grape_presenter.rb +48 -0
- data/lib/ruby-swagger/grape/grape_template.rb +67 -0
- data/lib/ruby-swagger/grape/method.rb +295 -0
- data/lib/ruby-swagger/grape/param.rb +33 -0
- data/lib/ruby-swagger/grape/route_path.rb +37 -0
- data/lib/ruby-swagger/grape/routes.rb +52 -0
- data/lib/ruby-swagger/grape/type.rb +141 -0
- data/lib/ruby-swagger/io/comparable.rb +30 -0
- data/lib/ruby-swagger/io/definitions.rb +48 -0
- data/lib/ruby-swagger/io/file_system.rb +97 -0
- data/lib/ruby-swagger/io/paths.rb +55 -0
- data/lib/ruby-swagger/io/security.rb +45 -0
- data/lib/ruby-swagger/object.rb +67 -0
- data/lib/ruby-swagger/railtie.rb +7 -0
- data/lib/ruby-swagger/template.rb +29 -0
- data/lib/tasks/swagger.rake +125 -0
- metadata +176 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'ruby-swagger/grape/type'
|
2
|
+
|
3
|
+
module Swagger::Grape
|
4
|
+
class Param
|
5
|
+
|
6
|
+
def initialize(param)
|
7
|
+
@param = param
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_swagger
|
11
|
+
swagger_param = {}
|
12
|
+
swagger_param['description'] = @param[:desc] if @param[:desc].present?
|
13
|
+
swagger_param['default'] = @param[:default] if @param[:default].present?
|
14
|
+
|
15
|
+
swagger_param.merge! Swagger::Grape::Type.new(@param[:type]).to_swagger
|
16
|
+
|
17
|
+
swagger_param
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_type_definition?
|
21
|
+
type.downcase == 'object'
|
22
|
+
end
|
23
|
+
|
24
|
+
def type_definition
|
25
|
+
(Object.const_get(type)).to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
def type
|
29
|
+
@param[:type].to_s || 'string'
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'ruby-swagger/data/path'
|
2
|
+
require 'ruby-swagger/data/operation'
|
3
|
+
require 'ruby-swagger/grape/method'
|
4
|
+
|
5
|
+
module Swagger::Grape
|
6
|
+
class RoutePath
|
7
|
+
|
8
|
+
attr_reader :types, :scopes
|
9
|
+
|
10
|
+
def initialize(route_name)
|
11
|
+
@name = route_name
|
12
|
+
@operations = {}
|
13
|
+
@types = []
|
14
|
+
@scopes = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_operation(route)
|
18
|
+
method = Swagger::Grape::Method.new(@name, route)
|
19
|
+
grape_operation = method.operation
|
20
|
+
|
21
|
+
@types = (@types | method.types).uniq
|
22
|
+
@scopes = (@scopes | method.scopes).uniq
|
23
|
+
@operations[route.route_method.downcase] = grape_operation
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_swagger
|
27
|
+
path = Swagger::Data::Path.new
|
28
|
+
|
29
|
+
@operations.each do |operation_verb, operation|
|
30
|
+
path.send("#{operation_verb}=", operation)
|
31
|
+
end
|
32
|
+
|
33
|
+
path
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'ruby-swagger/data/paths'
|
2
|
+
require 'ruby-swagger/data/path'
|
3
|
+
require 'ruby-swagger/grape/route_path'
|
4
|
+
|
5
|
+
module Swagger::Grape
|
6
|
+
class Routes
|
7
|
+
|
8
|
+
attr_reader :types, :scopes
|
9
|
+
|
10
|
+
def initialize(routes)
|
11
|
+
@routes = routes
|
12
|
+
@types = []
|
13
|
+
@scopes = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_swagger
|
17
|
+
swagger = Swagger::Data::Paths.new
|
18
|
+
paths = {}
|
19
|
+
|
20
|
+
@routes.each do |route|
|
21
|
+
|
22
|
+
next if route.route_hidden == true #implement custom "hidden" extension
|
23
|
+
|
24
|
+
swagger_path_name = swagger_path_name(route)
|
25
|
+
paths[swagger_path_name] ||= Swagger::Grape::RoutePath.new(swagger_path_name)
|
26
|
+
paths[swagger_path_name].add_operation(route)
|
27
|
+
end
|
28
|
+
|
29
|
+
paths.each do |path_name, path|
|
30
|
+
swagger.add_path(path_name, path.to_swagger)
|
31
|
+
@types = (@types | path.types).uniq
|
32
|
+
@scopes = (@scopes | path.scopes).uniq
|
33
|
+
end
|
34
|
+
|
35
|
+
swagger
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def swagger_path_name(grape_route)
|
41
|
+
grape_path_name = grape_route.route_path
|
42
|
+
grape_prefix = grape_route.route_prefix
|
43
|
+
grape_path_name.gsub!(/^\/#{grape_prefix}/, '') if grape_prefix
|
44
|
+
grape_path_name.gsub!(/^\/:version/, '') #remove api version - if any
|
45
|
+
grape_path_name.gsub!(/\(\.:format\)$/, '') #remove api format - if any
|
46
|
+
grape_path_name.gsub!(/\(\..+\)$/, '') #remove api format - if any
|
47
|
+
grape_path_name.gsub!(/\/:([a-zA-Z0-9_]+)/, '/{\1}') #convert parameters from :format into {format}
|
48
|
+
grape_path_name
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module Swagger::Grape
|
2
|
+
class Type
|
3
|
+
|
4
|
+
attr_reader :discovered_types
|
5
|
+
|
6
|
+
def initialize(type)
|
7
|
+
@type = type.to_s || 'String'
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_swagger(with_definition = true)
|
11
|
+
type_convert(@type.to_s, with_definition)
|
12
|
+
end
|
13
|
+
|
14
|
+
def sub_types
|
15
|
+
type = Object.const_get(@type)
|
16
|
+
return [] unless type.respond_to?(:exposures)
|
17
|
+
|
18
|
+
types = []
|
19
|
+
|
20
|
+
type.exposures.each do |property, definition|
|
21
|
+
types << definition[:using] if definition[:using].present?
|
22
|
+
end
|
23
|
+
|
24
|
+
types.uniq
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def type_convert(type, with_definition = true)
|
30
|
+
swagger_type = {}
|
31
|
+
|
32
|
+
case type.downcase
|
33
|
+
when 'string'
|
34
|
+
swagger_type['type'] = 'string'
|
35
|
+
when 'integer'
|
36
|
+
swagger_type['type'] = 'integer'
|
37
|
+
when 'array'
|
38
|
+
swagger_type['type'] = 'array'
|
39
|
+
swagger_type['items'] = {'type' => 'string'}
|
40
|
+
when 'hash'
|
41
|
+
swagger_type['type'] = 'object'
|
42
|
+
swagger_type['properties'] = {}
|
43
|
+
when 'boolean'
|
44
|
+
swagger_type['type'] = 'boolean'
|
45
|
+
when 'virtus::attribute::boolean'
|
46
|
+
swagger_type['type'] = 'boolean'
|
47
|
+
when 'symbol'
|
48
|
+
swagger_type['type'] = 'string'
|
49
|
+
when 'float'
|
50
|
+
swagger_type['type'] = 'number'
|
51
|
+
swagger_type['format'] = 'float'
|
52
|
+
when 'rack::multipart::uploadedfile'
|
53
|
+
swagger_type['type'] = 'string'
|
54
|
+
STDERR.puts "Warning - I have no idea how to handle the type file. Right now I will consider this a string, but we should probably handle it..."
|
55
|
+
when 'date'
|
56
|
+
swagger_type['type'] = 'date'
|
57
|
+
when 'datetime'
|
58
|
+
swagger_type['format'] = 'date-time'
|
59
|
+
swagger_type['format'] = 'string'
|
60
|
+
else
|
61
|
+
swagger_type['type'] = "object"
|
62
|
+
|
63
|
+
if with_definition
|
64
|
+
# I can just reference the name of the object here
|
65
|
+
swagger_type['$ref'] = "#/definitions/#{type}"
|
66
|
+
else
|
67
|
+
type = Object.const_get(@type)
|
68
|
+
# I need to define the full object
|
69
|
+
raise ArgumentError.new("Don't know how to translate the object #{@type}") unless type.respond_to?(:exposures)
|
70
|
+
|
71
|
+
swagger_type['properties'] = {}
|
72
|
+
|
73
|
+
type.exposures.each do |property, definition|
|
74
|
+
|
75
|
+
cursor = swagger_type
|
76
|
+
target = property.to_s
|
77
|
+
|
78
|
+
if definition[:nested]
|
79
|
+
# it's a nested parameter
|
80
|
+
path = target.split('__')
|
81
|
+
cursor = find_elem_in_schema(cursor, path.dup)
|
82
|
+
target = path.last
|
83
|
+
end
|
84
|
+
|
85
|
+
target = definition[:as].to_s if definition[:as].present?
|
86
|
+
|
87
|
+
cursor['properties'][target] = {}
|
88
|
+
|
89
|
+
if definition[:documentation].present? && definition[:documentation][:type].present?
|
90
|
+
cursor['properties'][target] = type_convert(definition[:documentation][:type].to_s, true)
|
91
|
+
end
|
92
|
+
|
93
|
+
if definition[:using].present?
|
94
|
+
#it's either an object or an array of object
|
95
|
+
using = type_convert(definition[:using].to_s, true)
|
96
|
+
|
97
|
+
if cursor['type'].present? && cursor['type'] == 'array'
|
98
|
+
cursor['items'] = using
|
99
|
+
else
|
100
|
+
cursor['properties'][target] = using
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
cursor['properties'][target]['description'] = definition[:documentation][:desc] if definition[:documentation].present?
|
106
|
+
cursor['properties'][target]['type'] ||= 'string' #no type defined, assuming it's a string
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
swagger_type
|
112
|
+
end
|
113
|
+
|
114
|
+
def find_elem_in_schema(root, schema_path)
|
115
|
+
return root if schema_path.nil? || schema_path.empty?
|
116
|
+
|
117
|
+
next_elem = schema_path.shift
|
118
|
+
|
119
|
+
return root if root['properties'][next_elem].nil?
|
120
|
+
|
121
|
+
case root['properties'][next_elem]['type']
|
122
|
+
when 'array'
|
123
|
+
#to descend an array this must be an array of objects
|
124
|
+
root['properties'][next_elem]['items']['type'] = 'object'
|
125
|
+
root['properties'][next_elem]['items']['properties'] ||= {}
|
126
|
+
|
127
|
+
find_elem_in_schema(root['properties'][next_elem]['items'], schema_path)
|
128
|
+
when 'object'
|
129
|
+
find_elem_in_schema(root['properties'][next_elem], schema_path)
|
130
|
+
else
|
131
|
+
# I'm discending an object that before I assumed was something else
|
132
|
+
root['properties'][next_elem]['type'] = 'object'
|
133
|
+
root['properties'][next_elem]['properties'] ||= {}
|
134
|
+
|
135
|
+
find_elem_in_schema(root['properties'][next_elem], schema_path)
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Swagger::IO
|
2
|
+
class Comparable
|
3
|
+
|
4
|
+
def self.copy_description_old_definition(definition, old_definition)
|
5
|
+
return if definition.nil? || old_definition.nil? || definition.class != old_definition.class
|
6
|
+
|
7
|
+
case definition
|
8
|
+
when Hash
|
9
|
+
|
10
|
+
definition.keys.each do |key|
|
11
|
+
old_v = definition[key]
|
12
|
+
|
13
|
+
if (key == 'description' || key == 'summary') && old_definition[key]
|
14
|
+
definition[key] = old_definition[key]
|
15
|
+
end
|
16
|
+
|
17
|
+
if old_v.is_a?(Hash) || old_v.is_a?(Array)
|
18
|
+
copy_description_old_definition(definition[key], old_definition[key])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
when Array
|
22
|
+
definition.each_with_index do |item, index|
|
23
|
+
copy_description_old_definition(definition[index], old_definition[index])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'ruby-swagger/io/file_system'
|
2
|
+
require 'ruby-swagger/io/comparable'
|
3
|
+
|
4
|
+
module Swagger::IO
|
5
|
+
class Definitions
|
6
|
+
|
7
|
+
def self.read_definitions
|
8
|
+
definitions = {}
|
9
|
+
|
10
|
+
Swagger::IO::FileSystem.all_files("definitions/**/*.yml").each do |file|
|
11
|
+
definitions[File.basename(file, ".yml")] = YAML::load_file(file)
|
12
|
+
end
|
13
|
+
|
14
|
+
definitions
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.write_definitions(definitions)
|
18
|
+
return if definitions.nil?
|
19
|
+
|
20
|
+
#Remove dead definitions
|
21
|
+
Swagger::IO::FileSystem.all_files("definitions/**/*.yml").each do |file|
|
22
|
+
def_name = File.basename(file, ".yml")
|
23
|
+
|
24
|
+
unless definitions[def_name]
|
25
|
+
STDERR.puts "#{def_name} is not present anymore, removing #{file}"
|
26
|
+
Swagger::IO::FileSystem.delete_file(file)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
# Write new definitions
|
32
|
+
definitions.each do |definition_name, definition|
|
33
|
+
|
34
|
+
# If an old definition exists, we copy over the documentation to the generated definition
|
35
|
+
if Swagger::IO::FileSystem.file_exists?("definitions/#{definition_name}.yml")
|
36
|
+
old_definition = Swagger::IO::FileSystem.read_file("definitions/#{definition_name}.yml")
|
37
|
+
|
38
|
+
Swagger::IO::Comparable.copy_description_old_definition(definition, old_definition)
|
39
|
+
end
|
40
|
+
|
41
|
+
Swagger::IO::FileSystem.write_file(definition.to_yaml, "definitions/#{definition_name}.yml", true)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'ruby-swagger/object'
|
2
|
+
require 'ruby-swagger/data/document'
|
3
|
+
require 'ruby-swagger/io/security'
|
4
|
+
require 'ruby-swagger/io/definitions'
|
5
|
+
require 'ruby-swagger/io/paths'
|
6
|
+
|
7
|
+
module Swagger::IO
|
8
|
+
class FileSystem
|
9
|
+
|
10
|
+
DOC_SUBPARTS = %w(responses security tags)
|
11
|
+
|
12
|
+
@@default_path = './doc/swagger'
|
13
|
+
|
14
|
+
def self.default_path=(new_path)
|
15
|
+
@@default_path = new_path
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.default_path
|
19
|
+
@@default_path
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.init_fs_structure
|
23
|
+
FileUtils.mkdir_p(@@default_path) unless Dir.exists?(@@default_path)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.read_file(name)
|
27
|
+
YAML::load_file(@@default_path + '/' + name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.write_file(content, location, overwrite = false)
|
31
|
+
file_path = @@default_path + '/' + location
|
32
|
+
|
33
|
+
return if !overwrite && File.exists?(file_path)
|
34
|
+
|
35
|
+
dir_path = File.dirname(file_path)
|
36
|
+
|
37
|
+
FileUtils.mkdir_p(dir_path) unless Dir.exists?(dir_path)
|
38
|
+
File.open(file_path, 'w') {|f| f.write(content) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.file_exists?(name)
|
42
|
+
File.exists?(@@default_path + '/' + name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.all_files(pattern)
|
46
|
+
Dir["#{@@default_path}/#{pattern}"]
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.delete_file(file)
|
50
|
+
FileUtils.rm_f(file)
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(swagger_doc)
|
54
|
+
@doc = swagger_doc
|
55
|
+
end
|
56
|
+
|
57
|
+
def write!
|
58
|
+
Swagger::IO::FileSystem.init_fs_structure
|
59
|
+
|
60
|
+
swagger = @doc.to_swagger
|
61
|
+
|
62
|
+
Swagger::IO::Paths.write_paths(swagger.delete('paths'))
|
63
|
+
|
64
|
+
DOC_SUBPARTS.each {|doc_part| write_subpart(doc_part, swagger.delete(doc_part))}
|
65
|
+
Swagger::IO::Definitions.write_definitions(swagger.delete('definitions'))
|
66
|
+
Swagger::IO::Security.write_security_definitions(swagger.delete('securityDefinitions'))
|
67
|
+
Swagger::IO::FileSystem.write_file(swagger.to_yaml, 'base_doc.yml')
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.read
|
71
|
+
doc = YAML::load_file("#{default_path}/base_doc.yml")
|
72
|
+
|
73
|
+
DOC_SUBPARTS.each do |doc_part|
|
74
|
+
file_path = "#{default_path}/#{doc_part}.yml"
|
75
|
+
doc[doc_part] = YAML::load_file(file_path) if File.exists?(file_path)
|
76
|
+
end
|
77
|
+
|
78
|
+
doc['paths'] = Swagger::IO::Paths.read_paths
|
79
|
+
doc['definitions'] = Swagger::IO::Definitions.read_definitions
|
80
|
+
doc['securityDefinitions'] = Swagger::IO::Security.read_security_definitions
|
81
|
+
|
82
|
+
Swagger::Data::Document.parse(doc)
|
83
|
+
end
|
84
|
+
|
85
|
+
def compile!
|
86
|
+
Swagger::IO::FileSystem.write_file(JSON.pretty_generate(@doc.to_swagger), 'swagger.json', true)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def write_subpart(subpart, content)
|
92
|
+
return unless content
|
93
|
+
write_file(content.to_yaml, "#{subpart}.yml")
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|