swaggable 0.4.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/swaggable.svg)](http://badge.fury.io/rb/swaggable)
|
4
|
+
[![Build Status](https://travis-ci.org/workshare/swaggable.svg)](https://travis-ci.org/workshare/swaggable)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/workshare/swaggable/badges/gpa.svg)](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
|