sinatra-swagger-exposer 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fa97395699d5708bb599a1793df03d11ee73cc6a
4
- data.tar.gz: 8dcad9fd2667784b9a6ba79362e6109b344779d8
3
+ metadata.gz: 60f345d0ee591c0a428d66ccfb5f5a7f2ae0e9b8
4
+ data.tar.gz: 4b750003265417b68303fa4f0112fbf0cb3f23a0
5
5
  SHA512:
6
- metadata.gz: ea832406300901d1975504d66725fec8669f465756c75abf68fdf8e3a58af5a3a530659acb50887c2ebceccd0b9287daf89cfc4e769a3365659b05bd53d16a76
7
- data.tar.gz: 995af3f7a7e77b2ba82ef9644856d324b9f96cc8acf582f8fdd448f3f69ebcee34a44075ae1c84fd7a066bce44ea2af4fdf59fe681554a129d88e0017fbd6834
6
+ metadata.gz: a2f6f72e7ce27da5ebcd9b6517a81b6a9c6cb5cecbda87639d60fabd1142f58ea748315ea220dd7b4779f6bc8e982bee00af770f2a7f267492653af3fee54162
7
+ data.tar.gz: aadfda04a6b04980d1b875966c6886f4fac0f3a903596b88817742f9038e872fc2b2e2ba853e0c78a11623332befa08d4d4c51894b62a287a5c5f29e553ac177
@@ -1,8 +1,15 @@
1
+ # 0.2.0
2
+
3
+ - Fix empty value returned from validation when a parameter is a String
4
+ - Added maxLength and minLength validations
5
+ - Add produces parameter
6
+ - Add fluent syntax
7
+
1
8
  # 0.1.0
2
9
 
3
10
  - Added verification and enrichment form params
4
11
  - Added type extends
5
- - Added validation for min and max value
12
+ - Added validation for min and max value for numbers
6
13
 
7
14
  # 0.0.1
8
15
 
data/README.md CHANGED
@@ -65,6 +65,24 @@ end
65
65
 
66
66
  The swagger json endpoint will be exposed at `/swagger_doc.json`.
67
67
 
68
+ You can also use a more fluent variant by providing a hash to the `endpoint` method
69
+
70
+
71
+ ```ruby
72
+ endpoint :description => 'Base method to ping',
73
+ :responses { 200 => ['Status', 'Standard response']}
74
+ :tags 'Ping'
75
+ get '/' do
76
+ json({'status' => 'OK'})
77
+ end
78
+ ```
79
+
80
+ The hash should contains the key `description`, `summary`, `path`, `tags`, `responses` and `params`.
81
+ Note both `responses` and `params` takes a hash as argument `hash(param_name =>param_details)` and `hash(status_code=>res_param)`
82
+
83
+ If the equivalent methods have more than one param, theses are wrapped in an array.
84
+
85
+
68
86
  ## Detailed example
69
87
 
70
88
  A more complete example is available [here](https://github.com/archiloque/sinatra-swagger-exposer/tree/master/example).
@@ -1,5 +1,7 @@
1
1
  require_relative 'swagger-invalid-exception'
2
+ require_relative 'swagger-parameter-helper'
2
3
  require_relative 'swagger-parameter-preprocessor'
4
+ require_relative 'swagger-type-property'
3
5
  require_relative 'swagger-utilities'
4
6
 
5
7
  module Sinatra
@@ -9,35 +11,7 @@ module Sinatra
9
11
  class SwaggerEndpointParameter
10
12
 
11
13
  include SwaggerUtilities
12
-
13
- HOW_TO_PASS_BODY = 'body'
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
- ]
14
+ include SwaggerParameterHelper
41
15
 
42
16
  def initialize(name, description, how_to_pass, required, type, params, known_types)
43
17
  unless name.is_a?(String) || name.is_a?(Symbol)
@@ -70,7 +44,7 @@ module Sinatra
70
44
  end
71
45
  @required = required
72
46
 
73
- white_list_params(params, PARAMS_LIST)
47
+ params = white_list_params(params, PARAMS_LIST, SwaggerTypeProperty::PROPERTIES)
74
48
  validate_params(params)
75
49
  @params = params
76
50
  end
@@ -78,14 +52,18 @@ module Sinatra
78
52
  # Validate parameters
79
53
  # @param params [Hash]
80
54
  def validate_params(params)
81
- validate_limit_parameter(params, PARAMS_MAXIMUM, PARAMS_EXCLUSIVE_MAXIMUM)
82
- validate_limit_parameter(params, PARAMS_MINIMUM, PARAMS_EXCLUSIVE_MINIMUM)
55
+ validate_limit_parameters(params)
56
+ validate_length_parameters(params)
83
57
  end
84
58
 
59
+ # Create the corresponding SwaggerParameterPreprocessor
60
+ # @return [Sinatra::SwaggerExposer::SwaggerParameterPreprocessor]
85
61
  def preprocessor
86
62
  SwaggerParameterPreprocessor.new(@name, @how_to_pass, @required, @type, @params[:default], @params)
87
63
  end
88
64
 
65
+ # Return the swagger version
66
+ # @return [Hash]
89
67
  def to_swagger
90
68
  result = {
91
69
  :name => @name,
@@ -138,7 +116,7 @@ module Sinatra
138
116
 
139
117
  # Test if a parameter is a boolean
140
118
  # @param name the parameter's name
141
- # @value value the parameter's value
119
+ # @param value value the parameter's value
142
120
  # @return [NilClass]
143
121
  def check_boolean(name, value)
144
122
  unless [true, false].include? value
@@ -146,23 +124,71 @@ module Sinatra
146
124
  end
147
125
  end
148
126
 
127
+ # Validate the limit parameters
128
+ # @param params [Hash] the parameters
129
+ def validate_limit_parameters(params)
130
+ max = validate_limit_parameter(params, PARAMS_MAXIMUM, PARAMS_EXCLUSIVE_MAXIMUM)
131
+ min = validate_limit_parameter(params, PARAMS_MINIMUM, PARAMS_EXCLUSIVE_MINIMUM)
132
+ if min && max && (max < min)
133
+ raise SwaggerInvalidException.new("Minimum value [#{min}] can't be more than maximum value [#{max}]")
134
+ end
135
+ end
136
+
149
137
  # Validate a limit param like maximum and exclusiveMaximum
150
138
  # @param params [Hash] the parameters
151
139
  # @param limit_param_name [Symbol] the limit parameter name
152
140
  # @param exclusive_limit_param_name [Symbol] the exclusive limit parameter name
153
141
  def validate_limit_parameter(params, limit_param_name, exclusive_limit_param_name)
142
+ parameter_value = nil
154
143
  if params.key? limit_param_name
155
144
  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}]")
145
+ raise SwaggerInvalidException.new("Parameter #{limit_param_name} can only be specified for types #{TYPE_INTEGER} or #{TYPE_NUMBER} and not for [#{@type}]")
146
+ end
147
+ parameter_value = params[limit_param_name]
148
+ unless parameter_value.is_a? Numeric
149
+ raise SwaggerInvalidException.new("Parameter #{limit_param_name} must be a numeric and can not be [#{parameter_value}]")
157
150
  end
158
151
  end
159
152
 
160
153
  if params.key? exclusive_limit_param_name
161
- check_boolean(PARAMS_EXCLUSIVE_MINIMUM, params[exclusive_limit_param_name])
154
+ check_boolean(exclusive_limit_param_name, params[exclusive_limit_param_name])
162
155
  unless params.key? limit_param_name
163
156
  raise SwaggerInvalidException.new("You can't have a #{exclusive_limit_param_name} value without a #{limit_param_name}")
164
157
  end
165
158
  end
159
+ parameter_value
160
+ end
161
+
162
+ # Validate the length parameters minLength and maxLength
163
+ # @param params [Hash] the parameters
164
+ def validate_length_parameters(params)
165
+ min_length = validate_length_parameter(params, PARAMS_MIN_LENGTH)
166
+ max_length = validate_length_parameter(params, PARAMS_MAX_LENGTH)
167
+
168
+ if min_length && max_length && (max_length < min_length)
169
+ raise SwaggerInvalidException.new("Minimum length #{min_length} can't be more than maximum length #{max_length}")
170
+ end
171
+ end
172
+
173
+ # Validate a length param like minLength and maxLength
174
+ # @param params [Hash] the parameters
175
+ # @param length_param_name [Symbol] the length parameter name
176
+ # @return [Integer] the parameter value if it is present
177
+ def validate_length_parameter(params, length_param_name)
178
+ if params.key? length_param_name
179
+ if @type == TYPE_STRING
180
+ parameter_value = params[length_param_name]
181
+ unless parameter_value.is_a? Integer
182
+ raise SwaggerInvalidException.new("Parameter #{length_param_name} must be an integer and can not be [#{parameter_value}]")
183
+ end
184
+ parameter_value
185
+ else
186
+ raise SwaggerInvalidException.new("Parameter #{length_param_name} can only be specified for type #{TYPE_STRING} and not for [#{@type}]")
187
+ end
188
+
189
+ else
190
+ nil
191
+ end
166
192
  end
167
193
 
168
194
  end
@@ -9,8 +9,10 @@ module Sinatra
9
9
 
10
10
  include SwaggerUtilities
11
11
 
12
+ RESPONSE_PRIMITIVES_FILES = PRIMITIVE_TYPES + [TYPE_FILE]
13
+
12
14
  def initialize(type, description, known_types)
13
- get_type(type, known_types + PRIMITIVE_TYPES)
15
+ get_type(type, known_types + RESPONSE_PRIMITIVES_FILES)
14
16
  if description
15
17
  @description = description
16
18
  end
@@ -23,7 +25,7 @@ module Sinatra
23
25
  if @type == 'array'
24
26
  schema = {:type => 'array'}
25
27
  if @items
26
- if PRIMITIVE_TYPES.include? @items
28
+ if RESPONSE_PRIMITIVES_FILES.include? @items
27
29
  schema[:items] = {:type => @items}
28
30
  else
29
31
  schema[:items] = ref_to_type(@items)
@@ -31,7 +33,7 @@ module Sinatra
31
33
  end
32
34
  result[:schema] = schema
33
35
  else
34
- if PRIMITIVE_TYPES.include? @type
36
+ if RESPONSE_PRIMITIVES_FILES.include? @type
35
37
  result[:schema] = {:type => @type}
36
38
  else
37
39
  result[:schema] = ref_to_type(@type)
@@ -12,9 +12,9 @@ module Sinatra
12
12
 
13
13
  attr_reader :path, :type, :request_preprocessor
14
14
 
15
- def initialize(type, path, parameters, responses, summary, description, tags)
15
+ def initialize(type, sinatra_path, parameters, responses, summary, description, tags, explicit_path, produces)
16
16
  @type = type
17
- @path = sinatra_path_to_swagger_path(path)
17
+ @path = swagger_path(sinatra_path, explicit_path)
18
18
  @request_preprocessor = SwaggerRequestPreprocessor.new
19
19
 
20
20
  @parameters = parameters
@@ -37,12 +37,13 @@ module Sinatra
37
37
  if tags
38
38
  @attributes[:tags] = tags
39
39
  end
40
+ if produces
41
+ @attributes[:produces] = produces
42
+ end
40
43
  end
41
44
 
42
45
  def to_swagger
43
- result = {
44
- produces: ['application/json'],
45
- }.merge(@attributes)
46
+ result = @attributes.clone
46
47
 
47
48
  unless @parameters.empty?
48
49
  result[:parameters] = @parameters.collect { |parameter| parameter.to_swagger }
@@ -58,14 +59,23 @@ module Sinatra
58
59
  REGEX_PATH_PARAM_MIDDLE = /\A(.*\/)\:([a-z]+)\/(.+)\z/
59
60
  REGEX_PATH_PARAM_END = /\A(.*)\/:([a-z]+)\z/
60
61
 
61
- def sinatra_path_to_swagger_path(path)
62
- while (m = REGEX_PATH_PARAM_MIDDLE.match(path))
63
- path = "#{m[1]}{#{m[2]}}/#{m[3]}"
64
- end
65
- if (m = REGEX_PATH_PARAM_END.match(path))
66
- path = "#{m[1]}/{#{m[2]}}"
62
+ # Get the endpoint swagger path
63
+ # @param sinatra_path the path declared in the sinatra app
64
+ # @param explicit_path an explicit path the user can specify
65
+ def swagger_path(sinatra_path, explicit_path)
66
+ if explicit_path
67
+ explicit_path
68
+ elsif sinatra_path.is_a? String
69
+ while (m = REGEX_PATH_PARAM_MIDDLE.match(sinatra_path))
70
+ sinatra_path = "#{m[1]}{#{m[2]}}/#{m[3]}"
71
+ end
72
+ if (m = REGEX_PATH_PARAM_END.match(sinatra_path))
73
+ sinatra_path = "#{m[1]}/{#{m[2]}}"
74
+ end
75
+ sinatra_path
76
+ else
77
+ raise SwaggerInvalidException.new("You need to specify a path when using a non-string path [#{sinatra_path}]")
67
78
  end
68
- path
69
79
  end
70
80
 
71
81
  def to_s
@@ -49,6 +49,11 @@ module Sinatra
49
49
  set_if_type_and_not_exist(summary, :summary, String)
50
50
  end
51
51
 
52
+ # Provide a path
53
+ def endpoint_path(path)
54
+ set_if_type_and_not_exist(path, :path, String)
55
+ end
56
+
52
57
  # Provide a description for the endpoint
53
58
  def endpoint_description(description)
54
59
  set_if_type_and_not_exist(description, :description, String)
@@ -56,7 +61,12 @@ module Sinatra
56
61
 
57
62
  # Provide tags for the endpoint
58
63
  def endpoint_tags(*tags)
59
- set_if_type_and_not_exist(tags, :tags, nil)
64
+ set_if_not_exist(tags, :tags)
65
+ end
66
+
67
+ # Provide produces params for the endpoint
68
+ def endpoint_produces(*produces)
69
+ set_if_not_exist(produces, :produces)
60
70
  end
61
71
 
62
72
  # Define parameter for the endpoint
@@ -73,6 +83,35 @@ module Sinatra
73
83
  settings.swagger_types.keys)
74
84
  end
75
85
 
86
+ # Define fluent endpoint dispatcher
87
+ # @param params [Hash] the parameters
88
+ def endpoint(params)
89
+ params.each_pair do |param_name, param_value|
90
+ case param_name
91
+ when :summary
92
+ endpoint_summary param_value
93
+ when :description
94
+ endpoint_description param_value
95
+ when :tags
96
+ endpoint_tags *param_value
97
+ when :produces
98
+ endpoint_produces *param_value
99
+ when :path
100
+ endpoint_path param_value
101
+ when :parameters
102
+ param_value.each do |param, args_param|
103
+ endpoint_parameter param, *args_param
104
+ end
105
+ when :responses
106
+ param_value.each do |code, args_response|
107
+ endpoint_response code, *args_response
108
+ end
109
+ else
110
+ raise SwaggerInvalidException.new("Invalid endpoint parameter [#{param_name}]")
111
+ end
112
+ end
113
+ end
114
+
76
115
  # General information
77
116
  def general_info(params)
78
117
  set :swagger_info, SwaggerInfo.new(params)
@@ -99,8 +138,8 @@ module Sinatra
99
138
  super(verb, path, options, &block)
100
139
  else
101
140
  request_preprocessor = process_endpoint(verb.downcase, path, options)
102
- super(verb, path, options) do
103
- request_preprocessor.run(self, &block)
141
+ super(verb, path, options) do |*params|
142
+ request_preprocessor.run(self, params, &block)
104
143
  end
105
144
  end
106
145
  end
@@ -120,7 +159,9 @@ module Sinatra
120
159
  current_endpoint_responses.clone,
121
160
  current_endpoint_info[:summary],
122
161
  current_endpoint_info[:description],
123
- current_endpoint_info[:tags])
162
+ current_endpoint_info[:tags],
163
+ current_endpoint_info[:path],
164
+ current_endpoint_info[:produces])
124
165
  settings.swagger_endpoints << endpoint
125
166
  current_endpoint_info.clear
126
167
  current_endpoint_parameters.clear
@@ -128,21 +169,23 @@ module Sinatra
128
169
  endpoint.request_preprocessor
129
170
  end
130
171
 
131
- def set_if_type_and_not_exist(value, name, type)
132
- if type
133
- unless value.is_a? type
134
- raise SwaggerInvalidException.new("#{name} [#{value}] should be a #{type.to_s.downcase}")
135
- end
136
- end
172
+ def set_if_not_exist(value, name)
137
173
  if settings.swagger_current_endpoint_info.key? name
138
- raise SwaggerInvalidException.new("#{name} with value [#{value}] already defined: #{settings.swagger_current_endpoint_info[name]}")
174
+ raise SwaggerInvalidException.new("#{name} with value [#{value}] already defined: [#{settings.swagger_current_endpoint_info[name]}]")
139
175
  end
140
176
  settings.swagger_current_endpoint_info[name] = value
141
177
  end
142
178
 
179
+ def set_if_type_and_not_exist(value, name, type)
180
+ unless value.is_a? type
181
+ raise SwaggerInvalidException.new("#{name} [#{value}] should be a #{type.to_s.downcase}")
182
+ end
183
+ set_if_not_exist(value, name)
184
+ end
185
+
143
186
  def check_if_not_duplicate(key, values, name)
144
187
  if values.key? key
145
- raise SwaggerInvalidException.new("#{name} already exist for #{key} with value #{values[key]}")
188
+ raise SwaggerInvalidException.new("#{name} already exist for #{key} with value [#{values[key]}]")
146
189
  end
147
190
  end
148
191
 
@@ -0,0 +1,73 @@
1
+ module Sinatra
2
+
3
+ module SwaggerExposer
4
+
5
+ # Helper for handling the parameters
6
+ module SwaggerParameterHelper
7
+
8
+ HOW_TO_PASS_BODY = 'body'
9
+ HOW_TO_PASS_HEADER = 'header'
10
+ HOW_TO_PASS_PATH = 'path'
11
+ HOW_TO_PASS_QUERY = 'query'
12
+ HOW_TO_PASS = [HOW_TO_PASS_PATH, HOW_TO_PASS_QUERY, HOW_TO_PASS_HEADER, 'formData', HOW_TO_PASS_BODY]
13
+
14
+ TYPE_BOOLEAN = 'boolean'
15
+ TYPE_BYTE = 'byte'
16
+ TYPE_DATE = 'date'
17
+ TYPE_DOUBLE = 'double'
18
+ TYPE_DATE_TIME = 'dateTime'
19
+ TYPE_FLOAT = 'float'
20
+ TYPE_INTEGER = 'integer'
21
+ TYPE_LONG = 'long'
22
+ TYPE_NUMBER = 'number'
23
+ TYPE_PASSWORD = 'password'
24
+ TYPE_STRING = 'string'
25
+
26
+ PRIMITIVE_TYPES = [
27
+ TYPE_INTEGER,
28
+ TYPE_LONG,
29
+ TYPE_FLOAT,
30
+ TYPE_DOUBLE,
31
+ TYPE_STRING,
32
+ TYPE_BYTE,
33
+ TYPE_BOOLEAN,
34
+ TYPE_DATE,
35
+ TYPE_DATE_TIME,
36
+ TYPE_PASSWORD,
37
+ ]
38
+
39
+ TYPE_FILE = 'file'
40
+
41
+ PRIMITIVE_TYPES_FOR_NON_BODY = [TYPE_STRING, TYPE_NUMBER, TYPE_INTEGER, TYPE_BOOLEAN]
42
+
43
+ PARAMS_FORMAT = :format
44
+ PARAMS_DEFAULT = :default
45
+ PARAMS_EXAMPLE = :example
46
+
47
+ # For numbers
48
+ PARAMS_MINIMUM = :minimum
49
+ PARAMS_MAXIMUM = :maximum
50
+ PARAMS_EXCLUSIVE_MINIMUM = :exclusiveMinimum
51
+ PARAMS_EXCLUSIVE_MAXIMUM = :exclusiveMaximum
52
+
53
+ # For strings
54
+ PARAMS_MIN_LENGTH = :minLength
55
+ PARAMS_MAX_LENGTH = :maxLength
56
+
57
+ PARAMS_LIST = [
58
+ PARAMS_FORMAT,
59
+ PARAMS_DEFAULT,
60
+ PARAMS_EXAMPLE,
61
+ PARAMS_MINIMUM,
62
+ PARAMS_MAXIMUM,
63
+ PARAMS_EXCLUSIVE_MINIMUM,
64
+ PARAMS_EXCLUSIVE_MAXIMUM,
65
+ PARAMS_MIN_LENGTH,
66
+ PARAMS_MAX_LENGTH,
67
+ ]
68
+
69
+
70
+ end
71
+
72
+ end
73
+ end
@@ -1,4 +1,7 @@
1
+ require 'date'
2
+
1
3
  require_relative 'swagger-endpoint-parameter'
4
+ require_relative 'swagger-parameter-helper'
2
5
  require_relative 'swagger-invalid-exception'
3
6
 
4
7
  module Sinatra
@@ -8,6 +11,8 @@ module Sinatra
8
11
  # Process the parameters for validation and enrichment
9
12
  class SwaggerParameterPreprocessor
10
13
 
14
+ include SwaggerParameterHelper
15
+
11
16
  def initialize(name, how_to_pass, required, type, default, params)
12
17
  @name = name.to_s
13
18
  @how_to_pass = how_to_pass
@@ -17,22 +22,27 @@ module Sinatra
17
22
  @params = params
18
23
 
19
24
  # All headers are upcased
20
- if how_to_pass == SwaggerEndpointParameter::HOW_TO_PASS_HEADER
25
+ if how_to_pass == HOW_TO_PASS_HEADER
21
26
  @name.upcase!
22
27
  end
23
28
  end
24
29
 
25
30
  def useful?
26
- @required || (!@default.nil?) || [SwaggerEndpointParameter::TYPE_NUMBER, SwaggerEndpointParameter::TYPE_INTEGER, SwaggerEndpointParameter::TYPE_BOOLEAN].include?(@type)
31
+ @required ||
32
+ (!@default.nil?) ||
33
+ [TYPE_NUMBER, TYPE_INTEGER, TYPE_BOOLEAN, TYPE_DATE_TIME].include?(@type) || # Must check type
34
+ (@params.key? PARAMS_MIN_LENGTH) || (@params.key? PARAMS_MAX_LENGTH) # Must check string
27
35
  end
28
36
 
29
37
  def run(app, parsed_body)
30
38
  case @how_to_pass
31
- when SwaggerEndpointParameter::HOW_TO_PASS_QUERY, SwaggerEndpointParameter::HOW_TO_PASS_PATH
39
+ when HOW_TO_PASS_PATH
40
+ # can't validate
41
+ when HOW_TO_PASS_QUERY
32
42
  check_param(app.params)
33
- when SwaggerEndpointParameter::HOW_TO_PASS_HEADER
43
+ when HOW_TO_PASS_HEADER
34
44
  check_param(app.headers)
35
- when SwaggerEndpointParameter::HOW_TO_PASS_BODY
45
+ when HOW_TO_PASS_BODY
36
46
  check_param(parsed_body || {})
37
47
  end
38
48
  end
@@ -50,15 +60,20 @@ module Sinatra
50
60
 
51
61
  def validate_param_value(value)
52
62
  case @type
53
- when SwaggerEndpointParameter::TYPE_NUMBER
63
+ when TYPE_NUMBER
54
64
  return validate_param_value_number(value)
55
- when SwaggerEndpointParameter::TYPE_INTEGER
65
+ when TYPE_INTEGER
56
66
  return validate_param_value_integer(value)
57
- when SwaggerEndpointParameter::TYPE_BOOLEAN
67
+ when TYPE_BOOLEAN
58
68
  return validate_param_value_boolean(value)
69
+ when TYPE_DATE_TIME
70
+ return validate_param_value_date_time(value)
71
+ else
72
+ return validate_param_value_string(value)
59
73
  end
60
74
  end
61
75
 
76
+ # Validate a boolean parameter
62
77
  def validate_param_value_boolean(value)
63
78
  if (value == 'true') || value.is_a?(TrueClass)
64
79
  return true
@@ -69,6 +84,7 @@ module Sinatra
69
84
  end
70
85
  end
71
86
 
87
+ # Validate an integer parameter
72
88
  def validate_param_value_integer(value)
73
89
  begin
74
90
  f = Float(value)
@@ -88,6 +104,7 @@ module Sinatra
88
104
  end
89
105
  end
90
106
 
107
+ # Validate a number parameter
91
108
  def validate_param_value_number(value)
92
109
  begin
93
110
  value = Float(value)
@@ -105,24 +122,55 @@ module Sinatra
105
122
  def validate_numerical_value(value)
106
123
  validate_numerical_value_internal(
107
124
  value,
108
- SwaggerEndpointParameter::PARAMS_MINIMUM,
109
- SwaggerEndpointParameter::PARAMS_EXCLUSIVE_MINIMUM,
125
+ PARAMS_MINIMUM,
126
+ PARAMS_EXCLUSIVE_MINIMUM,
110
127
  '>=',
111
128
  '>')
112
129
  validate_numerical_value_internal(
113
130
  value,
114
- SwaggerEndpointParameter::PARAMS_MAXIMUM,
115
- SwaggerEndpointParameter::PARAMS_EXCLUSIVE_MAXIMUM,
131
+ PARAMS_MAXIMUM,
132
+ PARAMS_EXCLUSIVE_MAXIMUM,
116
133
  '<=',
117
134
  '<')
118
135
  end
119
136
 
137
+ # Validate a date time parameter
138
+ def validate_param_value_date_time(value)
139
+ begin
140
+ DateTime.rfc3339(value)
141
+ rescue ArgumentError
142
+ raise SwaggerInvalidException.new("Parameter [#{@name}] should be a date time but is [#{value}]")
143
+ end
144
+ end
145
+
146
+ # Validate a string parameter
147
+ def validate_param_value_string(value)
148
+ if value
149
+ validate_param_value_string_length(value, PARAMS_MIN_LENGTH, '>=')
150
+ validate_param_value_string_length(value, PARAMS_MAX_LENGTH, '<=')
151
+ end
152
+ value
153
+ end
154
+
155
+ # Validate the length of a string parameter
156
+ # @param value the value to check
157
+ # @param limit_param_name [Symbol] the param that contain the value to compare to
158
+ # @param limit_param_method [String] the comparison method to call
159
+ def validate_param_value_string_length(value, limit_param_name, limit_param_method)
160
+ if @params.key? limit_param_name
161
+ target_value = @params[limit_param_name]
162
+ unless value.length.send(limit_param_method, target_value)
163
+ raise SwaggerInvalidException.new("Parameter [#{@name}] length should be #{limit_param_method} than #{target_value} but is #{value.length} for [#{value}]")
164
+ end
165
+ end
166
+ end
167
+
120
168
  # 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
169
+ # @param value the value to check
170
+ # @param limit_param_name [Symbol] the param that contain the value to compare to
171
+ # @param exclusive_limit_param_name [Symbol] the param that indicates if the comparison is absolute
172
+ # @param limit_param_method [String] the comparison method to call
173
+ # @param exclusive_limit_param_method [String] the absolute comparison method to call
126
174
  def validate_numerical_value_internal(value, limit_param_name, exclusive_limit_param_name, limit_param_method, exclusive_limit_param_method)
127
175
  if @params.key? limit_param_name
128
176
  target_value = @params[limit_param_name]
@@ -19,10 +19,14 @@ module Sinatra
19
19
  @preprocessors << preprocessor
20
20
  end
21
21
 
22
- def run(app, &block)
22
+ # Run the preprocessor the call the route content
23
+ # @param app the sinatra app being run
24
+ # @params block_params [Array] the block parameters
25
+ # @param block the block containing the route content
26
+ def run(app, block_params, &block)
23
27
  parsed_body = {}
24
28
  if app.env['CONTENT_TYPE'] == 'application/json'
25
- body = app.request.body
29
+ body = app.request.body.read
26
30
  unless body.empty?
27
31
  parsed_body = JSON.parse(body)
28
32
  end
@@ -40,7 +44,7 @@ module Sinatra
40
44
  end
41
45
  if block
42
46
  # Execute the block in the context of the app
43
- app.instance_eval(&block)
47
+ app.instance_exec(*block_params, &block)
44
48
  else
45
49
  ''
46
50
  end
@@ -10,7 +10,7 @@ module Sinatra
10
10
 
11
11
  include SwaggerUtilities
12
12
 
13
- OTHER_PROPERTIES = [:example, :description, :format]
13
+ OTHER_PROPERTIES = [:example, :description, :format, :minLength, :maxLength]
14
14
  PROPERTIES = [:type] + OTHER_PROPERTIES
15
15
 
16
16
  def initialize(type_name, property_name, property_properties, known_types)
@@ -1,4 +1,5 @@
1
1
  require_relative 'swagger-invalid-exception'
2
+ require_relative 'swagger-parameter-helper'
2
3
 
3
4
  module Sinatra
4
5
 
@@ -6,18 +7,7 @@ module Sinatra
6
7
 
7
8
  module SwaggerUtilities
8
9
 
9
- PRIMITIVE_TYPES = [
10
- 'integer',
11
- 'long',
12
- 'float',
13
- 'double',
14
- 'string',
15
- 'byte',
16
- 'boolean',
17
- 'date',
18
- 'dateTime',
19
- 'password',
20
- ]
10
+ include ::Sinatra::SwaggerExposer::SwaggerParameterHelper
21
11
 
22
12
  def ref_to_type(type)
23
13
  {'$ref' => "#/definitions/#{type}"}
@@ -35,7 +25,9 @@ module Sinatra
35
25
  # @return [String]
36
26
  def type_to_s(value)
37
27
  if [TrueClass, FalseClass].include? value
38
- 'boolean'
28
+ TYPE_BOOLEAN
29
+ elsif value == DateTime
30
+ TYPE_DATE_TIME
39
31
  elsif value.is_a? Class
40
32
  value.to_s.downcase
41
33
  else
@@ -60,15 +52,20 @@ module Sinatra
60
52
  end
61
53
 
62
54
  # Validate if a parameter is in a list of available values
63
- # @param params the parameter
55
+ # @param params [Hash] the parameters
64
56
  # @param allowed_values [Enumerable, #include?] the allowed values
65
- # @return [NilClass]
66
- def white_list_params(params, allowed_values)
57
+ # @param ignored_values [Enumerable, #include?] values to ignore
58
+ # @return [Hash] the filtered hash
59
+ def white_list_params(params, allowed_values, ignored_values = [])
60
+ result = {}
67
61
  params.each_pair do |key, value|
68
- unless allowed_values.include? key
62
+ if allowed_values.include? key
63
+ result[key] = value
64
+ elsif !ignored_values.include?(key)
69
65
  raise SwaggerInvalidException.new("Unknown property [#{key}] with value [#{value}]#{list_or_none(allowed_values, 'properties')}")
70
66
  end
71
67
  end
68
+ result
72
69
  end
73
70
 
74
71
  def list_or_none(list, name)
@@ -1,5 +1,5 @@
1
1
  module Sinatra
2
2
  module SwaggerExposer
3
- VERSION = '0.1.0'
3
+ VERSION = '0.2.0'
4
4
  end
5
5
  end
@@ -3,6 +3,8 @@ lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'sinatra/swagger-exposer/version'
5
5
 
6
+ excluded_patterns = ['test/', 'example/', '.travis.yml', '.gitignore']
7
+
6
8
  Gem::Specification.new do |spec|
7
9
  spec.name = 'sinatra-swagger-exposer'
8
10
  spec.version = Sinatra::SwaggerExposer::VERSION
@@ -13,7 +15,7 @@ Gem::Specification.new do |spec|
13
15
  spec.homepage = 'https://github.com/archiloque/sinatra-swagger-exposer'
14
16
  spec.license = 'MIT'
15
17
 
16
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| excluded_patterns.any?{|ep| f.start_with?(ep)}}
17
19
  spec.require_paths = ['lib']
18
20
 
19
21
  spec.add_dependency 'sinatra', '~> 1.4'
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.1.0
4
+ version: 0.2.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 00:00:00.000000000 Z
11
+ date: 2015-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sinatra
@@ -101,18 +101,12 @@ executables: []
101
101
  extensions: []
102
102
  extra_rdoc_files: []
103
103
  files:
104
- - ".gitignore"
105
- - ".travis.yml"
106
104
  - CHANGELOG.md
107
105
  - CODE_OF_CONDUCT.md
108
106
  - Gemfile
109
107
  - LICENSE.txt
110
108
  - README.md
111
109
  - Rakefile
112
- - example/Gemfile
113
- - example/Gemfile.lock
114
- - example/config.ru
115
- - example/petstore.rb
116
110
  - lib/sinatra/swagger-exposer/swagger-content-creator.rb
117
111
  - lib/sinatra/swagger-exposer/swagger-endpoint-parameter.rb
118
112
  - lib/sinatra/swagger-exposer/swagger-endpoint-response.rb
@@ -120,6 +114,7 @@ files:
120
114
  - lib/sinatra/swagger-exposer/swagger-exposer.rb
121
115
  - lib/sinatra/swagger-exposer/swagger-info.rb
122
116
  - lib/sinatra/swagger-exposer/swagger-invalid-exception.rb
117
+ - lib/sinatra/swagger-exposer/swagger-parameter-helper.rb
123
118
  - lib/sinatra/swagger-exposer/swagger-parameter-preprocessor.rb
124
119
  - lib/sinatra/swagger-exposer/swagger-request-preprocessor.rb
125
120
  - lib/sinatra/swagger-exposer/swagger-type-property.rb
@@ -152,3 +147,4 @@ signing_key:
152
147
  specification_version: 4
153
148
  summary: Expose swagger API from your Sinatra app
154
149
  test_files: []
150
+ has_rdoc:
data/.gitignore DELETED
@@ -1,10 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
- sinatra-swagger-exposer-*.gem
@@ -1,6 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.2.0
4
- addons:
5
- code_climate:
6
- repo_token: 9327b6a5a4de354ce0da555d4ae4ed262ee0fa2e4d210d8fb82511eee5a2dc2f
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'sinatra', '~> 1.4.5'
4
- gem 'sinatra-cross_origin', '~> 0.3.2'
@@ -1,19 +0,0 @@
1
- GEM
2
- remote: https://rubygems.org/
3
- specs:
4
- rack (1.6.0)
5
- rack-protection (1.5.3)
6
- rack
7
- sinatra (1.4.6)
8
- rack (~> 1.4)
9
- rack-protection (~> 1.4)
10
- tilt (>= 1.3, < 3)
11
- sinatra-cross_origin (0.3.2)
12
- tilt (2.0.1)
13
-
14
- PLATFORMS
15
- ruby
16
-
17
- DEPENDENCIES
18
- sinatra (~> 1.4.5)
19
- sinatra-cross_origin (~> 0.3.2)
@@ -1,2 +0,0 @@
1
- require_relative 'petstore'
2
- run Petstore
@@ -1,145 +0,0 @@
1
- require 'sinatra/base'
2
- require 'json'
3
- require 'sinatra/cross_origin'
4
-
5
- require_relative '../lib/sinatra/swagger-exposer/swagger-exposer'
6
-
7
- class Petstore < Sinatra::Base
8
-
9
- set :logging, true
10
-
11
- register Sinatra::CrossOrigin
12
- set :allow_origin, :any
13
- enable :cross_origin
14
-
15
- register Sinatra::SwaggerExposer
16
-
17
- general_info(
18
- {
19
- :version => '1.0.0',
20
- :title => 'Swagger Petstore',
21
- :description => 'A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification',
22
- :termsOfService => 'http://swagger.io/terms/',
23
- :contact => {:name => 'Swagger API Team',
24
- :email => 'apiteam@swagger.io',
25
- :url => 'http://swagger.io'
26
- },
27
- :license => {
28
- :name => 'Apache 2.0',
29
- :url => 'http://github.com/gruntjs/grunt/blob/master/LICENSE-MIT'
30
- }
31
- }
32
- )
33
-
34
- type 'Error', {
35
- :required => [:code, :message],
36
- :properties => {
37
- :code => {
38
- :type => Integer,
39
- :example => 404,
40
- :description => 'The error code',
41
- },
42
- :message => {
43
- :type => String,
44
- :example => 'Pet not found',
45
- :description => 'The error message',
46
- },
47
- },
48
- }
49
-
50
-
51
- type 'Pet', {
52
- :required => [:id, :name],
53
- :properties => {
54
- :id => {
55
- :type => Integer,
56
- :format => 'int64',
57
- },
58
- :name => {
59
- :type => String,
60
- :example => 'doggie',
61
- :description => 'The pet name',
62
- },
63
- :photoUrls => {
64
- :type => [String],
65
- },
66
- :tags => {
67
- :type => [String],
68
- :description => 'The pet\'s tags',
69
- },
70
- :status => {
71
- :type => String,
72
- :description => 'pet status in the store',
73
- :example => 'sleepy',
74
- },
75
- },
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
- }
88
-
89
- endpoint_summary 'Finds all the pets'
90
- endpoint_description 'Returns all pets from the system that the user has access to'
91
- endpoint_tags 'Pets'
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
- }
101
- get '/pets' do
102
- content_type :json
103
- [].to_json
104
- end
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
-
123
- endpoint_summary 'Finds a pet by its id'
124
- endpoint_description 'Finds a pet by its id, or 404 if the user does not have access to the pet'
125
- endpoint_tags 'Pets'
126
- endpoint_response 200, 'Pet', 'Standard response'
127
- endpoint_response 404, 'Error', 'Pet not found'
128
- endpoint_parameter :id, 'The pet id', :path, true, Integer, # Will fail if a non-numerical value is used
129
- {
130
- :example => 1234,
131
- }
132
- get '/pets/:id' do
133
- content_type :json
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
142
- end
143
-
144
- end
145
-