praxis 0.22.pre.2 → 2.0.pre.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +5 -20
- data/CHANGELOG.md +333 -324
- data/lib/praxis.rb +14 -9
- data/lib/praxis/action_definition.rb +8 -10
- data/lib/praxis/action_definition/headers_dsl_compiler.rb +1 -1
- data/lib/praxis/api_definition.rb +27 -44
- data/lib/praxis/api_general_info.rb +23 -3
- data/lib/praxis/application.rb +15 -142
- data/lib/praxis/bootloader.rb +1 -2
- data/lib/praxis/bootloader_stages/environment.rb +13 -0
- data/lib/praxis/config.rb +1 -1
- data/lib/praxis/controller.rb +0 -2
- data/lib/praxis/dispatcher.rb +4 -6
- data/lib/praxis/docs/generator.rb +19 -24
- data/lib/praxis/docs/link_builder.rb +1 -1
- data/lib/praxis/docs/open_api_generator.rb +255 -0
- data/lib/praxis/docs/openapi/info_object.rb +31 -0
- data/lib/praxis/docs/openapi/media_type_object.rb +59 -0
- data/lib/praxis/docs/openapi/operation_object.rb +40 -0
- data/lib/praxis/docs/openapi/parameter_object.rb +69 -0
- data/lib/praxis/docs/openapi/paths_object.rb +58 -0
- data/lib/praxis/docs/openapi/request_body_object.rb +51 -0
- data/lib/praxis/docs/openapi/response_object.rb +63 -0
- data/lib/praxis/docs/openapi/responses_object.rb +44 -0
- data/lib/praxis/docs/openapi/schema_object.rb +87 -0
- data/lib/praxis/docs/openapi/server_object.rb +24 -0
- data/lib/praxis/docs/openapi/tag_object.rb +21 -0
- data/lib/praxis/error_handler.rb +5 -5
- data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +1 -1
- data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +4 -0
- data/lib/praxis/extensions/attribute_filtering/sequel_filter_query_builder.rb +125 -0
- data/lib/praxis/extensions/field_selection.rb +1 -12
- data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +28 -34
- data/lib/praxis/extensions/field_selection/field_selector.rb +4 -0
- data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +35 -39
- data/lib/praxis/extensions/rendering.rb +1 -1
- data/lib/praxis/file_group.rb +1 -1
- data/lib/praxis/handlers/xml.rb +1 -1
- data/lib/praxis/links.rb +4 -0
- data/lib/praxis/mapper/active_model_compat.rb +98 -0
- data/lib/praxis/mapper/resource.rb +242 -0
- data/lib/praxis/mapper/selector_generator.rb +150 -0
- data/lib/praxis/mapper/sequel_compat.rb +76 -0
- data/lib/praxis/media_type_identifier.rb +2 -1
- data/lib/praxis/middleware_app.rb +13 -15
- data/lib/praxis/multipart/part.rb +8 -7
- data/lib/praxis/notifications.rb +1 -1
- data/lib/praxis/plugins/mapper_plugin.rb +64 -0
- data/lib/praxis/request.rb +14 -7
- data/lib/praxis/request_stages/response.rb +2 -3
- data/lib/praxis/resource_definition.rb +15 -19
- data/lib/praxis/response.rb +6 -5
- data/lib/praxis/response_definition.rb +6 -8
- data/lib/praxis/response_template.rb +3 -4
- data/lib/praxis/responses/http.rb +36 -0
- data/lib/praxis/responses/internal_server_error.rb +12 -3
- data/lib/praxis/responses/multipart_ok.rb +11 -4
- data/lib/praxis/responses/validation_error.rb +10 -1
- data/lib/praxis/route.rb +1 -1
- data/lib/praxis/router.rb +3 -3
- data/lib/praxis/routing_config.rb +1 -1
- data/lib/praxis/tasks/api_docs.rb +24 -9
- data/lib/praxis/tasks/routes.rb +0 -1
- data/lib/praxis/trait.rb +1 -1
- data/lib/praxis/types/media_type_common.rb +12 -2
- data/lib/praxis/types/multipart.rb +1 -1
- data/lib/praxis/types/multipart_array.rb +64 -2
- data/lib/praxis/types/multipart_array/part_definition.rb +1 -1
- data/lib/praxis/version.rb +1 -1
- data/praxis.gemspec +11 -9
- data/spec/functional_spec.rb +0 -1
- data/spec/praxis/action_definition_spec.rb +16 -27
- data/spec/praxis/api_definition_spec.rb +8 -13
- data/spec/praxis/api_general_info_spec.rb +8 -3
- data/spec/praxis/application_spec.rb +8 -14
- data/spec/praxis/collection_spec.rb +3 -2
- data/spec/praxis/config_spec.rb +2 -2
- data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +106 -0
- data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +147 -0
- data/spec/praxis/extensions/field_selection/support/spec_resources_active_model.rb +130 -0
- data/spec/praxis/extensions/field_selection/support/spec_resources_sequel.rb +106 -0
- data/spec/praxis/handlers/xml_spec.rb +2 -2
- data/spec/praxis/mapper/resource_spec.rb +169 -0
- data/spec/praxis/mapper/selector_generator_spec.rb +325 -0
- data/spec/praxis/media_type_spec.rb +0 -10
- data/spec/praxis/middleware_app_spec.rb +16 -10
- data/spec/praxis/request_spec.rb +7 -17
- data/spec/praxis/request_stages/action_spec.rb +8 -1
- data/spec/praxis/request_stages/validate_spec.rb +1 -1
- data/spec/praxis/resource_definition_spec.rb +10 -12
- data/spec/praxis/response_definition_spec.rb +19 -30
- data/spec/praxis/response_spec.rb +6 -13
- data/spec/praxis/responses/internal_server_error_spec.rb +5 -2
- data/spec/praxis/router_spec.rb +5 -9
- data/spec/spec_app/app/controllers/instances.rb +1 -1
- data/spec/spec_app/config.ru +6 -1
- data/spec/spec_app/config/environment.rb +3 -21
- data/spec/spec_app/design/api.rb +6 -0
- data/spec/spec_helper.rb +13 -17
- data/spec/support/be_deep_equal_matcher.rb +39 -0
- data/spec/support/spec_resources.rb +124 -0
- metadata +86 -53
- data/lib/praxis/extensions/attribute_filtering.rb +0 -28
- data/lib/praxis/extensions/attribute_filtering/query_builder.rb +0 -39
- data/lib/praxis/extensions/mapper_selectors.rb +0 -16
- data/lib/praxis/media_type_collection.rb +0 -127
- data/lib/praxis/plugins/praxis_mapper_plugin.rb +0 -246
- data/spec/praxis/media_type_collection_spec.rb +0 -157
- data/spec/praxis/plugins/praxis_mapper_plugin_spec.rb +0 -142
- data/spec/spec_app/app/models/person.rb +0 -3
data/lib/praxis/response.rb
CHANGED
@@ -63,23 +63,24 @@ module Praxis
|
|
63
63
|
self.class.response_name
|
64
64
|
end
|
65
65
|
|
66
|
-
def format!
|
66
|
+
def format!
|
67
67
|
end
|
68
68
|
|
69
|
-
def encode!
|
69
|
+
def encode!
|
70
70
|
case @body
|
71
71
|
when Hash, Array
|
72
72
|
# response payload is structured data; transform it into an entity using the handler
|
73
73
|
# implied by the response's media type. If no handler is registered for this
|
74
74
|
# name, assume JSON as a default handler.
|
75
|
+
handlers = Praxis::Application.instance.handlers
|
75
76
|
handler = (content_type && handlers[content_type.handler_name]) || handlers['json']
|
76
77
|
@body = handler.generate(@body)
|
77
78
|
end
|
78
79
|
end
|
79
80
|
|
80
|
-
def finish
|
81
|
-
format!
|
82
|
-
encode!
|
81
|
+
def finish
|
82
|
+
format!
|
83
|
+
encode!
|
83
84
|
|
84
85
|
@body = Array(@body)
|
85
86
|
|
@@ -4,9 +4,8 @@ module Praxis
|
|
4
4
|
|
5
5
|
class ResponseDefinition
|
6
6
|
attr_reader :name
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(response_name, application, **spec, &block)
|
7
|
+
|
8
|
+
def initialize(response_name, **spec, &block)
|
10
9
|
unless response_name
|
11
10
|
raise Exceptions::InvalidConfiguration.new(
|
12
11
|
"Response name is required for a response specification"
|
@@ -14,7 +13,6 @@ module Praxis
|
|
14
13
|
end
|
15
14
|
@spec = { headers:{} }
|
16
15
|
@name = response_name
|
17
|
-
@application = application
|
18
16
|
self.instance_exec(**spec, &block) if block_given?
|
19
17
|
|
20
18
|
if self.status.nil?
|
@@ -143,9 +141,9 @@ module Praxis
|
|
143
141
|
# FIXME: remove load when when MediaTypeCommon.identifier returns a MediaTypeIdentifier
|
144
142
|
identifier = MediaTypeIdentifier.load(self.media_type.identifier)
|
145
143
|
|
146
|
-
default_handlers =
|
144
|
+
default_handlers = ApiDefinition.instance.info.produces
|
147
145
|
|
148
|
-
handlers =
|
146
|
+
handlers = Praxis::Application.instance.handlers.select do |k,v|
|
149
147
|
default_handlers.include?(k)
|
150
148
|
end
|
151
149
|
|
@@ -165,7 +163,7 @@ module Praxis
|
|
165
163
|
end
|
166
164
|
end
|
167
165
|
|
168
|
-
content[:payload] = payload
|
166
|
+
content[:payload] = {type: payload}
|
169
167
|
end
|
170
168
|
|
171
169
|
unless parts == nil
|
@@ -202,7 +200,7 @@ module Praxis
|
|
202
200
|
raise ArgumentError, "Parts definition for response #{name} does not allow :like and a block simultaneously"
|
203
201
|
end
|
204
202
|
if like
|
205
|
-
template =
|
203
|
+
template = ApiDefinition.instance.response(like)
|
206
204
|
@parts = template.compile(nil, **args)
|
207
205
|
else # block
|
208
206
|
@parts = Praxis::ResponseDefinition.new('anonymous', **args, &a_proc)
|
@@ -1,12 +1,11 @@
|
|
1
1
|
module Praxis
|
2
2
|
|
3
3
|
class ResponseTemplate
|
4
|
-
attr_reader :name, :block
|
4
|
+
attr_reader :name, :block
|
5
5
|
|
6
|
-
def initialize(response_name,
|
6
|
+
def initialize(response_name, &block)
|
7
7
|
@name = response_name
|
8
8
|
@block = block
|
9
|
-
@application = application
|
10
9
|
end
|
11
10
|
|
12
11
|
def compile(action=nil, **args)
|
@@ -24,7 +23,7 @@ module Praxis
|
|
24
23
|
args[:media_type] = media_type
|
25
24
|
end
|
26
25
|
end
|
27
|
-
Praxis::ResponseDefinition.new(name,
|
26
|
+
Praxis::ResponseDefinition.new(name, **args, &block)
|
28
27
|
end
|
29
28
|
|
30
29
|
def describe
|
@@ -131,5 +131,41 @@ module Praxis
|
|
131
131
|
self.status = 422
|
132
132
|
end
|
133
133
|
|
134
|
+
ApiDefinition.define do |api|
|
135
|
+
|
136
|
+
|
137
|
+
[
|
138
|
+
[ :accepted, 202, "The request has been accepted for processing, but the processing has not been completed." ],
|
139
|
+
[ :no_content, 204,"The server successfully processed the request, but is not returning any content."],
|
140
|
+
[ :multiple_choices, 300,"Indicates multiple options for the resource that the client may follow."],
|
141
|
+
[ :moved_permanently, 301,"This and all future requests should be directed to the given URI."],
|
142
|
+
[ :found, 302,"The requested resource resides temporarily under a different URI."],
|
143
|
+
[ :see_other, 303,"The response to the request can be found under another URI using a GET method"],
|
144
|
+
[ :not_modified, 304,"Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-Match."],
|
145
|
+
[ :temporary_redirect, 307,"In this case, the request should be repeated with another URI; however, future requests should still use the original URI."],
|
146
|
+
[ :bad_request, 400,"The request cannot be fulfilled due to bad syntax."],
|
147
|
+
[ :unauthorized, 401,"Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided."],
|
148
|
+
[ :forbidden, 403,"The request was a valid request, but the server is refusing to respond to it."],
|
149
|
+
[ :not_found, 404,"The requested resource could not be found but may be available again in the future."],
|
150
|
+
[ :method_not_allowed, 405,"A request was made of a resource using a request method not supported by that resource."],
|
151
|
+
[ :not_acceptable, 406,"The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request."],
|
152
|
+
[ :request_timeout, 408,"The server timed out waiting for the request."],
|
153
|
+
[ :conflict, 409, "Indicates that the request could not be processed because of conflict in the request, such as an edit conflict in the case of multiple updates."],
|
154
|
+
[ :precondition_failed, 412,"The server does not meet one of the preconditions that the requester put on the request."],
|
155
|
+
[ :unprocessable_entity, 422,"The request was well-formed but was unable to be followed due to semantic errors."],
|
156
|
+
].each do |name, code, base_description|
|
157
|
+
api.response_template name do |media_type: nil, location: nil, headers: nil, description: nil|
|
158
|
+
status code
|
159
|
+
description( description || base_description ) # description can "potentially" be overriden in an individual action.
|
160
|
+
|
161
|
+
media_type media_type if media_type
|
162
|
+
location location if location
|
163
|
+
headers headers if headers
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
|
134
170
|
end
|
135
171
|
end
|
@@ -13,15 +13,16 @@ module Praxis
|
|
13
13
|
@error = error
|
14
14
|
end
|
15
15
|
|
16
|
-
def format!(exception = @error
|
16
|
+
def format!(exception = @error)
|
17
17
|
if @error
|
18
|
-
|
18
|
+
|
19
|
+
if Application.instance.config.praxis.show_exceptions == true
|
19
20
|
msg = {
|
20
21
|
name: exception.class.name,
|
21
22
|
message: exception.message,
|
22
23
|
backtrace: exception.backtrace
|
23
24
|
}
|
24
|
-
msg[:cause] = format!(exception.cause
|
25
|
+
msg[:cause] = format!(exception.cause) if exception.cause
|
25
26
|
else
|
26
27
|
msg = {name: 'InternalServerError', message: "Something bad happened."}
|
27
28
|
end
|
@@ -33,4 +34,12 @@ module Praxis
|
|
33
34
|
|
34
35
|
end
|
35
36
|
|
37
|
+
ApiDefinition.define do |api|
|
38
|
+
api.response_template :internal_server_error do
|
39
|
+
description "A generic error message, given when an unexpected condition was encountered and no more specific message is suitable."
|
40
|
+
status 500
|
41
|
+
media_type "application/json"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
36
45
|
end
|
@@ -19,7 +19,7 @@ module Praxis
|
|
19
19
|
end
|
20
20
|
|
21
21
|
|
22
|
-
def encode!
|
22
|
+
def encode!
|
23
23
|
case @body
|
24
24
|
when Praxis::Types::MultipartArray
|
25
25
|
@body = @body.dump
|
@@ -28,9 +28,9 @@ module Praxis
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
def finish
|
32
|
-
format!
|
33
|
-
encode!
|
31
|
+
def finish
|
32
|
+
format!
|
33
|
+
encode!
|
34
34
|
|
35
35
|
@body = Array(@body)
|
36
36
|
|
@@ -41,4 +41,11 @@ module Praxis
|
|
41
41
|
|
42
42
|
end
|
43
43
|
|
44
|
+
ApiDefinition.define do |api|
|
45
|
+
api.response_template :multipart_ok do |media_type: Praxis::Types::MultipartArray|
|
46
|
+
status 200
|
47
|
+
media_type media_type
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
44
51
|
end
|
@@ -15,7 +15,7 @@ module Praxis
|
|
15
15
|
@documentation = documentation
|
16
16
|
end
|
17
17
|
|
18
|
-
def format!
|
18
|
+
def format!
|
19
19
|
@body = {name: 'ValidationError', summary: @summary }
|
20
20
|
@body[:errors] = @errors if @errors
|
21
21
|
|
@@ -31,4 +31,13 @@ module Praxis
|
|
31
31
|
|
32
32
|
end
|
33
33
|
|
34
|
+
|
35
|
+
ApiDefinition.define do |api|
|
36
|
+
api.response_template :validation_error do
|
37
|
+
description "An error message indicating that one or more elements of the request did not match the API specification for the action"
|
38
|
+
status 400
|
39
|
+
media_type "application/json"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
34
43
|
end
|
data/lib/praxis/route.rb
CHANGED
@@ -24,7 +24,7 @@ module Praxis
|
|
24
24
|
path_params = example_hash.select{|k,v| path_param_keys.include? k }
|
25
25
|
# Let's generate the example only using required params, to avoid mixing incompatible parameters
|
26
26
|
query_params = example_hash.select{|k,v| required_query_param_keys.include? k }
|
27
|
-
example = { verb: self.verb, url: self.path.expand(path_params), query_params: query_params }
|
27
|
+
example = { verb: self.verb, url: self.path.expand(path_params.transform_values(&:to_s)), query_params: query_params }
|
28
28
|
|
29
29
|
end
|
30
30
|
|
data/lib/praxis/router.rb
CHANGED
@@ -50,7 +50,7 @@ module Praxis
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def add_route(target, route)
|
53
|
-
path_versioning = (
|
53
|
+
path_versioning = (Application.instance.versioning_scheme == :path)
|
54
54
|
|
55
55
|
# DEPRECATED: remove with ResourceDefinition.version using: :path
|
56
56
|
path_versioning ||= (target.action.resource_definition.version_options[:using] == :path)
|
@@ -65,7 +65,7 @@ module Praxis
|
|
65
65
|
def call(env_or_request)
|
66
66
|
request = case env_or_request
|
67
67
|
when Hash
|
68
|
-
request_class.new(env_or_request
|
68
|
+
request_class.new(env_or_request)
|
69
69
|
when request_class
|
70
70
|
env_or_request
|
71
71
|
else
|
@@ -101,7 +101,7 @@ module Praxis
|
|
101
101
|
body += " Available versions = #{pretty_versions}."
|
102
102
|
end
|
103
103
|
headers = {"Content-Type" => "text/plain"}
|
104
|
-
if
|
104
|
+
if Praxis::Application.instance.config.praxis.x_cascade
|
105
105
|
headers['X-Cascade'] = 'pass'
|
106
106
|
end
|
107
107
|
result = [404, headers, [body]]
|
@@ -55,7 +55,7 @@ module Praxis
|
|
55
55
|
path = (base + path).gsub('//','/')
|
56
56
|
# Reject our own options
|
57
57
|
route_name = options.delete(:name);
|
58
|
-
pattern = Mustermann.new(path, {ignore_unknown_options: true}.merge( options ))
|
58
|
+
pattern = Mustermann.new(path, **{ignore_unknown_options: true}.merge( options ))
|
59
59
|
route = Route.new(verb, pattern, version, name: route_name, prefixed_path: prefixed_path, **options)
|
60
60
|
@routes << route
|
61
61
|
route
|
@@ -58,16 +58,31 @@ namespace :praxis do
|
|
58
58
|
require 'fileutils'
|
59
59
|
|
60
60
|
Praxis::Blueprint.caching_enabled = false
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
61
|
+
generator = Praxis::Docs::Generator.new(Dir.pwd)
|
62
|
+
generator.save!
|
63
|
+
end
|
64
|
+
|
65
|
+
desc "Generate OpenAPI 3 docs for a Praxis App"
|
66
|
+
task :openapi => [:environment] do |t, args|
|
67
|
+
require 'fileutils'
|
68
|
+
|
69
|
+
Praxis::Blueprint.caching_enabled = false
|
70
|
+
generator = Praxis::Docs::OpenApiGenerator.new(Dir.pwd)
|
71
|
+
generator.save!
|
72
|
+
end
|
73
|
+
|
74
|
+
desc "Preview (and Generate) OpenAPI 3 docs for a Praxis App"
|
75
|
+
task :openapipreview => [:openapi] do |t, args|
|
76
|
+
require 'webrick'
|
77
|
+
docs_port = 9090
|
78
|
+
root = Dir.pwd + '/docs/openapi/'
|
79
|
+
wb = Thread.new do
|
80
|
+
s = WEBrick::HTTPServer.new(:Port => docs_port, :DocumentRoot => root)
|
81
|
+
trap('INT') { s.shutdown }
|
82
|
+
s.start
|
70
83
|
end
|
84
|
+
`open http://localhost:#{docs_port}/`
|
85
|
+
wb.join
|
71
86
|
end
|
72
87
|
|
73
88
|
end
|
data/lib/praxis/tasks/routes.rb
CHANGED
data/lib/praxis/trait.rb
CHANGED
@@ -76,7 +76,7 @@ module Praxis
|
|
76
76
|
dsl_compiler: ActionDefinition::HeadersDSLCompiler,
|
77
77
|
case_insensitive_load: true
|
78
78
|
}
|
79
|
-
Attributor::Hash.of(key: String).construct(Proc.new {}, hash_opts)
|
79
|
+
Attributor::Hash.of(key: String).construct(Proc.new {}, **hash_opts)
|
80
80
|
else
|
81
81
|
Attributor::Hash.construct(Proc.new {})
|
82
82
|
end
|
@@ -14,6 +14,16 @@ module Praxis
|
|
14
14
|
hash
|
15
15
|
end
|
16
16
|
|
17
|
+
def as_json_schema(**args)
|
18
|
+
the_type = @attribute && @attribute.type || member_type
|
19
|
+
the_type.as_json_schema(args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def json_schema_type
|
23
|
+
the_type = @attribute && @attribute.type || member_type
|
24
|
+
the_type.json_schema_type
|
25
|
+
end
|
26
|
+
|
17
27
|
def description(text=nil)
|
18
28
|
@description = text if text
|
19
29
|
@description
|
@@ -32,8 +42,8 @@ module Praxis
|
|
32
42
|
#
|
33
43
|
# @return [String] the string-representation of this type's identifier
|
34
44
|
def identifier(identifier=nil)
|
35
|
-
return @identifier
|
36
|
-
|
45
|
+
return @identifier unless identifier
|
46
|
+
@identifier = MediaTypeIdentifier.load(identifier)
|
37
47
|
end
|
38
48
|
end
|
39
49
|
|
@@ -12,7 +12,7 @@ module Praxis
|
|
12
12
|
return value if value.kind_of?(self) || value.nil?
|
13
13
|
|
14
14
|
unless (value.kind_of?(::String) && ! content_type.nil?)
|
15
|
-
raise Attributor::CoercionError
|
15
|
+
raise Attributor::CoercionError.new(context: context, from: value.class, to: self.name, value: value)
|
16
16
|
end
|
17
17
|
|
18
18
|
headers = {'Content-Type' => content_type}
|
@@ -83,7 +83,7 @@ module Praxis
|
|
83
83
|
|
84
84
|
self.multiple << name if multiple
|
85
85
|
|
86
|
-
compiler = Attributor::DSLCompiler.new(self, opts)
|
86
|
+
compiler = Attributor::DSLCompiler.new(self, **opts)
|
87
87
|
|
88
88
|
if filename
|
89
89
|
filename_attribute = compiler.define('filename', String, required: true)
|
@@ -153,6 +153,68 @@ module Praxis
|
|
153
153
|
example
|
154
154
|
end
|
155
155
|
|
156
|
+
def self.json_schema_type
|
157
|
+
:object
|
158
|
+
end
|
159
|
+
|
160
|
+
# Multipart request bodies are special in OPEN API
|
161
|
+
# schema: # Request payload
|
162
|
+
# type: object
|
163
|
+
# properties: # Request parts
|
164
|
+
# id: # Part 1 (string value)
|
165
|
+
# type: string
|
166
|
+
# format: uuid
|
167
|
+
# address: # Part2 (object)
|
168
|
+
# type: object
|
169
|
+
# properties:
|
170
|
+
# street:
|
171
|
+
# type: string
|
172
|
+
# city:
|
173
|
+
# type: string
|
174
|
+
# profileImage: # Part 3 (an image)
|
175
|
+
# type: string
|
176
|
+
# format: binary
|
177
|
+
#
|
178
|
+
# NOTE: not sure if this
|
179
|
+
def self.as_openapi_request_body( attribute_options: {} )
|
180
|
+
hash = { type: json_schema_type }
|
181
|
+
opts = self.options.merge( attribute_options )
|
182
|
+
hash[:description] = opts[:description] if opts[:description]
|
183
|
+
hash[:default] = opts[:default] if opts[:default]
|
184
|
+
|
185
|
+
unless self.attributes.empty?
|
186
|
+
props = {}
|
187
|
+
encoding = {}
|
188
|
+
self.attributes.each do |part_name, part_attribute|
|
189
|
+
part_example = part_attribute.example
|
190
|
+
key_to_use = part_name.is_a?(Regexp) ? part_name.source : part_name
|
191
|
+
|
192
|
+
part_info = {}
|
193
|
+
if (payload_attribute = part_attribute.options[:payload_attribute])
|
194
|
+
props[key_to_use] = payload_attribute.as_json_schema(example: part_example.payload)
|
195
|
+
end
|
196
|
+
#{
|
197
|
+
# contentType: 'fff',
|
198
|
+
# headers: {
|
199
|
+
# custom1: 'safd'
|
200
|
+
# }
|
201
|
+
if (headers_attribute = part_attribute.options[:headers_attribute])
|
202
|
+
# Does this 'Content-Type' string check work?...can it be a symbol? what does it mean anyway?
|
203
|
+
encoding[key_to_use][:contentType] = headers_attribute['Content-Type'] if headers_attribute['Content-Type']
|
204
|
+
# TODO?rethink? ...is this correct?: att a 'headers' key with some header schemas if this part have some
|
205
|
+
encoding[key_to_use]['headers'] = headers_attribute.as_json_schema(example: part_example.headers)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
hash[:properties] = props
|
210
|
+
hash[:encoding] = encoding unless encoding.empty?
|
211
|
+
end
|
212
|
+
hash
|
213
|
+
end
|
214
|
+
|
215
|
+
def self.as_json_schema( shallow: false, example: nil, attribute_options: {} )
|
216
|
+
as_openapi_request_body(attribute_options: attribute_options)
|
217
|
+
end
|
156
218
|
|
157
219
|
def self.describe(shallow=true, example: nil)
|
158
220
|
type_name = Attributor.type_name(self)
|
@@ -218,7 +280,7 @@ module Praxis
|
|
218
280
|
attr_accessor :preamble
|
219
281
|
attr_reader :content_type
|
220
282
|
|
221
|
-
def initialize(content_type: self.class.identifier)
|
283
|
+
def initialize(content_type: self.class.identifier.to_s)
|
222
284
|
self.content_type = content_type
|
223
285
|
end
|
224
286
|
|