swagger-blocks 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b309bb304d3b80b0a6e213b7f3ec59ae1c9fa51a
4
+ data.tar.gz: c68fce426638012787fc21ea5f6ee4c514ae4d18
5
+ SHA512:
6
+ metadata.gz: f4d95e4aedf7c8107d2c09e4c814cdc59c7336e5fb4370e345055fc01de0b7807787d74ca9f6250d4a5f7d6182adb3f7ef0252fe598171c7332d0dfa9490828c
7
+ data.tar.gz: f419056f5dd26c88d8dafd5f1fcbff859015257897db939ff1d46e13f97e481020362c2db6c948b35a8c80f3a9deb7d6bc21212d262fcdde88c546f5df6e9d10
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --warnings
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
4
+ - ruby-head
5
+ script: bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in swagger-rails.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2014 Mike Fotinakis
2
+
3
+ The MIT License (MIT)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Swagger::Blocks
2
+
3
+ [![Build Status](https://travis-ci.org/fotinakis/swagger-blocks.svg?branch=master)](https://travis-ci.org/fotinakis/swagger-blocks)
4
+
5
+ TODO: Write a gem description
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'swagger-blocks'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install swagger-blocks
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/fotinakis/swagger-blocks/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,403 @@
1
+ require 'json'
2
+ require 'swagger/blocks/version'
3
+
4
+ module Swagger
5
+ module Blocks
6
+ # Some custom error classes.
7
+ class Error < Exception; end
8
+ class DeclarationError < Error; end
9
+ class NotFoundError < Error; end
10
+
11
+ # Inject the swagger_root, swagger_api_root, and swagger_model class methods.
12
+ def self.included(base)
13
+ base.extend(ClassMethods)
14
+ end
15
+
16
+ module_function def build_root_json(swaggered_classes)
17
+ data = Swagger::Blocks::InternalHelpers.parse_swaggered_classes(swaggered_classes)
18
+ data[:root_node].as_json
19
+ end
20
+
21
+ module_function def build_api_json(resource_name, swaggered_classes)
22
+ data = Swagger::Blocks::InternalHelpers.parse_swaggered_classes(swaggered_classes)
23
+ api_node = data[:api_node_map][resource_name.to_sym]
24
+ raise Swagger::Blocks::NotFoundError.new(
25
+ "Not found: swagger_api_root named #{resource_name}") if !api_node
26
+
27
+ # Aggregate all model definitions into a new ModelsNode tree and add it to the JSON.
28
+ temp_models_node = Swagger::Blocks::ModelsNode.call(name: 'models') { }
29
+ data[:models_nodes].each do |models_node|
30
+ temp_models_node.merge!(models_node)
31
+ end
32
+ result = api_node.as_json
33
+ result.merge!(temp_models_node.as_json) if temp_models_node
34
+ result
35
+ end
36
+
37
+ module InternalHelpers
38
+ # Return [root_node, api_node_map] from all of the given swaggered_classes.
39
+ def self.parse_swaggered_classes(swaggered_classes)
40
+ root_nodes = []
41
+ api_node_map = {}
42
+ models_nodes = []
43
+ swaggered_classes.each do |swaggered_class|
44
+ next if !swaggered_class.respond_to?(:_swagger_nodes, true)
45
+ swagger_nodes = swaggered_class.send(:_swagger_nodes)
46
+ root_node = swagger_nodes[:resource_listing_node]
47
+ root_nodes << root_node if root_node
48
+ api_node_map.merge!(swagger_nodes[:api_node_map])
49
+ models_nodes << swagger_nodes[:models_node] if swagger_nodes[:models_node]
50
+ end
51
+ root_node = self.get_resource_listing(root_nodes)
52
+
53
+ {
54
+ root_node: root_node,
55
+ api_node_map: api_node_map,
56
+ models_nodes: models_nodes,
57
+ }
58
+ end
59
+
60
+ # Make sure there is exactly one root_node and return it.
61
+ def self.get_resource_listing(root_nodes)
62
+ if root_nodes.length == 0
63
+ raise Swagger::Blocks::DeclarationError.new(
64
+ 'swagger_root must be declared')
65
+ elsif root_nodes.length > 1
66
+ raise Swagger::Blocks::DeclarationError.new(
67
+ 'Only one swagger_root declaration is allowed.')
68
+ end
69
+ root_nodes.first
70
+ end
71
+ end
72
+
73
+ module ClassMethods
74
+ private
75
+
76
+ # Defines a Swagger Resource Listing.
77
+ # http://goo.gl/PvwUXj#51-resource-listing
78
+ def swagger_root(&block)
79
+ @swagger_root_node ||= Swagger::Blocks::ResourceListingNode.call(&block)
80
+ end
81
+
82
+ # Defines a Swagger API Declaration.
83
+ # http://goo.gl/PvwUXj#52-api-declaration
84
+ #
85
+ # @param resource_name [Symbol] An identifier for this API. All swagger_api_root declarations
86
+ # with the same resource_name will be merged into a single API root node.
87
+ def swagger_api_root(resource_name, &block)
88
+ resource_name = resource_name.to_sym
89
+
90
+ # Map of path names to ApiDeclarationNodes.
91
+ @swagger_api_root_node_map ||= {}
92
+
93
+ # Grab a previously declared node if it exists, otherwise create a new ApiDeclarationNode.
94
+ # This merges all declarations of swagger_api_root with the same resource_name key.
95
+ api_node = @swagger_api_root_node_map[resource_name]
96
+ if api_node
97
+ # Merge this swagger_api_root declaration into the previous one by the same resource_name.
98
+ api_node.instance_eval(&block)
99
+ else
100
+ # First time we've seen this `swagger_api_root :resource_name`.
101
+ api_node = Swagger::Blocks::ApiDeclarationNode.call(&block)
102
+ end
103
+
104
+ # Add it into the resource_name to node map (may harmlessly overwrite the same object).
105
+ @swagger_api_root_node_map[resource_name] = api_node
106
+ end
107
+
108
+ # Defines a Swagger Model.
109
+ # http://goo.gl/PvwUXj#526-models-object
110
+ def swagger_model(name, &block)
111
+ @swagger_models_node ||= Swagger::Blocks::ModelsNode.new
112
+ @swagger_models_node.model(name, &block)
113
+ end
114
+
115
+ def _swagger_nodes
116
+ @swagger_root_node ||= nil # Avoid initialization warnings.
117
+ @swagger_api_root_node_map ||= {}
118
+ @swagger_models_node ||= nil
119
+ {
120
+ resource_listing_node: @swagger_root_node,
121
+ api_node_map: @swagger_api_root_node_map,
122
+ models_node: @swagger_models_node,
123
+ }
124
+ end
125
+ end
126
+
127
+ # -----
128
+
129
+ # Base node for representing every object in the Swagger DSL.
130
+ class Node
131
+ attr_accessor :name
132
+
133
+ def self.call(name: nil, &block)
134
+ # Create a new instance and evaluate the block into it.
135
+ instance = new
136
+ instance.name = name if name # Set the first parameter given as the name.
137
+ instance.instance_eval(&block)
138
+ instance
139
+ end
140
+
141
+ def as_json
142
+ result = {}
143
+ self.data.each do |key, value|
144
+ if value.is_a?(Node)
145
+ result[key] = value.as_json
146
+ elsif value.is_a?(Array)
147
+ result[key] = []
148
+ value.each { |v| result[key] << (v.respond_to?(:as_json) ? v.as_json : v) }
149
+ else
150
+ result[key] = value
151
+ end
152
+ end
153
+ return result if !name
154
+ # If "name" is given to this node, wrap the data with a root element with the given name.
155
+ {name => result}
156
+ end
157
+
158
+ def data
159
+ @data ||= {}
160
+ end
161
+
162
+ def key(key, value)
163
+ self.data[key] = value
164
+ end
165
+ end
166
+
167
+ # -----
168
+ # Nodes for the Resource Listing.
169
+ # -----
170
+
171
+ # http://goo.gl/PvwUXj#51-resource-listing
172
+ class ResourceListingNode < Node
173
+ def initialize(*args)
174
+ # An internal list of the user-defined names that uniquely identify each API tree.
175
+ @api_paths = []
176
+ super
177
+ end
178
+
179
+ def has_api_path?(api_path)
180
+ api_paths = self.data[:apis].map { |x| x.data[:path] }
181
+ api_paths.include?(api_path)
182
+ end
183
+
184
+ def info(&block)
185
+ self.data[:info] = InfoNode.call(&block)
186
+ end
187
+
188
+ def authorization(name, &block)
189
+ self.data[:authorizations] ||= Swagger::Blocks::ResourceListingAuthorizationsNode.new
190
+ self.data[:authorizations].authorization(name, &block)
191
+ end
192
+
193
+ def api(&block)
194
+ self.data[:apis] ||= []
195
+ self.data[:apis] << Swagger::Blocks::ResourceNode.call(&block)
196
+ end
197
+ end
198
+
199
+ # http://goo.gl/PvwUXj#512-resource-object
200
+ class ResourceNode < Node; end
201
+
202
+ # NOTE: in the spec this is different than API Declaration authorizations.
203
+ # http://goo.gl/PvwUXj#514-authorizations-object
204
+ class ResourceListingAuthorizationsNode < Node
205
+ def authorization(name, &block)
206
+ self.data[name] = Swagger::Blocks::ResourceListingAuthorizationNode.call(&block)
207
+ end
208
+ end
209
+
210
+ # NOTE: in the spec this is different than API Declaration authorization.
211
+ # http://goo.gl/PvwUXj#515-authorization-object
212
+ class ResourceListingAuthorizationNode < Node
213
+ GRANT_TYPES = [:implicit, :authorization_code].freeze
214
+
215
+ def scope(&block)
216
+ self.data[:scopes] ||= []
217
+ self.data[:scopes] << Swagger::Blocks::ScopeNode.call(&block)
218
+ end
219
+
220
+ def grant_type(name, &block)
221
+ raise ArgumentError.new("#{name} not in #{GRANT_TYPES}") if !GRANT_TYPES.include?(name)
222
+ self.data[:grantTypes] ||= Swagger::Blocks::GrantTypesNode.new
223
+ self.data[:grantTypes].implicit(&block) if name == :implicit
224
+ self.data[:grantTypes].authorization_code(&block) if name == :authorization_code
225
+ end
226
+ end
227
+
228
+ # http://goo.gl/PvwUXj#513-info-object
229
+ class InfoNode < Node; end
230
+
231
+ # http://goo.gl/PvwUXj#516-scope-object
232
+ class ScopeNode < Node; end
233
+
234
+ # http://goo.gl/PvwUXj#517-grant-types-object
235
+ class GrantTypesNode < Node
236
+ def implicit(&block)
237
+ self.data[:implicit] = Swagger::Blocks::ImplicitNode.call(&block)
238
+ end
239
+
240
+ def authorization_code(&block)
241
+ self.data[:authorization_code] = Swagger::Blocks::AuthorizationCodeNode.call(&block)
242
+ end
243
+ end
244
+
245
+ # http://goo.gl/PvwUXj#518-implicit-object
246
+ class ImplicitNode < Node
247
+ def login_endpoint(&block)
248
+ self.data[:loginEndpoint] = Swagger::Blocks::LoginEndpointNode.call(&block)
249
+ end
250
+ end
251
+
252
+ # http://goo.gl/PvwUXj#5110-login-endpoint-object
253
+ class LoginEndpointNode < Node; end
254
+
255
+ # http://goo.gl/PvwUXj#519-authorization-code-object
256
+ class AuthorizationCodeNode < Node
257
+ def token_request_endpoint(&block)
258
+ self.data[:tokenRequestEndpoint] = Swagger::Blocks::TokenRequestEndpointNode.call(&block)
259
+ end
260
+
261
+ def token_endpoint(&block)
262
+ self.data[:tokenEndpoint] = Swagger::Blocks::TokenEndpointNode.call(&block)
263
+ end
264
+ end
265
+
266
+ # http://goo.gl/PvwUXj#5111-token-request-endpoint-object
267
+ class TokenRequestEndpointNode < Node; end
268
+
269
+ # http://goo.gl/PvwUXj#5112-token-endpoint-object
270
+ class TokenEndpointNode < Node; end
271
+
272
+ # -----
273
+ # Nodes for API Declarations.
274
+ # -----
275
+
276
+ # http://goo.gl/PvwUXj#52-api-declaration
277
+ class ApiDeclarationNode < Node
278
+ def api(&block)
279
+ self.data[:apis] ||= []
280
+
281
+ # Important: to conform with the Swagger spec, merge with any previous API declarations
282
+ # that have the same :path key. This ensures that operations affecting the same resource
283
+ # are all in the same operations node.
284
+ #
285
+ # http://goo.gl/PvwUXj#522-api-object
286
+ # - The API Object describes one or more operations on a single path. In the apis array,
287
+ # there MUST be only one API Object per path.
288
+ temp_api_node = Swagger::Blocks::ApiNode.call(&block)
289
+ api_node = self.data[:apis].select do |api|
290
+ api.data[:path] == temp_api_node.data[:path]
291
+ end[0] # Embrace Ruby wtfs.
292
+
293
+ if api_node
294
+ # Merge this block with the previous ApiNode by the same path key.
295
+ api_node.instance_eval(&block)
296
+ else
297
+ # First time we've seen an api block with the given path key.
298
+ self.data[:apis] << temp_api_node
299
+ end
300
+ end
301
+ end
302
+
303
+ # http://goo.gl/PvwUXj#522-api-object
304
+ class ApiNode < Node
305
+ def operation(&block)
306
+ self.data[:operations] ||= []
307
+ self.data[:operations] << Swagger::Blocks::OperationNode.call(&block)
308
+ end
309
+ end
310
+
311
+ class OperationNode < Node
312
+ def parameter(&block)
313
+ self.data[:parameters] ||= []
314
+ self.data[:parameters] << Swagger::Blocks::ParameterNode.call(&block)
315
+ end
316
+
317
+ def response_message(&block)
318
+ self.data[:responseMessages] ||= []
319
+ self.data[:responseMessages] << Swagger::Blocks::Node.call(&block)
320
+ end
321
+
322
+ def authorization(name, &block)
323
+ self.data[:authorizations] ||= Swagger::Blocks::ApiAuthorizationsNode.new
324
+ self.data[:authorizations].authorization(name, &block)
325
+ end
326
+
327
+ def items(&block)
328
+ self.data[:items] = Swagger::Blocks::ItemsNode.call(&block)
329
+ end
330
+ end
331
+
332
+ # NOTE: in the spec this is different than Resource Listing's authorizations.
333
+ # http://goo.gl/PvwUXj#514-authorizations-object
334
+ class ApiAuthorizationsNode < Node
335
+ def authorization(name, &block)
336
+ self.data[name] ||= Swagger::Blocks::ApiAuthorizationNode.call(&block)
337
+ end
338
+ end
339
+
340
+ # NOTE: in the spec this is different than Resource Listing's authorization.
341
+ # http://goo.gl/PvwUXj#515-authorization-object
342
+ class ApiAuthorizationNode < Node
343
+ def as_json
344
+ # Special case: the API Authorization object is weirdly the only array of hashes.
345
+ # Override the default hash behavior and return an array.
346
+ self.data[:_scopes] ||= []
347
+ self.data[:_scopes].map { |s| s.as_json }
348
+ end
349
+
350
+ def scope(&block)
351
+ self.data[:_scopes] ||= []
352
+ self.data[:_scopes] << Swagger::Blocks::ApiAuthorizationScopeNode.call(&block)
353
+ end
354
+ end
355
+
356
+ # NOTE: in the spec this is different than Resource Listing's scope object.
357
+ # http://goo.gl/PvwUXj#5211-scope-object
358
+ class ApiAuthorizationScopeNode < Node; end
359
+
360
+ # http://goo.gl/PvwUXj#434-items-object
361
+ class ItemsNode < Node; end
362
+
363
+ # http://goo.gl/PvwUXj#524-parameter-object
364
+ class ParameterNode < Node; end
365
+
366
+ # -----
367
+ # Nodes for Models.
368
+ # -----
369
+
370
+ # http://goo.gl/PvwUXj#526-models-object
371
+ class ModelsNode < Node
372
+ def merge!(other_models_node)
373
+ self.data.merge!(other_models_node.data)
374
+ end
375
+
376
+ def model(name, &block)
377
+ self.data[name] ||= Swagger::Blocks::ModelNode.call(&block)
378
+ end
379
+ end
380
+
381
+ # http://goo.gl/PvwUXj#527-model-object
382
+ class ModelNode < Node
383
+ def property(name, &block)
384
+ self.data[:properties] ||= Swagger::Blocks::PropertiesNode.new
385
+ self.data[:properties].property(name, &block)
386
+ end
387
+ end
388
+
389
+ # http://goo.gl/PvwUXj#527-model-object
390
+ class PropertiesNode < Node
391
+ def property(name, &block)
392
+ self.data[name] = Swagger::Blocks::PropertyNode.call(&block)
393
+ end
394
+ end
395
+
396
+ # http://goo.gl/PvwUXj#527-model-object
397
+ class PropertyNode < Node
398
+ def items(&block)
399
+ self.data[:items] = Swagger::Blocks::ItemsNode.call(&block)
400
+ end
401
+ end
402
+ end
403
+ end
@@ -0,0 +1,5 @@
1
+ module Swagger
2
+ module Blocks
3
+ VERSION = '0.0.1'
4
+ end
5
+ end
@@ -0,0 +1,201 @@
1
+ {
2
+ "apiVersion": "1.0.0",
3
+ "swaggerVersion": "1.2",
4
+ "basePath": "http://petstore.swagger.wordnik.com/api",
5
+ "resourcePath": "/pet",
6
+ "produces": [
7
+ "application/json",
8
+ "application/xml",
9
+ "text/plain",
10
+ "text/html"
11
+ ],
12
+ "apis": [
13
+ {
14
+ "path": "/pet/{petId}",
15
+ "operations": [
16
+ {
17
+ "method": "GET",
18
+ "summary": "Find pet by ID",
19
+ "notes": "Returns a pet based on ID",
20
+ "type": "Pet",
21
+ "nickname": "getPetById",
22
+ "parameters": [
23
+ {
24
+ "name": "petId",
25
+ "description": "ID of pet that needs to be fetched",
26
+ "required": true,
27
+ "type": "integer",
28
+ "format": "int64",
29
+ "paramType": "path",
30
+ "minimum": "1.0",
31
+ "maximum": "100000.0"
32
+ }
33
+ ],
34
+ "responseMessages": [
35
+ {
36
+ "code": 400,
37
+ "message": "Invalid ID supplied"
38
+ },
39
+ {
40
+ "code": 404,
41
+ "message": "Pet not found"
42
+ }
43
+ ]
44
+ },
45
+ {
46
+ "method": "PATCH",
47
+ "summary": "partial updates to a pet",
48
+ "notes": "",
49
+ "type": "array",
50
+ "items": {
51
+ "$ref": "Pet"
52
+ },
53
+ "nickname": "partialUpdate",
54
+ "produces": [
55
+ "application/json",
56
+ "application/xml"
57
+ ],
58
+ "consumes": [
59
+ "application/json",
60
+ "application/xml"
61
+ ],
62
+ "authorizations": {
63
+ "oauth2": [
64
+ {
65
+ "scope": "test:anything",
66
+ "description": "anything"
67
+ }
68
+ ]
69
+ },
70
+ "parameters": [
71
+ {
72
+ "name": "petId",
73
+ "description": "ID of pet that needs to be fetched",
74
+ "required": true,
75
+ "type": "string",
76
+ "paramType": "path"
77
+ },
78
+ {
79
+ "name": "body",
80
+ "description": "Pet object that needs to be added to the store",
81
+ "required": true,
82
+ "type": "Pet",
83
+ "paramType": "body"
84
+ }
85
+ ],
86
+ "responseMessages": [
87
+ {
88
+ "code": 400,
89
+ "message": "Invalid tag value"
90
+ }
91
+ ]
92
+ }
93
+ ]
94
+ },
95
+ {
96
+ "path": "/pet/findByStatus",
97
+ "operations": [
98
+ {
99
+ "method": "GET",
100
+ "summary": "Finds Pets by status",
101
+ "notes": "Multiple status values can be provided with comma seperated strings",
102
+ "type": "array",
103
+ "items": {
104
+ "$ref": "Pet"
105
+ },
106
+ "nickname": "findPetsByStatus",
107
+ "parameters": [
108
+ {
109
+ "name": "status",
110
+ "description": "Status values that need to be considered for filter",
111
+ "defaultValue": "available",
112
+ "required": true,
113
+ "type": "string",
114
+ "paramType": "query",
115
+ "enum": [
116
+ "available",
117
+ "pending",
118
+ "sold"
119
+ ]
120
+ }
121
+ ],
122
+ "responseMessages": [
123
+ {
124
+ "code": 400,
125
+ "message": "Invalid status value"
126
+ }
127
+ ]
128
+ }
129
+ ]
130
+ }
131
+ ],
132
+ "models": {
133
+ "Tag": {
134
+ "id": "Tag",
135
+ "properties": {
136
+ "id": {
137
+ "type": "integer",
138
+ "format": "int64"
139
+ },
140
+ "name": {
141
+ "type": "string"
142
+ }
143
+ }
144
+ },
145
+ "Pet": {
146
+ "id": "Pet",
147
+ "required": [
148
+ "id",
149
+ "name"
150
+ ],
151
+ "properties": {
152
+ "id": {
153
+ "type": "integer",
154
+ "format": "int64",
155
+ "description": "unique identifier for the pet",
156
+ "minimum": "0.0",
157
+ "maximum": "100.0"
158
+ },
159
+ "category": {
160
+ "$ref": "Category"
161
+ },
162
+ "name": {
163
+ "type": "string"
164
+ },
165
+ "photoUrls": {
166
+ "type": "array",
167
+ "items": {
168
+ "type": "string"
169
+ }
170
+ },
171
+ "tags": {
172
+ "type": "array",
173
+ "items": {
174
+ "$ref": "Tag"
175
+ }
176
+ },
177
+ "status": {
178
+ "type": "string",
179
+ "description": "pet status in the store",
180
+ "enum": [
181
+ "available",
182
+ "pending",
183
+ "sold"
184
+ ]
185
+ }
186
+ }
187
+ },
188
+ "Category": {
189
+ "id": "Category",
190
+ "properties": {
191
+ "id": {
192
+ "type": "integer",
193
+ "format": "int64"
194
+ },
195
+ "name": {
196
+ "type": "string"
197
+ }
198
+ }
199
+ }
200
+ }
201
+ }
@@ -0,0 +1,371 @@
1
+ require 'json'
2
+ require 'swagger/blocks'
3
+
4
+ # Test data originally based on the Swagger UI example data:
5
+ # https://github.com/wordnik/swagger-codegen/blob/master/src/test/resources/petstore-1.2/api-docs
6
+ RESOURCE_LISTING_JSON = open(File.expand_path('../swagger_resource_listing.json', __FILE__)).read
7
+ # https://github.com/wordnik/swagger-codegen/blob/master/src/test/resources/petstore-1.2/pet
8
+ API_DECLARATION_JSON = open(File.expand_path('../swagger_api_declaration.json', __FILE__)).read
9
+
10
+ class PetController
11
+ include Swagger::Blocks
12
+
13
+ swagger_root do
14
+ key :swaggerVersion, '1.2'
15
+ key :apiVersion, '1.0.0'
16
+ info do
17
+ key :title, 'Swagger Sample App'
18
+ key :description, "This is a sample server Petstore server. You can find out more about Swagger \n at <a href=\"http://swagger.wordnik.com\">http://swagger.wordnik.com</a> or on irc.freenode.net, #swagger. For this sample,\n you can use the api key \"special-key\" to test the authorization filters"
19
+ key :termsOfServiceUrl, 'http://helloreverb.com/terms/'
20
+ key :contact, 'apiteam@wordnik.com'
21
+ key :license, 'Apache 2.0'
22
+ key :licenseUrl, 'http://www.apache.org/licenses/LICENSE-2.0.html'
23
+ end
24
+ api do
25
+ key :path, '/pet'
26
+ key :description, 'Operations about pets'
27
+ end
28
+ api do
29
+ key :path, '/user'
30
+ key :description, 'Operations about user'
31
+ end
32
+ api do
33
+ key :path, '/store'
34
+ key :description, 'Operations about store'
35
+ end
36
+ authorization :oauth2 do
37
+ key :type, 'oauth2'
38
+ scope do
39
+ key :scope, 'email'
40
+ key :description, 'Access to your email address'
41
+ end
42
+ scope do
43
+ key :scope, 'pets'
44
+ key :description, 'Access to your pets'
45
+ end
46
+ grant_type :implicit do
47
+ login_endpoint do
48
+ key :url, 'http://petstore.swagger.wordnik.com/oauth/dialog'
49
+ end
50
+ key :tokenName, 'access_token'
51
+ end
52
+ grant_type :authorization_code do
53
+ token_request_endpoint do
54
+ key :url, 'http://petstore.swagger.wordnik.com/oauth/requestToken'
55
+ key :clientIdName, 'client_id'
56
+ key :clientSecretName, 'client_secret'
57
+ end
58
+ token_endpoint do
59
+ key :url, 'http://petstore.swagger.wordnik.com/oauth/token'
60
+ key :tokenName, 'access_code'
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ # All swagger_api_root declarations with the same key will be merged.
67
+ swagger_api_root :pets do
68
+ key :swaggerVersion, '1.2'
69
+ key :apiVersion, '1.0.0'
70
+ key :basePath, 'http://petstore.swagger.wordnik.com/api'
71
+ key :resourcePath, '/pet'
72
+ key :produces, [
73
+ 'application/json',
74
+ 'application/xml',
75
+ 'text/plain',
76
+ 'text/html',
77
+ ]
78
+ api do
79
+ key :path, '/pet/{petId}'
80
+ operation do
81
+ key :method, 'GET'
82
+ key :summary, 'Find pet by ID'
83
+ key :notes, 'Returns a pet based on ID'
84
+ key :type, :Pet
85
+ key :nickname, :getPetById
86
+ parameter do
87
+ key :paramType, :path
88
+ key :name, :petId
89
+ key :description, 'ID of pet that needs to be fetched'
90
+ key :required, true
91
+ key :type, :integer
92
+ key :format, :int64
93
+ key :minimum, '1.0'
94
+ key :maximum, '100000.0'
95
+ end
96
+ response_message do
97
+ key :code, 400
98
+ key :message, 'Invalid ID supplied'
99
+ end
100
+ response_message do
101
+ key :code, 404
102
+ key :message, 'Pet not found'
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ swagger_api_root :pets do
109
+ api do
110
+ key :path, '/pet/{petId}'
111
+ operation do
112
+ key :method, 'PATCH'
113
+ key :summary, 'partial updates to a pet'
114
+ key :notes, ''
115
+ key :type, :array
116
+ key :nickname, :partialUpdate
117
+ items do
118
+ key :'$ref', :Pet
119
+ end
120
+ key :produces, [
121
+ 'application/json',
122
+ 'application/xml',
123
+ ]
124
+ key :consumes, [
125
+ 'application/json',
126
+ 'application/xml',
127
+ ]
128
+ authorization :oauth2 do
129
+ scope do
130
+ key :scope, 'test:anything'
131
+ key :description, 'anything'
132
+ end
133
+ end
134
+ parameter do
135
+ key :paramType, :path
136
+ key :name, :petId
137
+ key :description, 'ID of pet that needs to be fetched'
138
+ key :required, true
139
+ key :type, :string
140
+ end
141
+ parameter do
142
+ key :paramType, :body
143
+ key :name, :body
144
+ key :description, 'Pet object that needs to be added to the store'
145
+ key :required, true
146
+ key :type, :Pet
147
+ end
148
+ response_message do
149
+ key :code, 400
150
+ key :message, 'Invalid tag value'
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ swagger_api_root :pets do
157
+ api do
158
+ key :path, '/pet/findByStatus'
159
+ operation do
160
+ key :method, 'GET'
161
+ key :summary, 'Finds Pets by status'
162
+ key :notes, 'Multiple status values can be provided with comma seperated strings'
163
+ key :type, :array
164
+ key :nickname, :findPetsByStatus
165
+ items do
166
+ key :'$ref', :Pet
167
+ end
168
+ parameter do
169
+ key :paramType, :query
170
+ key :name, :status
171
+ key :description, 'Status values that need to be considered for filter'
172
+ key :defaultValue, 'available'
173
+ key :required, true
174
+ key :type, :string
175
+ key :enum, [
176
+ 'available',
177
+ 'pending',
178
+ 'sold',
179
+ ]
180
+ end
181
+ response_message do
182
+ key :code, 400
183
+ key :message, 'Invalid status value'
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+
191
+ class StoreController
192
+ include Swagger::Blocks
193
+
194
+ swagger_api_root :stores do
195
+ key :path, '/store'
196
+ end
197
+ end
198
+
199
+
200
+ class UserController
201
+ include Swagger::Blocks
202
+
203
+ swagger_api_root :users do
204
+ key :path, '/user'
205
+ end
206
+ end
207
+
208
+
209
+ class TagModel
210
+ include Swagger::Blocks
211
+
212
+ swagger_model :Tag do
213
+ key :id, :Tag
214
+ property :id do
215
+ key :type, :integer
216
+ key :format, :int64
217
+ end
218
+ property :name do
219
+ key :type, :string
220
+ end
221
+ end
222
+ end
223
+
224
+
225
+ class OtherModelsContainer
226
+ include Swagger::Blocks
227
+
228
+ swagger_model :Pet do
229
+ key :id, :Pet
230
+ key :required, [:id, :name]
231
+ property :id do
232
+ key :type, :integer
233
+ key :format, :int64
234
+ key :description, 'unique identifier for the pet'
235
+ key :minimum, '0.0'
236
+ key :maximum, '100.0'
237
+ end
238
+ property :category do
239
+ key :'$ref', :Category
240
+ end
241
+ property :name do
242
+ key :type, :string
243
+ end
244
+ property :photoUrls do
245
+ key :type, :array
246
+ items do
247
+ key :type, :string
248
+ end
249
+ end
250
+ property :tags do
251
+ key :type, :array
252
+ items do
253
+ key :'$ref', :Tag
254
+ end
255
+ end
256
+ property :status do
257
+ key :type, :string
258
+ key :description, 'pet status in the store'
259
+ key :enum, [:available, :pending, :sold]
260
+ end
261
+ end
262
+
263
+ swagger_model :Category do
264
+ key :id, :Category
265
+ property :id do
266
+ key :type, :integer
267
+ key :format, :int64
268
+ end
269
+ property :name do
270
+ key :type, :string
271
+ end
272
+ end
273
+ end
274
+
275
+
276
+ class BlankController; end
277
+
278
+
279
+ describe Swagger::Blocks do
280
+ describe 'build_root_json' do
281
+ it 'outputs the correct data' do
282
+ swaggered_classes = [
283
+ PetController,
284
+ UserController,
285
+ StoreController,
286
+ TagModel,
287
+ OtherModelsContainer,
288
+ ]
289
+ actual = Swagger::Blocks.build_root_json(swaggered_classes)
290
+
291
+ # Multiple expectations for better test diff output.
292
+ actual = JSON.parse(actual.to_json) # For access consistency.
293
+ data = JSON.parse(RESOURCE_LISTING_JSON)
294
+ expect(actual['info']).to eq(data['info'])
295
+ expect(actual['authorizations']).to eq(data['authorizations'])
296
+ actual['apis'].each_with_index do |api_data, i|
297
+ expect(api_data).to eq(data['apis'][i])
298
+ end
299
+ expect(actual).to eq(data)
300
+ end
301
+ it 'is idempotent' do
302
+ swaggered_classes = [PetController, UserController, StoreController]
303
+ actual = JSON.parse(Swagger::Blocks.build_root_json(swaggered_classes).to_json)
304
+ actual = JSON.parse(Swagger::Blocks.build_root_json(swaggered_classes).to_json)
305
+ data = JSON.parse(RESOURCE_LISTING_JSON)
306
+ expect(actual).to eq(data)
307
+ end
308
+ it 'errors if no swagger_root is declared' do
309
+ expect {
310
+ Swagger::Blocks.build_root_json([])
311
+ }.to raise_error(Swagger::Blocks::DeclarationError)
312
+ end
313
+ it 'errors if mulitple swagger_roots are declared' do
314
+ expect {
315
+ Swagger::Blocks.build_root_json([PetController, PetController])
316
+ }.to raise_error(Swagger::Blocks::DeclarationError)
317
+ end
318
+ it 'does not error if given non-swaggered classes' do
319
+ Swagger::Blocks.build_root_json([PetController, BlankController])
320
+ end
321
+ end
322
+ describe 'build_api_json' do
323
+ it 'outputs the correct data' do
324
+ swaggered_classes = [
325
+ PetController,
326
+ UserController,
327
+ StoreController,
328
+ TagModel,
329
+ OtherModelsContainer,
330
+ ]
331
+ actual = Swagger::Blocks.build_api_json(:pets, swaggered_classes)
332
+
333
+ # Multiple expectations for better test diff output.
334
+ actual = JSON.parse(actual.to_json) # For access consistency.
335
+ data = JSON.parse(API_DECLARATION_JSON)
336
+ expect(actual['apis'][0]).to be
337
+ expect(actual['apis'][0]['operations']).to be
338
+ expect(actual['apis'][0]['operations'][0]).to be
339
+ expect(actual['apis'][0]['operations'][1]).to be
340
+ expect(actual['apis'][0]['operations'][0]).to eq(data['apis'][0]['operations'][0])
341
+ expect(actual['apis'][0]['operations'][1]).to eq(data['apis'][0]['operations'][1])
342
+ expect(actual['apis'][0]['operations']).to eq(data['apis'][0]['operations'])
343
+ expect(actual['apis']).to eq(data['apis'])
344
+ expect(actual['models']).to eq(data['models'])
345
+ expect(actual).to eq(data)
346
+ end
347
+ it 'is idempotent' do
348
+ swaggered_classes = [
349
+ PetController,
350
+ UserController,
351
+ StoreController,
352
+ TagModel,
353
+ OtherModelsContainer,
354
+ ]
355
+ actual = JSON.parse(Swagger::Blocks.build_api_json(:pets, swaggered_classes).to_json)
356
+ actual = JSON.parse(Swagger::Blocks.build_api_json(:pets, swaggered_classes).to_json)
357
+ data = JSON.parse(API_DECLARATION_JSON)
358
+ expect(actual).to eq(data)
359
+ end
360
+ it 'errors if no swagger_root is declared' do
361
+ expect {
362
+ Swagger::Blocks.build_root_json([])
363
+ }.to raise_error(Swagger::Blocks::DeclarationError)
364
+ end
365
+ it 'errors if mulitple swagger_roots are declared' do
366
+ expect {
367
+ Swagger::Blocks.build_root_json([PetController, PetController])
368
+ }.to raise_error(Swagger::Blocks::DeclarationError)
369
+ end
370
+ end
371
+ end
@@ -0,0 +1,60 @@
1
+ {
2
+ "apiVersion": "1.0.0",
3
+ "swaggerVersion": "1.2",
4
+ "apis": [
5
+ {
6
+ "path": "/pet",
7
+ "description": "Operations about pets"
8
+ },
9
+ {
10
+ "path": "/user",
11
+ "description": "Operations about user"
12
+ },
13
+ {
14
+ "path": "/store",
15
+ "description": "Operations about store"
16
+ }
17
+ ],
18
+ "authorizations": {
19
+ "oauth2": {
20
+ "type": "oauth2",
21
+ "scopes": [
22
+ {
23
+ "scope": "email",
24
+ "description": "Access to your email address"
25
+ },
26
+ {
27
+ "scope": "pets",
28
+ "description": "Access to your pets"
29
+ }
30
+ ],
31
+ "grantTypes": {
32
+ "implicit": {
33
+ "loginEndpoint": {
34
+ "url": "http://petstore.swagger.wordnik.com/oauth/dialog"
35
+ },
36
+ "tokenName": "access_token"
37
+ },
38
+ "authorization_code": {
39
+ "tokenRequestEndpoint": {
40
+ "url": "http://petstore.swagger.wordnik.com/oauth/requestToken",
41
+ "clientIdName": "client_id",
42
+ "clientSecretName": "client_secret"
43
+ },
44
+ "tokenEndpoint": {
45
+ "url": "http://petstore.swagger.wordnik.com/oauth/token",
46
+ "tokenName": "access_code"
47
+ }
48
+ }
49
+ }
50
+ }
51
+ },
52
+ "info": {
53
+ "title": "Swagger Sample App",
54
+ "description": "This is a sample server Petstore server. You can find out more about Swagger \n at <a href=\"http://swagger.wordnik.com\">http://swagger.wordnik.com</a> or on irc.freenode.net, #swagger. For this sample,\n you can use the api key \"special-key\" to test the authorization filters",
55
+ "termsOfServiceUrl": "http://helloreverb.com/terms/",
56
+ "contact": "apiteam@wordnik.com",
57
+ "license": "Apache 2.0",
58
+ "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html"
59
+ }
60
+ }
@@ -0,0 +1,42 @@
1
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
2
+ RSpec.configure do |config|
3
+ # Allow more verbose output when running an individual spec file.
4
+ if config.files_to_run.one?
5
+ config.default_formatter = 'doc'
6
+ end
7
+
8
+ # Run specs in random order to surface order dependencies. If you find an
9
+ # order dependency and want to debug it, you can fix the order by providing
10
+ # the seed, which is printed after each run.
11
+ # --seed 1234
12
+ config.order = :random
13
+
14
+ # Seed global randomization in this process using the `--seed` CLI option.
15
+ # Setting this allows you to use `--seed` to deterministically reproduce
16
+ # test failures related to randomization by passing the same `--seed` value
17
+ # as the one that triggered the failure.
18
+ Kernel.srand config.seed
19
+
20
+ # rspec-expectations config goes here. You can use an alternate
21
+ # assertion/expectation library such as wrong or the stdlib/minitest
22
+ # assertions if you prefer.
23
+ config.expect_with :rspec do |expectations|
24
+ # Enable only the newer, non-monkey-patching expect syntax.
25
+ # For more details, see:
26
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
27
+ expectations.syntax = :expect
28
+ end
29
+
30
+ # rspec-mocks config goes here. You can use an alternate test double
31
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
32
+ config.mock_with :rspec do |mocks|
33
+ # Enable only the newer, non-monkey-patching expect syntax.
34
+ # For more details, see:
35
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
36
+ mocks.syntax = :expect
37
+
38
+ # Prevents you from mocking or stubbing a method that does not exist on
39
+ # a real object. This is generally recommended.
40
+ mocks.verify_partial_doubles = true
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'swagger/blocks/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'swagger-blocks'
8
+ spec.version = Swagger::Blocks::VERSION
9
+ spec.authors = ['Mike Fotinakis']
10
+ spec.email = ['mike@fotinakis.com']
11
+ spec.summary = %q{Define and generate Swagger JSON files in Ruby.}
12
+ spec.description = %q{}
13
+ spec.homepage = 'https://github.com/fotinakis/swagger-blocks'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler'
22
+ spec.add_development_dependency 'rake'
23
+ spec.add_development_dependency 'rspec'
24
+ spec.add_development_dependency 'pry'
25
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: swagger-blocks
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mike Fotinakis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
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: rake
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: rspec
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: pry
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
+ - mike@fotinakis.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - LICENSE
81
+ - README.md
82
+ - Rakefile
83
+ - lib/swagger/blocks.rb
84
+ - lib/swagger/blocks/version.rb
85
+ - spec/lib/swagger_api_declaration.json
86
+ - spec/lib/swagger_blocks_spec.rb
87
+ - spec/lib/swagger_resource_listing.json
88
+ - spec/spec_helper.rb
89
+ - swagger-blocks.gemspec
90
+ homepage: https://github.com/fotinakis/swagger-blocks
91
+ licenses:
92
+ - MIT
93
+ metadata: {}
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 2.4.1
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: Define and generate Swagger JSON files in Ruby.
114
+ test_files:
115
+ - spec/lib/swagger_api_declaration.json
116
+ - spec/lib/swagger_blocks_spec.rb
117
+ - spec/lib/swagger_resource_listing.json
118
+ - spec/spec_helper.rb
119
+ has_rdoc: