rat_pack_swagger 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e40eeb8f6f982d76bec3166a5083fb9594077446
4
- data.tar.gz: e6a5eeed42020edc342c618c8a4b7195be54df8e
3
+ metadata.gz: 5fdeaee701efde21c026b00ab2dd9c0adb7d75cb
4
+ data.tar.gz: e7eab1bb0d62b5434b6f6daa5f1485cf2c564c92
5
5
  SHA512:
6
- metadata.gz: 8fc08f942f3e40ede0714b0351a849d558c9be8c5c99647dd058b15c51782a11327ac0f5c5f4e7a7ab552553c4ecbcf6f9dede07e49cdeda48330584792e64ec
7
- data.tar.gz: 587bae87402c87cf695387f9432f842ba028a08f478b2f40d3c8fc850824679bc1d36c03ab573a1f950b88dcb547ba0ac455cb4ff73bc6be9b6cb3932ec8989e
6
+ metadata.gz: 5637b41810a20347aa6c8bda4aab77f56afa10d515ad528776d42c2d9cd5e31d4b09df6d6b96af7808276366320e193dc9894fa0d6dc3eb4362c9a17cbaa18bb
7
+ data.tar.gz: 46d1c23c71f96ff53a052b7d5de52a2749dee97268ae0ecb78417914ee84beeed8dbb37d6ff162e8fcc217264918a2a42402167c79e28351cc4767ce47c19dc3
@@ -1,3 +1,3 @@
1
1
  module RatPackSwagger
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -1,100 +1,316 @@
1
1
  require 'sinatra/base'
2
+ require 'json'
2
3
 
3
- class SwaggerInfo
4
- def initialize(&block)
5
- instance_eval &block
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 title(app_title)
9
- @title = app_title
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 description(app_description)
13
- @description = app_description
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 version(app_version)
17
- @version = app_version
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 contact(app_contact)
21
- @contact = app_contact
52
+ def get
53
+ @obj
22
54
  end
55
+ end
23
56
 
24
- def to_hash
25
- {
26
- title: @title,
27
- description: @description,
28
- version: @version,
29
- contact: {
30
- name: @contact[:name],
31
- email: @contact[:email]
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(version)
226
+ def swagger(*args, **kwargs, &block)
40
227
  @@doc ||= {}
41
- @@doc["swagger"] = version
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 info(&block)
45
- @@doc ||= {}
46
- @@doc["info"] = SwaggerInfo.new(&block).to_hash
237
+ def description(d)
238
+ @@description = d
47
239
  end
48
240
 
49
- def desc(description)
50
- @@desc = description
241
+ def param(**kwargs, &block)
242
+ @@parameters ||= []
243
+ @@parameters << SwaggerObject.new(**kwargs, &block).get
51
244
  end
52
245
 
53
- def param(opts)
54
- puts "in param"
55
- @@parameters ||= []
56
- puts opts
57
- @@parameters << opts
58
- end
59
-
60
- def add_swagger_route(app)
61
- app.get "/v2/swagger.json" do
62
- content_type "application/json"
63
- @@doc.to_json
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 == "/v2/swagger.json"
69
- return unless ["GET", "POST", "PUT", "DELETE"].include?(verb)
291
+ return if path == '/v2/swagger.json'
292
+ return unless ['GET', 'POST', 'PUT', 'DELETE'].include?(verb)
70
293
 
71
- @@doc ||= {}
72
- @@doc["paths"] ||= {}
73
-
74
- @@doc["paths"][path] = {
75
- verb.downcase => {
76
- "description" => @@desc,
77
- "produces" => [ "application/json" ],
78
- "parameters" => @@parameters.map { |p|
79
- {
80
- "name" => p[:name],
81
- "in" => p[:type],
82
- "description" => p[:desc],
83
- "required" => p[:required] == true,
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.1.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-06-26 00:00:00.000000000 Z
12
+ date: 2015-07-15 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: sinatra