sinatra-swagger-exposer 0.0.1 → 0.1.0
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/CHANGELOG.md +9 -0
- data/README.md +97 -0
- data/example/petstore.rb +48 -5
- data/lib/sinatra/swagger-exposer/swagger-endpoint-parameter.rb +76 -10
- data/lib/sinatra/swagger-exposer/swagger-endpoint-response.rb +2 -2
- data/lib/sinatra/swagger-exposer/swagger-endpoint.rb +10 -1
- data/lib/sinatra/swagger-exposer/swagger-exposer.rb +23 -54
- data/lib/sinatra/swagger-exposer/swagger-info.rb +4 -1
- data/lib/sinatra/swagger-exposer/swagger-parameter-preprocessor.rb +139 -0
- data/lib/sinatra/swagger-exposer/swagger-request-preprocessor.rb +52 -0
- data/lib/sinatra/swagger-exposer/swagger-type-property.rb +1 -1
- data/lib/sinatra/swagger-exposer/swagger-type.rb +42 -18
- data/lib/sinatra/swagger-exposer/swagger-utilities.rb +34 -8
- data/lib/sinatra/swagger-exposer/version.rb +1 -1
- data/sinatra-swagger-exposer.gemspec +1 -1
- metadata +8 -5
- data/README.asciidoc +0 -75
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa97395699d5708bb599a1793df03d11ee73cc6a
|
4
|
+
data.tar.gz: 8dcad9fd2667784b9a6ba79362e6109b344779d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea832406300901d1975504d66725fec8669f465756c75abf68fdf8e3a58af5a3a530659acb50887c2ebceccd0b9287daf89cfc4e769a3365659b05bd53d16a76
|
7
|
+
data.tar.gz: 995af3f7a7e77b2ba82ef9644856d324b9f96cc8acf582f8fdd448f3f69ebcee34a44075ae1c84fd7a066bce44ea2af4fdf59fe681554a129d88e0017fbd6834
|
data/CHANGELOG.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# Sinatra::SwaggerExposer
|
2
|
+
|
3
|
+
[](https://codeclimate.com/github/archiloque/sinatra-swagger-exposer)
|
4
|
+
[](https://travis-ci.org/archiloque/sinatra-swagger-exposer)
|
5
|
+
[](https://coveralls.io/r/archiloque/sinatra-swagger-exposer?branch=master)
|
6
|
+
|
7
|
+
Create Swagger endpoint for your Sinatra application.
|
8
|
+
|
9
|
+
This Sinatra extension enable you to add metadata to your code to
|
10
|
+
|
11
|
+
- expose your API as a [Swagger](http://swagger.io) endpoint.
|
12
|
+
- validate and enrich the invocation parameters
|
13
|
+
|
14
|
+
I'm adding features as I need them and it currently doesn't use all the Swagger options, so if you need one that is missing please open an issue.
|
15
|
+
|
16
|
+
## Design choices
|
17
|
+
|
18
|
+
- All the declarations are validated when the server is started
|
19
|
+
- The declarations are defined to look as ruby-ish as possible
|
20
|
+
- Declarations are used for parameters validation and enrichment
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
To use it in your app :
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
require 'sinatra/swagger-exposer/swagger-exposer'
|
28
|
+
|
29
|
+
class MyApp < Sinatra::Base
|
30
|
+
|
31
|
+
register Sinatra::SwaggerExposer
|
32
|
+
|
33
|
+
general_info(
|
34
|
+
{
|
35
|
+
version: '0.0.1',
|
36
|
+
title: 'My app',
|
37
|
+
description: 'My wonderful app',
|
38
|
+
license: {
|
39
|
+
name: 'MIT',
|
40
|
+
url: 'http://opensource.org/licenses/MIT'
|
41
|
+
}
|
42
|
+
}
|
43
|
+
)
|
44
|
+
|
45
|
+
type 'Status',
|
46
|
+
{
|
47
|
+
:properties => {
|
48
|
+
:status => {
|
49
|
+
:type => String,
|
50
|
+
:example => 'OK,
|
51
|
+
},
|
52
|
+
},
|
53
|
+
:required => [:status]
|
54
|
+
}
|
55
|
+
|
56
|
+
endpoint_description 'Base method to ping'
|
57
|
+
endpoint_response 200, 'Status', 'Standard response'
|
58
|
+
endpoint_tags 'Ping'
|
59
|
+
get '/' do
|
60
|
+
json({'status' => 'OK'})
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
The swagger json endpoint will be exposed at `/swagger_doc.json`.
|
67
|
+
|
68
|
+
## Detailed example
|
69
|
+
|
70
|
+
A more complete example is available [here](https://github.com/archiloque/sinatra-swagger-exposer/tree/master/example).
|
71
|
+
|
72
|
+
## About swagger-ui
|
73
|
+
|
74
|
+
- If you to use [swagger-ui](https://github.com/swagger-api/swagger-ui) with your app you will need to add croo-origin setup.
|
75
|
+
The easiest way is to use the [sinatra-cross_origin](https://github.com/britg/sinatra-cross_origin) gem. Fro a simple sample you can have a look at [example application](https://github.com/archiloque/sinatra-swagger-exposer/tree/master/example).
|
76
|
+
- Swagger-ui doesn't work with all the swagger features
|
77
|
+
- Some of them like parameters maximum and minimum values are ignored
|
78
|
+
- Some of them like extending types make the endpoint unusable
|
79
|
+
|
80
|
+
## Changes
|
81
|
+
|
82
|
+
Changelog is [here](https://github.com/archiloque/sinatra-swagger-exposer/blob/master/CHANGELOG.md).
|
83
|
+
|
84
|
+
## Resources
|
85
|
+
|
86
|
+
- [Swagger RESTful API Documentation Specification](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md).
|
87
|
+
- [Swagger json examples](https://github.com/swagger-api/swagger-spec/tree/master/examples/v2.0/json).
|
88
|
+
- [The swagger json schema](https://raw.githubusercontent.com/swagger-api/swagger-spec/master/schemas/v2.0/schema.json).
|
89
|
+
|
90
|
+
## Todo
|
91
|
+
|
92
|
+
- More parameters taken into account
|
93
|
+
- More validations where possible
|
94
|
+
|
95
|
+
## License
|
96
|
+
|
97
|
+
This software is released under the MIT license.
|
data/example/petstore.rb
CHANGED
@@ -74,28 +74,71 @@ class Petstore < Sinatra::Base
|
|
74
74
|
},
|
75
75
|
},
|
76
76
|
}
|
77
|
+
type 'Cat', {
|
78
|
+
# Not yet supported in swagger-ui, see https://github.com/swagger-api/swagger-js/issues/188
|
79
|
+
:extends => 'Pet',
|
80
|
+
:properties => {
|
81
|
+
:fluffy => {
|
82
|
+
:type => TrueClass,
|
83
|
+
:description => 'is this cat fluffy ?',
|
84
|
+
:example => true,
|
85
|
+
},
|
86
|
+
},
|
87
|
+
}
|
77
88
|
|
78
89
|
endpoint_summary 'Finds all the pets'
|
79
90
|
endpoint_description 'Returns all pets from the system that the user has access to'
|
80
91
|
endpoint_tags 'Pets'
|
81
92
|
endpoint_response 200, ['Pet'], 'Standard response'
|
93
|
+
endpoint_parameter :size, 'The number of pets to return', :query, false, Integer,
|
94
|
+
{
|
95
|
+
:example => 100,
|
96
|
+
:default => 20, # If the caller send no value the default value will be set in the params
|
97
|
+
:maximum => 100,
|
98
|
+
:minimum => 0,
|
99
|
+
:exclusiveMinimum => true,
|
100
|
+
}
|
82
101
|
get '/pets' do
|
83
102
|
content_type :json
|
84
103
|
[].to_json
|
85
104
|
end
|
86
105
|
|
106
|
+
endpoint_summary 'Finds all the cats'
|
107
|
+
endpoint_description 'Returns all cats from the system that the user has access to'
|
108
|
+
endpoint_tags 'Cats'
|
109
|
+
endpoint_response 200, ['Cat'], 'Standard response'
|
110
|
+
endpoint_parameter :size, 'The number of cats to return', :query, false, Integer,
|
111
|
+
{
|
112
|
+
:example => 100,
|
113
|
+
:default => 20, # If the caller send no value the default value will be set in the params
|
114
|
+
:maximum => 100,
|
115
|
+
:minimum => 0,
|
116
|
+
:exclusiveMinimum => true,
|
117
|
+
}
|
118
|
+
get '/cats' do
|
119
|
+
content_type :json
|
120
|
+
[].to_json
|
121
|
+
end
|
122
|
+
|
87
123
|
endpoint_summary 'Finds a pet by its id'
|
88
124
|
endpoint_description 'Finds a pet by its id, or 404 if the user does not have access to the pet'
|
89
125
|
endpoint_tags 'Pets'
|
90
126
|
endpoint_response 200, 'Pet', 'Standard response'
|
91
127
|
endpoint_response 404, 'Error', 'Pet not found'
|
92
|
-
endpoint_parameter :id, 'The pet id', :path, true,
|
93
|
-
|
94
|
-
|
95
|
-
|
128
|
+
endpoint_parameter :id, 'The pet id', :path, true, Integer, # Will fail if a non-numerical value is used
|
129
|
+
{
|
130
|
+
:example => 1234,
|
131
|
+
}
|
96
132
|
get '/pets/:id' do
|
97
133
|
content_type :json
|
98
|
-
[404, {:code => 404, :message => 'Pet not found'}]
|
134
|
+
[404, {:code => 404, :message => 'Pet not found'}.to_json]
|
135
|
+
end
|
136
|
+
|
137
|
+
# See https://github.com/britg/sinatra-cross_origin/issues/18
|
138
|
+
options '*' do
|
139
|
+
response.headers['Allow'] = 'HEAD,GET,PUT,POST,DELETE,OPTIONS'
|
140
|
+
response.headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Cache-Control, Accept'
|
141
|
+
200
|
99
142
|
end
|
100
143
|
|
101
144
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'swagger-invalid-exception'
|
2
|
+
require_relative 'swagger-parameter-preprocessor'
|
2
3
|
require_relative 'swagger-utilities'
|
3
4
|
|
4
5
|
module Sinatra
|
@@ -10,8 +11,33 @@ module Sinatra
|
|
10
11
|
include SwaggerUtilities
|
11
12
|
|
12
13
|
HOW_TO_PASS_BODY = 'body'
|
13
|
-
|
14
|
-
|
14
|
+
HOW_TO_PASS_HEADER = 'header'
|
15
|
+
HOW_TO_PASS_PATH = 'path'
|
16
|
+
HOW_TO_PASS_QUERY = 'query'
|
17
|
+
HOW_TO_PASS = [HOW_TO_PASS_PATH, HOW_TO_PASS_QUERY, HOW_TO_PASS_HEADER, 'formData', HOW_TO_PASS_BODY]
|
18
|
+
|
19
|
+
TYPE_INTEGER = 'integer'
|
20
|
+
TYPE_BOOLEAN = 'boolean'
|
21
|
+
TYPE_NUMBER = 'number'
|
22
|
+
TYPE_STRING = 'string'
|
23
|
+
PRIMITIVE_TYPES_FOR_NON_BODY = [TYPE_STRING, TYPE_NUMBER, TYPE_INTEGER, TYPE_BOOLEAN]
|
24
|
+
|
25
|
+
PARAMS_FORMAT = :format
|
26
|
+
PARAMS_DEFAULT = :default
|
27
|
+
PARAMS_EXAMPLE = :example
|
28
|
+
PARAMS_MAXIMUM = :maximum
|
29
|
+
PARAMS_MINIMUM = :minimum
|
30
|
+
PARAMS_EXCLUSIVE_MINIMUM = :exclusiveMinimum
|
31
|
+
PARAMS_EXCLUSIVE_MAXIMUM = :exclusiveMaximum
|
32
|
+
PARAMS_LIST = [
|
33
|
+
PARAMS_FORMAT,
|
34
|
+
PARAMS_DEFAULT,
|
35
|
+
PARAMS_EXAMPLE,
|
36
|
+
PARAMS_MAXIMUM,
|
37
|
+
PARAMS_MINIMUM,
|
38
|
+
PARAMS_EXCLUSIVE_MINIMUM,
|
39
|
+
PARAMS_EXCLUSIVE_MAXIMUM,
|
40
|
+
]
|
15
41
|
|
16
42
|
def initialize(name, description, how_to_pass, required, type, params, known_types)
|
17
43
|
unless name.is_a?(String) || name.is_a?(Symbol)
|
@@ -29,7 +55,7 @@ module Sinatra
|
|
29
55
|
|
30
56
|
how_to_pass = how_to_pass.to_s
|
31
57
|
unless HOW_TO_PASS.include? how_to_pass
|
32
|
-
raise SwaggerInvalidException.new("Unknown how to pass value [#{how_to_pass}], registered types
|
58
|
+
raise SwaggerInvalidException.new("Unknown how to pass value [#{how_to_pass}]#{list_or_none(HOW_TO_PASS, 'registered types')}")
|
33
59
|
end
|
34
60
|
@how_to_pass = how_to_pass
|
35
61
|
|
@@ -44,11 +70,20 @@ module Sinatra
|
|
44
70
|
end
|
45
71
|
@required = required
|
46
72
|
|
47
|
-
|
48
|
-
|
49
|
-
end
|
73
|
+
white_list_params(params, PARAMS_LIST)
|
74
|
+
validate_params(params)
|
50
75
|
@params = params
|
76
|
+
end
|
51
77
|
|
78
|
+
# Validate parameters
|
79
|
+
# @param params [Hash]
|
80
|
+
def validate_params(params)
|
81
|
+
validate_limit_parameter(params, PARAMS_MAXIMUM, PARAMS_EXCLUSIVE_MAXIMUM)
|
82
|
+
validate_limit_parameter(params, PARAMS_MINIMUM, PARAMS_EXCLUSIVE_MINIMUM)
|
83
|
+
end
|
84
|
+
|
85
|
+
def preprocessor
|
86
|
+
SwaggerParameterPreprocessor.new(@name, @how_to_pass, @required, @type, @params[:default], @params)
|
52
87
|
end
|
53
88
|
|
54
89
|
def to_swagger
|
@@ -65,14 +100,14 @@ module Sinatra
|
|
65
100
|
if PRIMITIVE_TYPES.include? @items
|
66
101
|
result[:items] = {:type => @items}
|
67
102
|
else
|
68
|
-
result[:schema] =
|
103
|
+
result[:schema] = ref_to_type(@items)
|
69
104
|
end
|
70
105
|
end
|
71
106
|
else
|
72
107
|
if PRIMITIVE_TYPES.include? @type
|
73
108
|
result[:type] = @type
|
74
109
|
else
|
75
|
-
result[:schema] =
|
110
|
+
result[:schema] = ref_to_type(@type)
|
76
111
|
end
|
77
112
|
end
|
78
113
|
end
|
@@ -80,7 +115,7 @@ module Sinatra
|
|
80
115
|
if @description
|
81
116
|
result[:description] = @description
|
82
117
|
end
|
83
|
-
|
118
|
+
unless @params.empty?
|
84
119
|
result.merge!(@params)
|
85
120
|
end
|
86
121
|
|
@@ -94,11 +129,42 @@ module Sinatra
|
|
94
129
|
:required => @required,
|
95
130
|
:type => @type,
|
96
131
|
:items => @items,
|
97
|
-
:params => @params,
|
98
132
|
:description => @description,
|
133
|
+
:params => @params,
|
99
134
|
}.to_json
|
100
135
|
end
|
101
136
|
|
137
|
+
private
|
138
|
+
|
139
|
+
# Test if a parameter is a boolean
|
140
|
+
# @param name the parameter's name
|
141
|
+
# @value value the parameter's value
|
142
|
+
# @return [NilClass]
|
143
|
+
def check_boolean(name, value)
|
144
|
+
unless [true, false].include? value
|
145
|
+
raise SwaggerInvalidException.new("Invalid boolean value [#{value}] for [#{name}]")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Validate a limit param like maximum and exclusiveMaximum
|
150
|
+
# @param params [Hash] the parameters
|
151
|
+
# @param limit_param_name [Symbol] the limit parameter name
|
152
|
+
# @param exclusive_limit_param_name [Symbol] the exclusive limit parameter name
|
153
|
+
def validate_limit_parameter(params, limit_param_name, exclusive_limit_param_name)
|
154
|
+
if params.key? limit_param_name
|
155
|
+
unless [TYPE_INTEGER, TYPE_NUMBER].include? @type
|
156
|
+
raise SwaggerInvalidException.new("Parameter #{limit_param_name} can only be specified for type #{TYPE_INTEGER} and #{TYPE_NUMBER} and not for [#{@type}]")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
if params.key? exclusive_limit_param_name
|
161
|
+
check_boolean(PARAMS_EXCLUSIVE_MINIMUM, params[exclusive_limit_param_name])
|
162
|
+
unless params.key? limit_param_name
|
163
|
+
raise SwaggerInvalidException.new("You can't have a #{exclusive_limit_param_name} value without a #{limit_param_name}")
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
102
168
|
end
|
103
169
|
|
104
170
|
end
|
@@ -26,7 +26,7 @@ module Sinatra
|
|
26
26
|
if PRIMITIVE_TYPES.include? @items
|
27
27
|
schema[:items] = {:type => @items}
|
28
28
|
else
|
29
|
-
schema[:items] =
|
29
|
+
schema[:items] = ref_to_type(@items)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
result[:schema] = schema
|
@@ -34,7 +34,7 @@ module Sinatra
|
|
34
34
|
if PRIMITIVE_TYPES.include? @type
|
35
35
|
result[:schema] = {:type => @type}
|
36
36
|
else
|
37
|
-
result[:schema] =
|
37
|
+
result[:schema] = ref_to_type(@type)
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require_relative 'swagger-request-preprocessor'
|
1
2
|
require_relative 'swagger-utilities'
|
2
3
|
|
3
4
|
module Sinatra
|
@@ -9,13 +10,21 @@ module Sinatra
|
|
9
10
|
|
10
11
|
include SwaggerUtilities
|
11
12
|
|
12
|
-
attr_reader :path, :type
|
13
|
+
attr_reader :path, :type, :request_preprocessor
|
13
14
|
|
14
15
|
def initialize(type, path, parameters, responses, summary, description, tags)
|
15
16
|
@type = type
|
16
17
|
@path = sinatra_path_to_swagger_path(path)
|
18
|
+
@request_preprocessor = SwaggerRequestPreprocessor.new
|
17
19
|
|
18
20
|
@parameters = parameters
|
21
|
+
@parameters.each do |parameter|
|
22
|
+
preprocessor = parameter.preprocessor
|
23
|
+
if preprocessor.useful?
|
24
|
+
@request_preprocessor.add_preprocessor preprocessor
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
19
28
|
@responses = responses
|
20
29
|
|
21
30
|
@attributes = {}
|
@@ -21,13 +21,15 @@ module Sinatra
|
|
21
21
|
app.set :swagger_current_endpoint_parameters, {}
|
22
22
|
app.set :swagger_current_endpoint_responses, {}
|
23
23
|
app.set :swagger_types, {}
|
24
|
+
declare_swagger_endpoints(app)
|
25
|
+
end
|
24
26
|
|
25
|
-
|
27
|
+
def self.declare_swagger_endpoints(app)
|
26
28
|
app.endpoint_summary 'The swagger endpoint'
|
27
29
|
app.endpoint_tags 'swagger'
|
28
30
|
app.get '/swagger_doc.json' do
|
29
31
|
swagger_content = ::Sinatra::SwaggerExposer::SwaggerContentCreator.new(
|
30
|
-
settings.respond_to?(:swagger_info) ? settings.swagger_info : nil
|
32
|
+
settings.respond_to?(:swagger_info) ? settings.swagger_info : nil,
|
31
33
|
settings.swagger_types,
|
32
34
|
settings.swagger_endpoints
|
33
35
|
).to_swagger
|
@@ -40,26 +42,25 @@ module Sinatra
|
|
40
42
|
app.options '/swagger_doc.json' do
|
41
43
|
200
|
42
44
|
end
|
43
|
-
|
44
45
|
end
|
45
46
|
|
46
47
|
# Provide a summary for the endpoint
|
47
48
|
def endpoint_summary(summary)
|
48
|
-
set_if_type_and_not_exist(summary,
|
49
|
+
set_if_type_and_not_exist(summary, :summary, String)
|
49
50
|
end
|
50
51
|
|
51
52
|
# Provide a description for the endpoint
|
52
53
|
def endpoint_description(description)
|
53
|
-
set_if_type_and_not_exist(description,
|
54
|
+
set_if_type_and_not_exist(description, :description, String)
|
54
55
|
end
|
55
56
|
|
56
57
|
# Provide tags for the endpoint
|
57
58
|
def endpoint_tags(*tags)
|
58
|
-
set_if_type_and_not_exist(tags,
|
59
|
+
set_if_type_and_not_exist(tags, :tags, nil)
|
59
60
|
end
|
60
61
|
|
61
62
|
# Define parameter for the endpoint
|
62
|
-
def endpoint_parameter(name, description, how_to_pass, required, type, params =
|
63
|
+
def endpoint_parameter(name, description, how_to_pass, required, type, params = {})
|
63
64
|
parameters = settings.swagger_current_endpoint_parameters
|
64
65
|
check_if_not_duplicate(name, parameters, 'Parameter')
|
65
66
|
parameters[name] = SwaggerEndpointParameter.new(
|
@@ -93,70 +94,38 @@ module Sinatra
|
|
93
94
|
responses[code] = SwaggerEndpointResponse.new(type, description, settings.swagger_types.keys)
|
94
95
|
end
|
95
96
|
|
96
|
-
def
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
def head(*args, &block)
|
107
|
-
process_endpoint('head', args)
|
108
|
-
super(*args, &block)
|
109
|
-
end
|
110
|
-
|
111
|
-
def link(*args, &block)
|
112
|
-
process_endpoint('link', args)
|
113
|
-
super(*args, &block)
|
114
|
-
end
|
115
|
-
|
116
|
-
def options(*args, &block)
|
117
|
-
process_endpoint('options', args)
|
118
|
-
super(*args, &block)
|
119
|
-
end
|
120
|
-
|
121
|
-
def patch(*args, &block)
|
122
|
-
process_endpoint('patch', args)
|
123
|
-
super(*args, &block)
|
124
|
-
end
|
125
|
-
|
126
|
-
def post(*args, &block)
|
127
|
-
process_endpoint('post', args)
|
128
|
-
super(*args, &block)
|
129
|
-
end
|
130
|
-
|
131
|
-
def put(*args, &block)
|
132
|
-
process_endpoint('put', args)
|
133
|
-
super(*args, &block)
|
134
|
-
end
|
135
|
-
|
136
|
-
def unlink(*args, &block)
|
137
|
-
process_endpoint('unlink', args)
|
138
|
-
super(*args, &block)
|
97
|
+
def route(verb, path, options = {}, &block)
|
98
|
+
if verb == 'HEAD'
|
99
|
+
super(verb, path, options, &block)
|
100
|
+
else
|
101
|
+
request_preprocessor = process_endpoint(verb.downcase, path, options)
|
102
|
+
super(verb, path, options) do
|
103
|
+
request_preprocessor.run(self, &block)
|
104
|
+
end
|
105
|
+
end
|
139
106
|
end
|
140
107
|
|
141
108
|
private
|
142
109
|
|
143
110
|
# Call for each endpoint declaration
|
144
|
-
|
111
|
+
# @return [SwaggerRequestPreprocessor]
|
112
|
+
def process_endpoint(type, path, opts)
|
145
113
|
current_endpoint_info = settings.swagger_current_endpoint_info
|
146
114
|
current_endpoint_parameters = settings.swagger_current_endpoint_parameters
|
147
115
|
current_endpoint_responses = settings.swagger_current_endpoint_responses
|
148
|
-
|
149
|
-
settings.swagger_endpoints << SwaggerEndpoint.new(
|
116
|
+
endpoint = SwaggerEndpoint.new(
|
150
117
|
type,
|
151
|
-
|
118
|
+
path,
|
152
119
|
current_endpoint_parameters.values,
|
153
120
|
current_endpoint_responses.clone,
|
154
121
|
current_endpoint_info[:summary],
|
155
122
|
current_endpoint_info[:description],
|
156
123
|
current_endpoint_info[:tags])
|
124
|
+
settings.swagger_endpoints << endpoint
|
157
125
|
current_endpoint_info.clear
|
158
126
|
current_endpoint_parameters.clear
|
159
127
|
current_endpoint_responses.clear
|
128
|
+
endpoint.request_preprocessor
|
160
129
|
end
|
161
130
|
|
162
131
|
def set_if_type_and_not_exist(value, name, type)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'swagger-invalid-exception'
|
2
|
+
require_relative 'swagger-utilities'
|
2
3
|
|
3
4
|
module Sinatra
|
4
5
|
|
@@ -7,6 +8,8 @@ module Sinatra
|
|
7
8
|
# The info declaration
|
8
9
|
class SwaggerInfo
|
9
10
|
|
11
|
+
include SwaggerUtilities
|
12
|
+
|
10
13
|
def initialize(values)
|
11
14
|
@values = process(values, 'info', INFO_FIELDS, values)
|
12
15
|
end
|
@@ -47,7 +50,7 @@ module Sinatra
|
|
47
50
|
end
|
48
51
|
end
|
49
52
|
else
|
50
|
-
raise SwaggerInvalidException.new("Unknown property [#{current_key}] for #{current_field_name}
|
53
|
+
raise SwaggerInvalidException.new("Unknown property [#{current_key}] for #{current_field_name}#{list_or_none(current_fields.keys, 'values')}")
|
51
54
|
end
|
52
55
|
end
|
53
56
|
result.empty? ? nil : result
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require_relative 'swagger-endpoint-parameter'
|
2
|
+
require_relative 'swagger-invalid-exception'
|
3
|
+
|
4
|
+
module Sinatra
|
5
|
+
|
6
|
+
module SwaggerExposer
|
7
|
+
|
8
|
+
# Process the parameters for validation and enrichment
|
9
|
+
class SwaggerParameterPreprocessor
|
10
|
+
|
11
|
+
def initialize(name, how_to_pass, required, type, default, params)
|
12
|
+
@name = name.to_s
|
13
|
+
@how_to_pass = how_to_pass
|
14
|
+
@required = required
|
15
|
+
@type = type
|
16
|
+
@default = default
|
17
|
+
@params = params
|
18
|
+
|
19
|
+
# All headers are upcased
|
20
|
+
if how_to_pass == SwaggerEndpointParameter::HOW_TO_PASS_HEADER
|
21
|
+
@name.upcase!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def useful?
|
26
|
+
@required || (!@default.nil?) || [SwaggerEndpointParameter::TYPE_NUMBER, SwaggerEndpointParameter::TYPE_INTEGER, SwaggerEndpointParameter::TYPE_BOOLEAN].include?(@type)
|
27
|
+
end
|
28
|
+
|
29
|
+
def run(app, parsed_body)
|
30
|
+
case @how_to_pass
|
31
|
+
when SwaggerEndpointParameter::HOW_TO_PASS_QUERY, SwaggerEndpointParameter::HOW_TO_PASS_PATH
|
32
|
+
check_param(app.params)
|
33
|
+
when SwaggerEndpointParameter::HOW_TO_PASS_HEADER
|
34
|
+
check_param(app.headers)
|
35
|
+
when SwaggerEndpointParameter::HOW_TO_PASS_BODY
|
36
|
+
check_param(parsed_body || {})
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def check_param(params)
|
41
|
+
if params.key?(@name)
|
42
|
+
params[@name] = validate_param_value(params[@name])
|
43
|
+
elsif @required
|
44
|
+
raise SwaggerInvalidException.new("Mandatory parameter [#{@name}] is missing")
|
45
|
+
elsif @default
|
46
|
+
params[@name] = @default
|
47
|
+
end
|
48
|
+
params
|
49
|
+
end
|
50
|
+
|
51
|
+
def validate_param_value(value)
|
52
|
+
case @type
|
53
|
+
when SwaggerEndpointParameter::TYPE_NUMBER
|
54
|
+
return validate_param_value_number(value)
|
55
|
+
when SwaggerEndpointParameter::TYPE_INTEGER
|
56
|
+
return validate_param_value_integer(value)
|
57
|
+
when SwaggerEndpointParameter::TYPE_BOOLEAN
|
58
|
+
return validate_param_value_boolean(value)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def validate_param_value_boolean(value)
|
63
|
+
if (value == 'true') || value.is_a?(TrueClass)
|
64
|
+
return true
|
65
|
+
elsif (value == 'false') || value.is_a?(FalseClass)
|
66
|
+
return false
|
67
|
+
else
|
68
|
+
raise SwaggerInvalidException.new("Parameter [#{@name}] should be an boolean but is [#{value}]")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def validate_param_value_integer(value)
|
73
|
+
begin
|
74
|
+
f = Float(value)
|
75
|
+
i = Integer(value)
|
76
|
+
if f == i
|
77
|
+
i
|
78
|
+
else
|
79
|
+
raise SwaggerInvalidException.new("Parameter [#{@name}] should be an integer but is [#{value}]")
|
80
|
+
end
|
81
|
+
value = Integer(value)
|
82
|
+
validate_numerical_value(value)
|
83
|
+
value
|
84
|
+
rescue ArgumentError
|
85
|
+
raise SwaggerInvalidException.new("Parameter [#{@name}] should be an integer but is [#{value}]")
|
86
|
+
rescue TypeError
|
87
|
+
raise SwaggerInvalidException.new("Parameter [#{@name}] should be an integer but is [#{value}]")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def validate_param_value_number(value)
|
92
|
+
begin
|
93
|
+
value = Float(value)
|
94
|
+
validate_numerical_value(value)
|
95
|
+
return value
|
96
|
+
rescue ArgumentError
|
97
|
+
raise SwaggerInvalidException.new("Parameter [#{@name}] should be a float but is [#{value}]")
|
98
|
+
rescue TypeError
|
99
|
+
raise SwaggerInvalidException.new("Parameter [#{@name}] should be a float but is [#{value}]")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Validate a numerical value
|
104
|
+
# @param value [Numeric] the value
|
105
|
+
def validate_numerical_value(value)
|
106
|
+
validate_numerical_value_internal(
|
107
|
+
value,
|
108
|
+
SwaggerEndpointParameter::PARAMS_MINIMUM,
|
109
|
+
SwaggerEndpointParameter::PARAMS_EXCLUSIVE_MINIMUM,
|
110
|
+
'>=',
|
111
|
+
'>')
|
112
|
+
validate_numerical_value_internal(
|
113
|
+
value,
|
114
|
+
SwaggerEndpointParameter::PARAMS_MAXIMUM,
|
115
|
+
SwaggerEndpointParameter::PARAMS_EXCLUSIVE_MAXIMUM,
|
116
|
+
'<=',
|
117
|
+
'<')
|
118
|
+
end
|
119
|
+
|
120
|
+
# Validate the value of a number
|
121
|
+
# @params value the value to check
|
122
|
+
# @params limit_param_name [Symbol] the param that contain the value to compare to
|
123
|
+
# @params exclusive_limit_param_name [Symbol] the param that indicates if the comparison is absolute
|
124
|
+
# @params limit_param_method [String] the comparison method to call
|
125
|
+
# @params exclusive_limit_param_method [String] the absolute comparison method to call
|
126
|
+
def validate_numerical_value_internal(value, limit_param_name, exclusive_limit_param_name, limit_param_method, exclusive_limit_param_method)
|
127
|
+
if @params.key? limit_param_name
|
128
|
+
target_value = @params[limit_param_name]
|
129
|
+
method_to_call = @params[exclusive_limit_param_name] ? exclusive_limit_param_method : limit_param_method
|
130
|
+
unless value.send(method_to_call, target_value)
|
131
|
+
raise SwaggerInvalidException.new("Parameter [#{@name}] should be #{method_to_call} than [#{target_value}] but is [#{value}]")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
require_relative 'swagger-invalid-exception'
|
4
|
+
|
5
|
+
module Sinatra
|
6
|
+
|
7
|
+
module SwaggerExposer
|
8
|
+
|
9
|
+
# A preprocessor for a request, apply the parameter preprocessor then execute the query code
|
10
|
+
class SwaggerRequestPreprocessor
|
11
|
+
|
12
|
+
attr_reader :preprocessors
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@preprocessors = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_preprocessor(preprocessor)
|
19
|
+
@preprocessors << preprocessor
|
20
|
+
end
|
21
|
+
|
22
|
+
def run(app, &block)
|
23
|
+
parsed_body = {}
|
24
|
+
if app.env['CONTENT_TYPE'] == 'application/json'
|
25
|
+
body = app.request.body
|
26
|
+
unless body.empty?
|
27
|
+
parsed_body = JSON.parse(body)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
app.params['parsed_body'] = parsed_body
|
31
|
+
unless @preprocessors.empty?
|
32
|
+
@preprocessors.each do |preprocessor|
|
33
|
+
begin
|
34
|
+
preprocessor.run(app, parsed_body)
|
35
|
+
rescue SwaggerInvalidException => e
|
36
|
+
app.content_type :json
|
37
|
+
return [400, {:code => 400, :message => e.message}.to_json]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
if block
|
42
|
+
# Execute the block in the context of the app
|
43
|
+
app.instance_eval(&block)
|
44
|
+
else
|
45
|
+
''
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -11,19 +11,27 @@ module Sinatra
|
|
11
11
|
|
12
12
|
include SwaggerUtilities
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
PROPERTY_PROPERTIES = :properties
|
15
|
+
PROPERTY_REQUIRED = :required
|
16
|
+
PROPERTY_EXAMPLE = :example
|
17
|
+
PROPERTY_EXTENDS = :extends
|
18
|
+
PROPERTIES = [PROPERTY_PROPERTIES, PROPERTY_REQUIRED, PROPERTY_EXAMPLE, PROPERTY_EXTENDS]
|
19
|
+
|
20
|
+
def initialize(type_name, type_properties, known_types)
|
21
|
+
white_list_params(type_properties, PROPERTIES)
|
22
|
+
@properties = process_properties(type_name, type_properties, known_types)
|
23
|
+
@required = process_required(type_name, type_properties, @properties.keys)
|
24
|
+
@example = process_example(type_name, type_properties, @properties.keys)
|
25
|
+
@extends = process_extends(type_properties, known_types)
|
18
26
|
end
|
19
27
|
|
20
28
|
def process_properties(type_name, type_content, known_types)
|
21
|
-
possible_value = check_attribute_empty_or_bad(type_name, type_content,
|
29
|
+
possible_value = check_attribute_empty_or_bad(type_name, type_content, PROPERTY_PROPERTIES, Hash)
|
22
30
|
if possible_value
|
23
31
|
possible_value
|
24
32
|
else
|
25
33
|
result = {}
|
26
|
-
type_content[
|
34
|
+
type_content[PROPERTY_PROPERTIES].each_pair do |property_name, property_properties|
|
27
35
|
result[property_name.to_s] = SwaggerTypeProperty.new(type_name, property_name, property_properties, known_types)
|
28
36
|
end
|
29
37
|
result
|
@@ -31,32 +39,32 @@ module Sinatra
|
|
31
39
|
end
|
32
40
|
|
33
41
|
def process_required(type_name, type_content, properties_names)
|
34
|
-
possible_value = check_attribute_empty_or_bad(type_name, type_content,
|
42
|
+
possible_value = check_attribute_empty_or_bad(type_name, type_content, PROPERTY_REQUIRED, Array)
|
35
43
|
if possible_value
|
36
44
|
possible_value
|
37
45
|
else
|
38
|
-
type_content[
|
46
|
+
type_content[PROPERTY_REQUIRED].each do |property_name|
|
39
47
|
property_name = property_name.to_s
|
40
48
|
unless properties_names.include? property_name
|
41
|
-
raise SwaggerInvalidException.new("Required property [#{property_name}] of [#{type_name}] is unknown
|
49
|
+
raise SwaggerInvalidException.new("Required property [#{property_name}] of [#{type_name}] is unknown#{list_or_none(properties_names, 'properties')}")
|
42
50
|
end
|
43
51
|
end
|
44
|
-
type_content[
|
52
|
+
type_content[PROPERTY_REQUIRED]
|
45
53
|
end
|
46
54
|
end
|
47
55
|
|
48
56
|
def process_example(type_name, type_content, properties_names)
|
49
|
-
possible_value = check_attribute_empty_or_bad(type_name, type_content,
|
57
|
+
possible_value = check_attribute_empty_or_bad(type_name, type_content, PROPERTY_EXAMPLE, Hash)
|
50
58
|
if possible_value
|
51
59
|
possible_value
|
52
60
|
else
|
53
|
-
type_content[
|
61
|
+
type_content[PROPERTY_EXAMPLE].each_pair do |property_name, property_value|
|
54
62
|
property_name = property_name.to_s
|
55
63
|
unless properties_names.include? property_name
|
56
|
-
raise SwaggerInvalidException.new("Example property [#{property_name}] with value [#{property_value}] of [#{type_name}] is unknown
|
64
|
+
raise SwaggerInvalidException.new("Example property [#{property_name}] with value [#{property_value}] of [#{type_name}] is unknown#{list_or_none(properties_names, 'properties')}")
|
57
65
|
end
|
58
66
|
end
|
59
|
-
type_content[
|
67
|
+
type_content[PROPERTY_EXAMPLE]
|
60
68
|
end
|
61
69
|
end
|
62
70
|
|
@@ -68,19 +76,35 @@ module Sinatra
|
|
68
76
|
end
|
69
77
|
end
|
70
78
|
|
79
|
+
def process_extends(type_properties, known_types)
|
80
|
+
if type_properties.key? PROPERTY_EXTENDS
|
81
|
+
check_type(type_properties[PROPERTY_EXTENDS], known_types)
|
82
|
+
@extends = type_properties[PROPERTY_EXTENDS]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
71
86
|
def to_swagger
|
72
|
-
result = {}
|
87
|
+
result = {:type => 'object'}
|
73
88
|
|
74
89
|
unless @properties.empty?
|
75
|
-
result[
|
90
|
+
result[PROPERTY_PROPERTIES] = hash_to_swagger(@properties)
|
76
91
|
end
|
77
92
|
|
78
93
|
unless @required.empty?
|
79
|
-
result[
|
94
|
+
result[PROPERTY_REQUIRED] = @required
|
80
95
|
end
|
81
96
|
|
82
97
|
unless @example.empty?
|
83
|
-
result[
|
98
|
+
result[PROPERTY_EXAMPLE] = @example
|
99
|
+
end
|
100
|
+
|
101
|
+
if @extends
|
102
|
+
result = {
|
103
|
+
:allOf => [
|
104
|
+
ref_to_type(@extends),
|
105
|
+
result
|
106
|
+
]
|
107
|
+
}
|
84
108
|
end
|
85
109
|
|
86
110
|
result
|
@@ -16,9 +16,13 @@ module Sinatra
|
|
16
16
|
'boolean',
|
17
17
|
'date',
|
18
18
|
'dateTime',
|
19
|
-
'password'
|
19
|
+
'password',
|
20
20
|
]
|
21
21
|
|
22
|
+
def ref_to_type(type)
|
23
|
+
{'$ref' => "#/definitions/#{type}"}
|
24
|
+
end
|
25
|
+
|
22
26
|
def hash_to_swagger(hash)
|
23
27
|
result = {}
|
24
28
|
hash.each_pair do |key, value|
|
@@ -27,8 +31,12 @@ module Sinatra
|
|
27
31
|
result
|
28
32
|
end
|
29
33
|
|
34
|
+
# Transform a type into a String
|
35
|
+
# @return [String]
|
30
36
|
def type_to_s(value)
|
31
|
-
if
|
37
|
+
if [TrueClass, FalseClass].include? value
|
38
|
+
'boolean'
|
39
|
+
elsif value.is_a? Class
|
32
40
|
value.to_s.downcase
|
33
41
|
else
|
34
42
|
value
|
@@ -51,14 +59,26 @@ module Sinatra
|
|
51
59
|
end
|
52
60
|
end
|
53
61
|
|
54
|
-
|
62
|
+
# Validate if a parameter is in a list of available values
|
63
|
+
# @param params the parameter
|
64
|
+
# @param allowed_values [Enumerable, #include?] the allowed values
|
65
|
+
# @return [NilClass]
|
66
|
+
def white_list_params(params, allowed_values)
|
55
67
|
params.each_pair do |key, value|
|
56
|
-
unless
|
57
|
-
raise SwaggerInvalidException.new("Unknown property [#{key}]
|
68
|
+
unless allowed_values.include? key
|
69
|
+
raise SwaggerInvalidException.new("Unknown property [#{key}] with value [#{value}]#{list_or_none(allowed_values, 'properties')}")
|
58
70
|
end
|
59
71
|
end
|
60
72
|
end
|
61
73
|
|
74
|
+
def list_or_none(list, name)
|
75
|
+
if list.empty?
|
76
|
+
", no available #{name}"
|
77
|
+
else
|
78
|
+
", possible #{name} are #{list.join(', ')}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
62
82
|
private
|
63
83
|
|
64
84
|
def get_array_type(array)
|
@@ -71,9 +91,15 @@ module Sinatra
|
|
71
91
|
end
|
72
92
|
end
|
73
93
|
|
74
|
-
|
75
|
-
|
76
|
-
|
94
|
+
# Validate if a type is in a list of available values
|
95
|
+
# @param type [String] the parameter
|
96
|
+
# @param allowed_values [Enumerable, #include?] the allowed values
|
97
|
+
# @return [NilClass]
|
98
|
+
def check_type(type, allowed_values)
|
99
|
+
if allowed_values.empty?
|
100
|
+
raise SwaggerInvalidException.new("Unknown type [#{type}], no available type")
|
101
|
+
elsif !allowed_values.include?(type)
|
102
|
+
raise SwaggerInvalidException.new("Unknown type [#{type}]#{list_or_none(allowed_values, 'types')}")
|
77
103
|
end
|
78
104
|
end
|
79
105
|
|
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ['Julien Kirch']
|
10
10
|
|
11
11
|
spec.summary = %q{Expose swagger API from your Sinatra app}
|
12
|
-
spec.description = %q{This Sinatra extension enable you to add metadata to your code
|
12
|
+
spec.description = %q{This Sinatra extension enable you to add metadata to your code to expose your API as a Swagger endpoint and to validate and enrich the invocation parameters}
|
13
13
|
spec.homepage = 'https://github.com/archiloque/sinatra-swagger-exposer'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sinatra-swagger-exposer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julien Kirch
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sinatra
|
@@ -94,8 +94,8 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
-
description: This Sinatra extension enable you to add metadata to your code
|
98
|
-
|
97
|
+
description: This Sinatra extension enable you to add metadata to your code to expose
|
98
|
+
your API as a Swagger endpoint and to validate and enrich the invocation parameters
|
99
99
|
email:
|
100
100
|
executables: []
|
101
101
|
extensions: []
|
@@ -103,10 +103,11 @@ extra_rdoc_files: []
|
|
103
103
|
files:
|
104
104
|
- ".gitignore"
|
105
105
|
- ".travis.yml"
|
106
|
+
- CHANGELOG.md
|
106
107
|
- CODE_OF_CONDUCT.md
|
107
108
|
- Gemfile
|
108
109
|
- LICENSE.txt
|
109
|
-
- README.
|
110
|
+
- README.md
|
110
111
|
- Rakefile
|
111
112
|
- example/Gemfile
|
112
113
|
- example/Gemfile.lock
|
@@ -119,6 +120,8 @@ files:
|
|
119
120
|
- lib/sinatra/swagger-exposer/swagger-exposer.rb
|
120
121
|
- lib/sinatra/swagger-exposer/swagger-info.rb
|
121
122
|
- lib/sinatra/swagger-exposer/swagger-invalid-exception.rb
|
123
|
+
- lib/sinatra/swagger-exposer/swagger-parameter-preprocessor.rb
|
124
|
+
- lib/sinatra/swagger-exposer/swagger-request-preprocessor.rb
|
122
125
|
- lib/sinatra/swagger-exposer/swagger-type-property.rb
|
123
126
|
- lib/sinatra/swagger-exposer/swagger-type.rb
|
124
127
|
- lib/sinatra/swagger-exposer/swagger-utilities.rb
|
data/README.asciidoc
DELETED
@@ -1,75 +0,0 @@
|
|
1
|
-
# Sinatra::SwaggerExposer
|
2
|
-
|
3
|
-
image:https://codeclimate.com/github/archiloque/sinatra-swagger-exposer/badges/gpa.svg["Code status", link=https://codeclimate.com/github/archiloque/sinatra-swagger-exposer]
|
4
|
-
image:https://travis-ci.org/archiloque/sinatra-swagger-exposer.svg?branch=master["Build Status", link="https://travis-ci.org/archiloque/sinatra-swagger-exposer"]
|
5
|
-
image:https://coveralls.io/repos/archiloque/sinatra-swagger-exposer/badge.svg?branch=master["Coverage Status", link="https://coveralls.io/r/archiloque/sinatra-swagger-exposer?branch=master"]
|
6
|
-
|
7
|
-
Create Swagger endpoint for your Sinatra application.
|
8
|
-
|
9
|
-
This Sinatra extension enable you to add metadata to your code and to expose your API as a Swagger endpoint.
|
10
|
-
|
11
|
-
I'm adding features as I need them and it currently doesn't use all the Swagger options, so if you need one that is missing please open an issue or contribute.
|
12
|
-
|
13
|
-
## Design choices
|
14
|
-
|
15
|
-
- All the declarations are validated when the server is started
|
16
|
-
- The declarations are defined to look as ruby-ish as possible
|
17
|
-
|
18
|
-
To use it in your app :
|
19
|
-
|
20
|
-
[source,ruby]
|
21
|
-
----
|
22
|
-
require 'sinatra/swagger-exposer/swagger-exposer'
|
23
|
-
|
24
|
-
class MyApp < Sinatra::Base
|
25
|
-
|
26
|
-
register Sinatra::SwaggerExposer
|
27
|
-
|
28
|
-
general_info(
|
29
|
-
{
|
30
|
-
version: '0.0.1',
|
31
|
-
title: 'May app',
|
32
|
-
description: 'My wonderful app',
|
33
|
-
license: {
|
34
|
-
name: 'MIT',
|
35
|
-
url: 'http://opensource.org/licenses/MIT'
|
36
|
-
}
|
37
|
-
}
|
38
|
-
)
|
39
|
-
|
40
|
-
type 'Status',
|
41
|
-
{
|
42
|
-
:properties => {
|
43
|
-
:status => {
|
44
|
-
:type => String,
|
45
|
-
:example => 'OK,
|
46
|
-
},
|
47
|
-
},
|
48
|
-
:required => [:status]
|
49
|
-
}
|
50
|
-
|
51
|
-
endpoint_description 'Base method to ping'
|
52
|
-
endpoint_response 200, 'Standard response', 'Status'
|
53
|
-
endpoint_tags 'Ping'
|
54
|
-
get '/' do
|
55
|
-
json({'status' => 'OK'})
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
59
|
-
----
|
60
|
-
|
61
|
-
The swagger json endpoint will be exposed at `/swagger_doc.json`.
|
62
|
-
|
63
|
-
## Detailed example
|
64
|
-
|
65
|
-
A more complete example is available link:https://github.com/archiloque/sinatra-swagger-exposer/tree/master/example[here].
|
66
|
-
|
67
|
-
## Resources
|
68
|
-
|
69
|
-
- link:https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md[Swagger RESTful API Documentation Specification].
|
70
|
-
- link:https://github.com/swagger-api/swagger-spec/tree/master/examples/v2.0/json[Swagger json examples].
|
71
|
-
- link:https://raw.githubusercontent.com/swagger-api/swagger-spec/master/schemas/v2.0/schema.json[The swagger json schema].
|
72
|
-
|
73
|
-
## License
|
74
|
-
|
75
|
-
This software is released under the MIT license.
|