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.
- 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/praxis/trait_spec.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
|
4
4
|
|
5
|
+
describe Praxis::Trait do
|
5
6
|
subject(:trait) do
|
6
7
|
Praxis::Trait.new do
|
7
8
|
description 'my awesome trait'
|
@@ -19,14 +20,13 @@ describe Praxis::Trait do
|
|
19
20
|
end
|
20
21
|
params do
|
21
22
|
attribute :order, String,
|
22
|
-
|
23
|
+
description: 'Field to sort by.'
|
23
24
|
end
|
24
25
|
|
25
26
|
headers do
|
26
|
-
header
|
27
|
-
key
|
27
|
+
header 'Authorization'
|
28
|
+
key 'Header2', String, required: true, null: false
|
28
29
|
end
|
29
|
-
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -35,24 +35,24 @@ describe Praxis::Trait do
|
|
35
35
|
|
36
36
|
its([:description]) { should eq('my awesome trait') }
|
37
37
|
|
38
|
-
its([
|
39
|
-
its([
|
38
|
+
its(%i[responses something]) { should eq({}) }
|
39
|
+
its(%i[responses nothing]) { should eq({}) }
|
40
40
|
|
41
|
-
its([
|
42
|
-
its([
|
43
|
-
its([
|
41
|
+
its(%i[params app_name type name]) { should eq 'String' }
|
42
|
+
its(%i[params order type name]) { should eq 'String' }
|
43
|
+
its(%i[routing prefix]) { should eq '/:app_name' }
|
44
44
|
|
45
|
-
its([:headers,
|
45
|
+
its([:headers, 'Header2']) { should include({ required: true, null: false }) }
|
46
46
|
context 'using the special DSL syntax for headers' do
|
47
|
-
subject(:dsl_header) { describe[:headers][
|
48
|
-
its([:required]){ should be(true) }
|
49
|
-
its([:
|
47
|
+
subject(:dsl_header) { describe[:headers]['Authorization'] }
|
48
|
+
its([:required]) { should be(true) }
|
49
|
+
its([:null]) { should be_nil }
|
50
|
+
its([:type]) { should eq({ id: 'Attributor-String', name: 'String', family: 'string' }) }
|
50
51
|
end
|
51
|
-
|
52
52
|
end
|
53
53
|
|
54
54
|
context 'apply!' do
|
55
|
-
let(:target) { double(
|
55
|
+
let(:target) { double('Target') }
|
56
56
|
it 'does' do
|
57
57
|
expect(target).to receive(:routing).once
|
58
58
|
expect(target).to receive(:response).twice
|
@@ -1,20 +1,22 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'spec_helper'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
describe Praxis::Types::FuzzyHash do
|
6
|
+
let(:initial_value) do
|
7
|
+
{
|
8
|
+
'key' => 'value',
|
9
|
+
/bob/ => 'rob',
|
10
|
+
/\d+/ => 'one'
|
11
|
+
}
|
12
|
+
end
|
10
13
|
|
11
14
|
subject(:hash) { Praxis::Types::FuzzyHash.new(initial_value) }
|
12
15
|
|
13
16
|
its(['key']) { should eq 'value' }
|
14
17
|
its([/bob/]) { should eq 'rob' }
|
15
18
|
its(['bobby']) { should eq 'rob' }
|
16
|
-
|
19
|
+
|
17
20
|
its([1]) { should eq 'one' }
|
18
21
|
its(['1']) { should eq 'one' }
|
19
22
|
end
|
20
|
-
|
@@ -1,15 +1,16 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'spec_helper'
|
4
4
|
|
5
|
+
describe Praxis::Types::MultipartArray do
|
5
6
|
let(:type) do
|
6
7
|
Class.new(Praxis::Types::MultipartArray) do
|
7
|
-
|
8
|
+
options[:case_insensitive_load] = true
|
8
9
|
|
9
10
|
part 'title', String, required: true
|
10
11
|
part(/nam/, String, regexp: /Bob/i)
|
11
12
|
|
12
|
-
part
|
13
|
+
part(/stuff/) do
|
13
14
|
payload Hash
|
14
15
|
end
|
15
16
|
|
@@ -22,33 +23,31 @@ describe Praxis::Types::MultipartArray do
|
|
22
23
|
file 'thumbnail', Attributor::Tempfile
|
23
24
|
|
24
25
|
part 'image', Attributor::Tempfile, filename: true
|
25
|
-
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
29
|
let(:form) do
|
31
30
|
form_data = MIME::Multipart::FormData.new
|
32
31
|
entity = MIME::Text.new('Bob')
|
33
|
-
form_data.add entity,'name'
|
32
|
+
form_data.add entity, 'name'
|
34
33
|
|
35
34
|
entity = MIME::Text.new('Captain')
|
36
|
-
form_data.add entity,'title'
|
35
|
+
form_data.add entity, 'title'
|
37
36
|
|
38
37
|
entity = MIME::Application.new('file1')
|
39
|
-
form_data.add entity,'files', 'file1'
|
38
|
+
form_data.add entity, 'files', 'file1'
|
40
39
|
|
41
40
|
entity = MIME::Application.new('file2')
|
42
|
-
form_data.add entity,'files', 'file2'
|
41
|
+
form_data.add entity, 'files', 'file2'
|
43
42
|
|
44
43
|
entity = MIME::Application.new('{"first_name": "Frank"}', 'json')
|
45
|
-
form_data.add entity,'stuff2'
|
44
|
+
form_data.add entity, 'stuff2'
|
46
45
|
|
47
46
|
entity = MIME::Application.new('SOMEBINARYDATA', 'jpg')
|
48
|
-
form_data.add entity,'thumbnail', 'thumb.jpg'
|
47
|
+
form_data.add entity, 'thumbnail', 'thumb.jpg'
|
49
48
|
|
50
49
|
entity = MIME::Application.new('', 'jpg')
|
51
|
-
form_data.add entity,'image', 'image.jpg'
|
50
|
+
form_data.add entity, 'image', 'image.jpg'
|
52
51
|
|
53
52
|
form_data
|
54
53
|
end
|
@@ -59,7 +58,6 @@ describe Praxis::Types::MultipartArray do
|
|
59
58
|
subject(:payload) { type.load(body, content_type: content_type) }
|
60
59
|
|
61
60
|
it 'validates' do
|
62
|
-
part = payload.part('files').first
|
63
61
|
expect(payload.validate).to be_empty
|
64
62
|
end
|
65
63
|
|
@@ -94,9 +92,9 @@ describe Praxis::Types::MultipartArray do
|
|
94
92
|
end
|
95
93
|
end
|
96
94
|
|
97
|
-
let(:json_payload) { {sub_hash: {key:'value'}}.to_json }
|
95
|
+
let(:json_payload) { { sub_hash: { key: 'value' } }.to_json }
|
98
96
|
let(:body) { StringIO.new("--boundary\r\nContent-Disposition: form-data; name=blah\r\n\r\n#{json_payload}\r\n--boundary--") }
|
99
|
-
let(:content_type) {
|
97
|
+
let(:content_type) { 'multipart/form-data; boundary=boundary' }
|
100
98
|
|
101
99
|
it do
|
102
100
|
expect(payload.part('blah').payload['sub_hash']).to eq('key' => 'value')
|
@@ -117,9 +115,9 @@ describe Praxis::Types::MultipartArray do
|
|
117
115
|
end
|
118
116
|
end
|
119
117
|
|
120
|
-
let(:json_payload) { {attr: 'value'}.to_json }
|
118
|
+
let(:json_payload) { { attr: 'value' }.to_json }
|
121
119
|
let(:body) { StringIO.new("--boundary\r\nContent-Disposition: form-data; name=blah\r\n\r\n#{json_payload}\r\n--boundary--") }
|
122
|
-
let(:content_type) {
|
120
|
+
let(:content_type) { 'multipart/form-data; boundary=boundary' }
|
123
121
|
|
124
122
|
it do
|
125
123
|
expect(payload.part('blah').payload.class.ancestors).to include(Attributor::Struct)
|
@@ -142,7 +140,7 @@ describe Praxis::Types::MultipartArray do
|
|
142
140
|
|
143
141
|
context 'attributes' do
|
144
142
|
subject(:attributes) { description[:attributes] }
|
145
|
-
its(:keys) { should match_array [
|
143
|
+
its(:keys) { should match_array %w[title files thumbnail image] }
|
146
144
|
|
147
145
|
context 'the "title" part' do
|
148
146
|
subject(:title_description) { attributes['title'] }
|
@@ -172,14 +170,12 @@ describe Praxis::Types::MultipartArray do
|
|
172
170
|
expect(filename[:options]).to eq(regexp: /file/)
|
173
171
|
expect(filename[:type]).to eq Attributor::String.describe
|
174
172
|
end
|
175
|
-
|
176
173
|
end
|
177
|
-
|
178
174
|
end
|
179
175
|
|
180
176
|
context 'pattern attributes' do
|
181
177
|
subject(:pattern_attributes) { description[:pattern_attributes] }
|
182
|
-
its(:keys) { should match_array [
|
178
|
+
its(:keys) { should match_array %w[nam stuff] }
|
183
179
|
end
|
184
180
|
|
185
181
|
context 'with no parts defined' do
|
@@ -226,17 +222,16 @@ describe Praxis::Types::MultipartArray do
|
|
226
222
|
subject(:dumped) { payload.dump }
|
227
223
|
|
228
224
|
it 'dumps' do
|
229
|
-
|
225
|
+
type.load(dumped, content_type: payload.content_type)
|
230
226
|
end
|
231
227
|
context 'an example' do
|
232
228
|
let(:payload) { type.example }
|
233
229
|
|
234
230
|
it 'dumps' do
|
235
|
-
|
231
|
+
type.load(dumped, content_type: payload.content_type)
|
236
232
|
end
|
237
233
|
end
|
238
234
|
|
239
|
-
|
240
235
|
context 'with default_format' do
|
241
236
|
let(:type) do
|
242
237
|
Class.new(Praxis::Types::MultipartArray) do
|
@@ -267,7 +262,7 @@ describe Praxis::Types::MultipartArray do
|
|
267
262
|
|
268
263
|
let(:output) { example.dump(default_format: default_format) }
|
269
264
|
|
270
|
-
let(:parts) { Praxis::MultipartParser.parse({'Content-Type'=>example.content_type}, output).last }
|
265
|
+
let(:parts) { Praxis::MultipartParser.parse({ 'Content-Type' => example.content_type }, output).last }
|
271
266
|
|
272
267
|
it 'dumps the parts with the proper handler' do
|
273
268
|
json_handler = Praxis::Application.instance.handlers['json']
|
@@ -290,9 +285,7 @@ describe Praxis::Types::MultipartArray do
|
|
290
285
|
expect(instance.payload).to eq json_handler.generate(example.part('instances')[i].payload.dump)
|
291
286
|
end
|
292
287
|
end
|
293
|
-
|
294
288
|
end
|
295
|
-
|
296
289
|
end
|
297
290
|
|
298
291
|
context 'with errors' do
|
@@ -300,11 +293,10 @@ describe Praxis::Types::MultipartArray do
|
|
300
293
|
form_data = MIME::Multipart::FormData.new
|
301
294
|
|
302
295
|
entity = MIME::Text.new('James')
|
303
|
-
form_data.add entity,'name'
|
304
|
-
|
296
|
+
form_data.add entity, 'name'
|
305
297
|
|
306
298
|
entity = MIME::Text.new('file1')
|
307
|
-
form_data.add entity,'files', 'file1'
|
299
|
+
form_data.add entity, 'files', 'file1'
|
308
300
|
|
309
301
|
form_data
|
310
302
|
end
|
@@ -314,10 +306,10 @@ describe Praxis::Types::MultipartArray do
|
|
314
306
|
it 'validates part headers' do
|
315
307
|
expect(errors).to have(3).items
|
316
308
|
expect(errors).to match_array([
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
309
|
+
/\.name\.payload value .* does not match regexp/,
|
310
|
+
/\.files\.headers.* is not within the allowed values/,
|
311
|
+
/\.title is required/
|
312
|
+
])
|
321
313
|
end
|
322
314
|
end
|
323
315
|
|
@@ -327,14 +319,14 @@ describe Praxis::Types::MultipartArray do
|
|
327
319
|
it 'generates parts' do
|
328
320
|
title = payload.part('title')
|
329
321
|
expect(title).to_not be_nil
|
330
|
-
expect(title.payload).to match
|
322
|
+
expect(title.payload).to match(/\w+/)
|
331
323
|
|
332
324
|
files = payload.part('files')
|
333
325
|
expect(files).to have(2).items
|
334
326
|
|
335
327
|
files.each do |file|
|
336
328
|
expect(file.payload).to be_kind_of(Tempfile)
|
337
|
-
expect(file.filename).to match
|
329
|
+
expect(file.filename).to match(/\w+/)
|
338
330
|
|
339
331
|
expect(file.headers['Content-Type']).to eq 'application/octet-stream'
|
340
332
|
end
|
@@ -343,15 +335,14 @@ describe Praxis::Types::MultipartArray do
|
|
343
335
|
it 'is valid' do
|
344
336
|
expect(payload.validate).to have(0).items
|
345
337
|
end
|
346
|
-
|
347
338
|
end
|
348
339
|
|
349
340
|
context 'with a hackish default part definition' do
|
350
341
|
let(:type) do
|
351
342
|
Class.new(Praxis::Types::MultipartArray) do
|
352
|
-
|
343
|
+
options[:case_insensitive_load] = true
|
353
344
|
|
354
|
-
part
|
345
|
+
part(/\d+/, Instance)
|
355
346
|
end
|
356
347
|
end
|
357
348
|
|
@@ -362,7 +353,7 @@ describe Praxis::Types::MultipartArray do
|
|
362
353
|
instance = Instance.example("instance-#{i}")
|
363
354
|
body = JSON.pretty_generate(instance.render)
|
364
355
|
entity = MIME::Text.new(body)
|
365
|
-
form_data.add entity,i.to_s
|
356
|
+
form_data.add entity, i.to_s
|
366
357
|
end
|
367
358
|
|
368
359
|
form_data
|
@@ -373,10 +364,8 @@ describe Praxis::Types::MultipartArray do
|
|
373
364
|
expect(part.payload).to be_kind_of(Instance)
|
374
365
|
end
|
375
366
|
end
|
376
|
-
|
377
367
|
end
|
378
368
|
|
379
|
-
|
380
369
|
context 'anonymous generation' do
|
381
370
|
let(:definition_block) do
|
382
371
|
proc do
|
@@ -396,7 +385,7 @@ describe Praxis::Types::MultipartArray do
|
|
396
385
|
instance = Instance.example("instance-#{i}")
|
397
386
|
body = JSON.pretty_generate(instance.render)
|
398
387
|
entity = MIME::Text.new(body)
|
399
|
-
form_data.add entity,i.to_s
|
388
|
+
form_data.add entity, i.to_s
|
400
389
|
end
|
401
390
|
|
402
391
|
form_data
|
@@ -411,9 +400,5 @@ describe Praxis::Types::MultipartArray do
|
|
411
400
|
expect(part.payload).to be_kind_of(Instance)
|
412
401
|
end
|
413
402
|
end
|
414
|
-
|
415
|
-
|
416
403
|
end
|
417
|
-
|
418
|
-
|
419
404
|
end
|
@@ -1,15 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Concerns
|
2
4
|
module Authenticated
|
3
5
|
extend ActiveSupport::Concern
|
4
6
|
include Praxis::Callbacks
|
5
|
-
|
7
|
+
|
6
8
|
included do
|
7
9
|
before :action do |controller|
|
8
10
|
auth_data = controller.request.headers['Authorization']
|
9
|
-
if auth_data && auth_data !~ /secret/
|
10
|
-
Praxis::Responses::Unauthorized.new(body: 'Authentication info is invalid')
|
11
|
-
end
|
11
|
+
Praxis::Responses::Unauthorized.new(body: 'Authentication info is invalid') if auth_data && auth_data !~ /secret/
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
15
|
-
end
|
15
|
+
end
|
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Concerns
|
2
4
|
module LogWrapper
|
3
5
|
extend ActiveSupport::Concern
|
4
6
|
include Praxis::Callbacks
|
5
|
-
|
7
|
+
|
6
8
|
included do
|
7
|
-
before :around do |
|
9
|
+
before :around do |_controller, callee|
|
8
10
|
# Log something at the beginning
|
9
11
|
callee.call
|
10
12
|
# Log something at the end
|
11
13
|
end
|
12
14
|
end
|
13
15
|
end
|
14
|
-
end
|
16
|
+
end
|
@@ -1,14 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class BaseClass
|
2
4
|
# Do NOT use a controller inherited base class to add concerns!
|
3
5
|
# If you do that, you'd be sharing state (i.e. overriding/adding) from concerns across controllers
|
4
6
|
# which will probably lead to sharing issues you didn't expect.
|
5
7
|
# For example: any controller adding after/before/around filters will be visible
|
6
8
|
# to any other controllers sharing the concern.
|
7
|
-
# Include a concern to all of them instead
|
8
|
-
|
9
|
+
# Include a concern to all of them instead
|
10
|
+
|
9
11
|
# Inheritance of classes should be independent from the concerns.
|
10
12
|
# I.e., you can use class inheritance in cases where it makes sense from an OO point of view
|
11
13
|
# but for the most part, you can probably share code through modules/concerns too.
|
12
|
-
def this_is_shared
|
13
|
-
|
14
|
-
end
|
14
|
+
def this_is_shared; end
|
15
|
+
end
|
@@ -1,59 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Instances < BaseClass
|
2
4
|
include Praxis::Controller
|
3
5
|
|
4
6
|
implements ApiResources::Instances
|
5
7
|
include Concerns::BasicApi
|
6
8
|
|
7
|
-
before :validate, actions: [:index]
|
8
|
-
#p [:before, :validate, :params_and_headers, controller.request.action.name]
|
9
|
+
before :validate, actions: [:index] do |controller|
|
10
|
+
# p [:before, :validate, :params_and_headers, controller.request.action.name]
|
9
11
|
end
|
10
12
|
|
11
13
|
before actions: [:show] do |controller|
|
12
|
-
#puts "before action"
|
13
|
-
if controller.request.params.fail_filter
|
14
|
-
Praxis::Responses::Unauthorized.new
|
15
|
-
end
|
14
|
+
# puts "before action"
|
15
|
+
Praxis::Responses::Unauthorized.new if controller.request.params.fail_filter
|
16
16
|
end
|
17
17
|
|
18
|
-
around :validate, actions: [:show] do |
|
19
|
-
#puts "Before validate decorator (for show)"
|
18
|
+
around :validate, actions: [:show] do |_controller, blk|
|
19
|
+
# puts "Before validate decorator (for show)"
|
20
20
|
blk.call
|
21
|
-
#puts "After validate decorator"
|
21
|
+
# puts "After validate decorator"
|
22
22
|
end
|
23
23
|
|
24
|
-
around :action do |
|
25
|
-
#puts "Decorator one (all actions) start"
|
24
|
+
around :action do |_controller, blk|
|
25
|
+
# puts "Decorator one (all actions) start"
|
26
26
|
blk.call
|
27
|
-
#puts "Decorator one end"
|
27
|
+
# puts "Decorator one end"
|
28
28
|
end
|
29
29
|
|
30
|
-
around :action, actions: [:show] do |
|
31
|
-
#puts "Decorator two (show action) start"
|
30
|
+
around :action, actions: [:show] do |_controller, blk|
|
31
|
+
# puts "Decorator two (show action) start"
|
32
32
|
blk.call
|
33
|
-
#puts "Decorator two end"
|
33
|
+
# puts "Decorator two end"
|
34
34
|
end
|
35
35
|
|
36
|
-
around :action, actions: [:index] do |
|
37
|
-
#puts "Decorator three (index action) start"
|
36
|
+
around :action, actions: [:index] do |_controller, blk|
|
37
|
+
# puts "Decorator three (index action) start"
|
38
38
|
blk.call
|
39
|
-
#puts "Decorator three end"
|
39
|
+
# puts "Decorator three end"
|
40
40
|
end
|
41
41
|
|
42
|
-
def index(
|
42
|
+
def index(response_content_type: 'application/vnd.acme.instance;type=collection', **_params)
|
43
43
|
instances = Instance::Collection.example
|
44
|
-
response.body = JSON.pretty_generate(instances.collect
|
45
|
-
response.headers['Content-Type'] = response_content_type #'application/vnd.acme.instance;type=collection'
|
44
|
+
response.body = JSON.pretty_generate(instances.collect(&:render))
|
45
|
+
response.headers['Content-Type'] = response_content_type # 'application/vnd.acme.instance;type=collection'
|
46
46
|
response
|
47
47
|
end
|
48
48
|
|
49
49
|
def show(cloud_id:, id:, junk:, create_identity_map:, **other_params)
|
50
|
-
if create_identity_map
|
51
|
-
request.identity_map
|
52
|
-
end
|
50
|
+
request.identity_map if create_identity_map
|
53
51
|
|
54
52
|
payload = request.payload
|
55
53
|
|
56
|
-
response.body = {cloud_id: cloud_id, id: id, junk: junk, other_params: other_params, payload: payload && payload.dump}
|
54
|
+
response.body = { cloud_id: cloud_id, id: id, junk: junk, other_params: other_params, payload: payload && payload.dump }
|
57
55
|
response.headers['Content-Type'] = 'application/json'
|
58
56
|
response
|
59
57
|
end
|
@@ -68,12 +66,12 @@ class Instances < BaseClass
|
|
68
66
|
|
69
67
|
part_body = {
|
70
68
|
key: instance_id,
|
71
|
-
value: instance.render(fields: {id: true, name: true})
|
69
|
+
value: instance.render(fields: { id: true, name: true })
|
72
70
|
}
|
73
71
|
|
74
72
|
headers = {
|
75
73
|
'Status' => '201',
|
76
|
-
'Content-Type' =>
|
74
|
+
'Content-Type' => "#{Instance.identifier}+json".to_s,
|
77
75
|
'Location' => definition.to_href(cloud_id: cloud_id, id: instance.id)
|
78
76
|
}
|
79
77
|
|
@@ -84,14 +82,13 @@ class Instances < BaseClass
|
|
84
82
|
response
|
85
83
|
end
|
86
84
|
|
87
|
-
|
88
|
-
def attach_file(id:, cloud_id:)
|
85
|
+
def attach_file(*)
|
89
86
|
response.headers['Content-Type'] = 'application/json'
|
90
87
|
|
91
88
|
destination_path = request.payload.part('destination_path').payload
|
92
89
|
file = request.payload.part('file')
|
93
90
|
file.payload.rewind # Ensure contents is at the beggining.
|
94
|
-
extra_part_names = request.payload.map(&:name) - [
|
91
|
+
extra_part_names = request.payload.map(&:name) - %w[destination_path file]
|
95
92
|
extra_parts = extra_part_names.each_with_object({}) do |pname, hash|
|
96
93
|
hash[pname] = request.payload.part(pname).body
|
97
94
|
end
|
@@ -109,23 +106,23 @@ class Instances < BaseClass
|
|
109
106
|
response
|
110
107
|
end
|
111
108
|
|
112
|
-
def terminate(
|
109
|
+
def terminate(*)
|
113
110
|
response.headers['Content-Type'] = 'application/json'
|
114
111
|
response
|
115
112
|
end
|
116
113
|
|
117
|
-
def stop(
|
114
|
+
def stop(*)
|
118
115
|
response.headers['Content-Type'] = 'application/json'
|
119
116
|
response
|
120
117
|
end
|
121
118
|
|
122
|
-
def update(
|
119
|
+
def update(*)
|
123
120
|
response.body = JSON.pretty_generate(request.payload.dump)
|
124
121
|
response.headers['Content-Type'] = 'application/vnd.acme.instance'
|
125
122
|
response
|
126
123
|
end
|
127
124
|
|
128
|
-
def exceptional(
|
125
|
+
def exceptional(*)
|
129
126
|
response.headers['Content-Type'] = 'application/json'
|
130
127
|
response
|
131
128
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Volumes < BaseClass
|
2
4
|
include Praxis::Controller
|
3
5
|
|
@@ -5,22 +7,20 @@ class Volumes < BaseClass
|
|
5
7
|
include Concerns::BasicApi
|
6
8
|
|
7
9
|
before actions: [:show] do |controller|
|
8
|
-
#puts "before action for volumes"
|
10
|
+
# puts "before action for volumes"
|
9
11
|
end
|
10
12
|
|
11
13
|
def index
|
12
14
|
volumes = Volume::Collection.example
|
13
|
-
|
14
|
-
response.body = volumes.collect
|
15
|
+
|
16
|
+
response.body = volumes.collect(&:render)
|
15
17
|
response.headers['Content-Type'] = 'application/vnd.acme.volumes'
|
16
18
|
response
|
17
19
|
end
|
18
20
|
|
19
|
-
def show(
|
21
|
+
def show(*)
|
20
22
|
response.body = JSON.pretty_generate(Volume.example.render)
|
21
23
|
response.headers['Content-Type'] = 'application/vnd.acme.volume'
|
22
24
|
response
|
23
25
|
end
|
24
|
-
|
25
|
-
|
26
26
|
end
|