praxis 2.0.pre.17 → 2.0.pre.21
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/.rubocop.yml +54 -0
- data/.simplecov +3 -1
- data/.travis.yml +2 -1
- data/CHANGELOG.md +19 -0
- data/CONTRIBUTING.md +2 -79
- data/Gemfile +5 -1
- data/Guardfile +6 -4
- data/LICENSE +0 -2
- data/MAINTAINERS.md +1 -0
- data/README.md +15 -22
- data/Rakefile +4 -2
- data/bin/praxis +55 -58
- data/lib/praxis/action_definition/headers_dsl_compiler.rb +5 -6
- data/lib/praxis/action_definition.rb +65 -95
- data/lib/praxis/api_definition.rb +21 -29
- data/lib/praxis/api_general_info.rb +55 -66
- data/lib/praxis/application.rb +15 -32
- data/lib/praxis/blueprint.rb +80 -73
- data/lib/praxis/bootloader.rb +24 -33
- data/lib/praxis/bootloader_stages/environment.rb +5 -10
- data/lib/praxis/bootloader_stages/file_loader.rb +3 -6
- data/lib/praxis/bootloader_stages/plugin_config_load.rb +4 -6
- data/lib/praxis/bootloader_stages/plugin_config_prepare.rb +2 -2
- data/lib/praxis/bootloader_stages/plugin_loader.rb +3 -7
- data/lib/praxis/bootloader_stages/plugin_setup.rb +3 -3
- data/lib/praxis/bootloader_stages/routing.rb +5 -8
- data/lib/praxis/bootloader_stages/subgroup_loader.rb +2 -10
- data/lib/praxis/bootloader_stages/warn_unloaded_files.rb +15 -19
- data/lib/praxis/callbacks.rb +12 -11
- data/lib/praxis/collection.rb +11 -14
- data/lib/praxis/config.rb +17 -28
- data/lib/praxis/config_hash.rb +2 -1
- data/lib/praxis/controller.rb +7 -6
- data/lib/praxis/dispatcher.rb +34 -42
- data/lib/praxis/docs/open_api/info_object.rb +11 -8
- data/lib/praxis/docs/open_api/media_type_object.rb +18 -17
- data/lib/praxis/docs/open_api/operation_object.rb +7 -4
- data/lib/praxis/docs/open_api/parameter_object.rb +17 -14
- data/lib/praxis/docs/open_api/paths_object.rb +11 -9
- data/lib/praxis/docs/open_api/request_body_object.rb +14 -13
- data/lib/praxis/docs/open_api/response_object.rb +24 -18
- data/lib/praxis/docs/open_api/responses_object.rb +3 -1
- data/lib/praxis/docs/open_api/schema_object.rb +61 -29
- data/lib/praxis/docs/open_api/server_object.rb +5 -2
- data/lib/praxis/docs/open_api/tag_object.rb +9 -6
- data/lib/praxis/docs/open_api_generator.rb +114 -150
- data/lib/praxis/endpoint_definition.rb +60 -77
- data/lib/praxis/error_handler.rb +2 -2
- data/lib/praxis/exception.rb +2 -0
- data/lib/praxis/exceptions/config.rb +3 -1
- data/lib/praxis/exceptions/config_load.rb +2 -0
- data/lib/praxis/exceptions/config_validation.rb +3 -1
- data/lib/praxis/exceptions/invalid_configuration.rb +3 -1
- data/lib/praxis/exceptions/invalid_response.rb +3 -1
- data/lib/praxis/exceptions/invalid_trait.rb +3 -1
- data/lib/praxis/exceptions/stage_not_found.rb +3 -1
- data/lib/praxis/exceptions/validation.rb +4 -3
- data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +163 -149
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/5x.rb +18 -13
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_0.rb +13 -9
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_1_plus.rb +14 -11
- data/lib/praxis/extensions/attribute_filtering/active_record_patches.rb +12 -9
- data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +8 -5
- data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +89 -65
- data/lib/praxis/extensions/attribute_filtering/filters_parser.rb +68 -62
- data/lib/praxis/extensions/attribute_filtering.rb +3 -1
- data/lib/praxis/extensions/field_expansion.rb +6 -4
- data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +10 -8
- data/lib/praxis/extensions/field_selection/field_selector.rb +91 -92
- data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +12 -12
- data/lib/praxis/extensions/field_selection.rb +3 -1
- data/lib/praxis/extensions/pagination/active_record_pagination_handler.rb +6 -4
- data/lib/praxis/extensions/pagination/header_generator.rb +16 -11
- data/lib/praxis/extensions/pagination/ordering_params.rb +29 -28
- data/lib/praxis/extensions/pagination/pagination_handler.rb +44 -42
- data/lib/praxis/extensions/pagination/pagination_params.rb +29 -48
- data/lib/praxis/extensions/pagination/sequel_pagination_handler.rb +8 -7
- data/lib/praxis/extensions/pagination.rb +10 -15
- data/lib/praxis/extensions/rails_compat/request_methods.rb +3 -4
- data/lib/praxis/extensions/rails_compat.rb +2 -0
- data/lib/praxis/extensions/rendering.rb +12 -12
- data/lib/praxis/field_expander.rb +8 -9
- data/lib/praxis/file_group.rb +8 -12
- data/lib/praxis/finalizable.rb +1 -0
- data/lib/praxis/handlers/json.rb +5 -2
- data/lib/praxis/handlers/plain.rb +2 -1
- data/lib/praxis/handlers/www_form.rb +6 -3
- data/lib/praxis/handlers/{xml-sample.rb → xml_sample.rb} +26 -22
- data/lib/praxis/mapper/active_model_compat.rb +13 -10
- data/lib/praxis/mapper/resource.rb +196 -181
- data/lib/praxis/mapper/selector_generator.rb +106 -112
- data/lib/praxis/mapper/sequel_compat.rb +70 -67
- data/lib/praxis/media_type.rb +2 -2
- data/lib/praxis/media_type_identifier.rb +26 -22
- data/lib/praxis/middleware_app.rb +18 -15
- data/lib/praxis/multipart/parser.rb +46 -51
- data/lib/praxis/multipart/part.rb +78 -110
- data/lib/praxis/notifications.rb +2 -4
- data/lib/praxis/plugin.rb +11 -18
- data/lib/praxis/plugin_concern.rb +12 -15
- data/lib/praxis/plugins/mapper_plugin.rb +15 -13
- data/lib/praxis/plugins/pagination_plugin.rb +8 -6
- data/lib/praxis/plugins/rails_plugin.rb +33 -28
- data/lib/praxis/renderer.rb +11 -15
- data/lib/praxis/request.rb +48 -44
- data/lib/praxis/request_stages/action.rb +4 -6
- data/lib/praxis/request_stages/load_request.rb +2 -4
- data/lib/praxis/request_stages/request_stage.rb +19 -23
- data/lib/praxis/request_stages/response.rb +4 -6
- data/lib/praxis/request_stages/validate.rb +3 -5
- data/lib/praxis/request_stages/validate_params_and_headers.rb +15 -22
- data/lib/praxis/request_stages/validate_payload.rb +25 -28
- data/lib/praxis/request_superclassing.rb +3 -3
- data/lib/praxis/resource_definition.rb +1 -0
- data/lib/praxis/response.rb +24 -26
- data/lib/praxis/response_definition.rb +77 -122
- data/lib/praxis/response_template.rb +11 -15
- data/lib/praxis/responses/http.rb +23 -44
- data/lib/praxis/responses/internal_server_error.rb +18 -21
- data/lib/praxis/responses/multipart_ok.rb +4 -9
- data/lib/praxis/responses/validation_error.rb +8 -15
- data/lib/praxis/route.rb +8 -10
- data/lib/praxis/router/rack.rb +13 -7
- data/lib/praxis/router/simple.rb +10 -5
- data/lib/praxis/router.rb +27 -34
- data/lib/praxis/routing_config.rb +52 -29
- data/lib/praxis/simple_media_type.rb +5 -8
- data/lib/praxis/stage.rb +17 -25
- data/lib/praxis/tasks/api_docs.rb +17 -16
- data/lib/praxis/tasks/console.rb +3 -1
- data/lib/praxis/tasks/environment.rb +2 -0
- data/lib/praxis/tasks/routes.rb +26 -24
- data/lib/praxis/tasks.rb +3 -1
- data/lib/praxis/trait.rb +37 -46
- data/lib/praxis/types/fuzzy_hash.rb +13 -14
- data/lib/praxis/types/media_type_common.rb +11 -10
- data/lib/praxis/types/multipart_array/part_definition.rb +14 -17
- data/lib/praxis/types/multipart_array.rb +100 -115
- data/lib/praxis/validation_handler.rb +5 -3
- data/lib/praxis/version.rb +3 -1
- data/lib/praxis.rb +4 -5
- data/praxis.gemspec +22 -21
- data/spec/functional_spec.rb +44 -56
- data/spec/praxis/action_definition_spec.rb +39 -48
- data/spec/praxis/api_definition_spec.rb +45 -47
- data/spec/praxis/api_general_info_spec.rb +28 -29
- data/spec/praxis/application_spec.rb +18 -14
- data/spec/praxis/blueprint_spec.rb +33 -34
- data/spec/praxis/bootloader_spec.rb +32 -30
- data/spec/praxis/callbacks_spec.rb +37 -37
- data/spec/praxis/collection_spec.rb +18 -25
- data/spec/praxis/config_hash_spec.rb +5 -4
- data/spec/praxis/config_spec.rb +27 -26
- data/spec/praxis/controller_spec.rb +8 -9
- data/spec/praxis/endpoint_definition_spec.rb +25 -32
- data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +171 -114
- data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +22 -21
- data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +112 -60
- data/spec/praxis/extensions/attribute_filtering/filters_parser_spec.rb +37 -38
- data/spec/praxis/extensions/field_expansion_spec.rb +8 -10
- data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +14 -13
- data/spec/praxis/extensions/field_selection/field_selector_spec.rb +9 -16
- data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +50 -49
- data/spec/praxis/extensions/pagination/active_record_pagination_handler_spec.rb +32 -31
- data/spec/praxis/extensions/rendering_spec.rb +9 -9
- data/spec/praxis/extensions/support/spec_resources_active_model.rb +32 -49
- data/spec/praxis/extensions/support/spec_resources_sequel.rb +48 -48
- data/spec/praxis/field_expander_spec.rb +6 -5
- data/spec/praxis/file_group_spec.rb +3 -1
- data/spec/praxis/handlers/json_spec.rb +6 -5
- data/spec/praxis/mapper/resource_spec.rb +39 -29
- data/spec/praxis/mapper/selector_generator_spec.rb +80 -46
- data/spec/praxis/media_type_identifier_spec.rb +13 -10
- data/spec/praxis/media_type_spec.rb +12 -12
- data/spec/praxis/middleware_app_spec.rb +23 -22
- data/spec/praxis/multipart/parser_spec.rb +7 -9
- data/spec/praxis/notifications_spec.rb +4 -4
- data/spec/praxis/plugin_concern_spec.rb +5 -6
- data/spec/praxis/renderer_spec.rb +10 -9
- data/spec/praxis/request_spec.rb +38 -41
- data/spec/praxis/request_stages/action_spec.rb +14 -15
- data/spec/praxis/request_stages/request_stage_spec.rb +30 -41
- data/spec/praxis/request_stages/validate_spec.rb +3 -1
- data/spec/praxis/response_definition_spec.rb +79 -92
- data/spec/praxis/response_spec.rb +35 -40
- data/spec/praxis/responses/internal_server_error_spec.rb +6 -9
- data/spec/praxis/responses/validation_error_spec.rb +17 -18
- data/spec/praxis/route_spec.rb +4 -7
- data/spec/praxis/router_spec.rb +69 -79
- data/spec/praxis/routing_config_spec.rb +15 -14
- data/spec/praxis/stage_spec.rb +56 -53
- data/spec/praxis/trait_spec.rb +17 -17
- data/spec/praxis/types/fuzzy_hash_spec.rb +11 -9
- data/spec/praxis/types/multipart_array/part_definition_spec.rb +3 -2
- data/spec/praxis/types/multipart_array_spec.rb +33 -48
- data/spec/spec_app/app/concerns/authenticated.rb +5 -5
- data/spec/spec_app/app/concerns/basic_api.rb +3 -1
- data/spec/spec_app/app/concerns/log_wrapper.rb +5 -3
- data/spec/spec_app/app/controllers/base_class.rb +6 -5
- data/spec/spec_app/app/controllers/instances.rb +31 -34
- data/spec/spec_app/app/controllers/volumes.rb +6 -6
- data/spec/spec_app/app/responses/multipart.rb +1 -2
- data/spec/spec_app/app/responses/other_response.rb +2 -2
- data/spec/spec_app/config/environment.rb +19 -6
- data/spec/spec_app/config.ru +4 -3
- data/spec/spec_app/design/api.rb +13 -15
- data/spec/spec_app/design/media_types/instance.rb +6 -6
- data/spec/spec_app/design/media_types/volume.rb +2 -1
- data/spec/spec_app/design/media_types/volume_snapshot.rb +2 -1
- data/spec/spec_app/design/resources/instances.rb +11 -17
- data/spec/spec_app/design/resources/volume_snapshots.rb +4 -5
- data/spec/spec_app/design/resources/volumes.rb +4 -5
- data/spec/spec_helper.rb +11 -13
- data/spec/support/be_deep_equal_matcher.rb +5 -0
- data/spec/support/spec_authorization_plugin.rb +7 -12
- data/spec/support/spec_blueprints.rb +5 -4
- data/spec/support/spec_complex_authentication_plugin.rb +17 -34
- data/spec/support/spec_endpoint_definitions.rb +2 -3
- data/spec/support/spec_media_types.rb +28 -35
- data/spec/support/spec_resources.rb +22 -16
- data/spec/support/spec_simple_authentication_plugin.rb +5 -9
- data/tasks/loader.thor +4 -2
- data/tasks/thor/app.rb +7 -5
- data/tasks/thor/example.rb +23 -22
- data/tasks/thor/model.rb +7 -7
- data/tasks/thor/scaffold.rb +23 -23
- data/tasks/thor/templates/generator/example_app/app/v1/resources/user.rb +0 -8
- data/tasks/thor/templates/generator/scaffold/implementation/resources/item.rb +1 -2
- metadata +72 -84
- data/MAINTAINERS +0 -2
- data/TODO.md +0 -25
- data/spec/praxis/api_resource_spec.rb +0 -0
- data/spec/praxis/dispatcher_spec.rb +0 -0
- data/spec/spec_app/app/responses/bulk_response.rb +0 -0
data/spec/functional_spec.rb
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
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' =>
|
|
91
|
-
expect(last_response.status).to eq(
|
|
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(
|
|
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 '
|
|
108
|
-
expect
|
|
109
|
-
get '/api/clouds/1/instances?response_content_type=somejunk&api_version=1.0',nil,
|
|
110
|
-
|
|
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(
|
|
120
|
-
get '/api/clouds/1/instances/2?junk=foo&api_version=1.0', nil,'rack.input' => the_body,'CONTENT_TYPE' =>
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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
|
|
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[
|
|
177
|
-
expect(response_instance[
|
|
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([
|
|
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([
|
|
261
|
-
its(['options']){ should eq({
|
|
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[
|
|
274
|
-
expect(last_response.body).to eq(
|
|
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[
|
|
281
|
-
expect(last_response.body).to eq(
|
|
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[
|
|
291
|
-
expect(last_response.body).to eq(
|
|
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[
|
|
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 '
|
|
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(
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
identifier 'application/json'
|
|
5
|
+
class SpecMediaType < Praxis::MediaType
|
|
6
|
+
identifier 'application/json'
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
|
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
|
|
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
|
|
53
|
+
headers { header 'X_REQUESTED_WITH', 'XMLHttpRequest' }
|
|
52
54
|
params { attribute :one, String }
|
|
53
|
-
response :ok, headers: {
|
|
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
|
|
65
|
-
its('headers.attributes') { should have_key
|
|
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 [
|
|
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([
|
|
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(
|
|
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
|
-
|
|
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
|
|
201
|
+
header 'more'
|
|
203
202
|
end
|
|
204
203
|
|
|
205
|
-
expected_array = [
|
|
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
|
|
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
|
|
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 [
|
|
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
|
|
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)
|
|
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
|