sinatra-swagger-exposer 0.1.0 → 0.2.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 +8 -1
- data/README.md +18 -0
- data/lib/sinatra/swagger-exposer/swagger-endpoint-parameter.rb +61 -35
- data/lib/sinatra/swagger-exposer/swagger-endpoint-response.rb +5 -3
- data/lib/sinatra/swagger-exposer/swagger-endpoint.rb +22 -12
- data/lib/sinatra/swagger-exposer/swagger-exposer.rb +55 -12
- data/lib/sinatra/swagger-exposer/swagger-parameter-helper.rb +73 -0
- data/lib/sinatra/swagger-exposer/swagger-parameter-preprocessor.rb +65 -17
- data/lib/sinatra/swagger-exposer/swagger-request-preprocessor.rb +7 -3
- data/lib/sinatra/swagger-exposer/swagger-type-property.rb +1 -1
- data/lib/sinatra/swagger-exposer/swagger-utilities.rb +14 -17
- data/lib/sinatra/swagger-exposer/version.rb +1 -1
- data/sinatra-swagger-exposer.gemspec +3 -1
- metadata +4 -8
- data/.gitignore +0 -10
- data/.travis.yml +0 -6
- data/example/Gemfile +0 -4
- data/example/Gemfile.lock +0 -19
- data/example/config.ru +0 -2
- data/example/petstore.rb +0 -145
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60f345d0ee591c0a428d66ccfb5f5a7f2ae0e9b8
|
4
|
+
data.tar.gz: 4b750003265417b68303fa4f0112fbf0cb3f23a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2f6f72e7ce27da5ebcd9b6517a81b6a9c6cb5cecbda87639d60fabd1142f58ea748315ea220dd7b4779f6bc8e982bee00af770f2a7f267492653af3fee54162
|
7
|
+
data.tar.gz: aadfda04a6b04980d1b875966c6886f4fac0f3a903596b88817742f9038e872fc2b2e2ba853e0c78a11623332befa08d4d4c51894b62a287a5c5f29e553ac177
|
data/CHANGELOG.md
CHANGED
@@ -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
|
-
|
82
|
-
|
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
|
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(
|
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 +
|
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
|
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
|
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,
|
15
|
+
def initialize(type, sinatra_path, parameters, responses, summary, description, tags, explicit_path, produces)
|
16
16
|
@type = type
|
17
|
-
@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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
if
|
66
|
-
|
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
|
-
|
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
|
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 ==
|
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 ||
|
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
|
39
|
+
when HOW_TO_PASS_PATH
|
40
|
+
# can't validate
|
41
|
+
when HOW_TO_PASS_QUERY
|
32
42
|
check_param(app.params)
|
33
|
-
when
|
43
|
+
when HOW_TO_PASS_HEADER
|
34
44
|
check_param(app.headers)
|
35
|
-
when
|
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
|
63
|
+
when TYPE_NUMBER
|
54
64
|
return validate_param_value_number(value)
|
55
|
-
when
|
65
|
+
when TYPE_INTEGER
|
56
66
|
return validate_param_value_integer(value)
|
57
|
-
when
|
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
|
-
|
109
|
-
|
125
|
+
PARAMS_MINIMUM,
|
126
|
+
PARAMS_EXCLUSIVE_MINIMUM,
|
110
127
|
'>=',
|
111
128
|
'>')
|
112
129
|
validate_numerical_value_internal(
|
113
130
|
value,
|
114
|
-
|
115
|
-
|
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
|
-
# @
|
122
|
-
# @
|
123
|
-
# @
|
124
|
-
# @
|
125
|
-
# @
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
|
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
|
55
|
+
# @param params [Hash] the parameters
|
64
56
|
# @param allowed_values [Enumerable, #include?] the allowed values
|
65
|
-
# @
|
66
|
-
|
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
|
-
|
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)
|
@@ -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|
|
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.
|
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
|
+
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
data/.travis.yml
DELETED
data/example/Gemfile
DELETED
data/example/Gemfile.lock
DELETED
@@ -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)
|
data/example/config.ru
DELETED
data/example/petstore.rb
DELETED
@@ -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
|
-
|