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
|
# A RESTful action allows you to define the following:
|
|
2
4
|
# - a payload structure
|
|
3
5
|
# - a params structure
|
|
@@ -9,13 +11,7 @@
|
|
|
9
11
|
|
|
10
12
|
module Praxis
|
|
11
13
|
class ActionDefinition
|
|
12
|
-
|
|
13
|
-
attr_reader :name
|
|
14
|
-
attr_reader :endpoint_definition
|
|
15
|
-
attr_reader :api_definition
|
|
16
|
-
attr_reader :route
|
|
17
|
-
attr_reader :responses
|
|
18
|
-
attr_reader :traits
|
|
14
|
+
attr_reader :name, :endpoint_definition, :api_definition, :route, :responses, :traits
|
|
19
15
|
|
|
20
16
|
# opaque hash of user-defined medata, used to decorate the definition,
|
|
21
17
|
# and also available in the generated JSON documents
|
|
@@ -28,21 +24,19 @@ module Praxis
|
|
|
28
24
|
@doc_decorations = []
|
|
29
25
|
|
|
30
26
|
def self.decorate_docs(&callback)
|
|
31
|
-
|
|
27
|
+
doc_decorations << callback
|
|
32
28
|
end
|
|
33
29
|
|
|
34
|
-
def initialize(name, endpoint_definition, **
|
|
30
|
+
def initialize(name, endpoint_definition, **_opts, &block)
|
|
35
31
|
@name = name
|
|
36
32
|
@endpoint_definition = endpoint_definition
|
|
37
|
-
@responses =
|
|
38
|
-
@metadata =
|
|
33
|
+
@responses = {}
|
|
34
|
+
@metadata = {}
|
|
39
35
|
@route = nil
|
|
40
36
|
@traits = []
|
|
41
37
|
|
|
42
|
-
if (media_type = endpoint_definition.media_type)
|
|
43
|
-
|
|
44
|
-
@reference_media_type = media_type
|
|
45
|
-
end
|
|
38
|
+
if (media_type = endpoint_definition.media_type) && (media_type.is_a?(Class) && media_type < Praxis::Types::MediaTypeCommon)
|
|
39
|
+
@reference_media_type = media_type
|
|
46
40
|
end
|
|
47
41
|
|
|
48
42
|
version = endpoint_definition.version
|
|
@@ -59,13 +53,11 @@ module Praxis
|
|
|
59
53
|
|
|
60
54
|
endpoint_definition.action_defaults.apply!(self)
|
|
61
55
|
|
|
62
|
-
|
|
56
|
+
instance_eval(&block) if block_given?
|
|
63
57
|
end
|
|
64
58
|
|
|
65
59
|
def trait(trait_name)
|
|
66
|
-
unless ApiDefinition.instance.traits.
|
|
67
|
-
raise Exceptions::InvalidTrait.new("Trait #{trait_name} not found in the system")
|
|
68
|
-
end
|
|
60
|
+
raise Exceptions::InvalidTrait, "Trait #{trait_name} not found in the system" unless ApiDefinition.instance.traits.key? trait_name
|
|
69
61
|
|
|
70
62
|
trait = ApiDefinition.instance.traits.fetch(trait_name)
|
|
71
63
|
trait.apply!(self)
|
|
@@ -77,13 +69,11 @@ module Praxis
|
|
|
77
69
|
attribute.type.attributes(**options, &block)
|
|
78
70
|
end
|
|
79
71
|
|
|
80
|
-
def response(name, type=nil, **args, &block)
|
|
72
|
+
def response(name, type = nil, **args, &block)
|
|
81
73
|
if type
|
|
82
74
|
# should verify type is a media type
|
|
83
75
|
|
|
84
|
-
if block_given?
|
|
85
|
-
type = type.construct(block)
|
|
86
|
-
end
|
|
76
|
+
type = type.construct(block) if block_given?
|
|
87
77
|
|
|
88
78
|
args[:media_type] = type
|
|
89
79
|
end
|
|
@@ -92,27 +82,22 @@ module Praxis
|
|
|
92
82
|
@responses[name] = template.compile(self, **args)
|
|
93
83
|
end
|
|
94
84
|
|
|
95
|
-
def create_attribute(type=Attributor::Struct, **opts, &block)
|
|
96
|
-
|
|
97
|
-
opts[:reference] = @reference_media_type if @reference_media_type && block
|
|
98
|
-
end
|
|
85
|
+
def create_attribute(type = Attributor::Struct, **opts, &block)
|
|
86
|
+
opts[:reference] = @reference_media_type if !opts.key?(:reference) && (@reference_media_type && block)
|
|
99
87
|
|
|
100
|
-
|
|
88
|
+
Attributor::Attribute.new(type, opts, &block)
|
|
101
89
|
end
|
|
102
90
|
|
|
103
|
-
def params(type=Attributor::Struct, **opts, &block)
|
|
104
|
-
return @params if !block && (
|
|
91
|
+
def params(type = Attributor::Struct, **opts, &block)
|
|
92
|
+
return @params if !block && (opts.nil? || opts.empty?) && type == Attributor::Struct
|
|
105
93
|
|
|
106
|
-
unless
|
|
94
|
+
unless opts.key? :required
|
|
107
95
|
opts[:required] = true # Make the payload required by default
|
|
108
96
|
end
|
|
109
97
|
|
|
110
98
|
if @params
|
|
111
|
-
unless type == Attributor::Struct && @params.type < Attributor::Struct
|
|
112
|
-
|
|
113
|
-
"Invalid type received for extending params: #{type.name}"
|
|
114
|
-
)
|
|
115
|
-
end
|
|
99
|
+
raise Exceptions::InvalidConfiguration, "Invalid type received for extending params: #{type.name}" unless type == Attributor::Struct && @params.type < Attributor::Struct
|
|
100
|
+
|
|
116
101
|
update_attribute(@params, opts, block)
|
|
117
102
|
else
|
|
118
103
|
@params = create_attribute(type, **opts, &block)
|
|
@@ -121,66 +106,59 @@ module Praxis
|
|
|
121
106
|
@params
|
|
122
107
|
end
|
|
123
108
|
|
|
124
|
-
def payload(type=Attributor::Struct, **opts, &block)
|
|
125
|
-
return @payload if !block && (
|
|
109
|
+
def payload(type = Attributor::Struct, **opts, &block)
|
|
110
|
+
return @payload if !block && (opts.nil? || opts.empty?) && type == Attributor::Struct
|
|
126
111
|
|
|
127
|
-
unless
|
|
128
|
-
opts
|
|
112
|
+
unless opts.key?(:required)
|
|
113
|
+
opts = { required: true, null: false }.merge(opts) # Make the payload required and non-nullable by default
|
|
129
114
|
end
|
|
130
115
|
|
|
131
116
|
if @payload
|
|
132
|
-
unless type == Attributor::Struct && @payload.type < Attributor::Struct
|
|
133
|
-
|
|
134
|
-
"Invalid type received for extending params: #{type.name}"
|
|
135
|
-
)
|
|
136
|
-
end
|
|
117
|
+
raise Exceptions::InvalidConfiguration, "Invalid type received for extending params: #{type.name}" unless type == Attributor::Struct && @payload.type < Attributor::Struct
|
|
118
|
+
|
|
137
119
|
update_attribute(@payload, opts, block)
|
|
138
120
|
else
|
|
139
121
|
@payload = create_attribute(type, **opts, &block)
|
|
140
122
|
end
|
|
141
123
|
end
|
|
142
124
|
|
|
143
|
-
def headers(type=nil, **opts, &block)
|
|
125
|
+
def headers(type = nil, **opts, &block)
|
|
144
126
|
return @headers unless block
|
|
145
127
|
|
|
146
|
-
unless
|
|
128
|
+
unless opts.key? :required
|
|
147
129
|
opts[:required] = true # Make the payload required by default
|
|
148
130
|
end
|
|
149
131
|
|
|
150
132
|
if @headers
|
|
151
133
|
update_attribute(@headers, opts, block)
|
|
152
134
|
else
|
|
153
|
-
type
|
|
135
|
+
type ||= Attributor::Hash.of(key: String)
|
|
154
136
|
@headers = create_attribute(type,
|
|
155
137
|
dsl_compiler: HeadersDSLCompiler, case_insensitive_load: true,
|
|
156
138
|
**opts, &block)
|
|
157
139
|
|
|
158
140
|
@headers
|
|
159
141
|
end
|
|
160
|
-
@precomputed_header_keys_for_rack = nil #clear memoized data
|
|
142
|
+
@precomputed_header_keys_for_rack = nil # clear memoized data
|
|
161
143
|
end
|
|
162
144
|
|
|
163
145
|
# Good optimization to avoid creating lots of strings and comparisons
|
|
164
146
|
# on a per-request basis.
|
|
165
147
|
# However, this is hacky, as it is rack-specific, and does not really belong here
|
|
166
148
|
def precomputed_header_keys_for_rack
|
|
167
|
-
@precomputed_header_keys_for_rack ||=
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
hash[name] = key
|
|
172
|
-
end
|
|
149
|
+
@precomputed_header_keys_for_rack ||= @headers.attributes.keys.each_with_object({}) do |key, hash|
|
|
150
|
+
name = key.to_s
|
|
151
|
+
name = "HTTP_#{name.gsub('-', '_').upcase}" unless %w[CONTENT_TYPE CONTENT_LENGTH].include?(name)
|
|
152
|
+
hash[name] = key
|
|
173
153
|
end
|
|
174
154
|
end
|
|
175
155
|
|
|
176
|
-
|
|
177
156
|
def routing(&block)
|
|
178
|
-
@routing_config.instance_eval
|
|
157
|
+
@routing_config.instance_eval(&block)
|
|
179
158
|
|
|
180
159
|
@route = @routing_config.route
|
|
181
160
|
end
|
|
182
161
|
|
|
183
|
-
|
|
184
162
|
def description(text = nil)
|
|
185
163
|
@description = text if text
|
|
186
164
|
@description
|
|
@@ -194,7 +172,7 @@ module Praxis
|
|
|
194
172
|
|
|
195
173
|
query_string = URI.encode_www_form(hash[:query_params])
|
|
196
174
|
url = hash[:url]
|
|
197
|
-
url = [url,query_string].join('?') unless query_string.empty?
|
|
175
|
+
url = [url, query_string].join('?') unless query_string.empty?
|
|
198
176
|
|
|
199
177
|
route_description[:example] = url
|
|
200
178
|
route_description
|
|
@@ -219,24 +197,23 @@ module Praxis
|
|
|
219
197
|
hash[:payload] = payload_description(example: payload_example)
|
|
220
198
|
end
|
|
221
199
|
|
|
222
|
-
hash[:responses] = responses.
|
|
200
|
+
hash[:responses] = responses.each_with_object({}) do |(_response_name, response), memo|
|
|
223
201
|
memo[response.name] = response.describe(context: context)
|
|
224
|
-
memo
|
|
225
202
|
end
|
|
226
203
|
hash[:traits] = traits if traits.any?
|
|
227
204
|
# FIXME: change to :routes along with api browser
|
|
228
205
|
# FIXME: change urls to url ... (along with the browser)
|
|
229
|
-
hash[:urls] = [
|
|
206
|
+
hash[:urls] = [ActionDefinition.url_description(route: route, params: params, params_example: params_example)]
|
|
230
207
|
self.class.doc_decorations.each do |callback|
|
|
231
208
|
callback.call(self, hash)
|
|
232
209
|
end
|
|
233
210
|
end
|
|
234
211
|
end
|
|
235
212
|
|
|
236
|
-
def headers_description(example:
|
|
213
|
+
def headers_description(example:)
|
|
237
214
|
output = headers.describe(example: example)
|
|
238
|
-
required_headers =
|
|
239
|
-
output[:example] = required_headers.each_with_object({}) do |
|
|
215
|
+
required_headers = headers.attributes.select { |_k, attr| attr.options && attr.options[:required] == true }
|
|
216
|
+
output[:example] = required_headers.each_with_object({}) do |(name, _attr), hash|
|
|
240
217
|
hash[name] = example[name].to_s # Some simple types (like Boolean) can be used as header values, but must convert back to s
|
|
241
218
|
end
|
|
242
219
|
output
|
|
@@ -247,23 +224,23 @@ module Praxis
|
|
|
247
224
|
if route.nil?
|
|
248
225
|
warn "Warning: No route defined for #{endpoint_definition.name}##{name}."
|
|
249
226
|
else
|
|
250
|
-
route_params = route.path
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
227
|
+
route_params = route.path
|
|
228
|
+
.named_captures
|
|
229
|
+
.keys
|
|
230
|
+
.collect(&:to_sym)
|
|
254
231
|
end
|
|
255
232
|
|
|
256
233
|
desc = params.describe(example: example)
|
|
257
|
-
desc[:type][:attributes].
|
|
234
|
+
desc[:type][:attributes].each_key do |k|
|
|
258
235
|
source = if route_params.include? k
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
236
|
+
'url'
|
|
237
|
+
else
|
|
238
|
+
'query'
|
|
239
|
+
end
|
|
263
240
|
desc[:type][:attributes][k][:source] = source
|
|
264
241
|
end
|
|
265
|
-
required_params = desc[:type][:attributes].select{|
|
|
266
|
-
phash = required_params.each_with_object({}) do |
|
|
242
|
+
required_params = desc[:type][:attributes].select { |_k, v| v[:source] == 'query' && v[:required] == true }.keys
|
|
243
|
+
phash = required_params.each_with_object({}) do |name, hash|
|
|
267
244
|
hash[name] = example[name]
|
|
268
245
|
end
|
|
269
246
|
desc[:example] = URI.encode_www_form(phash)
|
|
@@ -277,12 +254,10 @@ module Praxis
|
|
|
277
254
|
# of the headers.
|
|
278
255
|
def derive_content_type(example, handler_name)
|
|
279
256
|
# MultipartArrays *must* use the provided content_type
|
|
280
|
-
if example.
|
|
281
|
-
return MediaTypeIdentifier.load(example.content_type)
|
|
282
|
-
end
|
|
257
|
+
return MediaTypeIdentifier.load(example.content_type) if example.is_a? Praxis::Types::MultipartArray
|
|
283
258
|
|
|
284
|
-
|
|
285
|
-
if content_type_attribute
|
|
259
|
+
_, content_type_attribute = headers&.attributes&.find { |k, _v| k.to_s =~ /^content[-_]{1}type$/i }
|
|
260
|
+
if content_type_attribute&.options&.key?(:values)
|
|
286
261
|
|
|
287
262
|
# if any defined value match the preferred handler_name, return it
|
|
288
263
|
content_type_attribute.options[:values].each do |ct|
|
|
@@ -295,18 +270,15 @@ module Praxis
|
|
|
295
270
|
|
|
296
271
|
# and return that one if it already corresponds to a registered handler
|
|
297
272
|
# otherwise, add the encoding
|
|
298
|
-
if Praxis::Application.instance.handlers.include?(pick.handler_name)
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
return pick + handler_name
|
|
302
|
-
end
|
|
273
|
+
return pick if Praxis::Application.instance.handlers.include?(pick.handler_name)
|
|
274
|
+
|
|
275
|
+
return pick + handler_name
|
|
303
276
|
end
|
|
304
277
|
|
|
305
278
|
# generic default encoding
|
|
306
279
|
MediaTypeIdentifier.load("application/#{handler_name}")
|
|
307
280
|
end
|
|
308
281
|
|
|
309
|
-
|
|
310
282
|
def payload_description(example:)
|
|
311
283
|
hash = payload.describe(example: example)
|
|
312
284
|
|
|
@@ -322,11 +294,10 @@ module Praxis
|
|
|
322
294
|
|
|
323
295
|
# in case handler is nil, use dumped_payload as-is.
|
|
324
296
|
generated_payload = if handler.nil?
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
297
|
+
dumped_payload
|
|
298
|
+
else
|
|
299
|
+
handler.generate(dumped_payload)
|
|
300
|
+
end
|
|
330
301
|
|
|
331
302
|
hash[:examples][default_handler] = {
|
|
332
303
|
content_type: content_type.to_s,
|
|
@@ -337,14 +308,13 @@ module Praxis
|
|
|
337
308
|
hash
|
|
338
309
|
end
|
|
339
310
|
|
|
340
|
-
|
|
341
311
|
def nodoc!
|
|
342
312
|
metadata[:doc_visibility] = :none
|
|
343
313
|
end
|
|
344
314
|
|
|
345
|
-
# [DEPRECATED] - Warn of the change of method name for the transition
|
|
315
|
+
# [DEPRECATED] - Warn of the change of method name for the transition
|
|
346
316
|
def resource_definition
|
|
347
|
-
raise
|
|
317
|
+
raise 'Praxis::ActionDefinition does not use `resource_definition` any longer. Use `endpoint_definition` instead.'
|
|
348
318
|
end
|
|
349
319
|
end
|
|
350
320
|
end
|
|
@@ -1,30 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'singleton'
|
|
2
4
|
require 'forwardable'
|
|
3
5
|
|
|
4
6
|
module Praxis
|
|
5
|
-
|
|
6
7
|
class ApiDefinition
|
|
7
8
|
include Singleton
|
|
8
9
|
extend Forwardable
|
|
9
10
|
|
|
10
|
-
attr_reader :traits
|
|
11
|
-
attr_reader :responses
|
|
12
|
-
attr_reader :infos
|
|
13
|
-
attr_reader :global_info
|
|
11
|
+
attr_reader :traits, :responses, :infos, :global_info
|
|
14
12
|
|
|
15
13
|
attr_accessor :versioning_scheme
|
|
16
14
|
|
|
17
15
|
def self.define(&block)
|
|
18
|
-
if block.arity
|
|
19
|
-
|
|
16
|
+
if block.arity.zero?
|
|
17
|
+
instance.instance_eval(&block)
|
|
20
18
|
else
|
|
21
|
-
yield(
|
|
19
|
+
yield(instance)
|
|
22
20
|
end
|
|
23
21
|
end
|
|
24
22
|
|
|
25
23
|
def initialize
|
|
26
|
-
@responses =
|
|
27
|
-
@traits =
|
|
24
|
+
@responses = {}
|
|
25
|
+
@traits = {}
|
|
28
26
|
@base_path = ''
|
|
29
27
|
|
|
30
28
|
@global_info = ApiGeneralInfo.new
|
|
@@ -39,20 +37,19 @@ module Praxis
|
|
|
39
37
|
end
|
|
40
38
|
|
|
41
39
|
def response(name)
|
|
42
|
-
|
|
40
|
+
@responses.fetch(name) do
|
|
43
41
|
raise ArgumentError, "no response template defined with name #{name.inspect}. Are you forgetting to register it with ApiDefinition?"
|
|
44
42
|
end
|
|
45
43
|
end
|
|
46
44
|
|
|
47
45
|
def trait(name, &block)
|
|
48
|
-
if
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
self.traits[name] = Trait.new(&block)
|
|
46
|
+
raise Exceptions::InvalidTrait, "Overwriting a previous trait with the same name (#{name})" if traits.key? name
|
|
47
|
+
|
|
48
|
+
traits[name] = Trait.new(&block)
|
|
52
49
|
end
|
|
53
50
|
|
|
54
51
|
# Setting info to the nil version, means setting it for all versions (if they don't override them)
|
|
55
|
-
def info(version=nil, &block)
|
|
52
|
+
def info(version = nil, &block)
|
|
56
53
|
if version.nil?
|
|
57
54
|
if block_given?
|
|
58
55
|
@global_info.instance_eval(&block)
|
|
@@ -61,26 +58,23 @@ module Praxis
|
|
|
61
58
|
end
|
|
62
59
|
else
|
|
63
60
|
i = @infos[version]
|
|
64
|
-
if block_given?
|
|
65
|
-
i.instance_eval(&block)
|
|
66
|
-
end
|
|
61
|
+
i.instance_eval(&block) if block_given?
|
|
67
62
|
i
|
|
68
63
|
end
|
|
69
64
|
end
|
|
70
65
|
|
|
71
66
|
def describe
|
|
72
67
|
data = Hash.new do |hash, version|
|
|
73
|
-
hash[version] =
|
|
68
|
+
hash[version] = {}
|
|
74
69
|
end
|
|
75
70
|
|
|
76
71
|
data[:global][:info] = @global_info.describe
|
|
77
72
|
|
|
78
73
|
# Fill in the "info" portion
|
|
79
|
-
@infos.each do |version,info|
|
|
74
|
+
@infos.each do |version, info|
|
|
80
75
|
data[version][:info] = info.describe
|
|
81
76
|
end
|
|
82
77
|
|
|
83
|
-
|
|
84
78
|
if traits.any?
|
|
85
79
|
data[:traits] = {}
|
|
86
80
|
traits.each do |name, trait|
|
|
@@ -92,12 +86,12 @@ module Praxis
|
|
|
92
86
|
end
|
|
93
87
|
|
|
94
88
|
define do |api|
|
|
95
|
-
api.response_template :ok do |media_type
|
|
89
|
+
api.response_template :ok do |media_type:, location: nil, headers: nil, description: nil|
|
|
96
90
|
status 200
|
|
97
|
-
description(
|
|
91
|
+
description(description || 'Standard response for successful HTTP requests.')
|
|
98
92
|
|
|
99
93
|
media_type media_type
|
|
100
|
-
location
|
|
94
|
+
location
|
|
101
95
|
headers&.each do |(name, value)|
|
|
102
96
|
header(name: name, value: value)
|
|
103
97
|
end
|
|
@@ -105,16 +99,14 @@ module Praxis
|
|
|
105
99
|
|
|
106
100
|
api.response_template :created do |media_type: nil, location: nil, headers: nil, description: nil|
|
|
107
101
|
status 201
|
|
108
|
-
description(
|
|
102
|
+
description(description || 'The request has been fulfilled and resulted in a new resource being created.')
|
|
109
103
|
|
|
110
104
|
media_type media_type if media_type
|
|
111
|
-
location
|
|
105
|
+
location
|
|
112
106
|
headers&.each do |(name, value)|
|
|
113
107
|
header(name: name, value: value)
|
|
114
108
|
end
|
|
115
109
|
end
|
|
116
110
|
end
|
|
117
|
-
|
|
118
111
|
end
|
|
119
|
-
|
|
120
112
|
end
|