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,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
class SetHeader
|
|
2
4
|
def initialize(app, header, value)
|
|
3
5
|
@app = app
|
|
@@ -8,12 +10,25 @@ class SetHeader
|
|
|
8
10
|
def call(env)
|
|
9
11
|
status, headers, body = @app.call(env)
|
|
10
12
|
headers[@header] = @value
|
|
11
|
-
[status, headers,body]
|
|
13
|
+
[status, headers, body]
|
|
12
14
|
end
|
|
13
15
|
end
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
class LowBudgetMutex
|
|
18
|
+
include Singleton
|
|
19
|
+
|
|
20
|
+
attr_reader :after_app_controllers
|
|
16
21
|
|
|
22
|
+
def initialize
|
|
23
|
+
@after_app_controllers = nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def after_app_controllers_called
|
|
27
|
+
@after_app_controllers = :worked
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
Praxis::Application.configure do |application|
|
|
17
32
|
application.middleware SetHeader, 'Spec-Middleware', 'used'
|
|
18
33
|
|
|
19
34
|
application.bootloader.use SimpleAuthenticationPlugin, config_file: 'config/authentication.yml'
|
|
@@ -26,13 +41,12 @@ Praxis::Application.configure do |application|
|
|
|
26
41
|
|
|
27
42
|
# Silly callback code pieces to test that the deferred callbacks work even for sub-stages
|
|
28
43
|
application.bootloader.after :app, :controllers do
|
|
29
|
-
|
|
44
|
+
LowBudgetMutex.instance.after_app_controllers_called
|
|
30
45
|
end
|
|
31
46
|
application.bootloader.after :app do
|
|
32
|
-
raise
|
|
47
|
+
raise 'After sub-stage hooks not working!' unless LowBudgetMutex.instance.after_app_controllers == :worked
|
|
33
48
|
end
|
|
34
49
|
|
|
35
|
-
|
|
36
50
|
application.layout do
|
|
37
51
|
layout do
|
|
38
52
|
map :initializers, 'config/initializers/**/*'
|
|
@@ -49,5 +63,4 @@ Praxis::Application.configure do |application|
|
|
|
49
63
|
end
|
|
50
64
|
end
|
|
51
65
|
end
|
|
52
|
-
|
|
53
66
|
end
|
data/spec/spec_app/config.ru
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'pp'
|
|
2
4
|
require 'json'
|
|
3
5
|
|
|
@@ -5,13 +7,12 @@ require 'bundler/setup'
|
|
|
5
7
|
|
|
6
8
|
require 'pry'
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
$LOAD_PATH.unshift File.expand_path('lib', __dir__)
|
|
9
11
|
|
|
10
12
|
require 'praxis'
|
|
11
13
|
|
|
12
|
-
|
|
13
14
|
application = Praxis::Application.instance
|
|
14
15
|
|
|
15
16
|
application.setup
|
|
16
17
|
|
|
17
|
-
run application
|
|
18
|
+
run application
|
data/spec/spec_app/design/api.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
Praxis::ApiDefinition.define do
|
|
3
4
|
response_template :other_response do |media_type:|
|
|
4
5
|
status 200
|
|
5
6
|
media_type media_type
|
|
@@ -10,33 +11,30 @@ Praxis::ApiDefinition.define do
|
|
|
10
11
|
media_type 'multipart/form-data'
|
|
11
12
|
end
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
14
|
trait :authenticated do
|
|
16
15
|
headers do
|
|
17
|
-
key
|
|
16
|
+
key 'Authorization', String, required: false
|
|
18
17
|
end
|
|
19
18
|
end
|
|
20
19
|
|
|
21
20
|
info do # applies to all API infos
|
|
22
|
-
name
|
|
23
|
-
title
|
|
24
|
-
description
|
|
21
|
+
name 'Spec App'
|
|
22
|
+
title 'A simple App to do some simple integration testing'
|
|
23
|
+
description 'This example API should really be replaced by a set of more full-fledged example apps in the future'
|
|
25
24
|
|
|
26
|
-
base_path
|
|
25
|
+
base_path '/api'
|
|
27
26
|
produces 'json'
|
|
28
|
-
#version_with :path
|
|
29
|
-
#base_path "/v:api_version"
|
|
27
|
+
# version_with :path
|
|
28
|
+
# base_path "/v:api_version"
|
|
30
29
|
|
|
31
30
|
# Custom attributes (for OpenApi, for example)
|
|
32
|
-
termsOfService
|
|
31
|
+
termsOfService 'http://example.com/tos'
|
|
33
32
|
contact name: 'Joe', email: 'joe@email.com'
|
|
34
|
-
license name:
|
|
35
|
-
url:
|
|
33
|
+
license name: 'Apache 2.0',
|
|
34
|
+
url: 'https://www.apache.org/licenses/LICENSE-2.0.html'
|
|
36
35
|
end
|
|
37
36
|
|
|
38
37
|
info '1.0' do # Applies to 1.0 version (and inherits everything else form the global one)
|
|
39
|
-
description
|
|
38
|
+
description 'A simple 1.0 App'
|
|
40
39
|
end
|
|
41
|
-
|
|
42
40
|
end
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
class Instance < Praxis::MediaType
|
|
3
4
|
identifier 'application/vnd.acme.instance'
|
|
4
5
|
|
|
5
6
|
attributes do
|
|
6
7
|
attribute :id, Integer
|
|
7
|
-
attribute :name, String,
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
attribute :name, String,
|
|
9
|
+
example: /[:first_name:]/,
|
|
10
|
+
regexp: /^\w+$/
|
|
10
11
|
|
|
11
12
|
attribute :href, String
|
|
12
13
|
|
|
13
|
-
attribute :root_volume, Volume
|
|
14
|
+
attribute :root_volume, Volume, null: true
|
|
14
15
|
|
|
15
16
|
attribute :volumes, Volume::Collection
|
|
16
|
-
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
default_fieldset do
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ApiResources
|
|
2
4
|
class Instances
|
|
3
5
|
include Praxis::EndpointDefinition
|
|
@@ -28,8 +30,8 @@ module ApiResources
|
|
|
28
30
|
|
|
29
31
|
headers do
|
|
30
32
|
# BOTH ARE EQUIVALENT
|
|
31
|
-
#key "FOO", String, required: true
|
|
32
|
-
header
|
|
33
|
+
# key "FOO", String, required: true
|
|
34
|
+
header 'FOO', /bar/
|
|
33
35
|
key 'Account-Id', Integer, required: false, min: 0
|
|
34
36
|
end
|
|
35
37
|
|
|
@@ -37,7 +39,6 @@ module ApiResources
|
|
|
37
39
|
attribute :response_content_type, String, default: 'application/vnd.acme.instance;type=collection'
|
|
38
40
|
end
|
|
39
41
|
|
|
40
|
-
|
|
41
42
|
response :ok, Praxis::Collection.of(Instance)
|
|
42
43
|
end
|
|
43
44
|
|
|
@@ -57,13 +58,12 @@ module ApiResources
|
|
|
57
58
|
attribute :create_identity_map, Attributor::Boolean, default: false
|
|
58
59
|
end
|
|
59
60
|
|
|
60
|
-
payload required: false do
|
|
61
|
+
payload required: false, null: true do
|
|
61
62
|
attribute :something, String
|
|
62
|
-
attribute :optional, String, default:
|
|
63
|
+
attribute :optional, String, default: 'not given'
|
|
63
64
|
end
|
|
64
65
|
end
|
|
65
66
|
|
|
66
|
-
|
|
67
67
|
action :bulk_create do
|
|
68
68
|
routing do
|
|
69
69
|
post ''
|
|
@@ -77,11 +77,11 @@ module ApiResources
|
|
|
77
77
|
response :multipart_ok, Praxis::Types::MultipartArray do
|
|
78
78
|
name_type Integer
|
|
79
79
|
|
|
80
|
-
part
|
|
80
|
+
part(/\d+/) do
|
|
81
81
|
header 'Status', '201'
|
|
82
82
|
header 'Content-Type', /^#{Instance.identifier}/
|
|
83
83
|
header 'Location', Attributor::URI,
|
|
84
|
-
|
|
84
|
+
example: proc { |part| ApiResources::Instances.to_href(cloud_id: part[:value].cloud_id, id: part[:value].id) }
|
|
85
85
|
|
|
86
86
|
payload Hash do
|
|
87
87
|
attribute :key, Integer, required: true
|
|
@@ -90,16 +90,14 @@ module ApiResources
|
|
|
90
90
|
attribute :name
|
|
91
91
|
end
|
|
92
92
|
end
|
|
93
|
-
|
|
94
93
|
end
|
|
95
94
|
end
|
|
96
95
|
|
|
97
|
-
|
|
98
96
|
# Using a hash param for parts
|
|
99
|
-
#response :multipart_ok, parts: {
|
|
97
|
+
# response :multipart_ok, parts: {
|
|
100
98
|
# like: :created,
|
|
101
99
|
# location: /\/instances\//
|
|
102
|
-
#}
|
|
100
|
+
# }
|
|
103
101
|
|
|
104
102
|
# Using a block for parts to defin a sub-request
|
|
105
103
|
# sub_request = proc do
|
|
@@ -140,7 +138,7 @@ module ApiResources
|
|
|
140
138
|
attribute :id
|
|
141
139
|
end
|
|
142
140
|
|
|
143
|
-
payload required: false do
|
|
141
|
+
payload required: false, null: true do
|
|
144
142
|
attribute :when, DateTime
|
|
145
143
|
end
|
|
146
144
|
|
|
@@ -157,7 +155,6 @@ module ApiResources
|
|
|
157
155
|
attribute :id
|
|
158
156
|
end
|
|
159
157
|
|
|
160
|
-
|
|
161
158
|
response :ok, media_type: 'application/json'
|
|
162
159
|
end
|
|
163
160
|
|
|
@@ -178,7 +175,6 @@ module ApiResources
|
|
|
178
175
|
response :ok
|
|
179
176
|
end
|
|
180
177
|
|
|
181
|
-
|
|
182
178
|
action :exceptional do
|
|
183
179
|
routing do
|
|
184
180
|
prefix '//clouds/:cloud_id/otherinstances'
|
|
@@ -229,7 +225,5 @@ module ApiResources
|
|
|
229
225
|
# key 'destination', String, required: true
|
|
230
226
|
# match /file-.+/, FileUpload
|
|
231
227
|
# end
|
|
232
|
-
|
|
233
228
|
end
|
|
234
|
-
|
|
235
229
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'volumes'
|
|
2
4
|
|
|
3
5
|
module ApiResources
|
|
@@ -7,8 +9,8 @@ module ApiResources
|
|
|
7
9
|
media_type VolumeSnapshot
|
|
8
10
|
|
|
9
11
|
version '1.0'
|
|
10
|
-
|
|
11
|
-
parent Volumes, :
|
|
12
|
+
|
|
13
|
+
parent Volumes, id: :volume_id
|
|
12
14
|
prefix '/snapshots'
|
|
13
15
|
|
|
14
16
|
action :index do
|
|
@@ -21,7 +23,6 @@ module ApiResources
|
|
|
21
23
|
params do
|
|
22
24
|
attribute :volume_id, Integer, description: 'id of parent volume'
|
|
23
25
|
end
|
|
24
|
-
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
action :show do
|
|
@@ -32,8 +33,6 @@ module ApiResources
|
|
|
32
33
|
params do
|
|
33
34
|
attribute :id
|
|
34
35
|
end
|
|
35
|
-
|
|
36
36
|
end
|
|
37
|
-
|
|
38
37
|
end
|
|
39
38
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ApiResources
|
|
2
4
|
class Volumes
|
|
3
5
|
include Praxis::EndpointDefinition
|
|
@@ -7,10 +9,10 @@ module ApiResources
|
|
|
7
9
|
prefix '/clouds/:cloud_id/volumes'
|
|
8
10
|
|
|
9
11
|
trait :authenticated
|
|
10
|
-
|
|
12
|
+
|
|
11
13
|
action_defaults do
|
|
12
14
|
params do
|
|
13
|
-
attribute :cloud_id, Integer, description:
|
|
15
|
+
attribute :cloud_id, Integer, description: 'id of the cloud'
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
|
|
@@ -35,9 +37,6 @@ module ApiResources
|
|
|
35
37
|
attribute :junk, String, default: ''
|
|
36
38
|
attribute :some_date, DateTime, default: DateTime.parse('2012-12-21')
|
|
37
39
|
end
|
|
38
|
-
|
|
39
40
|
end
|
|
40
|
-
|
|
41
41
|
end
|
|
42
|
-
|
|
43
42
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'coveralls'
|
|
2
4
|
Coveralls.wear!
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path(__dir__)
|
|
7
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
|
8
|
+
$LOAD_PATH.unshift File.expand_path('support', __dir__)
|
|
7
9
|
|
|
8
10
|
require 'bundler'
|
|
9
11
|
Bundler.setup :default, :test
|
|
@@ -13,9 +15,6 @@ RSpec.configure do |config|
|
|
|
13
15
|
config.run_all_when_everything_filtered = true
|
|
14
16
|
end
|
|
15
17
|
|
|
16
|
-
require 'simplecov'
|
|
17
|
-
SimpleCov.start 'praxis'
|
|
18
|
-
|
|
19
18
|
require 'pry'
|
|
20
19
|
require 'pry-byebug'
|
|
21
20
|
|
|
@@ -26,17 +25,17 @@ require 'rack/test'
|
|
|
26
25
|
require 'rspec/its'
|
|
27
26
|
require 'rspec/collection_matchers'
|
|
28
27
|
|
|
29
|
-
Dir["#{File.dirname(__FILE__)}/../lib/praxis/plugins/*.rb"].each do |file|
|
|
28
|
+
Dir["#{File.dirname(__FILE__)}/../lib/praxis/plugins/*.rb"].sort.each do |file|
|
|
30
29
|
require file
|
|
31
30
|
end
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
Dir["#{File.dirname(__FILE__)}/support/*.rb"].each do |file|
|
|
32
|
+
Dir["#{File.dirname(__FILE__)}/support/*.rb"].sort.each do |file|
|
|
35
33
|
require file
|
|
36
34
|
end
|
|
37
35
|
|
|
38
36
|
def suppress_output
|
|
39
|
-
original_stdout
|
|
37
|
+
original_stdout = $stdout.clone
|
|
38
|
+
original_stderr = $stderr.clone
|
|
40
39
|
$stderr.reopen File.new('/dev/null', 'w')
|
|
41
40
|
$stdout.reopen File.new('/dev/null', 'w')
|
|
42
41
|
yield
|
|
@@ -51,12 +50,12 @@ RSpec.configure do |config|
|
|
|
51
50
|
config.before(:suite) do
|
|
52
51
|
Praxis::Mapper::Resource.finalize!
|
|
53
52
|
Praxis::Blueprint.caching_enabled = true
|
|
54
|
-
Praxis::Application.instance.setup(root:'spec/spec_app')
|
|
53
|
+
Praxis::Application.instance.setup(root: 'spec/spec_app')
|
|
55
54
|
end
|
|
56
55
|
|
|
57
56
|
config.before(:each) do
|
|
58
57
|
Praxis::Blueprint.cache = Hash.new do |hash, key|
|
|
59
|
-
hash[key] =
|
|
58
|
+
hash[key] = {}
|
|
60
59
|
end
|
|
61
60
|
end
|
|
62
61
|
|
|
@@ -65,4 +64,3 @@ RSpec.configure do |config|
|
|
|
65
64
|
Praxis::Application.instance.logger.level = 2 # warn
|
|
66
65
|
end
|
|
67
66
|
end
|
|
68
|
-
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# Copied verbatim from: https://github.com/amogil/rspec-deep-ignore-order-matcher
|
|
2
4
|
|
|
3
5
|
RSpec::Matchers.define :be_deep_equal do |expected|
|
|
@@ -18,6 +20,7 @@ RSpec::Matchers.define :be_deep_equal do |expected|
|
|
|
18
20
|
def match?(actual, expected)
|
|
19
21
|
return arrays_match?(actual, expected) if expected.is_a?(Array) && actual.is_a?(Array)
|
|
20
22
|
return hashes_match?(actual, expected) if expected.is_a?(Hash) && actual.is_a?(Hash)
|
|
23
|
+
|
|
21
24
|
expected == actual
|
|
22
25
|
end
|
|
23
26
|
|
|
@@ -26,6 +29,7 @@ RSpec::Matchers.define :be_deep_equal do |expected|
|
|
|
26
29
|
actual.each do |a|
|
|
27
30
|
index = exp.find_index { |e| match? a, e }
|
|
28
31
|
return false if index.nil?
|
|
32
|
+
|
|
29
33
|
exp.delete_at(index)
|
|
30
34
|
end
|
|
31
35
|
exp.empty?
|
|
@@ -33,6 +37,7 @@ RSpec::Matchers.define :be_deep_equal do |expected|
|
|
|
33
37
|
|
|
34
38
|
def hashes_match?(actual, expected)
|
|
35
39
|
return false unless actual.keys.sort == expected.keys.sort
|
|
40
|
+
|
|
36
41
|
actual.each { |key, value| return false unless match? value, expected[key] }
|
|
37
42
|
true
|
|
38
43
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'singleton'
|
|
2
4
|
|
|
3
5
|
module AuthorizationPlugin
|
|
@@ -11,7 +13,8 @@ module AuthorizationPlugin
|
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
def initialize
|
|
14
|
-
|
|
16
|
+
super
|
|
17
|
+
@options = { config_file: 'config/authorization.yml' }
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
def prepare_config!(node)
|
|
@@ -30,7 +33,6 @@ module AuthorizationPlugin
|
|
|
30
33
|
|
|
31
34
|
(request.action.required_abilities - abilities).empty?
|
|
32
35
|
end
|
|
33
|
-
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
module Request
|
|
@@ -43,33 +45,27 @@ module AuthorizationPlugin
|
|
|
43
45
|
extend ActiveSupport::Concern
|
|
44
46
|
|
|
45
47
|
included do
|
|
46
|
-
|
|
47
48
|
before :action do |controller|
|
|
48
49
|
verify_abilities(controller.request)
|
|
49
50
|
end
|
|
50
|
-
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
|
|
54
53
|
module ClassMethods
|
|
55
54
|
def verify_abilities(request)
|
|
56
55
|
return true unless request.action.required_abilities
|
|
57
56
|
|
|
58
57
|
authorized = AuthorizationPlugin::Plugin.instance.authorized?(request)
|
|
59
58
|
|
|
60
|
-
unless authorized
|
|
61
|
-
return Praxis::Responses::Forbidden.new
|
|
62
|
-
end
|
|
59
|
+
return Praxis::Responses::Forbidden.new unless authorized
|
|
63
60
|
end
|
|
64
61
|
end
|
|
65
62
|
|
|
66
63
|
def subject
|
|
67
|
-
#p [self, :subject]
|
|
64
|
+
# p [self, :subject]
|
|
68
65
|
end
|
|
69
66
|
end
|
|
70
67
|
|
|
71
68
|
module EndpointDefinition
|
|
72
|
-
|
|
73
69
|
end
|
|
74
70
|
|
|
75
71
|
module ActionDefinition
|
|
@@ -77,6 +73,7 @@ module AuthorizationPlugin
|
|
|
77
73
|
|
|
78
74
|
included do
|
|
79
75
|
attr_accessor :required_abilities
|
|
76
|
+
|
|
80
77
|
decorate_docs do |action, docs|
|
|
81
78
|
docs[:required_abilities] = action.required_abilities
|
|
82
79
|
end
|
|
@@ -90,6 +87,4 @@ module AuthorizationPlugin
|
|
|
90
87
|
requires_authentication true
|
|
91
88
|
end
|
|
92
89
|
end
|
|
93
|
-
|
|
94
|
-
|
|
95
90
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
class PersonBlueprint < Praxis::Blueprint
|
|
3
4
|
attributes do
|
|
4
5
|
attribute :name, String, example: /[:first_name:]/
|
|
@@ -9,8 +10,8 @@ class PersonBlueprint < Praxis::Blueprint
|
|
|
9
10
|
attribute :full_name, FullName
|
|
10
11
|
attribute :aliases, Attributor::Collection.of(FullName)
|
|
11
12
|
|
|
12
|
-
attribute :address, AddressBlueprint, example: proc { |person, context| AddressBlueprint.example(context, resident: person) }
|
|
13
|
-
attribute :work_address, AddressBlueprint
|
|
13
|
+
attribute :address, AddressBlueprint, null: true, example: proc { |person, context| AddressBlueprint.example(context, resident: person) }
|
|
14
|
+
attribute :work_address, AddressBlueprint, null: true
|
|
14
15
|
|
|
15
16
|
attribute :prior_addresses, Attributor::Collection.of(AddressBlueprint)
|
|
16
17
|
attribute :parents do
|
|
@@ -21,7 +22,7 @@ class PersonBlueprint < Praxis::Blueprint
|
|
|
21
22
|
attribute :tags, Attributor::Collection.of(String)
|
|
22
23
|
attribute :href, String
|
|
23
24
|
attribute :alive, Attributor::Boolean, default: true
|
|
24
|
-
attribute :myself, PersonBlueprint
|
|
25
|
+
attribute :myself, PersonBlueprint, null: true
|
|
25
26
|
attribute :friends, Attributor::Collection.of(PersonBlueprint)
|
|
26
27
|
attribute :metadata, Attributor::Hash
|
|
27
28
|
end
|
|
@@ -44,7 +45,7 @@ class AddressBlueprint < Praxis::Blueprint
|
|
|
44
45
|
attribute :id, Integer
|
|
45
46
|
attribute :name, String
|
|
46
47
|
attribute :street, String
|
|
47
|
-
attribute :state, String, values: %w
|
|
48
|
+
attribute :state, String, values: %w[OR CA]
|
|
48
49
|
|
|
49
50
|
attribute :resident, PersonBlueprint, example: proc { |address, context| PersonBlueprint.example(context, address: address) }
|
|
50
51
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'singleton'
|
|
2
4
|
|
|
3
5
|
class Authenticator
|
|
@@ -7,7 +9,7 @@ class Authenticator
|
|
|
7
9
|
Class
|
|
8
10
|
end
|
|
9
11
|
|
|
10
|
-
def self.load(value,
|
|
12
|
+
def self.load(value, _context = Attributor::DEFAULT_ROOT_CONTEXT, **_options)
|
|
11
13
|
case value
|
|
12
14
|
when Hash
|
|
13
15
|
type = value.delete(:type) || value.delete('type')
|
|
@@ -15,43 +17,32 @@ class Authenticator
|
|
|
15
17
|
when self
|
|
16
18
|
value
|
|
17
19
|
else
|
|
18
|
-
raise "#{
|
|
20
|
+
raise "#{naem} can not load values of type #{value.class}"
|
|
19
21
|
end
|
|
20
22
|
end
|
|
21
23
|
|
|
22
|
-
def self.validate(*args)
|
|
23
|
-
end
|
|
24
|
+
def self.validate(*args); end
|
|
24
25
|
|
|
25
|
-
def self.describe
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def initialize(**options)
|
|
29
|
-
end
|
|
26
|
+
def self.describe; end
|
|
30
27
|
|
|
31
|
-
def authenticate(
|
|
32
|
-
raise
|
|
28
|
+
def authenticate(_request)
|
|
29
|
+
raise 'sublcass must implement authenticate'
|
|
33
30
|
end
|
|
34
|
-
|
|
35
31
|
end
|
|
36
32
|
|
|
37
|
-
|
|
38
33
|
class GlobalSessionAuthenticator < Authenticator
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
self.new(**value)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def self.describe
|
|
34
|
+
def self.load(value, _context = Attributor::DEFAULT_ROOT_CONTEXT, **_options)
|
|
35
|
+
new(**value)
|
|
45
36
|
end
|
|
46
37
|
|
|
47
|
-
def
|
|
48
|
-
end
|
|
38
|
+
def self.describe; end
|
|
49
39
|
|
|
50
40
|
def authenticate(request)
|
|
51
|
-
body = {name: 'Unauthorized'}
|
|
41
|
+
body = { name: 'Unauthorized' }
|
|
52
42
|
|
|
53
43
|
if (session = request.env['global_session'])
|
|
54
44
|
return true if session.valid?
|
|
45
|
+
|
|
55
46
|
body[:message] = 'Invalid session.'
|
|
56
47
|
else
|
|
57
48
|
body[:message] = 'Missing session.'
|
|
@@ -59,10 +50,8 @@ class GlobalSessionAuthenticator < Authenticator
|
|
|
59
50
|
|
|
60
51
|
Praxis::Responses::Unauthorized.new(body: body)
|
|
61
52
|
end
|
|
62
|
-
|
|
63
53
|
end
|
|
64
54
|
|
|
65
|
-
|
|
66
55
|
module ComplexAuthenticationPlugin
|
|
67
56
|
include Praxis::PluginConcern
|
|
68
57
|
|
|
@@ -70,24 +59,23 @@ module ComplexAuthenticationPlugin
|
|
|
70
59
|
include Singleton
|
|
71
60
|
|
|
72
61
|
def initialize
|
|
73
|
-
@options = {config_file: 'config/authentication.yml'}
|
|
62
|
+
@options = { config_file: 'config/authentication.yml' }
|
|
63
|
+
super
|
|
74
64
|
end
|
|
75
65
|
|
|
76
66
|
def config_key
|
|
77
67
|
:authentication
|
|
78
68
|
end
|
|
79
69
|
|
|
80
|
-
def prepare_config!(
|
|
70
|
+
def prepare_config!(_node)
|
|
81
71
|
self.config_attribute = Attributor::Attribute.new(Authenticator, required: true)
|
|
82
72
|
end
|
|
83
73
|
|
|
84
74
|
def self.authenticate(request)
|
|
85
75
|
instance.config.authenticate(request)
|
|
86
76
|
end
|
|
87
|
-
|
|
88
77
|
end
|
|
89
78
|
|
|
90
|
-
|
|
91
79
|
module Request
|
|
92
80
|
end
|
|
93
81
|
|
|
@@ -96,15 +84,11 @@ module ComplexAuthenticationPlugin
|
|
|
96
84
|
|
|
97
85
|
included do
|
|
98
86
|
before :action do |controller|
|
|
99
|
-
if controller.request.action.authentication_required
|
|
100
|
-
Plugin.authenticate(controller.request)
|
|
101
|
-
end
|
|
87
|
+
Plugin.authenticate(controller.request) if controller.request.action.authentication_required
|
|
102
88
|
end
|
|
103
89
|
end
|
|
104
|
-
|
|
105
90
|
end
|
|
106
91
|
|
|
107
|
-
|
|
108
92
|
module ActionDefinition
|
|
109
93
|
extend ActiveSupport::Concern
|
|
110
94
|
|
|
@@ -121,6 +105,5 @@ module ComplexAuthenticationPlugin
|
|
|
121
105
|
def authentication_required
|
|
122
106
|
@authentication_required ||= false
|
|
123
107
|
end
|
|
124
|
-
|
|
125
108
|
end
|
|
126
109
|
end
|