praxis 2.0.pre.18 → 2.0.pre.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +54 -0
- data/.simplecov +3 -1
- data/.travis.yml +2 -1
- data/CHANGELOG.md +6 -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 +64 -94
- 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 +64 -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 +10 -13
- 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 +6 -3
- 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 +16 -16
- data/lib/praxis/docs/open_api/server_object.rb +5 -2
- data/lib/praxis/docs/open_api/tag_object.rb +6 -3
- data/lib/praxis/docs/open_api_generator.rb +92 -95
- 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 +171 -180
- 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 +46 -47
- 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 -16
- data/lib/praxis/request_stages/validate_payload.rb +25 -27
- data/lib/praxis/request_superclassing.rb +3 -3
- data/lib/praxis/resource_definition.rb +1 -0
- data/lib/praxis/response.rb +13 -25
- 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 +15 -15
- 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 +88 -112
- 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 +40 -52
- data/spec/praxis/action_definition_spec.rb +36 -46
- 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 +27 -30
- data/spec/praxis/mapper/selector_generator_spec.rb +50 -50
- 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 +28 -39
- 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 -18
- 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 +5 -5
- 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 +9 -15
- 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 +2 -1
- 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 +20 -18
- 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
112
|
unless opts.key?(:required)
|
128
|
-
opts = {required: true, null: false}.merge(opts) # Make the payload required and non-nullable by default
|
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
|