swaggable 0.4.0 → 0.5.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 +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +13 -0
- data/Gemfile +9 -3
- data/README.md +86 -11
- data/lib/swaggable.rb +5 -0
- data/lib/swaggable/api_definition.rb +10 -0
- data/lib/swaggable/attribute_definition.rb +72 -0
- data/lib/swaggable/definition_base.rb +15 -0
- data/lib/swaggable/endpoint_definition.rb +1 -6
- data/lib/swaggable/enumerable_attributes.rb +29 -0
- data/lib/swaggable/grape_adapter.rb +4 -0
- data/lib/swaggable/grape_entity_translator.rb +38 -0
- data/lib/swaggable/parameter_definition.rb +11 -26
- data/lib/swaggable/rack_app.rb +4 -0
- data/lib/swaggable/response_definition.rb +1 -6
- data/lib/swaggable/schema_definition.rb +36 -0
- data/lib/swaggable/swagger_2_serializer.rb +42 -1
- data/lib/swaggable/swagger_2_validator.rb +5 -0
- data/lib/swaggable/tag_definition.rb +1 -6
- data/lib/swaggable/version.rb +1 -1
- data/spec/spec_helper.rb +7 -0
- data/spec/swaggable/api_definition_spec.rb +49 -1
- data/spec/swaggable/attribute_definition_spec.rb +87 -0
- data/spec/swaggable/grape_adapter_spec.rb +22 -0
- data/spec/swaggable/grape_entity_translator_spec.rb +64 -0
- data/spec/swaggable/integration_spec.rb +50 -6
- data/spec/swaggable/parameter_definition_spec.rb +27 -2
- data/spec/swaggable/rack_app_spec.rb +10 -0
- data/spec/swaggable/schema_definition_spec.rb +51 -0
- data/spec/swaggable/swagger_2_serializer_spec.rb +132 -1
- data/spec/swaggable/swagger_2_validator_spec.rb +15 -0
- data/swaggable.gemspec +3 -3
- metadata +28 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f8d7d4f89aa6aa7a1e59f301405bc5b59243bc1
|
4
|
+
data.tar.gz: ee74ad314bbcfa0d5348a38545524cac1630456a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9333877ec1e0a9f1e0c65cb5c5ecd28005ca454a807ff071747df5a143bb1fdfa8591d9dd82afc48f96fabd45d24232b7de06f4ca3d9d1f3d3869f45945fcad6
|
7
|
+
data.tar.gz: e21ae9c163049d6cd6da2e95f9c1d9629b847df05f27b3adf81159e40b398428c47be7cb24b1c81bb7cdd7a21a4602a26b92d83ab636635764b4a09bd9e0276b
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -4,11 +4,17 @@ gemspec
|
|
4
4
|
|
5
5
|
group :test, :development do
|
6
6
|
gem 'gem-release'
|
7
|
-
gem 'rspec'
|
8
7
|
gem 'pry'
|
9
8
|
gem 'rerun'
|
10
|
-
gem 'grape', '~> 0.11.0'
|
11
|
-
gem 'rack-test'
|
12
9
|
gem 'yard'
|
13
10
|
gem 'webmock'
|
14
11
|
end
|
12
|
+
|
13
|
+
group :test do
|
14
|
+
gem 'rspec'
|
15
|
+
gem 'rack-test'
|
16
|
+
gem 'codeclimate-test-reporter'
|
17
|
+
gem 'grape', '~> 0.11.0'
|
18
|
+
gem 'grape-entity', '~> 0.4.5'
|
19
|
+
end
|
20
|
+
|
data/README.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Swaggable
|
2
2
|
|
3
|
+
[](http://badge.fury.io/rb/swaggable)
|
4
|
+
[](https://travis-ci.org/workshare/swaggable)
|
5
|
+
[](https://codeclimate.com/github/workshare/swaggable)
|
6
|
+
|
3
7
|
Flexible swagger documentation generation tool.
|
4
8
|
Allows building a Rack application that
|
5
9
|
serves [Swagger 2](http://swagger.io/) documentation
|
@@ -16,6 +20,11 @@ api_def = Swaggable::ApiDefinition.from_grape_api(UsersApi)
|
|
16
20
|
rack_app = Swaggable::RackApp.new(api_definition: api_def)
|
17
21
|
```
|
18
22
|
|
23
|
+
Mount your rack app as `swagger.json` and you can point Swagger UI to it.
|
24
|
+
|
25
|
+
|
26
|
+
## Working with Grape
|
27
|
+
|
19
28
|
You can import several Grape APIs:
|
20
29
|
|
21
30
|
```ruby
|
@@ -34,18 +43,70 @@ api_def.endpoints['GET /users/{id}'].parameters['filter'].description = 'Allows
|
|
34
43
|
api_def.endpoints['GET /users/{id}'].responses[403].description = 'Forbidden'
|
35
44
|
```
|
36
45
|
|
46
|
+
It supports status codes and entities. A more complex Grape example here:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
class TeamCreateEntity < Grape::Entity
|
50
|
+
def self.name
|
51
|
+
'create team payload'
|
52
|
+
end
|
53
|
+
|
54
|
+
expose :name, documentation: {
|
55
|
+
type: 'String',
|
56
|
+
desc: 'Name for the Team',
|
57
|
+
required: true,
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
class TeamsApi < Grape::API
|
62
|
+
format :json
|
63
|
+
content_type :json, 'application/json'
|
64
|
+
version 'v1.0', using: :path, vendor: :workshare
|
65
|
+
prefix "api"
|
66
|
+
|
67
|
+
route_param :account_uuid do
|
68
|
+
resource :teams do
|
69
|
+
desc "Creates a new team for such account"
|
70
|
+
|
71
|
+
params do
|
72
|
+
requires :account_uuid, type: String, desc: 'UUID of the account to be associated with the new team'
|
73
|
+
end
|
74
|
+
|
75
|
+
codes = default_codes + [
|
76
|
+
[201, 'Created'],
|
77
|
+
[422, 'Validations failed'],
|
78
|
+
[404, 'Account not found']
|
79
|
+
]
|
80
|
+
|
81
|
+
post(http_codes: codes, entity: TeamCreateEntity) do
|
82
|
+
## Some action here...
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
api_def = Swaggable::ApiDefinition.from_grape_api(TeamsApi)
|
89
|
+
rack_app = Swaggable::RackApp.new(api_definition: api_def)
|
90
|
+
```
|
91
|
+
|
92
|
+
|
93
|
+
## Validating the resultant Swagger JSON
|
94
|
+
|
37
95
|
Validate the results against the corresponding schema in your tests:
|
38
96
|
|
39
97
|
```ruby
|
40
98
|
it "validates" do
|
41
|
-
expect(rack_app.validate
|
99
|
+
expect(rack_app.validate).to eq []
|
42
100
|
end
|
43
101
|
```
|
44
102
|
|
103
|
+
|
104
|
+
## Working directly with the DSL
|
105
|
+
|
45
106
|
Define the API without Grape:
|
46
107
|
|
47
108
|
```ruby
|
48
|
-
|
109
|
+
Swaggable::ApiDefinition.new do
|
49
110
|
version '1.0'
|
50
111
|
title 'My API'
|
51
112
|
description 'A test API'
|
@@ -53,9 +114,9 @@ api = Swaggable::ApiDefinition.new do
|
|
53
114
|
|
54
115
|
endpoints.add_new do
|
55
116
|
path '/users/{id}'
|
56
|
-
verb :
|
57
|
-
description '
|
58
|
-
summary '
|
117
|
+
verb :put
|
118
|
+
description 'Updates an user'
|
119
|
+
summary 'Updates attributes of such user'
|
59
120
|
|
60
121
|
tags.add_new do
|
61
122
|
name 'Users'
|
@@ -70,6 +131,24 @@ api = Swaggable::ApiDefinition.new do
|
|
70
131
|
type :boolean # [:string, :number, :integer, :boolean, :array, :file, nil]
|
71
132
|
end
|
72
133
|
|
134
|
+
parameters.add_new do
|
135
|
+
name 'user'
|
136
|
+
description 'The new attributes for the user'
|
137
|
+
location :body
|
138
|
+
required true
|
139
|
+
|
140
|
+
schema do
|
141
|
+
name :user
|
142
|
+
|
143
|
+
attributes do
|
144
|
+
add_new do
|
145
|
+
name :first_name
|
146
|
+
type :string
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
73
152
|
responses.add_new do
|
74
153
|
status 200
|
75
154
|
description 'Success'
|
@@ -88,21 +167,17 @@ end
|
|
88
167
|
|
89
168
|
## TODO
|
90
169
|
|
91
|
-
* Support response specs.
|
92
170
|
* Document classes.
|
93
171
|
* Include Redirector.
|
94
|
-
* DSL.
|
95
|
-
* Swagger validation.
|
96
172
|
* Request & response validations.
|
97
|
-
*
|
98
|
-
|
173
|
+
* Response body schemas.
|
99
174
|
|
100
175
|
## Contributing
|
101
176
|
|
102
177
|
Do not forget to run the tests with:
|
103
178
|
|
104
179
|
```bash
|
105
|
-
rake
|
180
|
+
bundle exec rake
|
106
181
|
```
|
107
182
|
|
108
183
|
|
data/lib/swaggable.rb
CHANGED
@@ -8,7 +8,12 @@ module Swaggable
|
|
8
8
|
autoload :ResponseDefinition, 'swaggable/response_definition'
|
9
9
|
autoload :RackApp, 'swaggable/rack_app'
|
10
10
|
autoload :GrapeAdapter, 'swaggable/grape_adapter'
|
11
|
+
autoload :GrapeEntityTranslator, 'swaggable/grape_entity_translator'
|
11
12
|
autoload :Swagger2Serializer, 'swaggable/swagger_2_serializer'
|
12
13
|
autoload :Swagger2Validator, 'swaggable/swagger_2_validator'
|
13
14
|
autoload :EndpointIndex, 'swaggable/endpoint_index'
|
15
|
+
autoload :DefinitionBase, 'swaggable/definition_base'
|
16
|
+
autoload :EnumerableAttributes, 'swaggable/enumerable_attributes'
|
17
|
+
autoload :SchemaDefinition, 'swaggable/schema_definition'
|
18
|
+
autoload :AttributeDefinition, 'swaggable/attribute_definition'
|
14
19
|
end
|
@@ -26,6 +26,16 @@ module Swaggable
|
|
26
26
|
def tags
|
27
27
|
(endpoints.map(&:tags).reduce(:merge) || []).dup.freeze
|
28
28
|
end
|
29
|
+
|
30
|
+
def used_schemas
|
31
|
+
endpoints.inject([]) do |acc, endpoint|
|
32
|
+
endpoint.parameters.each do |parameter|
|
33
|
+
acc << parameter.schema unless parameter.schema.empty?
|
34
|
+
end
|
35
|
+
|
36
|
+
acc.uniq
|
37
|
+
end.freeze
|
38
|
+
end
|
29
39
|
|
30
40
|
def self.from_grape_api grape
|
31
41
|
grape_adapter.import(grape, new)
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Swaggable
|
2
|
+
class AttributeDefinition
|
3
|
+
include DefinitionBase
|
4
|
+
|
5
|
+
getsetter(
|
6
|
+
:name,
|
7
|
+
:description,
|
8
|
+
:required,
|
9
|
+
)
|
10
|
+
|
11
|
+
attr_enum :type, [
|
12
|
+
:integer,
|
13
|
+
:long,
|
14
|
+
:float,
|
15
|
+
:double,
|
16
|
+
:string,
|
17
|
+
:byte,
|
18
|
+
:boolean,
|
19
|
+
:date,
|
20
|
+
:date_time,
|
21
|
+
:password,
|
22
|
+
]
|
23
|
+
|
24
|
+
def json_type
|
25
|
+
json_type_hash.fetch(type)
|
26
|
+
end
|
27
|
+
|
28
|
+
def json_format
|
29
|
+
json_format_hash.fetch(type)
|
30
|
+
end
|
31
|
+
|
32
|
+
def required?
|
33
|
+
!!required
|
34
|
+
end
|
35
|
+
|
36
|
+
def optional?
|
37
|
+
!required
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def json_type_hash
|
43
|
+
{
|
44
|
+
integer: :integer,
|
45
|
+
long: :integer,
|
46
|
+
float: :number,
|
47
|
+
double: :number,
|
48
|
+
string: :string,
|
49
|
+
byte: :string,
|
50
|
+
boolean: :boolean,
|
51
|
+
date: :string,
|
52
|
+
date_time: :string,
|
53
|
+
password: :string,
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def json_format_hash
|
58
|
+
{
|
59
|
+
integer: :int32,
|
60
|
+
long: :int64,
|
61
|
+
float: :float,
|
62
|
+
double: :double,
|
63
|
+
string: nil,
|
64
|
+
byte: :byte,
|
65
|
+
boolean: nil,
|
66
|
+
date: :date,
|
67
|
+
date_time: :"date-time",
|
68
|
+
password: :password,
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'forwarding_dsl'
|
2
|
+
|
3
|
+
module Swaggable
|
4
|
+
module DefinitionBase
|
5
|
+
def self.included klass
|
6
|
+
klass.send :include, ForwardingDsl::Getsetter
|
7
|
+
klass.send :include, EnumerableAttributes
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize args = {}, &block
|
11
|
+
args.each {|k, v| self.send("#{k}=", v) }
|
12
|
+
ForwardingDsl.run(self, &block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -3,7 +3,7 @@ require 'mini_object'
|
|
3
3
|
|
4
4
|
module Swaggable
|
5
5
|
class EndpointDefinition
|
6
|
-
include
|
6
|
+
include DefinitionBase
|
7
7
|
|
8
8
|
getsetter(
|
9
9
|
:path,
|
@@ -12,11 +12,6 @@ module Swaggable
|
|
12
12
|
:summary,
|
13
13
|
)
|
14
14
|
|
15
|
-
def initialize args = {}, &block
|
16
|
-
args.each {|k, v| self.send("#{k}=", v) }
|
17
|
-
configure(&block) if block_given?
|
18
|
-
end
|
19
|
-
|
20
15
|
def tags
|
21
16
|
@tags ||= MiniObject::IndexedList.new.tap do |l|
|
22
17
|
l.build { TagDefinition.new }
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'forwarding_dsl'
|
2
|
+
|
3
|
+
module Swaggable
|
4
|
+
module EnumerableAttributes
|
5
|
+
def self.included klass
|
6
|
+
klass.send :include, ForwardingDsl::Getsetter
|
7
|
+
klass.send :extend, ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def attr_enum name, choices
|
12
|
+
getsetter name
|
13
|
+
|
14
|
+
class_variable_set :"@@valid_#{name}_list", choices
|
15
|
+
|
16
|
+
define_method "#{name}=" do |value|
|
17
|
+
valid_values = self.class.class_variable_get :"@@valid_#{name}_list"
|
18
|
+
|
19
|
+
unless valid_values.include? value
|
20
|
+
raise ArgumentError.new("#{value.inspect} is not one a valid #{name}: #{valid_values.map(&:inspect).join(", ")}")
|
21
|
+
end
|
22
|
+
|
23
|
+
instance_variable_set(:"@#{name}", value)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -73,6 +73,10 @@ module Swaggable
|
|
73
73
|
api_endpoint.parameters << parameter_from(name, options, grape_endpoint)
|
74
74
|
end
|
75
75
|
|
76
|
+
if entity = grape_endpoint.route_entity
|
77
|
+
api_endpoint.parameters << GrapeEntityTranslator.parameter_from(entity)
|
78
|
+
end
|
79
|
+
|
76
80
|
(grape_endpoint.route_http_codes || []).each do |status, desc, entity|
|
77
81
|
api_endpoint.responses.add_new do |r|
|
78
82
|
r.status = status
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Swaggable
|
2
|
+
module GrapeEntityTranslator
|
3
|
+
def self.parameter_from entity
|
4
|
+
ParameterDefinition.new do
|
5
|
+
location :body
|
6
|
+
name entity.name
|
7
|
+
schema.name entity.name
|
8
|
+
|
9
|
+
entity.exposures.each do |name, opts|
|
10
|
+
schema.attributes.add_new do
|
11
|
+
this.name name
|
12
|
+
type type_from_options(opts)
|
13
|
+
description description_from_options(opts)
|
14
|
+
required required_from_options(opts)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def self.type_from_options opts
|
23
|
+
documentation = opts[:documentation] || {}
|
24
|
+
type = documentation[:type] || 'string'
|
25
|
+
type.downcase.to_sym
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.description_from_options opts
|
29
|
+
documentation = opts[:documentation] || {}
|
30
|
+
documentation[:desc]
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.required_from_options opts
|
34
|
+
documentation = opts[:documentation] || {}
|
35
|
+
documentation[:required]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -2,49 +2,34 @@ require 'mini_object'
|
|
2
2
|
|
3
3
|
module Swaggable
|
4
4
|
class ParameterDefinition
|
5
|
-
include
|
5
|
+
include DefinitionBase
|
6
6
|
|
7
7
|
getsetter(
|
8
8
|
:name,
|
9
9
|
:description,
|
10
|
-
:location,
|
11
10
|
:required,
|
12
11
|
:type,
|
12
|
+
:schema,
|
13
13
|
)
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
yield self if block_given?
|
18
|
-
end
|
15
|
+
attr_enum :location, [:path, :query, :header, :body, :form, nil]
|
16
|
+
attr_enum :type, [:string, :number, :integer, :boolean, :array, :file, nil]
|
19
17
|
|
20
18
|
def required?
|
21
19
|
!!required
|
22
20
|
end
|
23
21
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
@location = location
|
30
|
-
end
|
31
|
-
|
32
|
-
def type= type
|
33
|
-
unless valid_types.include? type
|
34
|
-
raise ArgumentError.new("#{type} is not one of the valid types: #{valid_types.join(", ")}")
|
35
|
-
end
|
36
|
-
|
37
|
-
@type = type
|
22
|
+
def schema &block
|
23
|
+
ForwardingDsl.run(
|
24
|
+
@schema ||= build_schema,
|
25
|
+
&block
|
26
|
+
)
|
38
27
|
end
|
39
28
|
|
40
29
|
private
|
41
30
|
|
42
|
-
def
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
def valid_types
|
47
|
-
[:string, :number, :integer, :boolean, :array, :file, nil]
|
31
|
+
def build_schema
|
32
|
+
SchemaDefinition.new
|
48
33
|
end
|
49
34
|
end
|
50
35
|
end
|