sinatra-swagger-exposer 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Code Climate](https://codeclimate.com/github/archiloque/sinatra-swagger-exposer/badges/gpa.svg)](https://codeclimate.com/github/archiloque/sinatra-swagger-exposer)
|
4
|
+
[![Build Status](https://travis-ci.org/archiloque/sinatra-swagger-exposer.svg?branch=master)](https://travis-ci.org/archiloque/sinatra-swagger-exposer)
|
5
|
+
[![Coverage Status](https://coveralls.io/repos/archiloque/sinatra-swagger-exposer/badge.svg?branch=master)](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.
|