rat_pack_swagger 0.1.0 → 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 +4 -4
- data/lib/rat_pack_swagger/version.rb +1 -1
- data/lib/rat_pack_swagger.rb +280 -64
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fdeaee701efde21c026b00ab2dd9c0adb7d75cb
|
4
|
+
data.tar.gz: e7eab1bb0d62b5434b6f6daa5f1485cf2c564c92
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5637b41810a20347aa6c8bda4aab77f56afa10d515ad528776d42c2d9cd5e31d4b09df6d6b96af7808276366320e193dc9894fa0d6dc3eb4362c9a17cbaa18bb
|
7
|
+
data.tar.gz: 46d1c23c71f96ff53a052b7d5de52a2749dee97268ae0ecb78417914ee84beeed8dbb37d6ff162e8fcc217264918a2a42402167c79e28351cc4767ce47c19dc3
|
data/lib/rat_pack_swagger.rb
CHANGED
@@ -1,100 +1,316 @@
|
|
1
1
|
require 'sinatra/base'
|
2
|
+
require 'json'
|
2
3
|
|
3
|
-
class
|
4
|
-
|
5
|
-
instance_eval
|
4
|
+
class SwaggerObject
|
5
|
+
instance_methods.each do |m|
|
6
|
+
unless m =~ /^__/ || [:inspect, :instance_eval, :object_id].include?(m)
|
7
|
+
undef_method m
|
8
|
+
end
|
6
9
|
end
|
7
10
|
|
8
|
-
def
|
9
|
-
|
11
|
+
def initialize(*args, **kwargs, &block)
|
12
|
+
if args.count > 0 && (!kwargs.empty? || block_given?)
|
13
|
+
raise "Cannot give both unnamed arguments AND named arguments or block to Swagger parameter '#{m}'."
|
14
|
+
elsif block_given?
|
15
|
+
@obj = kwargs unless kwargs.empty?
|
16
|
+
instance_eval &block
|
17
|
+
elsif !kwargs.empty?
|
18
|
+
@obj = kwargs
|
19
|
+
elsif args.count > 0
|
20
|
+
@obj = [*args]
|
21
|
+
else
|
22
|
+
raise "Cannot create SwaggerObject with no arguments."
|
23
|
+
end
|
10
24
|
end
|
11
25
|
|
12
|
-
def
|
13
|
-
@
|
26
|
+
def add(*args, **kwargs)
|
27
|
+
@obj ||= []
|
28
|
+
if !@obj.is_a?(Array)
|
29
|
+
raise "Swagger object must be an array to append data '#{item}'"
|
30
|
+
elsif args.count > 0
|
31
|
+
@obj << [*args, kwargs]
|
32
|
+
else
|
33
|
+
@obj << kwargs
|
34
|
+
end
|
14
35
|
end
|
15
36
|
|
16
|
-
def
|
17
|
-
@
|
37
|
+
def method_missing(m, *args, **kwargs, &block)
|
38
|
+
@obj ||= {}
|
39
|
+
if block_given?
|
40
|
+
@obj[m] = SwaggerObject.new(**kwargs, &block).get
|
41
|
+
elsif !kwargs.empty?
|
42
|
+
@obj[m] = SwaggerObject.new(**kwargs).get
|
43
|
+
elsif args.count > 1
|
44
|
+
@obj[m] = [*args]
|
45
|
+
elsif args.count == 1
|
46
|
+
@obj[m] = args[0]
|
47
|
+
else
|
48
|
+
raise "Cannot give zero arguments to Swagger key '#{m}'"
|
49
|
+
end
|
18
50
|
end
|
19
51
|
|
20
|
-
def
|
21
|
-
@
|
52
|
+
def get
|
53
|
+
@obj
|
22
54
|
end
|
55
|
+
end
|
23
56
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
57
|
+
module RatPackSwagger
|
58
|
+
module DefinitionClass
|
59
|
+
# makes sure @definition is initialized
|
60
|
+
def definition
|
61
|
+
@definition ||= {
|
62
|
+
type: 'object',
|
63
|
+
required: [],
|
64
|
+
properties: {}
|
32
65
|
}
|
33
|
-
|
66
|
+
@definition
|
67
|
+
end
|
68
|
+
|
69
|
+
# Class declaration API
|
70
|
+
def properties(&block)
|
71
|
+
definition[:properties].merge!(SwaggerObject.new(&block).get)
|
72
|
+
# create top-level property accessors for instance-like usage
|
73
|
+
definition[:properties].keys.each do |k|
|
74
|
+
self.send(:attr_accessor, k)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
def required(*args)
|
78
|
+
definition[:required].concat([*args]).uniq!
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
module Definition
|
83
|
+
def self.included mod
|
84
|
+
mod.extend DefinitionClass
|
85
|
+
end
|
86
|
+
|
87
|
+
def definition
|
88
|
+
self.class.definition
|
89
|
+
end
|
90
|
+
|
91
|
+
# Instance API
|
92
|
+
def validate
|
93
|
+
validate_object(definition, to_h(false))
|
94
|
+
return self
|
95
|
+
end
|
96
|
+
def from_h(h)
|
97
|
+
properties = definition[:properties]
|
98
|
+
h.each do |k,v|
|
99
|
+
k = k.to_sym
|
100
|
+
setter = "#{k}="
|
101
|
+
if properties.keys.include?(k)
|
102
|
+
# if property type references another class, instantiate it and use hash data to populate it
|
103
|
+
if properties[k][:$ref]
|
104
|
+
send(setter, properties[k][:$ref].new.from_h(v))
|
105
|
+
# if property type is an ARRAY that references another class, instantiate and use hash data to populate them
|
106
|
+
elsif properties[k][:type].to_sym == :array && properties[k][:items][:$ref]
|
107
|
+
send(setter, v.map{|_| properties[k][:items][:$ref].new.from_h(_) })
|
108
|
+
else
|
109
|
+
send(setter, v)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
return self
|
114
|
+
end
|
115
|
+
def to_h(recur = true)
|
116
|
+
h = {}
|
117
|
+
definition[:properties].keys.each do |p|
|
118
|
+
val = send(p)
|
119
|
+
puts val
|
120
|
+
if recur
|
121
|
+
if val.is_a? Array
|
122
|
+
h[p] = val.map{|v| v.is_a?(Definition) ? v.to_h : v}
|
123
|
+
elsif val.is_a?(Definition)
|
124
|
+
h[p] = val.to_h
|
125
|
+
else
|
126
|
+
h[p] = val
|
127
|
+
end
|
128
|
+
else
|
129
|
+
h[p] = val
|
130
|
+
end
|
131
|
+
end
|
132
|
+
return h
|
133
|
+
end
|
134
|
+
|
135
|
+
# Validation
|
136
|
+
def validate_object(object_definition, data)
|
137
|
+
check_requireds(object_definition, data)
|
138
|
+
check_object_types(object_definition, data)
|
139
|
+
end
|
140
|
+
def check_requireds(object_definition, data)
|
141
|
+
object_definition[:properties].keys.each do |k|
|
142
|
+
if object_definition[:required].include?(k) && data[k].nil?
|
143
|
+
raise "Missing required property #{k}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
def check_object_types(object_definition, data)
|
148
|
+
data.each do |k,v|
|
149
|
+
property = object_definition[:properties][k]
|
150
|
+
|
151
|
+
# verify 'type' if set
|
152
|
+
type = property[:type]
|
153
|
+
if type
|
154
|
+
case type
|
155
|
+
when :string
|
156
|
+
raise "Property #{k} must be a string, not a #{v.class}" unless [String, Symbol].include?(v.class)
|
157
|
+
when :number
|
158
|
+
raise "Property #{k} must be a number, not a #{v.class}" unless v.is_a?(Numeric)
|
159
|
+
when :integer
|
160
|
+
if v.is_a?(Numeric)
|
161
|
+
raise "Property #{k} must be an integer. Value is #{v}" unless v % 1 == 0
|
162
|
+
else
|
163
|
+
raise "Property #{k} must be an integer, not a #{v.class}" unless v.is_a?(Numeric)
|
164
|
+
end
|
165
|
+
when :boolean
|
166
|
+
raise "Property #{k} must be a string, not a #{v.class}" unless [FalseClass, TrueClass].include?(v.class)
|
167
|
+
when :array
|
168
|
+
raise "Property #{k} must be an array, not a #{v.class}" unless v.is_a?(Array)
|
169
|
+
when :object
|
170
|
+
validate_object(property, v)
|
171
|
+
else
|
172
|
+
raise "Unknown property type '#{type}'"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# verify type if a ref to another definition class
|
177
|
+
ref = property[:$ref]
|
178
|
+
if ref
|
179
|
+
raise "Property #{k} should be a #{ref}, not a #{v.class}" unless ref.to_s == v.class.name
|
180
|
+
end
|
181
|
+
|
182
|
+
# verify enum
|
183
|
+
enum = property[:enum]
|
184
|
+
if enum
|
185
|
+
raise "Enum for property #{k} must be an array, not a #{enum.class}" unless enum.is_a?(Array)
|
186
|
+
raise "Invalid enum value (#{v}) for property #{k}. Valid enum values are #{enum}" unless enum.include?(v) || enum.include?(v.to_sym)
|
187
|
+
end
|
188
|
+
|
189
|
+
# verify mins and maxes
|
190
|
+
min = property[:minimum]
|
191
|
+
if min
|
192
|
+
raise "Property #{k} value (#{v}) is less than the property minimum (#{min})" unless v >= min
|
193
|
+
end
|
194
|
+
max = property[:maximum]
|
195
|
+
if min
|
196
|
+
raise "Property #{k} value (#{v}) is less than the property maximum (#{max})" unless v <= max
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def transform_hash_values(obj, &block)
|
204
|
+
if obj.is_a? Hash
|
205
|
+
obj.each do |k,v|
|
206
|
+
if v.is_a?(Hash) || v.is_a?(Array)
|
207
|
+
obj[k] = transform_hash_values(v, &block)
|
208
|
+
else
|
209
|
+
obj[k] = yield(k,v)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
elsif obj.is_a? Array
|
213
|
+
obj.each_with_index do |e,i|
|
214
|
+
if e.is_a?(Hash) || e.is_a?(Array)
|
215
|
+
obj[i] = transform_hash_values(e, &block)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
else
|
219
|
+
raise "Argument must be a Hash or an Array."
|
34
220
|
end
|
221
|
+
obj
|
35
222
|
end
|
36
223
|
|
37
224
|
module Sinatra
|
38
225
|
module RatPackSwagger
|
39
|
-
def swagger(
|
226
|
+
def swagger(*args, **kwargs, &block)
|
40
227
|
@@doc ||= {}
|
41
|
-
|
228
|
+
if args.count.zero?
|
229
|
+
# assume passing data into method call
|
230
|
+
@@doc.merge!(SwaggerObject.new(**kwargs, &block).get)
|
231
|
+
else
|
232
|
+
# assume single argument is filename of existing json
|
233
|
+
@@doc.merge!(::JSON.parse(File.read(args[0])))
|
234
|
+
end
|
42
235
|
end
|
43
236
|
|
44
|
-
def
|
45
|
-
@@
|
46
|
-
@@doc["info"] = SwaggerInfo.new(&block).to_hash
|
237
|
+
def description(d)
|
238
|
+
@@description = d
|
47
239
|
end
|
48
240
|
|
49
|
-
def
|
50
|
-
@@
|
241
|
+
def param(**kwargs, &block)
|
242
|
+
@@parameters ||= []
|
243
|
+
@@parameters << SwaggerObject.new(**kwargs, &block).get
|
51
244
|
end
|
52
245
|
|
53
|
-
def
|
54
|
-
|
55
|
-
@@
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
246
|
+
def tags(*args)
|
247
|
+
@@tags ||= []
|
248
|
+
args.each{|a| @@tags << a}
|
249
|
+
end
|
250
|
+
|
251
|
+
def summary(s)
|
252
|
+
@@summary = s
|
253
|
+
end
|
254
|
+
|
255
|
+
def response(http_status_code, **kwargs, &block)
|
256
|
+
@@responses ||= {}
|
257
|
+
@@responses[http_status_code] = SwaggerObject.new(**kwargs, &block).get
|
258
|
+
end
|
259
|
+
|
260
|
+
def definitions(*constants)
|
261
|
+
@@doc[:definitions] ||= {}
|
262
|
+
constants.each do |constant|
|
263
|
+
if Module === constant
|
264
|
+
constant.constants.each do |c|
|
265
|
+
klass = constant.const_get(c)
|
266
|
+
if Class === klass
|
267
|
+
@@doc[:definitions][c] = klass.definition
|
268
|
+
end
|
269
|
+
end
|
270
|
+
else
|
271
|
+
@@doc[:definitions][constant.to_s.rpartition('::').last] = constant.definition
|
272
|
+
end
|
64
273
|
end
|
65
274
|
end
|
66
275
|
|
276
|
+
def self.registered(app)
|
277
|
+
app.get '/v2/swagger.json' do
|
278
|
+
content_type 'application/json'
|
279
|
+
response['Access-Control-Allow-Origin'] = '*'
|
280
|
+
response['Access-Control-Allow-Headers'] = 'Content-Type, api-key, Authorization'
|
281
|
+
response['Access-Control-Allow-Methods'] = 'GET, POST'
|
282
|
+
doc = transform_hash_values(@@doc) do |k,v|
|
283
|
+
k.to_s == '$ref' ? "\#/definitions/#{v.to_s.rpartition('::').last}" : v
|
284
|
+
end
|
285
|
+
doc.to_json
|
286
|
+
end
|
287
|
+
@@doc = {}
|
288
|
+
end
|
289
|
+
|
67
290
|
def self.route_added(verb, path, block)
|
68
|
-
return if path ==
|
69
|
-
return unless [
|
291
|
+
return if path == '/v2/swagger.json'
|
292
|
+
return unless ['GET', 'POST', 'PUT', 'DELETE'].include?(verb)
|
70
293
|
|
71
|
-
@@doc ||= {}
|
72
|
-
@@
|
73
|
-
|
74
|
-
@@
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
"type" => p[:type]
|
85
|
-
}
|
86
|
-
},
|
87
|
-
"responses" => {
|
88
|
-
"200" => {
|
89
|
-
"description" => @@desc
|
90
|
-
}
|
91
|
-
}
|
92
|
-
}
|
294
|
+
@@doc['paths'] ||= {}
|
295
|
+
@@description ||= ''
|
296
|
+
@@tags ||= []
|
297
|
+
@@summary ||= ''
|
298
|
+
@@responses ||= {}
|
299
|
+
|
300
|
+
@@doc['paths'][path] ||= {}
|
301
|
+
@@doc['paths'][path][verb.downcase] = {
|
302
|
+
tags: @@tags,
|
303
|
+
description: @@description,
|
304
|
+
summary: @@summary,
|
305
|
+
parameters: @@parameters,
|
306
|
+
responses: @@responses
|
93
307
|
}
|
94
308
|
|
95
309
|
@@parameters = []
|
310
|
+
@@description = nil
|
311
|
+
@@tags = nil
|
312
|
+
@@summary = nil
|
313
|
+
@@responses = nil
|
96
314
|
end
|
97
315
|
end
|
98
|
-
|
99
|
-
register RatPackSwagger
|
100
316
|
end
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rat_pack_swagger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Hildebrand
|
8
|
+
- Mike Schreiber
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2015-
|
12
|
+
date: 2015-07-15 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: sinatra
|