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
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
describe Praxis::ResponseDefinition do
|
|
4
6
|
subject(:response_definition) { Praxis::ResponseDefinition.new(name, &block) }
|
|
5
7
|
let(:name) { 'response_name' }
|
|
6
8
|
|
|
7
9
|
let(:block) do
|
|
8
|
-
|
|
10
|
+
proc do
|
|
9
11
|
status 200
|
|
10
12
|
description 'test description'
|
|
11
|
-
header(
|
|
12
|
-
header(
|
|
13
|
+
header('X-Header', 'value', description: 'Very nais header')
|
|
14
|
+
header('Content-Type', 'application/some-type')
|
|
13
15
|
end
|
|
14
16
|
end
|
|
15
17
|
|
|
@@ -17,11 +19,10 @@ describe Praxis::ResponseDefinition do
|
|
|
17
19
|
its(:description) { should == 'test description' }
|
|
18
20
|
its(:parts) { should be(nil) }
|
|
19
21
|
let(:response_status) { 200 }
|
|
20
|
-
let(:response_content_type) {
|
|
21
|
-
let(:response_headers) { {
|
|
22
|
-
|
|
23
|
-
let(:response) { instance_double("Praxis::Response", status: response_status , headers: response_headers, content_type: response_content_type ) }
|
|
22
|
+
let(:response_content_type) { 'application/some-type' }
|
|
23
|
+
let(:response_headers) { { 'X-Header' => 'value', 'Content-Type' => response_content_type, 'Location' => '/somewhere/over/the/rainbow' } }
|
|
24
24
|
|
|
25
|
+
let(:response) { instance_double('Praxis::Response', status: response_status, headers: response_headers, content_type: response_content_type) }
|
|
25
26
|
|
|
26
27
|
context '#media_type' do
|
|
27
28
|
it 'accepts a MediaType object and returns the media_type that was set' do
|
|
@@ -42,11 +43,11 @@ describe Praxis::ResponseDefinition do
|
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
it 'should return an error when media_type is not a String or a MediaType' do
|
|
45
|
-
expect{ response_definition.media_type Object.new }.to raise_error(Praxis::Exceptions::InvalidConfiguration)
|
|
46
|
+
expect { response_definition.media_type Object.new }.to raise_error(Praxis::Exceptions::InvalidConfiguration)
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
it 'should return an error when media_type is a Symbol other than :controller_defined' do
|
|
49
|
-
expect{ response_definition.media_type :symbol }.to raise_error(Praxis::Exceptions::InvalidConfiguration)
|
|
50
|
+
expect { response_definition.media_type :symbol }.to raise_error(Praxis::Exceptions::InvalidConfiguration)
|
|
50
51
|
end
|
|
51
52
|
end
|
|
52
53
|
|
|
@@ -73,7 +74,7 @@ describe Praxis::ResponseDefinition do
|
|
|
73
74
|
end
|
|
74
75
|
|
|
75
76
|
# TODO: Complete/correct the "example" generation when it is done in the ResponseDefinition class
|
|
76
|
-
#context 'with media_type set to a MediaType' do
|
|
77
|
+
# context 'with media_type set to a MediaType' do
|
|
77
78
|
# let(:media_type) { Person }
|
|
78
79
|
#
|
|
79
80
|
# let(:expected_context) { "Person-#{name}" }
|
|
@@ -87,17 +88,17 @@ describe Praxis::ResponseDefinition do
|
|
|
87
88
|
# it 'is rendered in the describe output' do
|
|
88
89
|
# expect(response_definition.describe[:example]).to eq(example.render)
|
|
89
90
|
# end
|
|
90
|
-
#end
|
|
91
|
+
# end
|
|
91
92
|
end
|
|
92
93
|
|
|
93
94
|
context '#location' do
|
|
94
95
|
it 'accepts a String' do
|
|
95
|
-
response_definition.location
|
|
96
|
-
expect(response_definition.location).to eq(
|
|
96
|
+
response_definition.location 'string_location'
|
|
97
|
+
expect(response_definition.location).to eq('string_location')
|
|
97
98
|
end
|
|
98
99
|
|
|
99
100
|
it 'accepts a Regex' do
|
|
100
|
-
response_definition.location
|
|
101
|
+
response_definition.location(/regex_location/)
|
|
101
102
|
expect(response_definition.location).to eq(/regex_location/)
|
|
102
103
|
end
|
|
103
104
|
|
|
@@ -114,31 +115,30 @@ describe Praxis::ResponseDefinition do
|
|
|
114
115
|
|
|
115
116
|
subject(:parts) { response_definition.parts }
|
|
116
117
|
|
|
117
|
-
it{ should be_kind_of(Praxis::ResponseDefinition) }
|
|
118
|
-
its('media_type.identifier'){ should == 'application/special' }
|
|
119
|
-
its(:name){ should be(:ok) }
|
|
120
|
-
its(:status){ should be(
|
|
121
|
-
|
|
118
|
+
it { should be_kind_of(Praxis::ResponseDefinition) }
|
|
119
|
+
its('media_type.identifier') { should == 'application/special' }
|
|
120
|
+
its(:name) { should be(:ok) }
|
|
121
|
+
its(:status) { should be(200) }
|
|
122
122
|
end
|
|
123
123
|
context 'without a :like argument, and without a block' do
|
|
124
124
|
it 'complains' do
|
|
125
|
-
expect
|
|
125
|
+
expect do
|
|
126
126
|
response_definition.parts media_type: 'application/special'
|
|
127
|
-
|
|
127
|
+
end.to raise_error(ArgumentError, /needs a :like argument or a block/)
|
|
128
128
|
end
|
|
129
129
|
end
|
|
130
130
|
context 'with a :like argument, and a block' do
|
|
131
131
|
it 'complains' do
|
|
132
|
-
expect
|
|
132
|
+
expect do
|
|
133
133
|
response_definition.parts like: :something, media_type: 'application/special' do
|
|
134
134
|
end
|
|
135
|
-
|
|
135
|
+
end.to raise_error(ArgumentError, /does not allow :like and a block simultaneously/)
|
|
136
136
|
end
|
|
137
137
|
end
|
|
138
138
|
|
|
139
139
|
context 'with a proc' do
|
|
140
140
|
let(:the_proc) do
|
|
141
|
-
|
|
141
|
+
proc do
|
|
142
142
|
status 201
|
|
143
143
|
media_type 'from_proc'
|
|
144
144
|
end
|
|
@@ -150,10 +150,9 @@ describe Praxis::ResponseDefinition do
|
|
|
150
150
|
|
|
151
151
|
subject(:parts) { response_definition.parts }
|
|
152
152
|
|
|
153
|
-
it{ should be_kind_of(Praxis::ResponseDefinition) }
|
|
154
|
-
its('media_type.identifier'){ should == 'from_proc' }
|
|
155
|
-
its(:status){ should be(
|
|
156
|
-
|
|
153
|
+
it { should be_kind_of(Praxis::ResponseDefinition) }
|
|
154
|
+
its('media_type.identifier') { should == 'from_proc' }
|
|
155
|
+
its(:status) { should be(201) }
|
|
157
156
|
end
|
|
158
157
|
|
|
159
158
|
context 'with a block' do
|
|
@@ -166,10 +165,9 @@ describe Praxis::ResponseDefinition do
|
|
|
166
165
|
|
|
167
166
|
subject(:parts) { response_definition.parts }
|
|
168
167
|
|
|
169
|
-
it{ should be_kind_of(Praxis::ResponseDefinition) }
|
|
170
|
-
its('media_type.identifier'){ should == 'from_proc' }
|
|
171
|
-
its(:status){ should be(
|
|
172
|
-
|
|
168
|
+
it { should be_kind_of(Praxis::ResponseDefinition) }
|
|
169
|
+
its('media_type.identifier') { should == 'from_proc' }
|
|
170
|
+
its(:status) { should be(201) }
|
|
173
171
|
end
|
|
174
172
|
end
|
|
175
173
|
# context '#multipart' do
|
|
@@ -217,8 +215,7 @@ describe Praxis::ResponseDefinition do
|
|
|
217
215
|
|
|
218
216
|
context '#validate' do
|
|
219
217
|
context 'functional test' do
|
|
220
|
-
|
|
221
|
-
it "calls all the validation sub-functions" do
|
|
218
|
+
it 'calls all the validation sub-functions' do
|
|
222
219
|
expect(response_definition).to receive(:validate_status!).once
|
|
223
220
|
expect(response_definition).to receive(:validate_headers!).once
|
|
224
221
|
expect(response_definition).to receive(:validate_content_type!).once
|
|
@@ -227,68 +224,64 @@ describe Praxis::ResponseDefinition do
|
|
|
227
224
|
end
|
|
228
225
|
|
|
229
226
|
describe 'custom validate_xxx! methods' do
|
|
230
|
-
|
|
231
|
-
describe "#validate_status!" do
|
|
227
|
+
describe '#validate_status!' do
|
|
232
228
|
context 'that is completely valid' do
|
|
233
229
|
it 'should succeed' do
|
|
234
|
-
expect
|
|
230
|
+
expect do
|
|
235
231
|
response_definition.validate_status!(response)
|
|
236
|
-
|
|
232
|
+
end.to_not raise_error
|
|
237
233
|
end
|
|
238
234
|
end
|
|
239
235
|
|
|
240
|
-
|
|
241
236
|
context 'with internal error' do
|
|
242
237
|
let(:response_status) { 500 }
|
|
243
238
|
it 'should raise an error that later gets swallowed' do
|
|
244
|
-
expect
|
|
239
|
+
expect do
|
|
245
240
|
response_definition.validate_status!(response)
|
|
246
|
-
|
|
241
|
+
end.to raise_error(Praxis::Exceptions::Validation)
|
|
247
242
|
end
|
|
248
243
|
end
|
|
249
|
-
|
|
250
244
|
end
|
|
251
245
|
|
|
252
|
-
describe
|
|
246
|
+
describe '#validate_headers!' do
|
|
253
247
|
context 'when there are missing headers' do
|
|
254
248
|
it 'should raise error' do
|
|
255
249
|
response_definition.header('X-Unknown', 'test')
|
|
256
|
-
expect
|
|
250
|
+
expect do
|
|
257
251
|
response_definition.validate_headers!(response)
|
|
258
|
-
|
|
252
|
+
end.to raise_error(Praxis::Exceptions::Validation)
|
|
259
253
|
end
|
|
260
254
|
end
|
|
261
255
|
context 'when headers with same names are returned' do
|
|
262
256
|
it 'a simply required header should not raise error just by being there' do
|
|
263
257
|
response_definition.header('X-Header', nil)
|
|
264
|
-
expect
|
|
258
|
+
expect do
|
|
265
259
|
response_definition.validate_headers!(response)
|
|
266
|
-
|
|
260
|
+
end.to_not raise_error
|
|
267
261
|
end
|
|
268
262
|
it 'an exact string header should not raise error if it fully matches' do
|
|
269
263
|
response_definition.header('X-Header', 'value')
|
|
270
|
-
expect
|
|
264
|
+
expect do
|
|
271
265
|
response_definition.validate_headers!(response)
|
|
272
|
-
|
|
266
|
+
end.to_not raise_error
|
|
273
267
|
end
|
|
274
268
|
it 'a regexp header should not raise error if it matches the regexp' do
|
|
275
269
|
response_definition.header('X-Header', /value/)
|
|
276
|
-
expect
|
|
270
|
+
expect do
|
|
277
271
|
response_definition.validate_headers!(response)
|
|
278
|
-
|
|
272
|
+
end.to_not raise_error
|
|
279
273
|
end
|
|
280
274
|
it 'a regexp header should raise error if it does not match the regexp' do
|
|
281
275
|
response_definition.header('X-Header', /anotherthing/)
|
|
282
|
-
expect
|
|
276
|
+
expect do
|
|
283
277
|
response_definition.validate_headers!(response)
|
|
284
|
-
|
|
278
|
+
end.to raise_error(Praxis::Exceptions::Validation)
|
|
285
279
|
end
|
|
286
280
|
end
|
|
287
281
|
end
|
|
288
282
|
|
|
289
|
-
describe
|
|
290
|
-
|
|
291
|
-
let(:response_headers) { {'Content-Type' => content_type } }
|
|
283
|
+
describe '#validate_content_type!' do
|
|
284
|
+
let(:response_headers) { { 'Content-Type' => content_type } }
|
|
292
285
|
let(:content_type) { 'application/none' }
|
|
293
286
|
|
|
294
287
|
let(:media_type) do
|
|
@@ -299,9 +292,9 @@ describe Praxis::ResponseDefinition do
|
|
|
299
292
|
|
|
300
293
|
context 'for definition without media_type defined' do
|
|
301
294
|
it 'should not check that it matches the content type' do
|
|
302
|
-
expect
|
|
295
|
+
expect do
|
|
303
296
|
response_definition.validate_content_type!(response)
|
|
304
|
-
|
|
297
|
+
end.to_not raise_error
|
|
305
298
|
end
|
|
306
299
|
end
|
|
307
300
|
|
|
@@ -312,60 +305,58 @@ describe Praxis::ResponseDefinition do
|
|
|
312
305
|
let(:response_content_type) { content_type }
|
|
313
306
|
|
|
314
307
|
it 'validates successfully' do
|
|
315
|
-
expect
|
|
308
|
+
expect do
|
|
316
309
|
response_definition.validate_content_type!(response)
|
|
317
|
-
|
|
310
|
+
end.to_not raise_error
|
|
318
311
|
end
|
|
319
312
|
end
|
|
320
313
|
|
|
321
314
|
context 'when content type includes a parameter' do
|
|
322
315
|
let(:response_content_type) { "#{content_type}; collection=true" }
|
|
323
316
|
it 'validates successfully' do
|
|
324
|
-
expect
|
|
317
|
+
expect do
|
|
325
318
|
response_definition.validate_content_type!(response)
|
|
326
|
-
|
|
319
|
+
end.to_not raise_error
|
|
327
320
|
end
|
|
328
321
|
end
|
|
329
322
|
|
|
330
323
|
context 'when content type does not match' do
|
|
331
|
-
let(:response_content_type) {
|
|
324
|
+
let(:response_content_type) { 'application/will_never_match' }
|
|
332
325
|
|
|
333
326
|
it 'should raise error telling you so' do
|
|
334
|
-
expect
|
|
327
|
+
expect do
|
|
335
328
|
response_definition.validate_content_type!(response)
|
|
336
|
-
|
|
329
|
+
end.to raise_error(Praxis::Exceptions::Validation)
|
|
337
330
|
end
|
|
338
331
|
end
|
|
339
332
|
|
|
340
333
|
context 'when content type is not set' do
|
|
341
334
|
let(:response_headers) { {} }
|
|
342
335
|
it 'should still raise an error' do
|
|
343
|
-
expect
|
|
336
|
+
expect do
|
|
344
337
|
response_definition.validate_content_type!(response)
|
|
345
|
-
|
|
338
|
+
end.to raise_error(Praxis::Exceptions::Validation)
|
|
346
339
|
end
|
|
347
340
|
end
|
|
348
341
|
end
|
|
349
342
|
end
|
|
350
343
|
end
|
|
351
|
-
|
|
352
344
|
end
|
|
353
345
|
|
|
354
|
-
|
|
355
346
|
context 'with invalid definitions' do
|
|
356
347
|
it 'raises an error if status code is not part of the definition' do
|
|
357
348
|
expect do
|
|
358
349
|
Praxis::ResponseDefinition.new('response name') do
|
|
359
|
-
description
|
|
350
|
+
description 'testing'
|
|
360
351
|
end
|
|
361
352
|
end.to raise_error(Praxis::Exceptions::InvalidConfiguration)
|
|
362
353
|
end
|
|
363
354
|
end
|
|
364
355
|
|
|
365
356
|
context '.describe' do
|
|
366
|
-
let(:description) {
|
|
357
|
+
let(:description) { 'A description' }
|
|
367
358
|
let(:location) { %r{/my/url/} }
|
|
368
|
-
let(:headers) { {'Header1' => 'Value1'} }
|
|
359
|
+
let(:headers) { { 'Header1' => 'Value1' } }
|
|
369
360
|
let(:parts) { nil }
|
|
370
361
|
let(:parts_block) { nil }
|
|
371
362
|
|
|
@@ -380,7 +371,7 @@ describe Praxis::ResponseDefinition do
|
|
|
380
371
|
response.description(description) if description
|
|
381
372
|
response.location(location) if location
|
|
382
373
|
if parts || parts_block
|
|
383
|
-
parts ? response.parts(nil, **parts, &parts_block) : response.parts(nil, &parts_block)
|
|
374
|
+
parts ? response.parts(nil, **parts, &parts_block) : response.parts(nil, &parts_block)
|
|
384
375
|
end
|
|
385
376
|
|
|
386
377
|
headers&.each do |(name, value)|
|
|
@@ -403,7 +394,6 @@ describe Praxis::ResponseDefinition do
|
|
|
403
394
|
it 'properly encodes the example bodies' do
|
|
404
395
|
expect(JSON.parse(examples['json'][:body])).to be_kind_of(Hash)
|
|
405
396
|
end
|
|
406
|
-
|
|
407
397
|
end
|
|
408
398
|
|
|
409
399
|
context 'which does not have a identifier' do
|
|
@@ -416,51 +406,48 @@ describe Praxis::ResponseDefinition do
|
|
|
416
406
|
expect(subject['json'][:content_type]).to eq('application/json')
|
|
417
407
|
end
|
|
418
408
|
end
|
|
419
|
-
|
|
420
|
-
|
|
421
409
|
end
|
|
422
410
|
|
|
423
|
-
|
|
424
411
|
context 'for a definition without parts' do
|
|
425
|
-
it{ should be_kind_of(::Hash) }
|
|
426
|
-
its([:description]){ should be(description) }
|
|
427
|
-
its([:location]){ should == {value: location.inspect
|
|
412
|
+
it { should be_kind_of(::Hash) }
|
|
413
|
+
its([:description]) { should be(description) }
|
|
414
|
+
its([:location]) { should == { value: location.inspect, type: :regexp } }
|
|
428
415
|
|
|
429
416
|
it 'should have a header defined with value and type keys' do
|
|
430
|
-
expect(
|
|
431
|
-
expect(
|
|
432
|
-
expect(
|
|
417
|
+
expect(output[:headers]).to have(2).keys
|
|
418
|
+
expect(output[:headers]['Header1']).to eq({ value: 'Value1', type: :string })
|
|
419
|
+
expect(output[:headers]['Location']).to eq({ value: '/\\/my\\/url\\//', type: :regexp })
|
|
433
420
|
end
|
|
434
421
|
end
|
|
435
422
|
|
|
436
423
|
context 'for a definition with (homogeneous) parts' do
|
|
437
|
-
subject(:described_parts){ output[:parts_like] }
|
|
424
|
+
subject(:described_parts) { output[:parts_like] }
|
|
438
425
|
context 'using :like' do
|
|
439
|
-
let(:parts) { {like: :ok, media_type: 'foobar'} }
|
|
426
|
+
let(:parts) { { like: :ok, media_type: 'foobar' } }
|
|
440
427
|
|
|
441
428
|
it 'should contain a parts_like key with a hash' do
|
|
442
|
-
expect(
|
|
429
|
+
expect(output).to have_key(:parts_like)
|
|
443
430
|
end
|
|
444
431
|
|
|
445
|
-
it{ should be_kind_of(::Hash) }
|
|
432
|
+
it { should be_kind_of(::Hash) }
|
|
446
433
|
it 'has the right type info' do
|
|
447
434
|
expect(subject[:payload][:type]).to match(id: 'Praxis-SimpleMediaType', name: 'Praxis::SimpleMediaType', family: 'string', identifier: 'foobar')
|
|
448
435
|
end
|
|
449
|
-
its([:status]){ should == 200 }
|
|
436
|
+
its([:status]) { should == 200 }
|
|
450
437
|
end
|
|
451
438
|
context 'using a full response definition block' do
|
|
452
439
|
let(:parts_block) do
|
|
453
|
-
|
|
440
|
+
proc do
|
|
454
441
|
status 234
|
|
455
442
|
media_type 'custom_media'
|
|
456
443
|
end
|
|
457
444
|
end
|
|
458
445
|
|
|
459
446
|
it 'should contain a parts_like key with a hash' do
|
|
460
|
-
expect(
|
|
447
|
+
expect(output).to have_key(:parts_like)
|
|
461
448
|
end
|
|
462
449
|
|
|
463
|
-
it{ should be_kind_of(::Hash) }
|
|
450
|
+
it { should be_kind_of(::Hash) }
|
|
464
451
|
it 'has the right type info' do
|
|
465
452
|
expect(subject[:payload][:type]).to match(id: 'Praxis-SimpleMediaType', name: 'Praxis::SimpleMediaType', family: 'string', identifier: 'custom_media')
|
|
466
453
|
end
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'spec_helper'
|
|
3
4
|
|
|
4
5
|
describe Praxis::Response do
|
|
5
6
|
let(:spec_status) { 200 }
|
|
6
7
|
let(:spec_location) { /resources/ }
|
|
7
8
|
let(:spec_headers) { { 'X-Header' => 'Foobar' } }
|
|
8
|
-
let(:spec_mime_type) {
|
|
9
|
+
let(:spec_mime_type) { 'application/vnd.resource' }
|
|
9
10
|
|
|
10
11
|
let(:application_vnd_resource_media_type) do
|
|
11
12
|
Class.new(Praxis::MediaType) do
|
|
@@ -18,33 +19,33 @@ describe Praxis::Response do
|
|
|
18
19
|
let(:response_spec) do
|
|
19
20
|
instance_double(
|
|
20
21
|
Praxis::ResponseDefinition,
|
|
21
|
-
:
|
|
22
|
-
:
|
|
23
|
-
:
|
|
24
|
-
:
|
|
25
|
-
:
|
|
22
|
+
status: spec_status,
|
|
23
|
+
location: spec_location,
|
|
24
|
+
headers: spec_headers,
|
|
25
|
+
media_type: spec_media_type,
|
|
26
|
+
name: :ok
|
|
26
27
|
)
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
let(:action) do
|
|
30
31
|
instance_double(
|
|
31
32
|
Praxis::ActionDefinition,
|
|
32
|
-
:
|
|
33
|
+
endpoint_definition: config_class
|
|
33
34
|
)
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
let(:response_status) { 200 }
|
|
37
|
-
let(:response_headers)
|
|
38
|
+
let(:response_headers) do
|
|
38
39
|
{ 'Content-Type' => 'application/vnd.resource+json;type=collection',
|
|
39
|
-
'X-Header'
|
|
40
|
-
'Location'
|
|
41
|
-
|
|
40
|
+
'X-Header' => 'Foobar',
|
|
41
|
+
'Location' => '/api/resources/123' }
|
|
42
|
+
end
|
|
42
43
|
|
|
43
44
|
let(:config_media_type) { nil }
|
|
44
45
|
let(:config_class) do
|
|
45
46
|
instance_double(
|
|
46
47
|
Praxis::ResponseDefinition,
|
|
47
|
-
:
|
|
48
|
+
media_type: config_media_type
|
|
48
49
|
)
|
|
49
50
|
end
|
|
50
51
|
|
|
@@ -52,7 +53,7 @@ describe Praxis::Response do
|
|
|
52
53
|
|
|
53
54
|
describe '#validate' do
|
|
54
55
|
before do
|
|
55
|
-
allow(action).to receive(:responses).and_return({response_spec.name => response_spec })
|
|
56
|
+
allow(action).to receive(:responses).and_return({ response_spec.name => response_spec })
|
|
56
57
|
end
|
|
57
58
|
context 'response spec is not defined' do
|
|
58
59
|
before :each do
|
|
@@ -60,26 +61,29 @@ describe Praxis::Response do
|
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
it 'should raise an error' do
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
resp = nil
|
|
65
|
+
expect do
|
|
66
|
+
resp = response.validate(action)
|
|
67
|
+
end.to_not raise_error
|
|
68
|
+
|
|
69
|
+
expect(resp.status).to eq(500)
|
|
70
|
+
expect(resp.body[:name]).to eq('ValidationError')
|
|
71
|
+
expect(resp.body[:summary]).to eq('Error validating response')
|
|
72
|
+
expect(resp.body[:errors]).to include(/response definition with that name can be found/)
|
|
66
73
|
end
|
|
67
74
|
end
|
|
68
75
|
|
|
69
76
|
context 'with an existing response spec in the action' do
|
|
70
|
-
|
|
71
77
|
it 'should use the spec to validate the response' do
|
|
72
78
|
expect(response_spec).to receive(:validate).and_return(nil)
|
|
73
79
|
|
|
74
80
|
response.validate(action)
|
|
75
81
|
end
|
|
76
82
|
end
|
|
77
|
-
|
|
78
|
-
|
|
79
83
|
end
|
|
80
84
|
|
|
81
85
|
describe '#encode!' do
|
|
82
|
-
let(:response_body_structured_data) { [{
|
|
86
|
+
let(:response_body_structured_data) { [{ 'moo' => 'bah' }] }
|
|
83
87
|
let(:response_body_entity) { '[{"moo": "bah"}]' }
|
|
84
88
|
|
|
85
89
|
context 'given a String body' do
|
|
@@ -96,31 +100,28 @@ describe Praxis::Response do
|
|
|
96
100
|
|
|
97
101
|
it 'encodes using a suitable handler' do
|
|
98
102
|
response.encode!
|
|
99
|
-
expect(JSON.
|
|
103
|
+
expect(JSON.parse(response.body)).to eq(response_body_structured_data)
|
|
100
104
|
end
|
|
101
105
|
end
|
|
102
106
|
|
|
103
107
|
context 'when no Content-Type is defined' do
|
|
104
108
|
before { response.body = response_body_structured_data }
|
|
105
109
|
|
|
106
|
-
let(:response_headers)
|
|
107
|
-
{ 'X-Header'
|
|
108
|
-
'Location'
|
|
109
|
-
|
|
110
|
+
let(:response_headers) do
|
|
111
|
+
{ 'X-Header' => 'Foobar',
|
|
112
|
+
'Location' => '/api/resources/123' }
|
|
113
|
+
end
|
|
110
114
|
it 'still defaults to the default handler' do
|
|
111
115
|
response.encode!
|
|
112
|
-
expect(JSON.
|
|
116
|
+
expect(JSON.parse(response.body)).to eq(response_body_structured_data)
|
|
113
117
|
end
|
|
114
118
|
end
|
|
115
|
-
|
|
116
|
-
|
|
117
119
|
end
|
|
118
120
|
|
|
119
121
|
context 'multipart responses' do
|
|
120
|
-
let(:part) { Praxis::MultipartPart.new('not so ok', {'Status' => 400,
|
|
122
|
+
let(:part) { Praxis::MultipartPart.new('not so ok', { 'Status' => 400, 'Location' => 'somewhere' }) }
|
|
121
123
|
|
|
122
124
|
context '#add_part' do
|
|
123
|
-
|
|
124
125
|
context 'without a name' do
|
|
125
126
|
before do
|
|
126
127
|
response.add_part(part)
|
|
@@ -140,18 +141,16 @@ describe Praxis::Response do
|
|
|
140
141
|
context 'with a name' do
|
|
141
142
|
let(:part_name) { 'a-part' }
|
|
142
143
|
before do
|
|
143
|
-
response.add_part(
|
|
144
|
+
response.add_part(part, part_name)
|
|
144
145
|
end
|
|
145
146
|
|
|
146
147
|
it 'adds the part' do
|
|
147
148
|
expect(response.parts[part_name]).to be(part)
|
|
148
149
|
end
|
|
149
150
|
end
|
|
150
|
-
|
|
151
151
|
end
|
|
152
152
|
|
|
153
153
|
context '#finish for a multipart response' do
|
|
154
|
-
|
|
155
154
|
before do
|
|
156
155
|
response.add_part(part)
|
|
157
156
|
response.body = 'a preamble'
|
|
@@ -172,7 +171,7 @@ describe Praxis::Response do
|
|
|
172
171
|
end
|
|
173
172
|
|
|
174
173
|
it 'sets the headers' do
|
|
175
|
-
expect(response.headers['Content-Type']).to match(/
|
|
174
|
+
expect(response.headers['Content-Type']).to match(%r{multipart/form-data})
|
|
176
175
|
expect(response.headers['Location']).to eq('/api/resources/123')
|
|
177
176
|
end
|
|
178
177
|
|
|
@@ -180,7 +179,7 @@ describe Praxis::Response do
|
|
|
180
179
|
parser = Praxis::MultipartParser.new(response.headers, response.body)
|
|
181
180
|
preamble, parts = parser.parse
|
|
182
181
|
|
|
183
|
-
expect(preamble).to eq(
|
|
182
|
+
expect(preamble).to eq('a preamble')
|
|
184
183
|
expect(parts).to have(1).item
|
|
185
184
|
|
|
186
185
|
part_response = parts.first
|
|
@@ -189,9 +188,5 @@ describe Praxis::Response do
|
|
|
189
188
|
expect(part_response.body).to eq('not so ok')
|
|
190
189
|
end
|
|
191
190
|
end
|
|
192
|
-
|
|
193
191
|
end
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
192
|
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'spec_helper'
|
|
3
4
|
|
|
4
5
|
describe Praxis::Responses::InternalServerError do
|
|
5
|
-
|
|
6
6
|
context 'a basic response' do
|
|
7
7
|
subject(:response) { Praxis::Responses::InternalServerError.new }
|
|
8
8
|
it 'always sets the json content type' do
|
|
@@ -15,12 +15,11 @@ describe Praxis::Responses::InternalServerError do
|
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
context '.format!' do
|
|
20
19
|
let(:error) { double('error', message: 'error message', backtrace: [1, 2], cause: cause) }
|
|
21
20
|
|
|
22
21
|
let(:show_exceptions) { true }
|
|
23
|
-
let(:config) { double(
|
|
22
|
+
let(:config) { double('config', show_exceptions: show_exceptions) }
|
|
24
23
|
|
|
25
24
|
before do
|
|
26
25
|
allow(Praxis::Application.instance.config).to receive(:praxis).and_return(config)
|
|
@@ -30,18 +29,17 @@ describe Praxis::Responses::InternalServerError do
|
|
|
30
29
|
end
|
|
31
30
|
|
|
32
31
|
context 'with show_exceptions true' do
|
|
33
|
-
|
|
34
32
|
context 'without a cause' do
|
|
35
33
|
let(:cause) { nil }
|
|
36
34
|
it 'it fills message and backtrace' do
|
|
37
|
-
expect(response.body).to eq({name: error.class.name, message: error.message, backtrace: error.backtrace})
|
|
35
|
+
expect(response.body).to eq({ name: error.class.name, message: error.message, backtrace: error.backtrace })
|
|
38
36
|
end
|
|
39
37
|
end
|
|
40
38
|
|
|
41
39
|
context 'with a cause' do
|
|
42
40
|
let(:cause) { Exception.new('cause message') }
|
|
43
41
|
it 'it fills message, backtrace and cause' do
|
|
44
|
-
expect(response.body.keys).to eq([
|
|
42
|
+
expect(response.body.keys).to eq(%i[name message backtrace cause])
|
|
45
43
|
expect(response.body[:name]).to eq(error.class.name)
|
|
46
44
|
expect(response.body[:message]).to eq(error.message)
|
|
47
45
|
expect(response.body[:backtrace]).to eq(error.backtrace)
|
|
@@ -55,11 +53,10 @@ describe Praxis::Responses::InternalServerError do
|
|
|
55
53
|
expect(response.body).to be_nil
|
|
56
54
|
response.format!
|
|
57
55
|
end
|
|
58
|
-
|
|
59
56
|
end
|
|
60
57
|
|
|
61
58
|
context 'its response template' do
|
|
62
|
-
let(:template){ Praxis::ApiDefinition.instance.responses[:internal_server_error] }
|
|
59
|
+
let(:template) { Praxis::ApiDefinition.instance.responses[:internal_server_error] }
|
|
63
60
|
it 'is registered with the ApiDefinition' do
|
|
64
61
|
expect(template).to be_kind_of(Praxis::ResponseTemplate)
|
|
65
62
|
end
|