swagger-blocks 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 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: