praxis 2.0.pre.17 → 2.0.pre.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +54 -0
  3. data/.simplecov +3 -1
  4. data/.travis.yml +2 -1
  5. data/CHANGELOG.md +19 -0
  6. data/CONTRIBUTING.md +2 -79
  7. data/Gemfile +5 -1
  8. data/Guardfile +6 -4
  9. data/LICENSE +0 -2
  10. data/MAINTAINERS.md +1 -0
  11. data/README.md +15 -22
  12. data/Rakefile +4 -2
  13. data/bin/praxis +55 -58
  14. data/lib/praxis/action_definition/headers_dsl_compiler.rb +5 -6
  15. data/lib/praxis/action_definition.rb +65 -95
  16. data/lib/praxis/api_definition.rb +21 -29
  17. data/lib/praxis/api_general_info.rb +55 -66
  18. data/lib/praxis/application.rb +15 -32
  19. data/lib/praxis/blueprint.rb +80 -73
  20. data/lib/praxis/bootloader.rb +24 -33
  21. data/lib/praxis/bootloader_stages/environment.rb +5 -10
  22. data/lib/praxis/bootloader_stages/file_loader.rb +3 -6
  23. data/lib/praxis/bootloader_stages/plugin_config_load.rb +4 -6
  24. data/lib/praxis/bootloader_stages/plugin_config_prepare.rb +2 -2
  25. data/lib/praxis/bootloader_stages/plugin_loader.rb +3 -7
  26. data/lib/praxis/bootloader_stages/plugin_setup.rb +3 -3
  27. data/lib/praxis/bootloader_stages/routing.rb +5 -8
  28. data/lib/praxis/bootloader_stages/subgroup_loader.rb +2 -10
  29. data/lib/praxis/bootloader_stages/warn_unloaded_files.rb +15 -19
  30. data/lib/praxis/callbacks.rb +12 -11
  31. data/lib/praxis/collection.rb +11 -14
  32. data/lib/praxis/config.rb +17 -28
  33. data/lib/praxis/config_hash.rb +2 -1
  34. data/lib/praxis/controller.rb +7 -6
  35. data/lib/praxis/dispatcher.rb +34 -42
  36. data/lib/praxis/docs/open_api/info_object.rb +11 -8
  37. data/lib/praxis/docs/open_api/media_type_object.rb +18 -17
  38. data/lib/praxis/docs/open_api/operation_object.rb +7 -4
  39. data/lib/praxis/docs/open_api/parameter_object.rb +17 -14
  40. data/lib/praxis/docs/open_api/paths_object.rb +11 -9
  41. data/lib/praxis/docs/open_api/request_body_object.rb +14 -13
  42. data/lib/praxis/docs/open_api/response_object.rb +24 -18
  43. data/lib/praxis/docs/open_api/responses_object.rb +3 -1
  44. data/lib/praxis/docs/open_api/schema_object.rb +61 -29
  45. data/lib/praxis/docs/open_api/server_object.rb +5 -2
  46. data/lib/praxis/docs/open_api/tag_object.rb +9 -6
  47. data/lib/praxis/docs/open_api_generator.rb +114 -150
  48. data/lib/praxis/endpoint_definition.rb +60 -77
  49. data/lib/praxis/error_handler.rb +2 -2
  50. data/lib/praxis/exception.rb +2 -0
  51. data/lib/praxis/exceptions/config.rb +3 -1
  52. data/lib/praxis/exceptions/config_load.rb +2 -0
  53. data/lib/praxis/exceptions/config_validation.rb +3 -1
  54. data/lib/praxis/exceptions/invalid_configuration.rb +3 -1
  55. data/lib/praxis/exceptions/invalid_response.rb +3 -1
  56. data/lib/praxis/exceptions/invalid_trait.rb +3 -1
  57. data/lib/praxis/exceptions/stage_not_found.rb +3 -1
  58. data/lib/praxis/exceptions/validation.rb +4 -3
  59. data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +163 -149
  60. data/lib/praxis/extensions/attribute_filtering/active_record_patches/5x.rb +18 -13
  61. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_0.rb +13 -9
  62. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_1_plus.rb +14 -11
  63. data/lib/praxis/extensions/attribute_filtering/active_record_patches.rb +12 -9
  64. data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +8 -5
  65. data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +89 -65
  66. data/lib/praxis/extensions/attribute_filtering/filters_parser.rb +68 -62
  67. data/lib/praxis/extensions/attribute_filtering.rb +3 -1
  68. data/lib/praxis/extensions/field_expansion.rb +6 -4
  69. data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +10 -8
  70. data/lib/praxis/extensions/field_selection/field_selector.rb +91 -92
  71. data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +12 -12
  72. data/lib/praxis/extensions/field_selection.rb +3 -1
  73. data/lib/praxis/extensions/pagination/active_record_pagination_handler.rb +6 -4
  74. data/lib/praxis/extensions/pagination/header_generator.rb +16 -11
  75. data/lib/praxis/extensions/pagination/ordering_params.rb +29 -28
  76. data/lib/praxis/extensions/pagination/pagination_handler.rb +44 -42
  77. data/lib/praxis/extensions/pagination/pagination_params.rb +29 -48
  78. data/lib/praxis/extensions/pagination/sequel_pagination_handler.rb +8 -7
  79. data/lib/praxis/extensions/pagination.rb +10 -15
  80. data/lib/praxis/extensions/rails_compat/request_methods.rb +3 -4
  81. data/lib/praxis/extensions/rails_compat.rb +2 -0
  82. data/lib/praxis/extensions/rendering.rb +12 -12
  83. data/lib/praxis/field_expander.rb +8 -9
  84. data/lib/praxis/file_group.rb +8 -12
  85. data/lib/praxis/finalizable.rb +1 -0
  86. data/lib/praxis/handlers/json.rb +5 -2
  87. data/lib/praxis/handlers/plain.rb +2 -1
  88. data/lib/praxis/handlers/www_form.rb +6 -3
  89. data/lib/praxis/handlers/{xml-sample.rb → xml_sample.rb} +26 -22
  90. data/lib/praxis/mapper/active_model_compat.rb +13 -10
  91. data/lib/praxis/mapper/resource.rb +196 -181
  92. data/lib/praxis/mapper/selector_generator.rb +106 -112
  93. data/lib/praxis/mapper/sequel_compat.rb +70 -67
  94. data/lib/praxis/media_type.rb +2 -2
  95. data/lib/praxis/media_type_identifier.rb +26 -22
  96. data/lib/praxis/middleware_app.rb +18 -15
  97. data/lib/praxis/multipart/parser.rb +46 -51
  98. data/lib/praxis/multipart/part.rb +78 -110
  99. data/lib/praxis/notifications.rb +2 -4
  100. data/lib/praxis/plugin.rb +11 -18
  101. data/lib/praxis/plugin_concern.rb +12 -15
  102. data/lib/praxis/plugins/mapper_plugin.rb +15 -13
  103. data/lib/praxis/plugins/pagination_plugin.rb +8 -6
  104. data/lib/praxis/plugins/rails_plugin.rb +33 -28
  105. data/lib/praxis/renderer.rb +11 -15
  106. data/lib/praxis/request.rb +48 -44
  107. data/lib/praxis/request_stages/action.rb +4 -6
  108. data/lib/praxis/request_stages/load_request.rb +2 -4
  109. data/lib/praxis/request_stages/request_stage.rb +19 -23
  110. data/lib/praxis/request_stages/response.rb +4 -6
  111. data/lib/praxis/request_stages/validate.rb +3 -5
  112. data/lib/praxis/request_stages/validate_params_and_headers.rb +15 -22
  113. data/lib/praxis/request_stages/validate_payload.rb +25 -28
  114. data/lib/praxis/request_superclassing.rb +3 -3
  115. data/lib/praxis/resource_definition.rb +1 -0
  116. data/lib/praxis/response.rb +24 -26
  117. data/lib/praxis/response_definition.rb +77 -122
  118. data/lib/praxis/response_template.rb +11 -15
  119. data/lib/praxis/responses/http.rb +23 -44
  120. data/lib/praxis/responses/internal_server_error.rb +18 -21
  121. data/lib/praxis/responses/multipart_ok.rb +4 -9
  122. data/lib/praxis/responses/validation_error.rb +8 -15
  123. data/lib/praxis/route.rb +8 -10
  124. data/lib/praxis/router/rack.rb +13 -7
  125. data/lib/praxis/router/simple.rb +10 -5
  126. data/lib/praxis/router.rb +27 -34
  127. data/lib/praxis/routing_config.rb +52 -29
  128. data/lib/praxis/simple_media_type.rb +5 -8
  129. data/lib/praxis/stage.rb +17 -25
  130. data/lib/praxis/tasks/api_docs.rb +17 -16
  131. data/lib/praxis/tasks/console.rb +3 -1
  132. data/lib/praxis/tasks/environment.rb +2 -0
  133. data/lib/praxis/tasks/routes.rb +26 -24
  134. data/lib/praxis/tasks.rb +3 -1
  135. data/lib/praxis/trait.rb +37 -46
  136. data/lib/praxis/types/fuzzy_hash.rb +13 -14
  137. data/lib/praxis/types/media_type_common.rb +11 -10
  138. data/lib/praxis/types/multipart_array/part_definition.rb +14 -17
  139. data/lib/praxis/types/multipart_array.rb +100 -115
  140. data/lib/praxis/validation_handler.rb +5 -3
  141. data/lib/praxis/version.rb +3 -1
  142. data/lib/praxis.rb +4 -5
  143. data/praxis.gemspec +22 -21
  144. data/spec/functional_spec.rb +44 -56
  145. data/spec/praxis/action_definition_spec.rb +39 -48
  146. data/spec/praxis/api_definition_spec.rb +45 -47
  147. data/spec/praxis/api_general_info_spec.rb +28 -29
  148. data/spec/praxis/application_spec.rb +18 -14
  149. data/spec/praxis/blueprint_spec.rb +33 -34
  150. data/spec/praxis/bootloader_spec.rb +32 -30
  151. data/spec/praxis/callbacks_spec.rb +37 -37
  152. data/spec/praxis/collection_spec.rb +18 -25
  153. data/spec/praxis/config_hash_spec.rb +5 -4
  154. data/spec/praxis/config_spec.rb +27 -26
  155. data/spec/praxis/controller_spec.rb +8 -9
  156. data/spec/praxis/endpoint_definition_spec.rb +25 -32
  157. data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +171 -114
  158. data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +22 -21
  159. data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +112 -60
  160. data/spec/praxis/extensions/attribute_filtering/filters_parser_spec.rb +37 -38
  161. data/spec/praxis/extensions/field_expansion_spec.rb +8 -10
  162. data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +14 -13
  163. data/spec/praxis/extensions/field_selection/field_selector_spec.rb +9 -16
  164. data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +50 -49
  165. data/spec/praxis/extensions/pagination/active_record_pagination_handler_spec.rb +32 -31
  166. data/spec/praxis/extensions/rendering_spec.rb +9 -9
  167. data/spec/praxis/extensions/support/spec_resources_active_model.rb +32 -49
  168. data/spec/praxis/extensions/support/spec_resources_sequel.rb +48 -48
  169. data/spec/praxis/field_expander_spec.rb +6 -5
  170. data/spec/praxis/file_group_spec.rb +3 -1
  171. data/spec/praxis/handlers/json_spec.rb +6 -5
  172. data/spec/praxis/mapper/resource_spec.rb +39 -29
  173. data/spec/praxis/mapper/selector_generator_spec.rb +80 -46
  174. data/spec/praxis/media_type_identifier_spec.rb +13 -10
  175. data/spec/praxis/media_type_spec.rb +12 -12
  176. data/spec/praxis/middleware_app_spec.rb +23 -22
  177. data/spec/praxis/multipart/parser_spec.rb +7 -9
  178. data/spec/praxis/notifications_spec.rb +4 -4
  179. data/spec/praxis/plugin_concern_spec.rb +5 -6
  180. data/spec/praxis/renderer_spec.rb +10 -9
  181. data/spec/praxis/request_spec.rb +38 -41
  182. data/spec/praxis/request_stages/action_spec.rb +14 -15
  183. data/spec/praxis/request_stages/request_stage_spec.rb +30 -41
  184. data/spec/praxis/request_stages/validate_spec.rb +3 -1
  185. data/spec/praxis/response_definition_spec.rb +79 -92
  186. data/spec/praxis/response_spec.rb +35 -40
  187. data/spec/praxis/responses/internal_server_error_spec.rb +6 -9
  188. data/spec/praxis/responses/validation_error_spec.rb +17 -18
  189. data/spec/praxis/route_spec.rb +4 -7
  190. data/spec/praxis/router_spec.rb +69 -79
  191. data/spec/praxis/routing_config_spec.rb +15 -14
  192. data/spec/praxis/stage_spec.rb +56 -53
  193. data/spec/praxis/trait_spec.rb +17 -17
  194. data/spec/praxis/types/fuzzy_hash_spec.rb +11 -9
  195. data/spec/praxis/types/multipart_array/part_definition_spec.rb +3 -2
  196. data/spec/praxis/types/multipart_array_spec.rb +33 -48
  197. data/spec/spec_app/app/concerns/authenticated.rb +5 -5
  198. data/spec/spec_app/app/concerns/basic_api.rb +3 -1
  199. data/spec/spec_app/app/concerns/log_wrapper.rb +5 -3
  200. data/spec/spec_app/app/controllers/base_class.rb +6 -5
  201. data/spec/spec_app/app/controllers/instances.rb +31 -34
  202. data/spec/spec_app/app/controllers/volumes.rb +6 -6
  203. data/spec/spec_app/app/responses/multipart.rb +1 -2
  204. data/spec/spec_app/app/responses/other_response.rb +2 -2
  205. data/spec/spec_app/config/environment.rb +19 -6
  206. data/spec/spec_app/config.ru +4 -3
  207. data/spec/spec_app/design/api.rb +13 -15
  208. data/spec/spec_app/design/media_types/instance.rb +6 -6
  209. data/spec/spec_app/design/media_types/volume.rb +2 -1
  210. data/spec/spec_app/design/media_types/volume_snapshot.rb +2 -1
  211. data/spec/spec_app/design/resources/instances.rb +11 -17
  212. data/spec/spec_app/design/resources/volume_snapshots.rb +4 -5
  213. data/spec/spec_app/design/resources/volumes.rb +4 -5
  214. data/spec/spec_helper.rb +11 -13
  215. data/spec/support/be_deep_equal_matcher.rb +5 -0
  216. data/spec/support/spec_authorization_plugin.rb +7 -12
  217. data/spec/support/spec_blueprints.rb +5 -4
  218. data/spec/support/spec_complex_authentication_plugin.rb +17 -34
  219. data/spec/support/spec_endpoint_definitions.rb +2 -3
  220. data/spec/support/spec_media_types.rb +28 -35
  221. data/spec/support/spec_resources.rb +22 -16
  222. data/spec/support/spec_simple_authentication_plugin.rb +5 -9
  223. data/tasks/loader.thor +4 -2
  224. data/tasks/thor/app.rb +7 -5
  225. data/tasks/thor/example.rb +23 -22
  226. data/tasks/thor/model.rb +7 -7
  227. data/tasks/thor/scaffold.rb +23 -23
  228. data/tasks/thor/templates/generator/example_app/app/v1/resources/user.rb +0 -8
  229. data/tasks/thor/templates/generator/scaffold/implementation/resources/item.rb +1 -2
  230. metadata +72 -84
  231. data/MAINTAINERS +0 -2
  232. data/TODO.md +0 -25
  233. data/spec/praxis/api_resource_spec.rb +0 -0
  234. data/spec/praxis/dispatcher_spec.rb +0 -0
  235. data/spec/spec_app/app/responses/bulk_response.rb +0 -0
@@ -1,20 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe 'Functional specs' do
4
-
5
6
  def app
6
7
  Praxis::Application.instance
7
8
  end
8
9
 
9
- let(:session) { double("session", valid?: true)}
10
+ let(:session) { double('session', valid?: true) }
10
11
 
11
12
  context 'index' do
12
-
13
13
  context 'with a valid request' do
14
14
  it 'is successful' do
15
15
  get '/api/clouds/1/instances?api_version=1.0', nil, 'global_session' => session
16
16
  expect(last_response.headers['Content-Type']).to(
17
- eq("application/vnd.acme.instance;type=collection"))
17
+ eq('application/vnd.acme.instance;type=collection')
18
+ )
18
19
  end
19
20
  end
20
21
 
@@ -59,7 +60,6 @@ describe 'Functional specs' do
59
60
  expect(response['summary']).to eq 'Error validating request data.'
60
61
  expect(response['errors']).to match_array([/.*cloud_id.*is smaller than the allowed min/])
61
62
  end
62
-
63
63
  end
64
64
 
65
65
  context 'with a header that is invalid' do
@@ -87,12 +87,12 @@ describe 'Functional specs' do
87
87
  end
88
88
 
89
89
  it 'fails to validate the response' do
90
- get '/api/clouds/1/instances?response_content_type=somejunk&api_version=1.0', nil, 'HTTP_FOO' => "bar", 'global_session' => session
91
- expect(last_response.status).to eq(400)
90
+ get '/api/clouds/1/instances?response_content_type=somejunk&api_version=1.0', nil, 'HTTP_FOO' => 'bar', 'global_session' => session
91
+ expect(last_response.status).to eq(500)
92
92
  response = JSON.parse(last_response.body)
93
93
 
94
94
  expect(response['name']).to eq('ValidationError')
95
- expect(response['summary']).to eq("Error validating response")
95
+ expect(response['summary']).to eq('Error validating response')
96
96
  expect(response['errors'].first).to match(/Bad Content-Type/)
97
97
  end
98
98
 
@@ -104,31 +104,30 @@ describe 'Functional specs' do
104
104
  expect(Praxis::Application.instance.config).to receive(:praxis).and_return(praxis_config)
105
105
  end
106
106
 
107
- it 'fails to validate the response' do
108
- expect {
109
- get '/api/clouds/1/instances?response_content_type=somejunk&api_version=1.0',nil, 'global_session' => session
110
- }.to_not raise_error
107
+ it 'does not validate the response and succeeds' do
108
+ expect do
109
+ get '/api/clouds/1/instances?response_content_type=somejunk&api_version=1.0', nil, 'global_session' => session
110
+ end.to_not raise_error
111
111
  end
112
-
113
112
  end
114
113
  end
115
-
116
114
  end
117
115
 
118
116
  it 'works' do
119
- the_body = StringIO.new("{}") # This is a funny, GET request expecting a body
120
- get '/api/clouds/1/instances/2?junk=foo&api_version=1.0', nil,'rack.input' => the_body,'CONTENT_TYPE' => "application/json", 'global_session' => session
117
+ the_body = StringIO.new('{}') # This is a funny, GET request expecting a body
118
+ get '/api/clouds/1/instances/2?junk=foo&api_version=1.0', nil, 'rack.input' => the_body, 'CONTENT_TYPE' => 'application/json', 'global_session' => session
121
119
  expect(last_response.status).to eq(200)
122
120
  expected = {
123
- "cloud_id" => 1,
124
- "id"=>2,
125
- "junk"=>"foo",
126
- "other_params"=>{
127
- "some_date"=>"2012-12-21T00:00:00+00:00",
128
- "fail_filter"=>false
121
+ 'cloud_id' => 1,
122
+ 'id' => 2,
123
+ 'junk' => 'foo',
124
+ 'other_params' => {
125
+ 'some_date' => '2012-12-21T00:00:00+00:00',
126
+ 'fail_filter' => false
129
127
  },
130
- "payload"=>{
131
- "optional"=>"not given"}
128
+ 'payload' => {
129
+ 'optional' => 'not given'
130
+ }
132
131
  }
133
132
 
134
133
  expect(JSON.parse(last_response.body)).to eq(expected)
@@ -144,9 +143,8 @@ describe 'Functional specs' do
144
143
  end
145
144
 
146
145
  context 'bulk_create multipart' do
147
-
148
146
  let(:instance) { Instance.example }
149
- let(:instance_json) { JSON.pretty_generate(instance.render(fields: {id: true, name: true})) }
147
+ let(:instance_json) { JSON.pretty_generate(instance.render(fields: { id: true, name: true })) }
150
148
 
151
149
  let(:form) do
152
150
  form_data = MIME::Multipart::FormData.new
@@ -170,15 +168,14 @@ describe 'Functional specs' do
170
168
 
171
169
  instance_headers = instance_part.headers
172
170
  expect(instance_headers['Status']).to eq('201')
173
- expect(instance_headers['Location']).to match(%r|/clouds/.*/instances/.*|)
171
+ expect(instance_headers['Location']).to match(%r{/clouds/.*/instances/.*})
174
172
 
175
173
  response_instance = JSON.parse(instance_part.body)
176
- expect(response_instance["key"]).to eq(instance.id)
177
- expect(response_instance["value"].values).to eq(instance.render(fields: {id: true, name: true}).values)
174
+ expect(response_instance['key']).to eq(instance.id)
175
+ expect(response_instance['value'].values).to eq(instance.render(fields: { id: true, name: true }).values)
178
176
  end
179
177
  end
180
178
 
181
-
182
179
  context 'attach_file' do
183
180
  let(:form) do
184
181
  form_data = MIME::Multipart::FormData.new
@@ -229,9 +226,8 @@ describe 'Functional specs' do
229
226
  response = JSON.parse(last_response.body)
230
227
 
231
228
  expect(response['name']).to eq('ValidationError')
232
- expect(response['errors']).to eq(["Attribute $.payload.destination_path is required"])
229
+ expect(response['errors']).to eq(['Attribute $.payload.destination_path is required'])
233
230
  end
234
-
235
231
  end
236
232
 
237
233
  context 'with an extra key in the form' do
@@ -257,28 +253,26 @@ describe 'Functional specs' do
257
253
  before do
258
254
  post '/api/clouds/1/instances/2/files?api_version=1.0', body, 'CONTENT_TYPE' => content_type, 'global_session' => session
259
255
  end
260
- its(:keys){ should eq(['destination_path','name','filename','type','contents','options'])}
261
- its(['options']){ should eq({"extra_thing"=>"I am extra"})}
256
+ its(:keys) { should eq(%w[destination_path name filename type contents options]) }
257
+ its(['options']) { should eq({ 'extra_thing' => 'I am extra' }) }
262
258
  end
263
-
264
259
  end
265
260
 
266
-
267
261
  context 'not found and API versions' do
268
262
  context 'when no version is specified' do
269
263
  it 'it tells you which available api versions would match' do
270
- get '/api/clouds/1/instances/2?junk=foo',nil, 'global_session' => session
264
+ get '/api/clouds/1/instances/2?junk=foo', nil, 'global_session' => session
271
265
 
272
266
  expect(last_response.status).to eq(404)
273
- expect(last_response.headers["Content-Type"]).to eq("text/plain")
274
- expect(last_response.body).to eq("NotFound. Your request did not specify an API version. Available versions = \"1.0\".")
267
+ expect(last_response.headers['Content-Type']).to eq('text/plain')
268
+ expect(last_response.body).to eq('NotFound. Your request did not specify an API version. Available versions = "1.0".')
275
269
  end
276
270
  it 'it just gives you a simple not found when nothing would have matched' do
277
271
  get '/foobar?junk=foo', nil, 'global_session' => session
278
272
 
279
273
  expect(last_response.status).to eq(404)
280
- expect(last_response.headers["Content-Type"]).to eq("text/plain")
281
- expect(last_response.body).to eq("NotFound")
274
+ expect(last_response.headers['Content-Type']).to eq('text/plain')
275
+ expect(last_response.body).to eq('NotFound')
282
276
  end
283
277
  end
284
278
 
@@ -287,11 +281,10 @@ describe 'Functional specs' do
287
281
  get '/api/clouds/1/instances/2?junk=foo&api_version=50.0', nil, 'global_session' => session
288
282
 
289
283
  expect(last_response.status).to eq(404)
290
- expect(last_response.headers["Content-Type"]).to eq("text/plain")
291
- expect(last_response.body).to eq("NotFound. Your request specified API version = \"50.0\". Available versions = \"1.0\".")
284
+ expect(last_response.headers['Content-Type']).to eq('text/plain')
285
+ expect(last_response.body).to eq('NotFound. Your request specified API version = "50.0". Available versions = "1.0".')
292
286
  end
293
287
  end
294
-
295
288
  end
296
289
 
297
290
  context 'volumes' do
@@ -304,7 +297,7 @@ describe 'Functional specs' do
304
297
  get '/api/clouds/1/volumes/123?junk=stuff', nil, 'global_session' => session
305
298
  expect(last_response.status).to eq(200)
306
299
  expect(Volume.load(last_response.body).validate).to be_empty
307
- expect(last_response.headers["Content-Type"]).to eq("application/vnd.acme.volume")
300
+ expect(last_response.headers['Content-Type']).to eq('application/vnd.acme.volume')
308
301
  end
309
302
  end
310
303
  context 'when an authorization header is passed' do
@@ -317,7 +310,6 @@ describe 'Functional specs' do
317
310
  get '/api/clouds/1/volumes/123?junk=stuff', nil, 'HTTP_AUTHORIZATION' => 'the secret', 'global_session' => session
318
311
  expect(last_response.status).to eq(200)
319
312
  end
320
-
321
313
  end
322
314
 
323
315
  context 'index action with no args defined' do
@@ -329,7 +321,7 @@ describe 'Functional specs' do
329
321
  end
330
322
 
331
323
  context 'wildcard verb routing' do
332
- let(:content_type){ 'application/json' }
324
+ let(:content_type) { 'application/json' }
333
325
  it 'can terminate instances with POST' do
334
326
  post '/api/clouds/23/instances/1/terminate?api_version=1.0', nil, 'CONTENT_TYPE' => content_type, 'global_session' => session
335
327
  expect(last_response.status).to eq(200)
@@ -338,7 +330,6 @@ describe 'Functional specs' do
338
330
  post '/api/clouds/23/instances/1/terminate?api_version=1.0', nil, 'CONTENT_TYPE' => content_type, 'global_session' => session
339
331
  expect(last_response.status).to eq(200)
340
332
  end
341
-
342
333
  end
343
334
 
344
335
  context 'route options' do
@@ -381,7 +372,6 @@ describe 'Functional specs' do
381
372
  end
382
373
 
383
374
  context 'update' do
384
-
385
375
  let(:body) { JSON.pretty_generate(request_payload) }
386
376
  let(:content_type) { 'application/json' }
387
377
 
@@ -399,29 +389,27 @@ describe 'Functional specs' do
399
389
  end
400
390
 
401
391
  context 'with a provided name' do
402
- let(:request_payload) { {name: 'MyInstance'} }
392
+ let(:request_payload) { { name: 'MyInstance' } }
403
393
  its(['name']) { should eq('MyInstance') }
404
394
  it { should_not have_key('root_volume') }
405
395
  end
406
396
 
407
397
  context 'with an explicitly-nil root_volme' do
408
- let(:request_payload) { {name: 'MyInstance', root_volume: nil} }
398
+ let(:request_payload) { { name: 'MyInstance', root_volume: nil } }
409
399
  its(['name']) { should eq('MyInstance') }
410
400
  its(['root_volume']) { should be(nil) }
411
401
  end
412
402
 
413
- context 'with an invalid name' do
414
- let(:request_payload) { {name: 'Invalid Name'} }
403
+ context 'returning an invalid name' do
404
+ let(:request_payload) { { name: 'Invalid Name' } }
415
405
 
416
406
  its(['name']) { should eq 'ValidationError' }
417
407
  its(['summary']) { should eq 'Error validating response' }
418
408
  its(['errors']) { should match_array [/\$\.name value \(Invalid Name\) does not match regexp/] }
419
409
 
420
410
  it 'returns a validation error' do
421
- expect(last_response.status).to eq(400)
411
+ expect(last_response.status).to eq(500)
422
412
  end
423
413
  end
424
-
425
414
  end
426
-
427
415
  end
@@ -1,18 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
- describe Praxis::ActionDefinition do
4
- class SpecMediaType < Praxis::MediaType
5
- identifier 'application/json'
5
+ class SpecMediaType < Praxis::MediaType
6
+ identifier 'application/json'
6
7
 
7
- attributes do
8
- attribute :one, String
9
- attribute :two, Integer
10
- end
11
- default_fieldset do
12
- attribute :one
13
- end
8
+ attributes do
9
+ attribute :one, String
10
+ attribute :two, Integer
11
+ end
12
+ default_fieldset do
13
+ attribute :one
14
14
  end
15
+ end
15
16
 
17
+ describe Praxis::ActionDefinition do
16
18
  let(:endpoint_definition) do
17
19
  Class.new do
18
20
  include Praxis::EndpointDefinition
@@ -26,20 +28,20 @@ describe Praxis::ActionDefinition do
26
28
  prefix '/foobars/hello_world'
27
29
  action_defaults do
28
30
  payload { attribute :inherited, String }
29
- headers { header "Inherited", String }
31
+ headers { header 'Inherited', String }
30
32
  params { attribute :inherited, String }
31
33
  end
32
34
  end
33
35
  end
34
36
 
35
37
  subject(:action) do
36
-
37
38
  Praxis::ApiDefinition.define do |api|
38
- api.response_template :ok do |media_type: , location: nil, headers: nil, description: nil |
39
+ api.response_template :ok do |media_type:, location: nil, headers: nil, description: nil|
39
40
  status 200
40
41
 
41
42
  media_type media_type
42
43
  location location
44
+ description description
43
45
  headers&.each do |(name, value)|
44
46
  header(name, value)
45
47
  end
@@ -48,9 +50,9 @@ describe Praxis::ActionDefinition do
48
50
  Praxis::ActionDefinition.new(:foo, endpoint_definition) do
49
51
  routing { get '/:one' }
50
52
  payload { attribute :two, String }
51
- headers { header "X_REQUESTED_WITH", 'XMLHttpRequest' }
53
+ headers { header 'X_REQUESTED_WITH', 'XMLHttpRequest' }
52
54
  params { attribute :one, String }
53
- response :ok, headers: { "Foo" => "Bar"}, location: %r{/some/thing}
55
+ response :ok, headers: { 'Foo' => 'Bar' }, location: %r{/some/thing}
54
56
  end
55
57
  end
56
58
 
@@ -61,8 +63,8 @@ describe Praxis::ActionDefinition do
61
63
  its('params.attributes') { should have_key :inherited }
62
64
  its('payload.attributes') { should have_key :two }
63
65
  its('payload.attributes') { should have_key :inherited }
64
- its('headers.attributes') { should have_key "X_REQUESTED_WITH" }
65
- its('headers.attributes') { should have_key "Inherited" }
66
+ its('headers.attributes') { should have_key 'X_REQUESTED_WITH' }
67
+ its('headers.attributes') { should have_key 'Inherited' }
66
68
  its('metadata') { should_not have_key :doc_visibility }
67
69
  end
68
70
 
@@ -99,23 +101,21 @@ describe Praxis::ActionDefinition do
99
101
  attribute :app_name, String
100
102
  attribute :name, String
101
103
  end
102
-
103
104
  end
104
105
  end
105
- let(:traits) { {test: trait} }
106
+ let(:traits) { { test: trait } }
106
107
 
107
108
  before do
108
109
  allow(Praxis::ApiDefinition.instance).to receive(:traits).and_return(traits)
109
110
  end
110
111
 
111
- its('params.attributes.keys') { should eq [:inherited, :app_name, :name, :one]}
112
+ its('params.attributes.keys') { should eq %i[inherited app_name name one] }
112
113
  its('route.path.to_s') { should eq '/api/foobars/hello_world/test_trait/:app_name/:one' }
113
114
  its(:traits) { should eq [:test] }
114
115
 
115
116
  it 'is reflected in the describe output' do
116
117
  expect(action.describe[:traits]).to eq [:test]
117
118
  end
118
-
119
119
  end
120
120
 
121
121
  describe '#params' do
@@ -129,7 +129,7 @@ describe Praxis::ActionDefinition do
129
129
  end
130
130
 
131
131
  attributes = subject.params.attributes.keys
132
- expect(attributes).to match_array([:one, :inherited, :more])
132
+ expect(attributes).to match_array(%i[one inherited more])
133
133
  end
134
134
 
135
135
  it 'merges options (which allows overriding)' do
@@ -144,35 +144,34 @@ describe Praxis::ActionDefinition do
144
144
  before do
145
145
  action.params do
146
146
  attribute :two
147
- #requires.at_most(1).of :one, :two
147
+ # requires.at_most(1).of :one, :two
148
148
  requires :one
149
149
  end
150
150
  end
151
151
 
152
- let(:value) { {two: 2} }
152
+ let(:value) { { two: 2 } }
153
153
  it 'includes the requirements in the param struct type' do
154
154
  errors = action.params.load(value).validate
155
155
  expect(errors).to have(1).item
156
- expect(errors.first).to match(/Key one is required/)
156
+ expect(errors.first).to match('Attribute $.key(:one) is required.')
157
157
  end
158
158
  end
159
-
160
159
  end
161
160
 
162
161
  describe '#payload' do
163
- it 'defaults to being required if omitted' do
162
+ it 'defaults to being required and non nullable if omitted' do
164
163
  expect(subject.payload.options[:required]).to be(true)
164
+ expect(subject.payload.options[:null]).to be(false)
165
165
  end
166
166
 
167
-
168
167
  it 'merges in more payload' do
169
168
  subject.payload do
170
169
  attribute :more, Attributor::Integer
171
170
  end
172
171
 
173
- expect(subject.payload.attributes.keys).to match_array([
174
- :two, :inherited, :more
175
- ])
172
+ expect(subject.payload.attributes.keys).to match_array(%i[
173
+ two inherited more
174
+ ])
176
175
  end
177
176
 
178
177
  it 'merges options (which allows overriding)' do
@@ -199,10 +198,10 @@ describe Praxis::ActionDefinition do
199
198
 
200
199
  it 'merges in more headers' do
201
200
  subject.headers do
202
- header "more"
201
+ header 'more'
203
202
  end
204
203
 
205
- expected_array = ["X_REQUESTED_WITH", "Inherited", "more"]
204
+ expected_array = %w[X_REQUESTED_WITH Inherited more]
206
205
  expect(subject.headers.attributes.keys).to match_array(expected_array)
207
206
  end
208
207
 
@@ -210,7 +209,7 @@ describe Praxis::ActionDefinition do
210
209
  expect(subject.headers.options[:required]).to be(true)
211
210
 
212
211
  subject.headers required: false do
213
- header "even_more"
212
+ header 'even_more'
214
213
  end
215
214
 
216
215
  expect(subject.headers.options[:required]).to be(false)
@@ -228,7 +227,7 @@ describe Praxis::ActionDefinition do
228
227
  expect(action.route.path.to_s).to eq '/api/clouds/:cloud_id/volumes/:volume_id/snapshots/:id'
229
228
  end
230
229
 
231
- its('params.attributes'){ should have_key(:cloud_id) }
230
+ its('params.attributes') { should have_key(:cloud_id) }
232
231
 
233
232
  context 'with pre-existing parent param' do
234
233
  let(:action) { resource.actions[:index] }
@@ -241,7 +240,6 @@ describe Praxis::ActionDefinition do
241
240
  it { should_not be nil }
242
241
  its(:options) { should eq parent_param.options }
243
242
  end
244
-
245
243
  end
246
244
  end
247
245
 
@@ -276,9 +274,8 @@ describe Praxis::ActionDefinition do
276
274
 
277
275
  it 'works' do
278
276
  expansion = action.route.path.expand(cloud_id: '232', id: '2')
279
- expect(expansion).to eq "/api/clouds/232/instances/2"
277
+ expect(expansion).to eq '/api/clouds/232/instances/2'
280
278
  end
281
-
282
279
  end
283
280
 
284
281
  context 'with nodoc!' do
@@ -293,16 +290,14 @@ describe Praxis::ActionDefinition do
293
290
  it 'is exposed by describe' do
294
291
  expect(action.describe[:metadata][:doc_visibility]).to be(:none)
295
292
  end
296
-
297
293
  end
298
294
 
299
295
  context 'with a base_path and base_params on ApiDefinition' do
300
296
  # Without getting a fresh new ApiDefinition it is very difficult to test stuff using the Singleton
301
297
  # So for some tests we're gonna create a new instance and work with it to avoid the singleton issues
302
298
  let(:non_singleton_api) do
303
- api_def=Praxis::ApiDefinition.__send__(:new)
299
+ api_def = Praxis::ApiDefinition.__send__(:new)
304
300
  api_def.instance_eval do |api|
305
-
306
301
  api.info do
307
302
  base_path '/apps/:app_name'
308
303
  end
@@ -312,7 +307,6 @@ describe Praxis::ActionDefinition do
312
307
  attribute :app_name, String
313
308
  end
314
309
  end
315
-
316
310
  end
317
311
  api_def
318
312
  end
@@ -322,10 +316,9 @@ describe Praxis::ActionDefinition do
322
316
  end
323
317
 
324
318
  its('route.path.to_s') { should eq '/apps/:app_name/foobars/hello_world/:one' }
325
- its('params.attributes.keys') { should match_array [:inherited, :app_name, :one]}
319
+ its('params.attributes.keys') { should match_array %i[inherited app_name one] }
326
320
 
327
321
  context 'where the action overrides a base_param' do
328
-
329
322
  let(:endpoint_definition) do
330
323
  Class.new do
331
324
  include Praxis::EndpointDefinition
@@ -337,7 +330,7 @@ describe Praxis::ActionDefinition do
337
330
  prefix '/foobars/hello_world'
338
331
  action_defaults do
339
332
  payload { attribute :inherited, String }
340
- headers { header "Inherited", String }
333
+ headers { header 'Inherited', String }
341
334
  end
342
335
  end
343
336
  end
@@ -351,13 +344,11 @@ describe Praxis::ActionDefinition do
351
344
 
352
345
  subject(:attributes) { action.params.attributes }
353
346
 
354
- its(:keys) { should eq [:app_name]}
347
+ its(:keys) { should eq [:app_name] }
355
348
 
356
349
  it 'overrides the base param' do
357
350
  expect(attributes[:app_name].type).to eq(Attributor::Integer)
358
351
  end
359
-
360
352
  end
361
-
362
353
  end
363
354
  end