ruby-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/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
|