praxis 0.17.1 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +18 -0
- data/lib/api_browser/package.json +1 -1
- data/lib/praxis/action_definition.rb +119 -14
- data/lib/praxis/api_general_info.rb +21 -3
- data/lib/praxis/application.rb +1 -0
- data/lib/praxis/dispatcher.rb +18 -15
- data/lib/praxis/docs/generator.rb +208 -0
- data/lib/praxis/handlers/www_form.rb +2 -1
- data/lib/praxis/media_type.rb +1 -1
- data/lib/praxis/multipart/part.rb +58 -6
- data/lib/praxis/request_stages/action.rb +10 -8
- data/lib/praxis/request_stages/request_stage.rb +2 -2
- data/lib/praxis/resource_definition.rb +11 -3
- data/lib/praxis/response_definition.rb +49 -25
- data/lib/praxis/restful_doc_generator.rb +9 -1
- data/lib/praxis/route.rb +16 -0
- data/lib/praxis/router.rb +1 -1
- data/lib/praxis/simple_media_type.rb +2 -1
- data/lib/praxis/tasks/api_docs.rb +10 -1
- data/lib/praxis/tasks/console.rb +10 -3
- data/lib/praxis/types/media_type_common.rb +8 -1
- data/lib/praxis/types/multipart.rb +5 -0
- data/lib/praxis/types/multipart_array.rb +12 -4
- data/lib/praxis/version.rb +1 -1
- data/lib/praxis.rb +3 -0
- data/praxis.gemspec +3 -3
- data/spec/functional_spec.rb +3 -3
- data/spec/praxis/action_definition_spec.rb +40 -0
- data/spec/praxis/api_general_info_spec.rb +10 -3
- data/spec/praxis/media_type_collection_spec.rb +11 -4
- data/spec/praxis/media_type_spec.rb +3 -1
- data/spec/praxis/request_stages/action_spec.rb +13 -6
- data/spec/praxis/resource_definition_spec.rb +1 -1
- data/spec/praxis/response_definition_spec.rb +29 -3
- data/spec/praxis/router_spec.rb +1 -1
- data/spec/praxis/types/multipart_array_spec.rb +92 -0
- data/spec/praxis/types/multipart_spec.rb +5 -0
- data/spec/spec_app/design/api.rb +1 -0
- data/spec/spec_app/design/media_types/volume.rb +1 -3
- data/spec/spec_app/design/resources/instances.rb +1 -1
- data/spec/support/spec_media_types.rb +5 -4
- metadata +10 -9
@@ -9,6 +9,7 @@ module Praxis
|
|
9
9
|
attr_accessor :payload_attribute
|
10
10
|
attr_accessor :headers_attribute
|
11
11
|
attr_accessor :filename_attribute
|
12
|
+
attr_accessor :default_handler
|
12
13
|
|
13
14
|
def self.check_option!(name, definition)
|
14
15
|
case name
|
@@ -76,6 +77,7 @@ module Praxis
|
|
76
77
|
@name = name
|
77
78
|
@body = body
|
78
79
|
@headers = headers
|
80
|
+
@default_handler = Praxis::Application.instance.handlers['json']
|
79
81
|
|
80
82
|
if content_type.nil?
|
81
83
|
self.content_type = 'text/plain'
|
@@ -211,24 +213,74 @@ module Praxis
|
|
211
213
|
|
212
214
|
def handler
|
213
215
|
handlers = Praxis::Application.instance.handlers
|
214
|
-
(content_type && handlers[content_type.handler_name]) ||
|
216
|
+
(content_type && handlers[content_type.handler_name]) || @default_handler
|
215
217
|
end
|
216
218
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
219
|
+
# Determine an appropriate default content_type for this part given
|
220
|
+
# the preferred handler_name, if possible.
|
221
|
+
#
|
222
|
+
# Considers any pre-defined set of values on the content_type attributge
|
223
|
+
# of the headers.
|
224
|
+
def derive_content_type(handler_name)
|
225
|
+
possible_values = if self.content_type.match 'text/plain'
|
226
|
+
_, content_type_attribute = self.headers_attribute && self.headers_attribute.attributes.find { |k,v| k.to_s =~ /^content[-_]{1}type$/i }
|
227
|
+
if content_type_attribute && content_type_attribute.options.key?(:values)
|
228
|
+
content_type_attribute.options[:values]
|
229
|
+
else
|
230
|
+
[]
|
231
|
+
end
|
232
|
+
else
|
233
|
+
[self.content_type]
|
234
|
+
end
|
235
|
+
|
236
|
+
# generic default encoding is the best we can do
|
237
|
+
if possible_values.empty?
|
238
|
+
return MediaTypeIdentifier.load("application/#{handler_name}")
|
239
|
+
end
|
240
|
+
|
241
|
+
# if any defined value match the preferred handler_name, return it
|
242
|
+
possible_values.each do |ct|
|
243
|
+
mti = MediaTypeIdentifier.load(ct)
|
244
|
+
return mti if mti.handler_name == handler_name
|
245
|
+
end
|
246
|
+
|
247
|
+
# otherwise, pick the first
|
248
|
+
pick = MediaTypeIdentifier.load(possible_values.first)
|
249
|
+
|
250
|
+
# and return that one if it already corresponds to a registered handler
|
251
|
+
# otherwise, add the encoding
|
252
|
+
if Praxis::Application.instance.handlers.include?(pick.handler_name)
|
253
|
+
return pick
|
254
|
+
else
|
255
|
+
return pick + handler_name
|
256
|
+
end
|
257
|
+
|
258
|
+
end
|
259
|
+
|
260
|
+
def dump(default_format: nil, **opts)
|
261
|
+
original_content_type = self.content_type
|
221
262
|
|
222
263
|
body = self.payload_attribute.dump(self.payload, **opts)
|
223
264
|
|
224
265
|
body_string = case body
|
225
266
|
when Hash, Array
|
226
|
-
|
267
|
+
if default_format
|
268
|
+
self.content_type = derive_content_type(default_format)
|
269
|
+
end
|
270
|
+
|
271
|
+
self.handler.generate(body)
|
227
272
|
else
|
228
273
|
body
|
229
274
|
end
|
230
275
|
|
276
|
+
header_string = self.headers.collect do |name, value|
|
277
|
+
"#{name}: #{value}"
|
278
|
+
end.join("\r\n")
|
279
|
+
|
280
|
+
|
231
281
|
"#{header_string}\r\n\r\n#{body_string}"
|
282
|
+
ensure
|
283
|
+
self.content_type = original_content_type
|
232
284
|
end
|
233
285
|
|
234
286
|
end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
module Praxis
|
2
2
|
module RequestStages
|
3
3
|
|
4
|
-
class Action < RequestStage
|
5
|
-
|
4
|
+
class Action < RequestStage
|
5
|
+
|
6
6
|
def execute
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
response = Notifications.instrument 'praxis.request_stage.execute'.freeze, controller: controller do
|
8
|
+
if controller.method(action.name).arity == 0
|
9
|
+
controller.__send__(action.name)
|
10
|
+
else
|
11
|
+
controller.__send__(action.name, **request.params_hash)
|
12
|
+
end
|
11
13
|
end
|
12
14
|
|
13
15
|
case response
|
@@ -21,8 +23,8 @@ module Praxis
|
|
21
23
|
controller.response.request = request
|
22
24
|
nil # Action cannot return its OK request, as it would indicate the end of the stage chain
|
23
25
|
end
|
24
|
-
|
26
|
+
|
25
27
|
end
|
26
28
|
|
27
29
|
end
|
28
|
-
end
|
30
|
+
end
|
@@ -40,7 +40,7 @@ module Praxis
|
|
40
40
|
def action
|
41
41
|
@context.action
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
def request
|
45
45
|
@context.request
|
46
46
|
end
|
@@ -111,7 +111,7 @@ module Praxis
|
|
111
111
|
shortcut = stage.run
|
112
112
|
if shortcut && shortcut.kind_of?(Praxis::Response)
|
113
113
|
controller.response = shortcut
|
114
|
-
return shortcut
|
114
|
+
return shortcut
|
115
115
|
end
|
116
116
|
end
|
117
117
|
nil
|
@@ -60,6 +60,13 @@ module Praxis
|
|
60
60
|
|
61
61
|
attr_accessor :controller
|
62
62
|
|
63
|
+
def display_name( string=nil )
|
64
|
+
unless string
|
65
|
+
return @display_name ||= self.name.split("::").last # Best guess at a display name?
|
66
|
+
end
|
67
|
+
@display_name = string
|
68
|
+
end
|
69
|
+
|
63
70
|
def on_finalize
|
64
71
|
if block_given?
|
65
72
|
@on_finalize << Proc.new
|
@@ -268,13 +275,14 @@ module Praxis
|
|
268
275
|
self.name.gsub('::'.freeze,'-'.freeze)
|
269
276
|
end
|
270
277
|
|
271
|
-
def describe
|
278
|
+
def describe(context: nil)
|
272
279
|
{}.tap do |hash|
|
273
280
|
hash[:description] = description
|
274
|
-
hash[:media_type] = media_type.
|
275
|
-
hash[:actions] = actions.values.
|
281
|
+
hash[:media_type] = media_type.describe(true) if media_type
|
282
|
+
hash[:actions] = actions.values.collect{|action| action.describe(context: context)}
|
276
283
|
hash[:name] = self.name
|
277
284
|
hash[:parent] = self.parent.id if self.parent
|
285
|
+
hash[:display_name] = self.display_name
|
278
286
|
hash[:metadata] = metadata
|
279
287
|
hash[:traits] = self.traits
|
280
288
|
end
|
@@ -55,15 +55,6 @@ module Praxis
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
def example(context=nil)
|
59
|
-
return nil if self.media_type.nil?
|
60
|
-
return nil if self.media_type.kind_of?(SimpleMediaType)
|
61
|
-
if context.nil?
|
62
|
-
context = "#{self.media_type.name}-#{self.name}"
|
63
|
-
end
|
64
|
-
self.media_type.example(context)
|
65
|
-
end
|
66
|
-
|
67
58
|
def location(loc=nil)
|
68
59
|
return @spec[:location] if loc.nil?
|
69
60
|
unless ( loc.is_a?(Regexp) || loc.is_a?(String) )
|
@@ -112,7 +103,19 @@ module Praxis
|
|
112
103
|
end
|
113
104
|
end
|
114
105
|
|
115
|
-
def
|
106
|
+
def example(context=nil)
|
107
|
+
return nil if self.media_type.nil?
|
108
|
+
return nil if self.media_type.kind_of?(SimpleMediaType)
|
109
|
+
|
110
|
+
if context.nil?
|
111
|
+
context = "#{self.media_type.name}-#{self.name}"
|
112
|
+
end
|
113
|
+
|
114
|
+
self.media_type.example(context)
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def describe(context: nil)
|
116
119
|
location_type = location.is_a?(Regexp) ? :regexp : :string
|
117
120
|
location_value = location.is_a?(Regexp) ? location.inspect : location
|
118
121
|
content = {
|
@@ -121,27 +124,48 @@ module Praxis
|
|
121
124
|
:headers => {}
|
122
125
|
}
|
123
126
|
content[:location] = _describe_header(location) unless location == nil
|
124
|
-
# TODO: Change the mime_type key to media_type!!
|
125
|
-
if media_type
|
126
|
-
content[:media_type] = if media_type.is_a? Symbol
|
127
|
-
media_type
|
128
|
-
else
|
129
|
-
media_type.describe(true) # TODO: is a shallow describe what we want? or just the name?
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
# Generate an appropriate response example.
|
134
|
-
# A rendered response will need to contain not just the body of the associated media_type...
|
135
|
-
# but also headers, url...etc...
|
136
|
-
#if (media_type_example = self.example)
|
137
|
-
# content[:example] = self.media_type.dump( media_type_example )
|
138
|
-
#end
|
139
127
|
|
140
128
|
unless headers == nil
|
141
129
|
headers.each do |name, value|
|
142
130
|
content[:headers][name] = _describe_header(value)
|
143
131
|
end
|
144
132
|
end
|
133
|
+
|
134
|
+
if self.media_type
|
135
|
+
payload = media_type.describe(true)
|
136
|
+
|
137
|
+
if (example_payload = self.example(context))
|
138
|
+
payload[:examples] = {}
|
139
|
+
rendered_payload = example_payload.dump
|
140
|
+
|
141
|
+
# FIXME: remove load when when MediaTypeCommon.identifier returns a MediaTypeIdentifier
|
142
|
+
identifier = MediaTypeIdentifier.load(self.media_type.identifier)
|
143
|
+
|
144
|
+
default_handlers = ApiDefinition.instance.info.produces
|
145
|
+
|
146
|
+
handlers = Praxis::Application.instance.handlers.select do |k,v|
|
147
|
+
default_handlers.include?(k)
|
148
|
+
end
|
149
|
+
|
150
|
+
if (handler = handlers[identifier.handler_name])
|
151
|
+
payload[:examples][identifier.handler_name] = {
|
152
|
+
content_type: identifier.to_s,
|
153
|
+
body: handler.generate(rendered_payload)
|
154
|
+
}
|
155
|
+
else
|
156
|
+
handlers.each do |name, handler|
|
157
|
+
content_type = identifier + name
|
158
|
+
payload[:examples][name] = {
|
159
|
+
content_type: content_type.to_s,
|
160
|
+
body: handler.generate(rendered_payload)
|
161
|
+
}
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
content[:payload] = payload
|
167
|
+
end
|
168
|
+
|
145
169
|
unless parts == nil
|
146
170
|
content[:parts_like] = parts.describe
|
147
171
|
end
|
@@ -29,7 +29,13 @@ module Praxis
|
|
29
29
|
the_type = the_type.type if the_type.is_a? Attributor::Attribute
|
30
30
|
|
31
31
|
# Collection types are special since they wrap a member type, so let's reach in and grab it
|
32
|
-
|
32
|
+
if the_type < Attributor::Collection
|
33
|
+
if the_type.member_attribute.nil?
|
34
|
+
the_type = the_type.member_type
|
35
|
+
else
|
36
|
+
the_type = the_type.member_attribute.type
|
37
|
+
end
|
38
|
+
end
|
33
39
|
|
34
40
|
if @inspected_types.include? the_type
|
35
41
|
# We're done if we've already inspected it
|
@@ -134,6 +140,8 @@ module Praxis
|
|
134
140
|
@doc_root_dir = File.join(@root_dir, API_DOCS_DIRNAME)
|
135
141
|
@resources = []
|
136
142
|
|
143
|
+
Attributor::AttributeResolver.current = Attributor::AttributeResolver.new
|
144
|
+
|
137
145
|
remove_previous_doc_data
|
138
146
|
load_resources
|
139
147
|
|
data/lib/praxis/route.rb
CHANGED
@@ -12,6 +12,22 @@ module Praxis
|
|
12
12
|
@prefixed_path = prefixed_path
|
13
13
|
end
|
14
14
|
|
15
|
+
def example(example_hash:{}, params:)
|
16
|
+
path_param_keys = self.path.named_captures.keys.collect(&:to_sym)
|
17
|
+
|
18
|
+
param_attributes = params ? params.attributes : {}
|
19
|
+
query_param_keys = param_attributes.keys - path_param_keys
|
20
|
+
required_query_param_keys = query_param_keys.each_with_object([]) do |p, array|
|
21
|
+
array << p if params.attributes[p].options[:required]
|
22
|
+
end
|
23
|
+
|
24
|
+
path_params = example_hash.select{|k,v| path_param_keys.include? k }
|
25
|
+
# Let's generate the example only using required params, to avoid mixing incompatible parameters
|
26
|
+
query_params = example_hash.select{|k,v| required_query_param_keys.include? k }
|
27
|
+
example = { verb: self.verb, url: self.path.expand(path_params), query_params: query_params }
|
28
|
+
|
29
|
+
end
|
30
|
+
|
15
31
|
def describe
|
16
32
|
result = {
|
17
33
|
verb: verb,
|
data/lib/praxis/router.rb
CHANGED
@@ -95,7 +95,7 @@ module Praxis
|
|
95
95
|
body += if version == 'n/a'
|
96
96
|
". Your request did not specify an API version.".freeze
|
97
97
|
else
|
98
|
-
". Your request
|
98
|
+
". Your request specified API version = \"#{version}\"."
|
99
99
|
end
|
100
100
|
pretty_versions = attempted_versions.collect(&:inspect).join(', ')
|
101
101
|
body += " Available versions = #{pretty_versions}."
|
@@ -27,7 +27,7 @@ namespace :praxis do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
desc "Run API Documentation Browser"
|
30
|
-
task :preview, [:port] => [:install, :generate] do |t, args|
|
30
|
+
task :preview, [:port] => [:install, :generate] do |t, args|
|
31
31
|
doc_port = args[:port] || '9090'
|
32
32
|
exec({'USER_DOCS_PATH' => File.join(Dir.pwd, 'docs'), 'DOC_PORT' => doc_port}, "#{path}/node_modules/.bin/grunt serve --gruntfile '#{path}/Gruntfile.js'")
|
33
33
|
end
|
@@ -45,6 +45,15 @@ namespace :praxis do
|
|
45
45
|
generator = Praxis::RestfulDocGenerator.new(Dir.pwd)
|
46
46
|
end
|
47
47
|
|
48
|
+
desc "Generate BETA API docs (JSON definitions) for a Praxis App"
|
49
|
+
task :generate_beta => [:environment] do |t, args|
|
50
|
+
require 'fileutils'
|
51
|
+
|
52
|
+
Praxis::Blueprint.caching_enabled = false
|
53
|
+
generator = Praxis::Docs::Generator.new(Dir.pwd)
|
54
|
+
generator.save!
|
55
|
+
end
|
56
|
+
|
48
57
|
end
|
49
58
|
|
50
59
|
desc "Generate API docs (JSON definitions) for a Praxis App"
|
data/lib/praxis/tasks/console.rb
CHANGED
@@ -11,7 +11,6 @@ namespace :praxis do
|
|
11
11
|
rescue LoadError
|
12
12
|
# Fall back on irb
|
13
13
|
require 'irb'
|
14
|
-
require 'irb/ext/multi-irb'
|
15
14
|
end
|
16
15
|
|
17
16
|
Rake::Task['praxis:environment'].invoke
|
@@ -19,10 +18,18 @@ namespace :praxis do
|
|
19
18
|
if have_pry
|
20
19
|
Praxis::Application.instance.pry
|
21
20
|
else
|
22
|
-
#
|
23
|
-
|
21
|
+
# Keep IRB.setup from complaining about bad ARGV options
|
22
|
+
old_argv = ARGV.dup
|
23
|
+
ARGV.clear
|
24
24
|
IRB.setup nil
|
25
|
+
ARGV.concat(old_argv)
|
26
|
+
|
27
|
+
# Allow reentrant IRB
|
25
28
|
IRB.conf[:MAIN_CONTEXT] = IRB::Irb.new.context
|
29
|
+
require 'irb/ext/multi-irb'
|
30
|
+
|
31
|
+
# Use some special initialization magic to ensure that 'self' in the
|
32
|
+
# IRB session refers to Praxis::Application.instance.
|
26
33
|
IRB.irb(nil, Praxis::Application.instance)
|
27
34
|
end
|
28
35
|
end
|
@@ -9,7 +9,7 @@ module Praxis
|
|
9
9
|
def describe(shallow = false, **opts)
|
10
10
|
hash = super
|
11
11
|
unless shallow
|
12
|
-
hash.merge!(identifier: @identifier.to_s, description: @description)
|
12
|
+
hash.merge!(identifier: @identifier.to_s, description: @description, display_name: self.display_name)
|
13
13
|
end
|
14
14
|
hash
|
15
15
|
end
|
@@ -19,6 +19,13 @@ module Praxis
|
|
19
19
|
@description
|
20
20
|
end
|
21
21
|
|
22
|
+
def display_name( string=nil )
|
23
|
+
unless string
|
24
|
+
return @display_name ||= self.name.split("::").last # Best guess at a display name?
|
25
|
+
end
|
26
|
+
@display_name = string
|
27
|
+
end
|
28
|
+
|
22
29
|
# Get or set the identifier of this media type.
|
23
30
|
#
|
24
31
|
# @deprecated this method is not deprecated, but its return type will change to MediaTypeIdentifier in Praxis 1.0
|
@@ -52,8 +52,13 @@ module Praxis
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def self.payload_type(type=nil, **opts, &block)
|
55
|
-
|
56
|
-
|
55
|
+
if type.nil?
|
56
|
+
if block_given?
|
57
|
+
type = Attributor::Struct
|
58
|
+
else
|
59
|
+
return @payload_type
|
60
|
+
end
|
61
|
+
end
|
57
62
|
@payload_type = Attributor.resolve_type(type)
|
58
63
|
@payload_attribute = Attributor::Attribute.new(@payload_type, **opts, &block)
|
59
64
|
@part_attribute = nil
|
@@ -188,7 +193,7 @@ module Praxis
|
|
188
193
|
end
|
189
194
|
|
190
195
|
sub_hash = part_attribute.describe(shallow, example: sub_example)
|
191
|
-
|
196
|
+
|
192
197
|
|
193
198
|
if (options = sub_hash.delete(:options))
|
194
199
|
sub_hash[:options] = {}
|
@@ -204,7 +209,7 @@ module Praxis
|
|
204
209
|
end
|
205
210
|
|
206
211
|
sub_hash[:type] = MultipartPart.describe(shallow, example: sub_example, options: part_attribute.options)
|
207
|
-
|
212
|
+
|
208
213
|
|
209
214
|
parts[part_name] = sub_hash
|
210
215
|
end
|
@@ -302,6 +307,9 @@ module Praxis
|
|
302
307
|
errors
|
303
308
|
end
|
304
309
|
|
310
|
+
def self.dump(value, **opts)
|
311
|
+
value.dump(**opts)
|
312
|
+
end
|
305
313
|
|
306
314
|
def dump(**opts)
|
307
315
|
boundary = content_type.parameters.get 'boundary'
|
data/lib/praxis/version.rb
CHANGED
data/lib/praxis.rb
CHANGED
data/praxis.gemspec
CHANGED
@@ -24,9 +24,9 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_dependency 'mustermann', '~> 0'
|
25
25
|
spec.add_dependency 'activesupport', '>= 3'
|
26
26
|
spec.add_dependency 'mime', '~> 0'
|
27
|
-
spec.add_dependency 'praxis-mapper', '>= 4.
|
28
|
-
spec.add_dependency 'praxis-blueprints', '>= 2.
|
29
|
-
spec.add_dependency 'attributor', '>= 4.0.
|
27
|
+
spec.add_dependency 'praxis-mapper', '>= 4.1'
|
28
|
+
spec.add_dependency 'praxis-blueprints', '>= 2.2'
|
29
|
+
spec.add_dependency 'attributor', '>= 4.0.1'
|
30
30
|
spec.add_dependency 'thor', '~> 0.18'
|
31
31
|
spec.add_dependency 'terminal-table', '~> 1.4'
|
32
32
|
spec.add_dependency 'harness', '~> 2'
|
data/spec/functional_spec.rb
CHANGED
@@ -268,7 +268,7 @@ describe 'Functional specs' do
|
|
268
268
|
|
269
269
|
|
270
270
|
context 'not found and API versions' do
|
271
|
-
context 'when no version is
|
271
|
+
context 'when no version is specified' do
|
272
272
|
it 'it tells you which available api versions would match' do
|
273
273
|
get '/api/clouds/1/instances/2?junk=foo',nil, 'global_session' => session
|
274
274
|
|
@@ -285,13 +285,13 @@ describe 'Functional specs' do
|
|
285
285
|
end
|
286
286
|
end
|
287
287
|
|
288
|
-
context 'when some version is
|
288
|
+
context 'when some version is specified, but wrong' do
|
289
289
|
it 'it tells you which possible correcte api versions exist' do
|
290
290
|
get '/api/clouds/1/instances/2?junk=foo&api_version=50.0', nil, 'global_session' => session
|
291
291
|
|
292
292
|
expect(last_response.status).to eq(404)
|
293
293
|
expect(last_response.headers["Content-Type"]).to eq("text/plain")
|
294
|
-
expect(last_response.body).to eq("NotFound. Your request
|
294
|
+
expect(last_response.body).to eq("NotFound. Your request specified API version = \"50.0\". Available versions = \"1.0\".")
|
295
295
|
end
|
296
296
|
end
|
297
297
|
|
@@ -100,6 +100,10 @@ describe Praxis::ActionDefinition do
|
|
100
100
|
end
|
101
101
|
|
102
102
|
describe '#params' do
|
103
|
+
it 'defaults to being required if omitted' do
|
104
|
+
expect(subject.params.options[:required]).to be(true)
|
105
|
+
end
|
106
|
+
|
103
107
|
it 'merges in more params' do
|
104
108
|
subject.params do
|
105
109
|
attribute :more, Attributor::Integer
|
@@ -108,9 +112,22 @@ describe Praxis::ActionDefinition do
|
|
108
112
|
attributes = subject.params.attributes.keys
|
109
113
|
expect(attributes).to match_array([:one, :inherited, :more])
|
110
114
|
end
|
115
|
+
|
116
|
+
it 'merges options (which allows overriding)' do
|
117
|
+
expect(subject.params.options[:required]).to be(true)
|
118
|
+
|
119
|
+
subject.params required: false
|
120
|
+
|
121
|
+
expect(subject.params.options[:required]).to be(false)
|
122
|
+
end
|
111
123
|
end
|
112
124
|
|
113
125
|
describe '#payload' do
|
126
|
+
it 'defaults to being required if omitted' do
|
127
|
+
expect(subject.payload.options[:required]).to be(true)
|
128
|
+
end
|
129
|
+
|
130
|
+
|
114
131
|
it 'merges in more payload' do
|
115
132
|
subject.payload do
|
116
133
|
attribute :more, Attributor::Integer
|
@@ -120,6 +137,14 @@ describe Praxis::ActionDefinition do
|
|
120
137
|
:two, :inherited, :more
|
121
138
|
])
|
122
139
|
end
|
140
|
+
|
141
|
+
it 'merges options (which allows overriding)' do
|
142
|
+
expect(subject.payload.options[:required]).to be(true)
|
143
|
+
|
144
|
+
subject.payload required: false
|
145
|
+
|
146
|
+
expect(subject.payload.options[:required]).to be(false)
|
147
|
+
end
|
123
148
|
end
|
124
149
|
|
125
150
|
describe '#headers' do
|
@@ -131,6 +156,10 @@ describe Praxis::ActionDefinition do
|
|
131
156
|
expect(subject.headers.type.options[:case_insensitive_load]).to be(true)
|
132
157
|
end
|
133
158
|
|
159
|
+
it 'defaults to being required if omitted' do
|
160
|
+
expect(subject.headers.options[:required]).to be(true)
|
161
|
+
end
|
162
|
+
|
134
163
|
it 'merges in more headers' do
|
135
164
|
subject.headers do
|
136
165
|
header "more"
|
@@ -139,6 +168,16 @@ describe Praxis::ActionDefinition do
|
|
139
168
|
expected_array = ["X_REQUESTED_WITH", "Inherited", "more"]
|
140
169
|
expect(subject.headers.attributes.keys).to match_array(expected_array)
|
141
170
|
end
|
171
|
+
|
172
|
+
it 'merges options (which allows overriding)' do
|
173
|
+
expect(subject.headers.options[:required]).to be(true)
|
174
|
+
|
175
|
+
subject.headers required: false do
|
176
|
+
header "even_more"
|
177
|
+
end
|
178
|
+
|
179
|
+
expect(subject.headers.options[:required]).to be(false)
|
180
|
+
end
|
142
181
|
end
|
143
182
|
|
144
183
|
context '#routing' do
|
@@ -187,6 +226,7 @@ describe Praxis::ActionDefinition do
|
|
187
226
|
expect(attributes[:one][:source]).to eq('url')
|
188
227
|
end
|
189
228
|
end
|
229
|
+
|
190
230
|
end
|
191
231
|
|
192
232
|
context 'href generation' do
|
@@ -16,6 +16,10 @@ describe Praxis::ApiGeneralInfo do
|
|
16
16
|
description "Description"
|
17
17
|
endpoint 'api.example.com'
|
18
18
|
base_path "/base"
|
19
|
+
|
20
|
+
consumes 'xml', 'x-www-form-urlencoded'
|
21
|
+
produces 'json', 'x-www-form-urlencoded'
|
22
|
+
|
19
23
|
base_params do
|
20
24
|
attribute :name, String
|
21
25
|
end
|
@@ -36,8 +40,10 @@ describe Praxis::ApiGeneralInfo do
|
|
36
40
|
end
|
37
41
|
|
38
42
|
its(:name) { should eq 'Name' }
|
39
|
-
|
43
|
+
its(:consumes) { should eq ['xml', 'x-www-form-urlencoded']}
|
44
|
+
its(:produces) { should eq ['json', 'x-www-form-urlencoded']}
|
40
45
|
end
|
46
|
+
|
41
47
|
context '.describe' do
|
42
48
|
before do
|
43
49
|
info.instance_exec &info_block
|
@@ -53,6 +59,8 @@ describe Praxis::ApiGeneralInfo do
|
|
53
59
|
its([:base_params, :name, :type, :name]) { should eq 'String' }
|
54
60
|
its([:version_with]) { should eq([:header, :params]) }
|
55
61
|
its([:endpoint]) { should eq 'api.example.com' }
|
62
|
+
its([:consumes]) { should eq ['xml', 'x-www-form-urlencoded'] }
|
63
|
+
its([:produces]) { should eq ['json', 'x-www-form-urlencoded'] }
|
56
64
|
end
|
57
65
|
|
58
66
|
context 'base_path with versioning' do
|
@@ -66,8 +74,7 @@ describe Praxis::ApiGeneralInfo do
|
|
66
74
|
|
67
75
|
global_info.version_with :path
|
68
76
|
global_info.base_path '/api/v:api_version'
|
69
|
-
end
|
70
|
-
|
77
|
+
end
|
71
78
|
|
72
79
|
its(:base_path) { should eq '/api/v1.0'}
|
73
80
|
end
|