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,17 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
3
|
+
module Praxis
|
|
4
4
|
module BootloaderStages
|
|
5
|
-
|
|
6
5
|
class Routing < Stage
|
|
7
6
|
class Target
|
|
8
7
|
attr_reader :action
|
|
8
|
+
|
|
9
9
|
def initialize(application, controller, action)
|
|
10
10
|
@application = application
|
|
11
11
|
@controller = controller
|
|
12
12
|
@action = action
|
|
13
13
|
end
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
def call(request)
|
|
16
16
|
request.action = @action
|
|
17
17
|
dispatcher = Dispatcher.current(application: @application)
|
|
@@ -19,7 +19,7 @@ module Praxis
|
|
|
19
19
|
dispatcher.dispatch(@controller, @action, request)
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
def execute
|
|
24
24
|
application.controllers.each do |controller|
|
|
25
25
|
controller.definition.actions.each do |action_name, action|
|
|
@@ -29,14 +29,11 @@ module Praxis
|
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
|
|
33
32
|
def target_factory(controller, action_name)
|
|
34
33
|
action = controller.definition.actions.fetch(action_name)
|
|
35
34
|
|
|
36
35
|
Target.new(application, controller, action)
|
|
37
36
|
end
|
|
38
|
-
|
|
39
37
|
end
|
|
40
|
-
|
|
41
38
|
end
|
|
42
39
|
end
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
module Praxis
|
|
3
4
|
module BootloaderStages
|
|
4
|
-
|
|
5
5
|
class SubgroupLoader < Stage
|
|
6
|
-
|
|
7
6
|
attr_writer :order
|
|
8
7
|
|
|
9
|
-
def initialize(name, application)
|
|
10
|
-
super
|
|
11
|
-
# always finalize Taylor after loading app code.
|
|
12
|
-
|
|
13
|
-
end
|
|
14
|
-
|
|
15
8
|
def order
|
|
16
9
|
@order ||= application.file_layout[name] == [] ? [] : application.file_layout[name].groups.keys
|
|
17
10
|
end
|
|
@@ -23,6 +16,5 @@ module Praxis
|
|
|
23
16
|
setup_deferred_callbacks!
|
|
24
17
|
end
|
|
25
18
|
end
|
|
26
|
-
|
|
27
19
|
end
|
|
28
20
|
end
|
|
@@ -1,28 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
3
|
+
module Praxis
|
|
4
4
|
module BootloaderStages
|
|
5
|
-
|
|
6
5
|
class WarnUnloadedFiles < Stage
|
|
7
6
|
@enabled = true
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
class << self
|
|
9
|
+
attr_writer :enabled
|
|
11
10
|
end
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
class << self
|
|
13
|
+
attr_reader :enabled
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
def execute
|
|
18
17
|
return unless self.class.enabled
|
|
19
18
|
|
|
20
|
-
if application.file_layout[:app] == []
|
|
21
|
-
return
|
|
22
|
-
end
|
|
19
|
+
return if application.file_layout[:app] == []
|
|
23
20
|
|
|
24
21
|
base = application.file_layout[:app].base
|
|
25
22
|
return unless base.exist?
|
|
23
|
+
|
|
26
24
|
file_enum = base.find.to_a
|
|
27
25
|
files = file_enum.select do |file|
|
|
28
26
|
path = file.relative_path_from(base)
|
|
@@ -30,17 +28,15 @@ module Praxis
|
|
|
30
28
|
end
|
|
31
29
|
|
|
32
30
|
missing = Set.new(files) - application.loaded_files
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
warn msg
|
|
31
|
+
return unless missing.any?
|
|
32
|
+
|
|
33
|
+
msg = "The following application files under #{base} were not loaded:\n"
|
|
34
|
+
missing.each do |file|
|
|
35
|
+
path = file.relative_path_from(base)
|
|
36
|
+
msg << " * #{path}\n"
|
|
40
37
|
end
|
|
38
|
+
warn msg
|
|
41
39
|
end
|
|
42
|
-
|
|
43
|
-
|
|
44
40
|
end
|
|
45
41
|
end
|
|
46
42
|
end
|
data/lib/praxis/callbacks.rb
CHANGED
|
@@ -1,33 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Praxis
|
|
2
4
|
module Callbacks
|
|
3
5
|
extend ::ActiveSupport::Concern
|
|
4
6
|
|
|
5
7
|
included do
|
|
6
8
|
class_attribute :before_callbacks, :after_callbacks, :around_callbacks
|
|
7
|
-
self.before_callbacks =
|
|
8
|
-
self.after_callbacks =
|
|
9
|
-
self.around_callbacks =
|
|
9
|
+
self.before_callbacks = ({})
|
|
10
|
+
self.after_callbacks = ({})
|
|
11
|
+
self.around_callbacks = ({})
|
|
10
12
|
end
|
|
11
|
-
|
|
13
|
+
|
|
12
14
|
module ClassMethods
|
|
13
|
-
|
|
14
15
|
def before(*stage_path, **conditions, &block)
|
|
15
16
|
stage_path = [:action] if stage_path.empty?
|
|
16
|
-
before_callbacks[stage_path] ||=
|
|
17
|
+
before_callbacks[stage_path] ||= []
|
|
17
18
|
before_callbacks[stage_path] << [conditions, block]
|
|
18
19
|
end
|
|
19
|
-
|
|
20
|
+
|
|
20
21
|
def after(*stage_path, **conditions, &block)
|
|
21
22
|
stage_path = [:action] if stage_path.empty?
|
|
22
|
-
after_callbacks[stage_path] ||=
|
|
23
|
+
after_callbacks[stage_path] ||= []
|
|
23
24
|
after_callbacks[stage_path] << [conditions, block]
|
|
24
25
|
end
|
|
25
|
-
|
|
26
|
+
|
|
26
27
|
def around(*stage_path, **conditions, &block)
|
|
27
28
|
stage_path = [:action] if stage_path.empty?
|
|
28
|
-
around_callbacks[stage_path] ||=
|
|
29
|
+
around_callbacks[stage_path] ||= []
|
|
29
30
|
around_callbacks[stage_path] << [conditions, block]
|
|
30
31
|
end
|
|
31
32
|
end
|
|
32
33
|
end
|
|
33
|
-
end
|
|
34
|
+
end
|
data/lib/praxis/collection.rb
CHANGED
|
@@ -1,28 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Praxis
|
|
2
4
|
class Collection < Attributor::Collection
|
|
3
5
|
include Types::MediaTypeCommon
|
|
4
6
|
|
|
5
7
|
def self.of(type)
|
|
6
|
-
if defined?(type::Collection)
|
|
7
|
-
return type::Collection
|
|
8
|
-
end
|
|
8
|
+
return type::Collection if defined?(type::Collection)
|
|
9
9
|
|
|
10
10
|
klass = super
|
|
11
11
|
klass.anonymous_type
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
klass.member_type type
|
|
15
|
-
type.const_set :Collection, klass
|
|
16
|
-
else
|
|
17
|
-
raise "Praxis::Collection.of() for non-MediaTypes is unsupported. Use Attributor::Collection.of() instead."
|
|
18
|
-
end
|
|
13
|
+
raise 'Praxis::Collection.of() for non-MediaTypes is unsupported. Use Attributor::Collection.of() instead.' unless type < Praxis::Types::MediaTypeCommon
|
|
19
14
|
|
|
15
|
+
klass.member_type type
|
|
16
|
+
type.const_set :Collection, klass
|
|
20
17
|
end
|
|
21
18
|
|
|
22
|
-
def self.member_type(type=nil)
|
|
19
|
+
def self.member_type(type = nil)
|
|
23
20
|
unless type.nil?
|
|
24
21
|
@member_type = type
|
|
25
|
-
|
|
22
|
+
identifier("#{type.identifier};type=collection") unless type.identifier.nil?
|
|
26
23
|
end
|
|
27
24
|
|
|
28
25
|
@member_type
|
|
@@ -36,11 +33,11 @@ module Praxis
|
|
|
36
33
|
:array
|
|
37
34
|
end
|
|
38
35
|
|
|
39
|
-
def self.as_json_schema(**
|
|
40
|
-
the_type = @attribute
|
|
36
|
+
def self.as_json_schema(**_args)
|
|
37
|
+
the_type = @attribute&.type || member_type
|
|
41
38
|
{
|
|
42
39
|
type: json_schema_type,
|
|
43
|
-
items:
|
|
40
|
+
items: the_type.as_json_schema
|
|
44
41
|
}
|
|
45
42
|
end
|
|
46
43
|
end
|
data/lib/praxis/config.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Praxis
|
|
2
4
|
class Config
|
|
3
5
|
include Attributor::Type
|
|
@@ -10,7 +12,7 @@ module Praxis
|
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
# You can define the configuration in different ways
|
|
13
|
-
#
|
|
15
|
+
#
|
|
14
16
|
# Add a key to the top struct
|
|
15
17
|
# define do
|
|
16
18
|
# attribute :added_to_top, String
|
|
@@ -28,46 +30,36 @@ module Praxis
|
|
|
28
30
|
# attribute :two String
|
|
29
31
|
# end
|
|
30
32
|
# end
|
|
31
|
-
|
|
33
|
+
|
|
32
34
|
# ...or using this way too (equivalent)
|
|
33
35
|
# define(:app) do
|
|
34
36
|
# attribute :two, String
|
|
35
37
|
# end
|
|
36
38
|
# You can also define a key to be a non-Struct type
|
|
37
39
|
# define :app, Attributor::Hash
|
|
38
|
-
|
|
39
|
-
def define(key=nil, type=Attributor::Struct, **opts, &block)
|
|
40
|
-
if key.nil? && type != Attributor::Struct
|
|
41
|
-
|
|
42
|
-
"You cannot define the top level configuration with a non-Struct type. Got: #{type.inspect}"
|
|
43
|
-
)
|
|
44
|
-
end
|
|
45
|
-
|
|
40
|
+
|
|
41
|
+
def define(key = nil, type = Attributor::Struct, **opts, &block)
|
|
42
|
+
raise Exceptions::InvalidConfiguration, "You cannot define the top level configuration with a non-Struct type. Got: #{type.inspect}" if key.nil? && type != Attributor::Struct
|
|
43
|
+
|
|
46
44
|
case key
|
|
47
45
|
when String, Symbol, NilClass
|
|
48
46
|
|
|
49
47
|
top = key.nil? ? @attribute : @attribute.attributes[key]
|
|
50
|
-
if top #key defined...redefine
|
|
51
|
-
unless
|
|
52
|
-
|
|
53
|
-
"Incompatible type received for extending configuration key [#{key}]. Got type #{type.name}"
|
|
54
|
-
)
|
|
55
|
-
end
|
|
48
|
+
if top # key defined...redefine
|
|
49
|
+
raise Exceptions::InvalidConfiguration, "Incompatible type received for extending configuration key [#{key}]. Got type #{type.name}" unless type == Attributor::Struct && top.type < Attributor::Struct
|
|
50
|
+
|
|
56
51
|
top.options.merge!(opts)
|
|
57
52
|
top.type.attributes(**opts, &block)
|
|
58
53
|
else
|
|
59
54
|
@attribute.attributes[key] = Attributor::Attribute.new(type, opts, &block)
|
|
60
55
|
end
|
|
61
56
|
else
|
|
62
|
-
raise Exceptions::InvalidConfiguration.
|
|
63
|
-
"Defining a configuration key requires a String or a Symbol key. Got: #{key.inspect}"
|
|
64
|
-
)
|
|
57
|
+
raise Exceptions::InvalidConfiguration, "Defining a configuration key requires a String or a Symbol key. Got: #{key.inspect}"
|
|
65
58
|
end
|
|
66
|
-
|
|
67
59
|
end
|
|
68
60
|
|
|
69
61
|
def set(config)
|
|
70
|
-
context = [
|
|
62
|
+
context = %w[Application config]
|
|
71
63
|
|
|
72
64
|
begin
|
|
73
65
|
@value = @attribute.load(config, context, recurse: true)
|
|
@@ -77,17 +69,14 @@ module Praxis
|
|
|
77
69
|
|
|
78
70
|
errors = @attribute.validate(@value, context)
|
|
79
71
|
|
|
80
|
-
unless errors.empty?
|
|
81
|
-
raise Exceptions::ConfigValidation.new(errors: errors)
|
|
82
|
-
end
|
|
72
|
+
raise Exceptions::ConfigValidation.new(errors: errors) unless errors.empty?
|
|
83
73
|
end
|
|
84
74
|
|
|
85
75
|
def get
|
|
86
|
-
@value ||= begin
|
|
87
|
-
context = [
|
|
88
|
-
@attribute.load({},context, recurse: true)
|
|
76
|
+
@value ||= begin # rubocop:disable Naming/MemoizedInstanceVariableName
|
|
77
|
+
context = %w[Application config].freeze
|
|
78
|
+
@attribute.load({}, context, recurse: true)
|
|
89
79
|
end
|
|
90
80
|
end
|
|
91
|
-
|
|
92
81
|
end
|
|
93
82
|
end
|
data/lib/praxis/config_hash.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
# TODO: move to Praxis proper...it's not used here
|
|
3
4
|
module Praxis
|
|
4
5
|
class ConfigHash < BasicObject
|
|
@@ -22,7 +23,7 @@ module Praxis
|
|
|
22
23
|
true
|
|
23
24
|
end
|
|
24
25
|
|
|
25
|
-
def method_missing(name, value, *rest, &block)
|
|
26
|
+
def method_missing(name, value, *rest, &block)
|
|
26
27
|
if (existing = @hash[name])
|
|
27
28
|
if block
|
|
28
29
|
existing << [value, block]
|
data/lib/praxis/controller.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_support/concern'
|
|
2
4
|
require 'active_support/all'
|
|
3
5
|
|
|
@@ -24,11 +26,11 @@ module Praxis
|
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
def id
|
|
27
|
-
|
|
29
|
+
name.gsub('::', '-')
|
|
28
30
|
end
|
|
29
31
|
end
|
|
30
32
|
|
|
31
|
-
def initialize(request, response=Responses::Ok.new)
|
|
33
|
+
def initialize(request, response = Responses::Ok.new)
|
|
32
34
|
@request = request
|
|
33
35
|
@response = response
|
|
34
36
|
end
|
|
@@ -38,12 +40,11 @@ module Praxis
|
|
|
38
40
|
end
|
|
39
41
|
|
|
40
42
|
def media_type
|
|
41
|
-
if (response_definition =
|
|
42
|
-
|
|
43
|
+
if (response_definition = request.action.responses[response.name])
|
|
44
|
+
response_definition.media_type
|
|
43
45
|
else
|
|
44
|
-
|
|
46
|
+
definition.media_type
|
|
45
47
|
end
|
|
46
48
|
end
|
|
47
|
-
|
|
48
49
|
end
|
|
49
50
|
end
|
data/lib/praxis/dispatcher.rb
CHANGED
|
@@ -1,35 +1,33 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
module Praxis
|
|
3
4
|
CONTEXT_FOR = {
|
|
4
|
-
params: [Attributor::
|
|
5
|
-
headers: [Attributor::
|
|
6
|
-
payload: [Attributor::
|
|
5
|
+
params: [Attributor::ROOT_PREFIX, 'params'],
|
|
6
|
+
headers: [Attributor::ROOT_PREFIX, 'headers'],
|
|
7
|
+
payload: [Attributor::ROOT_PREFIX, 'payload']
|
|
7
8
|
}.freeze
|
|
8
9
|
|
|
9
10
|
class Dispatcher
|
|
10
|
-
attr_reader :controller
|
|
11
|
-
attr_reader :action
|
|
12
|
-
attr_reader :request
|
|
13
|
-
attr_reader :application
|
|
11
|
+
attr_reader :controller, :action, :request, :application
|
|
14
12
|
|
|
15
|
-
@deferred_callbacks = Hash.new do |hash,stage|
|
|
16
|
-
hash[stage] = {before: [], after:[]}
|
|
13
|
+
@deferred_callbacks = Hash.new do |hash, stage|
|
|
14
|
+
hash[stage] = { before: [], after: [] }
|
|
17
15
|
end
|
|
18
16
|
|
|
19
17
|
class << self
|
|
20
18
|
attr_reader :deferred_callbacks
|
|
21
19
|
end
|
|
22
20
|
|
|
23
|
-
def self.before(*
|
|
21
|
+
def self.before(*_stage_path, **conditions, &block)
|
|
24
22
|
@deferred_callbacks[:before] << [conditions, block]
|
|
25
23
|
end
|
|
26
24
|
|
|
27
|
-
def self.after(*
|
|
25
|
+
def self.after(*_stage_path, **conditions, &block)
|
|
28
26
|
@deferred_callbacks[:after] << [conditions, block]
|
|
29
27
|
end
|
|
30
28
|
|
|
31
29
|
def self.current(thread: Thread.current, application: Application.instance)
|
|
32
|
-
thread[:praxis_dispatcher] ||=
|
|
30
|
+
thread[:praxis_dispatcher] ||= new(application: application)
|
|
33
31
|
end
|
|
34
32
|
|
|
35
33
|
def initialize(application: Application.instance)
|
|
@@ -43,20 +41,18 @@ module Praxis
|
|
|
43
41
|
@stages << RequestStages::Validate.new(:validate, self)
|
|
44
42
|
@stages << RequestStages::Action.new(:action, self)
|
|
45
43
|
@stages << RequestStages::Response.new(:response, self)
|
|
46
|
-
@stages.each
|
|
47
|
-
s.setup!
|
|
48
|
-
end
|
|
44
|
+
@stages.each(&:setup!)
|
|
49
45
|
setup_deferred_callbacks!
|
|
50
46
|
end
|
|
51
47
|
|
|
52
48
|
def setup_deferred_callbacks!
|
|
53
49
|
self.class.deferred_callbacks.each do |stage_name, callbacks|
|
|
54
50
|
callbacks[:before].each do |(*stage_path, block)|
|
|
55
|
-
|
|
51
|
+
before(stage_name, *stage_path, &block)
|
|
56
52
|
end
|
|
57
53
|
|
|
58
54
|
callbacks[:after].each do |(*stage_path, block)|
|
|
59
|
-
|
|
55
|
+
after(stage_name, *stage_path, &block)
|
|
60
56
|
end
|
|
61
57
|
end
|
|
62
58
|
end
|
|
@@ -76,38 +72,35 @@ module Praxis
|
|
|
76
72
|
@action = action
|
|
77
73
|
@request = request
|
|
78
74
|
|
|
79
|
-
payload = {request: request, response: nil, controller: @controller}
|
|
80
|
-
|
|
81
|
-
instrumented_dispatch( payload )
|
|
75
|
+
payload = { request: request, response: nil, controller: @controller }
|
|
82
76
|
|
|
77
|
+
instrumented_dispatch(payload)
|
|
83
78
|
ensure
|
|
84
79
|
@controller = nil
|
|
85
80
|
@action = nil
|
|
86
81
|
@request = nil
|
|
87
82
|
end
|
|
88
83
|
|
|
89
|
-
def instrumented_dispatch(
|
|
90
|
-
Notifications.instrument 'praxis.request.all'
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
break
|
|
101
|
-
end
|
|
84
|
+
def instrumented_dispatch(payload)
|
|
85
|
+
Notifications.instrument 'praxis.request.all', payload do
|
|
86
|
+
# the response stage must be the final stage in the list
|
|
87
|
+
*stages, response_stage = @stages
|
|
88
|
+
|
|
89
|
+
stages.each do |stage|
|
|
90
|
+
result = stage.run
|
|
91
|
+
case result
|
|
92
|
+
when Response
|
|
93
|
+
controller.response = result
|
|
94
|
+
break
|
|
102
95
|
end
|
|
96
|
+
end
|
|
103
97
|
|
|
104
|
-
|
|
98
|
+
response_stage.run
|
|
105
99
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
end
|
|
100
|
+
payload[:response] = controller.response
|
|
101
|
+
controller.response.finish
|
|
102
|
+
rescue StandardError => e
|
|
103
|
+
@application.error_handler.handle!(request, e)
|
|
111
104
|
end
|
|
112
105
|
end
|
|
113
106
|
|
|
@@ -116,9 +109,8 @@ module Praxis
|
|
|
116
109
|
return unless Praxis::Blueprint.caching_enabled?
|
|
117
110
|
|
|
118
111
|
Praxis::Blueprint.cache = Hash.new do |hash, key|
|
|
119
|
-
hash[key] =
|
|
112
|
+
hash[key] = {}
|
|
120
113
|
end
|
|
121
114
|
end
|
|
122
|
-
|
|
123
115
|
end
|
|
124
116
|
end
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Praxis
|
|
2
4
|
module Docs
|
|
3
5
|
module OpenApi
|
|
4
6
|
class InfoObject
|
|
5
7
|
attr_reader :info, :version
|
|
6
|
-
|
|
8
|
+
|
|
9
|
+
def initialize(version:, api_definition_info:)
|
|
7
10
|
@version = version
|
|
8
11
|
@info = api_definition_info
|
|
9
12
|
raise "OpenApi docs require a 'Title' for your API." unless info.title
|
|
@@ -11,12 +14,12 @@ module Praxis
|
|
|
11
14
|
|
|
12
15
|
def dump
|
|
13
16
|
data = { version: version }
|
|
14
|
-
[
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
%i[
|
|
18
|
+
title
|
|
19
|
+
description
|
|
20
|
+
termsOfService
|
|
21
|
+
contact
|
|
22
|
+
license
|
|
20
23
|
].each do |attr|
|
|
21
24
|
val = info.send(attr)
|
|
22
25
|
data[attr] = val if val
|
|
@@ -27,7 +30,7 @@ module Praxis
|
|
|
27
30
|
if info.logo_url
|
|
28
31
|
data[:'x-logo'] = {
|
|
29
32
|
url: info.logo_url,
|
|
30
|
-
backgroundColor:
|
|
33
|
+
backgroundColor: '#FFFFFF',
|
|
31
34
|
altText: info.title
|
|
32
35
|
}
|
|
33
36
|
end
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Praxis
|
|
2
4
|
module Docs
|
|
3
5
|
module OpenApi
|
|
4
6
|
class MediaTypeObject
|
|
5
7
|
attr_reader :schema, :example
|
|
8
|
+
|
|
6
9
|
def initialize(schema:, example:)
|
|
7
10
|
@schema = schema
|
|
8
11
|
@example = example
|
|
@@ -10,26 +13,24 @@ module Praxis
|
|
|
10
13
|
|
|
11
14
|
def dump
|
|
12
15
|
{
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
schema: schema,
|
|
17
|
+
example: example
|
|
18
|
+
# encoding: TODO SUPPORT IT maybe be great/necessary for multipart
|
|
16
19
|
}
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
# Helper to create the typical content attribute for responses and request bodies
|
|
20
|
-
def self.create_content_attribute_helper(type
|
|
23
|
+
def self.create_content_attribute_helper(type:, example_payload:, example_handlers: nil)
|
|
21
24
|
# Will produce 1 example encoded with a given handler (and marking it with the given content type)
|
|
22
|
-
|
|
23
|
-
example_handlers = [ {'application/json' => 'json' } ]
|
|
24
|
-
end
|
|
25
|
+
example_handlers ||= [{ 'application/json' => 'json' }]
|
|
25
26
|
# NOTE2: we should just create a $ref here unless it's an anon mediatype...
|
|
26
|
-
return {} if type.is_a? SimpleMediaType
|
|
27
|
+
return {} if type.is_a? SimpleMediaType # NOTE: skip if it's a SimpleMediaType?? ... is that correct?
|
|
27
28
|
|
|
28
|
-
the_schema = if type.anonymous? || !
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
the_schema = if type.anonymous? || !(type < Praxis::MediaType) # Avoid referencing custom/simple Types? (i.e., just MTs)
|
|
30
|
+
SchemaObject.new(info: type).dump_schema(shallow: false, allow_ref: false)
|
|
31
|
+
else
|
|
32
|
+
SchemaObject.new(info: type).dump_schema(shallow: true, allow_ref: true)
|
|
33
|
+
end
|
|
33
34
|
|
|
34
35
|
if example_payload
|
|
35
36
|
examples_by_content_type = {}
|
|
@@ -40,16 +41,16 @@ module Praxis
|
|
|
40
41
|
handler = Praxis::Application.instance.handlers[handler_name]
|
|
41
42
|
# ReDoc is not happy to display json generated outputs when served as JSON...wtf?
|
|
42
43
|
generated = handler.generate(rendered_payload)
|
|
43
|
-
final =
|
|
44
|
+
final = handler_name == 'json' ? JSON.parse(generated) : generated
|
|
44
45
|
examples_by_content_type[content_type] = final
|
|
45
46
|
end
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
# Key string (of MT) , value MTObject
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
examples_by_content_type.transform_values do |example_hash|
|
|
51
|
+
MediaTypeObject.new(
|
|
51
52
|
schema: the_schema, # Every MT will have the same exact type..oh well .. maybe a REF?
|
|
52
|
-
example: example_hash
|
|
53
|
+
example: example_hash
|
|
53
54
|
).dump
|
|
54
55
|
end
|
|
55
56
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'parameter_object'
|
|
2
4
|
require_relative 'request_body_object'
|
|
3
5
|
require_relative 'responses_object'
|
|
@@ -8,6 +10,7 @@ module Praxis
|
|
|
8
10
|
class OperationObject
|
|
9
11
|
# https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operation-object
|
|
10
12
|
attr_reader :id, :url, :action, :tags
|
|
13
|
+
|
|
11
14
|
def initialize(id:, url:, action:, tags:)
|
|
12
15
|
@id = id
|
|
13
16
|
@url = url
|
|
@@ -20,18 +23,18 @@ module Praxis
|
|
|
20
23
|
all_tags = tags + action.traits
|
|
21
24
|
h = {
|
|
22
25
|
summary: action.name.to_s,
|
|
23
|
-
|
|
24
|
-
#externalDocs: {}, # TODO/FIXME
|
|
26
|
+
# externalDocs: {}, # TODO/FIXME
|
|
25
27
|
operationId: id,
|
|
26
|
-
responses: ResponsesObject.new(responses: action.responses).dump
|
|
28
|
+
responses: ResponsesObject.new(responses: action.responses).dump
|
|
27
29
|
# callbacks
|
|
28
30
|
# deprecated: false
|
|
29
31
|
# security: [{}]
|
|
30
32
|
# servers: [{}]
|
|
31
33
|
}
|
|
34
|
+
h[:description] = action.description if action.description
|
|
32
35
|
h[:tags] = all_tags.uniq unless all_tags.empty?
|
|
33
36
|
h[:parameters] = all_parameters unless all_parameters.empty?
|
|
34
|
-
h[:requestBody] = RequestBodyObject.new(attribute: action.payload
|
|
37
|
+
h[:requestBody] = RequestBodyObject.new(attribute: action.payload).dump if action.payload
|
|
35
38
|
h
|
|
36
39
|
end
|
|
37
40
|
end
|