jsapi 0.6.2 → 0.7.0
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/lib/jsapi/controller/methods.rb +32 -32
- data/lib/jsapi/dsl/class_methods.rb +4 -1
- data/lib/jsapi/dsl/definitions.rb +4 -4
- data/lib/jsapi/meta/base/attributes.rb +21 -15
- data/lib/jsapi/meta/base/model.rb +5 -4
- data/lib/jsapi/meta/base/reference.rb +4 -5
- data/lib/jsapi/meta/base/type_caster.rb +2 -1
- data/lib/jsapi/meta/defaults.rb +2 -2
- data/lib/jsapi/meta/definitions.rb +162 -154
- data/lib/jsapi/meta/openapi/callback/model.rb +3 -5
- data/lib/jsapi/meta/openapi/extensions.rb +13 -2
- data/lib/jsapi/meta/openapi/header/model.rb +6 -3
- data/lib/jsapi/meta/openapi.rb +13 -0
- data/lib/jsapi/meta/operation.rb +4 -2
- data/lib/jsapi/meta/parameter/model.rb +3 -3
- data/lib/jsapi/meta/property.rb +2 -2
- data/lib/jsapi/meta/request_body/model.rb +2 -2
- data/lib/jsapi/meta/rescue_handler.rb +19 -19
- data/lib/jsapi/meta/response/model.rb +8 -4
- data/lib/jsapi/meta/schema/additional_properties.rb +1 -1
- data/lib/jsapi/meta/schema/array.rb +5 -3
- data/lib/jsapi/meta/schema/base.rb +4 -2
- data/lib/jsapi/meta/schema/numeric.rb +5 -3
- data/lib/jsapi/meta/schema/object.rb +4 -2
- data/lib/jsapi/meta/schema/string.rb +5 -3
- data/lib/jsapi/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba5cf0667b03c271ef93b725cbcdcc25d51b2afaf33efe43372c66880ae46996
|
4
|
+
data.tar.gz: 1826d5fbc22a972c8f38476a45eff4052930480d7b1a195255422de272d181a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63ca2ff516d31035884694dfb5a1969b1a697800f9afe694d30e6d250ca181bca0ae71d096d3e566d7de4a50f326233e9e5c9c3f199f38171d65bd27e36e6dec
|
7
|
+
data.tar.gz: f1b25727248b4bad12b2d57d665f86ee2f75fe826de22cd1d86b4e435152e8c0d2cb07152666b23a61bfb14d3810d516f3eeee53369f6f837559efc29156e996
|
@@ -35,7 +35,7 @@ module Jsapi
|
|
35
35
|
#
|
36
36
|
# Raises an InvalidArgumentError when the value of +:omit+ is invalid.
|
37
37
|
def api_operation(operation_name = nil, omit: nil, status: nil, strong: false, &block)
|
38
|
-
|
38
|
+
_api_operation(
|
39
39
|
operation_name,
|
40
40
|
bang: false,
|
41
41
|
omit: omit,
|
@@ -53,7 +53,7 @@ module Jsapi
|
|
53
53
|
# end
|
54
54
|
#
|
55
55
|
def api_operation!(operation_name = nil, omit: nil, status: nil, strong: false, &block)
|
56
|
-
|
56
|
+
_api_operation(
|
57
57
|
operation_name,
|
58
58
|
bang: true,
|
59
59
|
omit: omit,
|
@@ -78,7 +78,7 @@ module Jsapi
|
|
78
78
|
def api_params(operation_name = nil, strong: false)
|
79
79
|
definitions = api_definitions
|
80
80
|
_api_params(
|
81
|
-
|
81
|
+
_find_api_operation(operation_name, definitions),
|
82
82
|
definitions,
|
83
83
|
strong: strong
|
84
84
|
)
|
@@ -100,7 +100,7 @@ module Jsapi
|
|
100
100
|
# Raises an InvalidArgumentError when the value of +:omit+ is invalid.
|
101
101
|
def api_response(result, operation_name = nil, omit: nil, status: nil)
|
102
102
|
definitions = api_definitions
|
103
|
-
operation =
|
103
|
+
operation = _find_api_operation(operation_name, definitions)
|
104
104
|
response = _api_response(operation, status, definitions)
|
105
105
|
|
106
106
|
Response.new(result, response, api_definitions, omit: omit)
|
@@ -108,35 +108,9 @@ module Jsapi
|
|
108
108
|
|
109
109
|
private
|
110
110
|
|
111
|
-
def _api_operation(operation_name,
|
112
|
-
operation = definitions.operation(operation_name)
|
113
|
-
return operation if operation
|
114
|
-
|
115
|
-
raise "operation not defined: #{operation_name}"
|
116
|
-
end
|
117
|
-
|
118
|
-
def _api_params(operation, definitions, strong:)
|
119
|
-
(operation.model || Model::Base).new(
|
120
|
-
Parameters.new(
|
121
|
-
params.except(:action, :controller, :format).permit!,
|
122
|
-
headers,
|
123
|
-
operation,
|
124
|
-
definitions,
|
125
|
-
strong: strong
|
126
|
-
)
|
127
|
-
)
|
128
|
-
end
|
129
|
-
|
130
|
-
def _api_response(operation, status, definitions)
|
131
|
-
response = operation.response(status)
|
132
|
-
return response.resolve(definitions) if response
|
133
|
-
|
134
|
-
raise "status code not defined: #{status}"
|
135
|
-
end
|
136
|
-
|
137
|
-
def _perform_api_operation(operation_name, bang:, omit:, status:, strong:, &block)
|
111
|
+
def _api_operation(operation_name, bang:, omit:, status:, strong:, &block)
|
138
112
|
definitions = api_definitions
|
139
|
-
operation =
|
113
|
+
operation = _find_api_operation(operation_name, definitions)
|
140
114
|
response = _api_response(operation, status, definitions)
|
141
115
|
|
142
116
|
if block
|
@@ -172,6 +146,32 @@ module Jsapi
|
|
172
146
|
end
|
173
147
|
self.content_type = response.content_type
|
174
148
|
end
|
149
|
+
|
150
|
+
def _api_params(operation, definitions, strong:)
|
151
|
+
(operation.model || Model::Base).new(
|
152
|
+
Parameters.new(
|
153
|
+
params.except(:action, :controller, :format).permit!,
|
154
|
+
headers,
|
155
|
+
operation,
|
156
|
+
definitions,
|
157
|
+
strong: strong
|
158
|
+
)
|
159
|
+
)
|
160
|
+
end
|
161
|
+
|
162
|
+
def _api_response(operation, status, definitions)
|
163
|
+
response = operation.response(status)
|
164
|
+
return response.resolve(definitions) if response
|
165
|
+
|
166
|
+
raise "status code not defined: #{status}"
|
167
|
+
end
|
168
|
+
|
169
|
+
def _find_api_operation(operation_name, definitions)
|
170
|
+
operation = definitions.find_operation(operation_name)
|
171
|
+
return operation if operation
|
172
|
+
|
173
|
+
raise "operation not defined: #{operation_name}"
|
174
|
+
end
|
175
175
|
end
|
176
176
|
end
|
177
177
|
end
|
@@ -13,7 +13,10 @@ module Jsapi
|
|
13
13
|
|
14
14
|
# The API definitions of the current class.
|
15
15
|
def api_definitions(&block)
|
16
|
-
@api_definitions ||= Meta::Definitions.new(
|
16
|
+
@api_definitions ||= Meta::Definitions.new(
|
17
|
+
owner: self,
|
18
|
+
parent: superclass.try(:api_definitions)
|
19
|
+
)
|
17
20
|
Definitions.new(@api_definitions, &block) if block
|
18
21
|
@api_definitions
|
19
22
|
end
|
@@ -19,7 +19,7 @@ module Jsapi
|
|
19
19
|
# Includes API definitions from +klasses+.
|
20
20
|
def include(*klasses)
|
21
21
|
klasses.each do |klass|
|
22
|
-
_meta_model.
|
22
|
+
_meta_model.add_included(klass.api_definitions)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -37,8 +37,8 @@ module Jsapi
|
|
37
37
|
# end
|
38
38
|
def openapi(**keywords, &block)
|
39
39
|
_define('openapi') do
|
40
|
-
_meta_model.
|
41
|
-
OpenAPI::Root.new(_meta_model.
|
40
|
+
_meta_model.openapi = keywords
|
41
|
+
OpenAPI::Root.new(_meta_model.openapi, &block) if block
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -88,7 +88,7 @@ module Jsapi
|
|
88
88
|
#
|
89
89
|
def rescue_from(*klasses, with: nil)
|
90
90
|
klasses.each do |klass|
|
91
|
-
_meta_model.add_rescue_handler(klass, status: with)
|
91
|
+
_meta_model.add_rescue_handler({ error_class: klass, status: with })
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
@@ -6,11 +6,11 @@ module Jsapi
|
|
6
6
|
module Attributes
|
7
7
|
# Defines an attribute.
|
8
8
|
def attribute(name, type = Object,
|
9
|
-
keys: nil,
|
10
|
-
values: nil,
|
11
9
|
default: nil,
|
12
10
|
default_key: nil,
|
13
|
-
|
11
|
+
keys: nil,
|
12
|
+
read_only: false,
|
13
|
+
values: nil)
|
14
14
|
|
15
15
|
(@attribute_names ||= []) << name.to_sym
|
16
16
|
|
@@ -24,20 +24,19 @@ module Jsapi
|
|
24
24
|
|
25
25
|
case type
|
26
26
|
when Array
|
27
|
-
|
27
|
+
unless read_only
|
28
28
|
singular_name = name.to_s.singularize
|
29
|
+
add_method = "add_#{singular_name}"
|
30
|
+
|
29
31
|
type_caster = TypeCaster.new(type.first, values: values, name: singular_name)
|
30
32
|
|
31
33
|
# Attribute writer
|
32
34
|
define_method("#{name}=") do |argument|
|
33
|
-
|
34
|
-
instance_variable_name,
|
35
|
-
Array.wrap(argument).map { |element| type_caster.cast(element) }
|
36
|
-
)
|
35
|
+
Array.wrap(argument).each { |element| send(add_method, element) }
|
37
36
|
end
|
38
37
|
|
39
|
-
#
|
40
|
-
define_method(
|
38
|
+
# Add method
|
39
|
+
define_method(add_method) do |argument = nil|
|
41
40
|
type_caster.cast(argument).tap do |casted_argument|
|
42
41
|
if instance_variable_defined?(instance_variable_name)
|
43
42
|
instance_variable_get(instance_variable_name)
|
@@ -49,20 +48,27 @@ module Jsapi
|
|
49
48
|
end
|
50
49
|
when Hash
|
51
50
|
singular_name = name.to_s.singularize
|
51
|
+
add_method = "add_#{singular_name}"
|
52
|
+
|
52
53
|
key_type, value_type = type.first
|
53
54
|
key_type_caster = TypeCaster.new(key_type, values: keys, name: 'key')
|
54
55
|
|
55
|
-
#
|
56
|
+
# Hash value reader
|
56
57
|
define_method(singular_name) do |key = nil|
|
57
58
|
key = default_key if key.to_s.empty?
|
58
59
|
send(name)&.[](key_type_caster.cast(key))
|
59
60
|
end
|
60
61
|
|
61
|
-
|
62
|
+
unless read_only
|
62
63
|
value_type_caster = TypeCaster.new(value_type, values: values)
|
63
64
|
|
64
|
-
#
|
65
|
-
define_method("
|
65
|
+
# Attribute writer
|
66
|
+
define_method("#{name}=") do |argument|
|
67
|
+
argument.each { |key, value| send(add_method, key, value) }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Add method
|
71
|
+
define_method(add_method) do |key_or_value, value = nil|
|
66
72
|
if value.nil? && default_key
|
67
73
|
key = default_key
|
68
74
|
value = key_or_value
|
@@ -89,7 +95,7 @@ module Jsapi
|
|
89
95
|
value.nil? ? default || false : value
|
90
96
|
end if values == [true, false]
|
91
97
|
|
92
|
-
|
98
|
+
unless read_only
|
93
99
|
type_caster = TypeCaster.new(type, values: values, name: name)
|
94
100
|
|
95
101
|
# Attribute writer
|
@@ -20,12 +20,13 @@ module Jsapi
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def inspect # :nodoc:
|
23
|
+
def inspect(*attributes) # :nodoc:
|
24
24
|
klass = self.class
|
25
|
+
attribute_names = klass.attribute_names
|
26
|
+
attribute_names = attributes & attribute_names if attributes.any?
|
27
|
+
|
25
28
|
"#<#{klass.name} #{
|
26
|
-
|
27
|
-
"#{name}: #{send(name).inspect}"
|
28
|
-
end.join(', ')
|
29
|
+
attribute_names.map { |name| "#{name}: #{send(name).inspect}" }.join(', ')
|
29
30
|
}>"
|
30
31
|
end
|
31
32
|
|
@@ -10,10 +10,9 @@ module Jsapi
|
|
10
10
|
# The name of the referred object.
|
11
11
|
attribute :ref, String
|
12
12
|
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
@lookup_method_name ||= name.delete_suffix('::Reference').demodulize.underscore
|
13
|
+
# Derrives the component type from the inner most module name.
|
14
|
+
def self.component_type
|
15
|
+
@component_type ||= name.split('::')[-2].underscore
|
17
16
|
end
|
18
17
|
|
19
18
|
# Returns true.
|
@@ -25,7 +24,7 @@ module Jsapi
|
|
25
24
|
#
|
26
25
|
# Raises a ReferenceError if +ref+ could not be resolved.
|
27
26
|
def resolve(definitions)
|
28
|
-
object = definitions.
|
27
|
+
object = definitions.find_component(self.class.component_type, ref)
|
29
28
|
raise ReferenceError, ref if object.nil?
|
30
29
|
|
31
30
|
object.resolve(definitions)
|
data/lib/jsapi/meta/defaults.rb
CHANGED
@@ -7,12 +7,12 @@ module Jsapi
|
|
7
7
|
##
|
8
8
|
# :attr: read
|
9
9
|
# The default value of parameters and properties when reading requests.
|
10
|
-
attribute :within_requests
|
10
|
+
attribute :within_requests
|
11
11
|
|
12
12
|
##
|
13
13
|
# :attr: write
|
14
14
|
# The default value of properties when writing responses.
|
15
|
-
attribute :within_responses
|
15
|
+
attribute :within_responses
|
16
16
|
|
17
17
|
# Returns the default value within +context+.
|
18
18
|
def value(context:)
|
@@ -2,170 +2,225 @@
|
|
2
2
|
|
3
3
|
module Jsapi
|
4
4
|
module Meta
|
5
|
-
class Definitions
|
6
|
-
extend Base::Attributes
|
7
|
-
|
5
|
+
class Definitions < Base::Model
|
8
6
|
##
|
9
7
|
# :attr: defaults
|
10
|
-
# The
|
8
|
+
# The Defaults.
|
11
9
|
attribute :defaults, { String => Defaults }, keys: Schema::TYPES, default: {}
|
12
10
|
|
13
11
|
##
|
14
|
-
# :attr:
|
15
|
-
# The
|
16
|
-
attribute :
|
12
|
+
# :attr: included
|
13
|
+
# The Definitions included.
|
14
|
+
attribute :included, [Definitions], default: []
|
17
15
|
|
18
|
-
|
19
|
-
|
16
|
+
##
|
17
|
+
# :attr: on_rescues
|
18
|
+
# The methods or procs to be called when rescuing an exception.
|
19
|
+
attribute :on_rescues, [], default: []
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
@parameters = {}
|
26
|
-
@request_bodies = {}
|
27
|
-
@rescue_handlers = []
|
28
|
-
@responses = {}
|
29
|
-
@schemas = {}
|
30
|
-
@self_and_included = [self]
|
31
|
-
end
|
21
|
+
##
|
22
|
+
# :attr: openapi
|
23
|
+
# The OpenAPI root object.
|
24
|
+
attribute :openapi, OpenAPI
|
32
25
|
|
33
|
-
|
34
|
-
|
35
|
-
|
26
|
+
##
|
27
|
+
# :attr: operations
|
28
|
+
# The Operation objects.
|
29
|
+
attribute :operations, { String => Operation }, default: {}
|
36
30
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
31
|
+
##
|
32
|
+
# :attr_reader: owner
|
33
|
+
# The class to which it is assigned.
|
34
|
+
attribute :owner, read_only: true
|
41
35
|
|
42
|
-
|
43
|
-
|
44
|
-
|
36
|
+
##
|
37
|
+
# :attr: parameters
|
38
|
+
# The reusable Parameter objects.
|
39
|
+
attribute :parameters, { String => Parameter }, default: {}
|
40
|
+
|
41
|
+
##
|
42
|
+
# :attr_reader: parent
|
43
|
+
# The Definitions from which it inherits.
|
44
|
+
attribute :parent, read_only: true
|
45
|
+
|
46
|
+
##
|
47
|
+
# :attr: rescue_handlers
|
48
|
+
# The RescueHandler objects.
|
49
|
+
attribute :rescue_handlers, [RescueHandler], default: []
|
50
|
+
|
51
|
+
##
|
52
|
+
# :attr: request_bodies
|
53
|
+
# The reusable RequestBody objects.
|
54
|
+
attribute :request_bodies, { String => RequestBody }, default: {}
|
55
|
+
|
56
|
+
##
|
57
|
+
# :attr: responses
|
58
|
+
# The reusable Response objects.
|
59
|
+
attribute :responses, { String => Response }, default: {}
|
60
|
+
|
61
|
+
##
|
62
|
+
# :attr: schemas
|
63
|
+
# The reusable Schema objects.
|
64
|
+
attribute :schemas, { String => Schema }, default: {}
|
65
|
+
|
66
|
+
undef add_included, add_operation, add_parameter
|
67
|
+
|
68
|
+
def initialize(keywords = {})
|
69
|
+
@owner = keywords.delete(:owner)
|
70
|
+
@parent = keywords.delete(:parent)
|
71
|
+
|
72
|
+
super(keywords)
|
45
73
|
end
|
46
74
|
|
47
|
-
def
|
48
|
-
|
75
|
+
def add_included(definitions) # :nodoc:
|
76
|
+
if circular_dependency?(definitions)
|
77
|
+
raise ArgumentError, 'detected circular dependency between ' \
|
78
|
+
"#{owner.inspect} and " \
|
79
|
+
"#{definitions.owner.inspect}"
|
80
|
+
end
|
81
|
+
|
82
|
+
(@included ||= []) << definitions
|
83
|
+
self
|
49
84
|
end
|
50
85
|
|
51
|
-
def
|
52
|
-
|
86
|
+
def add_operation(name = nil, keywords = {}) # :nodoc:
|
87
|
+
name = name.nil? ? default_operation_name : name.to_s
|
88
|
+
keywords = keywords.reverse_merge(path: default_path)
|
89
|
+
(@operations ||= {})[name] = Operation.new(name, keywords)
|
53
90
|
end
|
54
91
|
|
55
|
-
def
|
92
|
+
def add_parameter(name, keywords = {}) # :nodoc:
|
56
93
|
name = name.to_s
|
57
|
-
@
|
94
|
+
(@parameters ||= {})[name] = Parameter.new(name, keywords)
|
58
95
|
end
|
59
96
|
|
60
|
-
|
61
|
-
|
62
|
-
|
97
|
+
# Returns an array containing itself and all of the definitions inherited/included.
|
98
|
+
def ancestors
|
99
|
+
[self].tap do |ancestors|
|
100
|
+
(included + Array(parent)).each do |included_or_parent|
|
101
|
+
included_or_parent.ancestors.each do |definitions|
|
102
|
+
ancestors << definitions
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end.uniq
|
63
106
|
end
|
64
107
|
|
108
|
+
# Returns the default value for +type+ within +context+.
|
65
109
|
def default_value(type, context: nil)
|
66
110
|
return unless (type = type.to_s).present?
|
67
111
|
|
68
|
-
|
112
|
+
ancestors.each do |definitions|
|
69
113
|
default = definitions.default(type)
|
70
114
|
return default.value(context: context) if default
|
71
115
|
end
|
72
116
|
nil
|
73
117
|
end
|
74
118
|
|
75
|
-
|
76
|
-
|
119
|
+
# Returns the component with the specified type and name.
|
120
|
+
def find_component(type, name)
|
121
|
+
return unless (name = name.to_s).present?
|
122
|
+
|
123
|
+
ancestors.each do |definitions|
|
124
|
+
component = definitions.send(type, name)
|
125
|
+
return component if component.present?
|
126
|
+
end
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns the operation with the specified name.
|
131
|
+
def find_operation(name = nil)
|
132
|
+
return find_component(:operation, name) if name.present?
|
77
133
|
|
78
|
-
|
134
|
+
# Return the one and only operation
|
135
|
+
operations.values.first if operations.one?
|
79
136
|
end
|
80
137
|
|
81
|
-
def inspect # :nodoc:
|
82
|
-
|
83
|
-
%i[owner operations parameters request_bodies responses schemas
|
84
|
-
openapi_root rescue_handlers].map do |name|
|
85
|
-
"#{name}: #{instance_variable_get("@#{name}").inspect}"
|
86
|
-
end.join(', ')
|
87
|
-
}>"
|
138
|
+
def inspect(*attributes) # :nodoc:
|
139
|
+
super(*(attributes.presence || %i[owner parent included]))
|
88
140
|
end
|
89
141
|
|
90
142
|
# Returns a hash representing the \JSON \Schema document for +name+.
|
91
143
|
def json_schema_document(name)
|
92
|
-
schema
|
93
|
-
definitions =
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
.transform_values(&:to_json_schema)
|
144
|
+
find_component(:schema, name)&.to_json_schema&.tap do |hash|
|
145
|
+
definitions = ancestors
|
146
|
+
.map(&:schemas)
|
147
|
+
.reduce(&:merge)
|
148
|
+
.except(name.to_s)
|
149
|
+
.transform_values(&:to_json_schema)
|
99
150
|
|
100
151
|
hash[:definitions] = definitions if definitions.any?
|
101
152
|
end
|
102
153
|
end
|
103
154
|
|
155
|
+
# Returns the methods or procs to be called when rescuing an exception.
|
104
156
|
def on_rescue_callbacks
|
105
|
-
|
106
|
-
definitions.callbacks[:on_rescue]
|
107
|
-
end
|
157
|
+
ancestors.flat_map(&:on_rescues)
|
108
158
|
end
|
109
159
|
|
110
160
|
# Returns a hash representing the \OpenAPI document for +version+.
|
111
|
-
|
161
|
+
#
|
112
162
|
# Raises an +ArgumentError+ if +version+ is not supported.
|
113
163
|
def openapi_document(version = nil)
|
114
164
|
version = OpenAPI::Version.from(version)
|
115
165
|
|
116
|
-
|
117
|
-
|
166
|
+
operations = ancestors.map(&:operations).reduce(&:reverse_merge).values
|
167
|
+
|
168
|
+
components = if version.major == 2
|
169
|
+
{
|
170
|
+
definitions: :schemas,
|
171
|
+
parameters: :parameters,
|
172
|
+
responses: :responses
|
173
|
+
}
|
174
|
+
else
|
175
|
+
{
|
176
|
+
schemas: :schemas,
|
177
|
+
parameters: :parameters,
|
178
|
+
requestBodies: :request_bodies,
|
179
|
+
responses: :responses
|
180
|
+
}
|
181
|
+
end
|
182
|
+
|
183
|
+
openapi_components = ancestors.map do |definitions|
|
184
|
+
components.transform_values do |method|
|
185
|
+
definitions.send(method).transform_values do |component|
|
186
|
+
case method
|
187
|
+
when :parameters
|
188
|
+
component.to_openapi(version, self).first
|
189
|
+
when :responses
|
190
|
+
component.to_openapi(version, self)
|
191
|
+
else
|
192
|
+
component.to_openapi(version)
|
193
|
+
end
|
194
|
+
end.presence
|
195
|
+
end.compact
|
196
|
+
end.reduce(&:reverse_merge)
|
197
|
+
|
198
|
+
(openapi&.to_openapi(version, self) || {}).tap do |h|
|
199
|
+
h[:paths] = operations
|
200
|
+
.group_by { |operation| operation.path || default_path }
|
201
|
+
.transform_values do |op|
|
202
|
+
op.index_by(&:method).transform_values do |operation|
|
203
|
+
operation.to_openapi(version, self)
|
204
|
+
end
|
205
|
+
end.presence
|
118
206
|
|
119
207
|
if version.major == 2
|
120
|
-
h.merge!(
|
121
|
-
definitions: openapi_schemas(version),
|
122
|
-
parameters: openapi_parameters(version),
|
123
|
-
responses: openapi_responses(version)
|
124
|
-
)
|
125
|
-
operations = @self_and_included.map(&:operations).reduce(&:reverse_merge).values
|
126
|
-
|
127
208
|
consumes = operations.filter_map { |operation| operation.consumes(self) }
|
128
209
|
h[:consumes] = consumes.uniq.sort if consumes.present?
|
129
210
|
|
130
211
|
produces = operations.flat_map { |operation| operation.produces(self) }
|
131
212
|
h[:produces] = produces.uniq.sort if produces.present?
|
132
|
-
|
133
|
-
h
|
134
|
-
|
135
|
-
|
136
|
-
requestBodies: openapi_request_bodies(version),
|
137
|
-
responses: openapi_responses(version)
|
138
|
-
).compact.presence
|
213
|
+
|
214
|
+
h.merge!(openapi_components)
|
215
|
+
elsif openapi_components.any?
|
216
|
+
(h[:components] ||= {}).merge!(openapi_components)
|
139
217
|
end
|
140
218
|
end.compact
|
141
219
|
end
|
142
220
|
|
143
|
-
|
144
|
-
if (name = name.to_s).present?
|
145
|
-
definitions = @self_and_included.find { |d| d.operations.key?(name) }
|
146
|
-
definitions.operations[name] if definitions
|
147
|
-
elsif @operations.one?
|
148
|
-
# return the one and only operation
|
149
|
-
@operations.values.first
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
def parameter(name)
|
154
|
-
return unless (name = name.to_s).present?
|
155
|
-
|
156
|
-
definitions = @self_and_included.find { |d| d.parameters.key?(name) }
|
157
|
-
definitions.parameters[name] if definitions
|
158
|
-
end
|
159
|
-
|
160
|
-
def request_body(name)
|
161
|
-
return unless (name = name.to_s).present?
|
162
|
-
|
163
|
-
definitions = @self_and_included.find { |d| d.request_bodies.key?(name) }
|
164
|
-
definitions.request_bodies[name] if definitions
|
165
|
-
end
|
166
|
-
|
221
|
+
# Returns the first RescueHandler to handle +exception+, or nil if no one could be found.
|
167
222
|
def rescue_handler_for(exception)
|
168
|
-
|
223
|
+
ancestors.each do |definitions|
|
169
224
|
definitions.rescue_handlers.each do |rescue_handler|
|
170
225
|
return rescue_handler if rescue_handler.match?(exception)
|
171
226
|
end
|
@@ -173,22 +228,15 @@ module Jsapi
|
|
173
228
|
nil
|
174
229
|
end
|
175
230
|
|
176
|
-
|
177
|
-
return unless (name = name.to_s).present?
|
178
|
-
|
179
|
-
definitions = @self_and_included.find { |d| d.responses.key?(name) }
|
180
|
-
definitions.responses[name] if definitions
|
181
|
-
end
|
231
|
+
private
|
182
232
|
|
183
|
-
def
|
184
|
-
return
|
233
|
+
def circular_dependency?(other)
|
234
|
+
return true if other == self
|
235
|
+
return false if other.included.none?
|
185
236
|
|
186
|
-
|
187
|
-
definitions.schemas[name] if definitions
|
237
|
+
other.included.any? { |included| circular_dependency?(included) }
|
188
238
|
end
|
189
239
|
|
190
|
-
private
|
191
|
-
|
192
240
|
def default_operation_name
|
193
241
|
@default_operation_name ||=
|
194
242
|
@owner.to_s.demodulize.delete_suffix('Controller').underscore
|
@@ -197,46 +245,6 @@ module Jsapi
|
|
197
245
|
def default_path
|
198
246
|
@default_path ||= "/#{default_operation_name}"
|
199
247
|
end
|
200
|
-
|
201
|
-
def openapi_parameters(version)
|
202
|
-
@self_and_included
|
203
|
-
.map(&:parameters).reduce(&:merge)
|
204
|
-
.transform_values do |parameter|
|
205
|
-
parameter.to_openapi(version, self).first
|
206
|
-
end.presence
|
207
|
-
end
|
208
|
-
|
209
|
-
def openapi_paths(version)
|
210
|
-
@self_and_included
|
211
|
-
.map(&:operations).reduce(&:merge).values
|
212
|
-
.group_by { |operation| operation.path || default_path }
|
213
|
-
.transform_values do |operations|
|
214
|
-
operations.index_by(&:method).transform_values do |operation|
|
215
|
-
operation.to_openapi(version, self)
|
216
|
-
end
|
217
|
-
end.presence
|
218
|
-
end
|
219
|
-
|
220
|
-
def openapi_request_bodies(version)
|
221
|
-
@self_and_included
|
222
|
-
.map(&:request_bodies).reduce(&:merge).transform_values do |request_body|
|
223
|
-
request_body.to_openapi(version)
|
224
|
-
end.presence
|
225
|
-
end
|
226
|
-
|
227
|
-
def openapi_responses(version)
|
228
|
-
@self_and_included
|
229
|
-
.map(&:responses).reduce(&:merge).transform_values do |response|
|
230
|
-
response.to_openapi(version, self)
|
231
|
-
end.presence
|
232
|
-
end
|
233
|
-
|
234
|
-
def openapi_schemas(version)
|
235
|
-
@self_and_included
|
236
|
-
.map(&:schemas).reduce(&:merge).transform_values do |schema|
|
237
|
-
schema.to_openapi(version)
|
238
|
-
end.presence
|
239
|
-
end
|
240
248
|
end
|
241
249
|
end
|
242
250
|
end
|
@@ -8,7 +8,9 @@ module Jsapi
|
|
8
8
|
class Model < Meta::Base::Model
|
9
9
|
##
|
10
10
|
# :attr: operations
|
11
|
-
attribute :operations,
|
11
|
+
attribute :operations, { String => Object }, default: {}
|
12
|
+
|
13
|
+
undef add_operation
|
12
14
|
|
13
15
|
# Adds a callback operation.
|
14
16
|
#
|
@@ -19,10 +21,6 @@ module Jsapi
|
|
19
21
|
(@operations ||= {})[expression.to_s] = Operation.new(nil, keywords)
|
20
22
|
end
|
21
23
|
|
22
|
-
def operation(expression) # :nodoc:
|
23
|
-
@operations&.[](expression&.to_s)
|
24
|
-
end
|
25
|
-
|
26
24
|
# Returns a hash representing the \OpenAPI callback object.
|
27
25
|
def to_openapi(version, definitions)
|
28
26
|
operations.transform_values do |operation|
|
@@ -4,6 +4,10 @@ module Jsapi
|
|
4
4
|
module Meta
|
5
5
|
module OpenAPI
|
6
6
|
module Extensions
|
7
|
+
##
|
8
|
+
# :attr: openapi_extensions
|
9
|
+
# The \OpenAPI extensions.
|
10
|
+
|
7
11
|
# Adds an \OpenAPI extension.
|
8
12
|
#
|
9
13
|
# Raises an +ArgumentError+ if +name+ is blank.
|
@@ -13,11 +17,18 @@ module Jsapi
|
|
13
17
|
openapi_extensions["x-#{name}".to_sym] = value
|
14
18
|
end
|
15
19
|
|
16
|
-
|
17
|
-
def openapi_extensions
|
20
|
+
def openapi_extensions # :nodoc:
|
18
21
|
@openapi_extensions ||= {}
|
19
22
|
end
|
20
23
|
|
24
|
+
def openapi_extensions=(extensions) # :nodoc:
|
25
|
+
@openapi_extensions = {}
|
26
|
+
|
27
|
+
extensions.each do |name, value|
|
28
|
+
add_openapi_extension(name, value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
21
32
|
private
|
22
33
|
|
23
34
|
def with_openapi_extensions(keywords = {}) # :nodoc:
|
@@ -39,14 +39,17 @@ module Jsapi
|
|
39
39
|
##
|
40
40
|
# :attr_reader: schema
|
41
41
|
# The Schema of the header.
|
42
|
-
attribute :schema,
|
42
|
+
attribute :schema, read_only: true
|
43
43
|
|
44
44
|
def initialize(keywords = {})
|
45
45
|
raise ArgumentError, "type can't be object" if keywords[:type] == 'object'
|
46
46
|
|
47
47
|
keywords = keywords.dup
|
48
|
-
super(
|
49
|
-
|
48
|
+
super(
|
49
|
+
keywords.extract!(
|
50
|
+
:collection_format, :deprecated, :description, :examples, :openapi_extensions
|
51
|
+
)
|
52
|
+
)
|
50
53
|
add_example(value: keywords.delete(:example)) if keywords.key?(:example)
|
51
54
|
|
52
55
|
@schema = Schema.new(keywords)
|
data/lib/jsapi/meta/openapi.rb
CHANGED
@@ -17,3 +17,16 @@ require_relative 'openapi/server'
|
|
17
17
|
require_relative 'openapi/link'
|
18
18
|
require_relative 'openapi/tag'
|
19
19
|
require_relative 'openapi/root'
|
20
|
+
|
21
|
+
module Jsapi
|
22
|
+
module Meta
|
23
|
+
module OpenAPI
|
24
|
+
class << self
|
25
|
+
# Creates a new \OpenAPI root object.
|
26
|
+
def new(keywords = {})
|
27
|
+
Root.new(keywords)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/jsapi/meta/operation.rb
CHANGED
@@ -48,12 +48,12 @@ module Jsapi
|
|
48
48
|
##
|
49
49
|
# :attr_reader: name
|
50
50
|
# The name of the operation.
|
51
|
-
attribute :name,
|
51
|
+
attribute :name, read_only: true
|
52
52
|
|
53
53
|
##
|
54
54
|
# :attr: parameters
|
55
55
|
# The parameters of the operation.
|
56
|
-
attribute :parameters, { String => Parameter }, default: {}
|
56
|
+
attribute :parameters, { String => Parameter }, default: {}
|
57
57
|
|
58
58
|
##
|
59
59
|
# :attr: path
|
@@ -104,6 +104,8 @@ module Jsapi
|
|
104
104
|
# The tags used to group operations in an \OpenAPI document.
|
105
105
|
attribute :tags, [String]
|
106
106
|
|
107
|
+
undef :add_parameter
|
108
|
+
|
107
109
|
def initialize(name = nil, keywords = {})
|
108
110
|
@name = name&.to_s
|
109
111
|
super(keywords)
|
@@ -37,12 +37,12 @@ module Jsapi
|
|
37
37
|
##
|
38
38
|
# :attr_reader: name
|
39
39
|
# The name of the parameter.
|
40
|
-
attribute :name,
|
40
|
+
attribute :name, read_only: true
|
41
41
|
|
42
42
|
##
|
43
43
|
# :attr_reader: schema
|
44
44
|
# The Schema of the parameter.
|
45
|
-
attribute :schema,
|
45
|
+
attribute :schema, read_only: true
|
46
46
|
|
47
47
|
# Creates a new parameter.
|
48
48
|
#
|
@@ -53,7 +53,7 @@ module Jsapi
|
|
53
53
|
@name = name.to_s
|
54
54
|
|
55
55
|
keywords = keywords.dup
|
56
|
-
super(keywords.extract!(:deprecated, :description, :examples, :in))
|
56
|
+
super(keywords.extract!(:deprecated, :description, :examples, :in, :openapi_extensions))
|
57
57
|
|
58
58
|
add_example(value: keywords.delete(:example)) if keywords.key?(:example)
|
59
59
|
keywords[:ref] = keywords.delete(:schema) if keywords.key?(:schema)
|
data/lib/jsapi/meta/property.rb
CHANGED
@@ -8,7 +8,7 @@ module Jsapi
|
|
8
8
|
##
|
9
9
|
# :attr_reader: name
|
10
10
|
# The name of the property.
|
11
|
-
attribute :name,
|
11
|
+
attribute :name, read_only: true
|
12
12
|
|
13
13
|
##
|
14
14
|
# :attr: read_only
|
@@ -17,7 +17,7 @@ module Jsapi
|
|
17
17
|
##
|
18
18
|
# :attr_reader: schema
|
19
19
|
# The Schema of the parameter.
|
20
|
-
attribute :schema,
|
20
|
+
attribute :schema, read_only: true
|
21
21
|
|
22
22
|
##
|
23
23
|
# :attr: source
|
@@ -26,11 +26,11 @@ module Jsapi
|
|
26
26
|
##
|
27
27
|
# :attr_reader: schema
|
28
28
|
# The Schema of the request body.
|
29
|
-
attribute :schema,
|
29
|
+
attribute :schema, read_only: true
|
30
30
|
|
31
31
|
def initialize(keywords = {})
|
32
32
|
keywords = keywords.dup
|
33
|
-
super(keywords.extract!(:content_type, :description, :examples))
|
33
|
+
super(keywords.extract!(:content_type, :description, :examples, :openapi_extensions))
|
34
34
|
|
35
35
|
add_example(value: keywords.delete(:example)) if keywords.key?(:example)
|
36
36
|
keywords[:ref] = keywords.delete(:schema) if keywords.key?(:schema)
|
@@ -2,31 +2,31 @@
|
|
2
2
|
|
3
3
|
module Jsapi
|
4
4
|
module Meta
|
5
|
-
# Maps
|
6
|
-
class RescueHandler
|
7
|
-
|
8
|
-
|
5
|
+
# Maps an error class to a response status.
|
6
|
+
class RescueHandler < Base::Model
|
7
|
+
##
|
8
|
+
# :attr: error_class
|
9
|
+
# The error class to be mapped.
|
10
|
+
attribute :error_class, default: StandardError
|
9
11
|
|
10
|
-
|
11
|
-
#
|
12
|
-
#
|
13
|
-
|
14
|
-
def initialize(klass, status: nil)
|
15
|
-
unless klass.is_a?(Class) && klass.ancestors.include?(StandardError)
|
16
|
-
raise ArgumentError, "#{klass.inspect} must be a standard error class"
|
17
|
-
end
|
18
|
-
|
19
|
-
@klass = klass
|
20
|
-
@status = status || 'default'
|
21
|
-
end
|
12
|
+
##
|
13
|
+
# :attr: status
|
14
|
+
# The response status. The default is <code>"default"</code>.
|
15
|
+
attribute :status, default: 'default'
|
22
16
|
|
23
|
-
def
|
24
|
-
|
17
|
+
def initialize(keywords = {})
|
18
|
+
super
|
19
|
+
unless error_class.is_a?(Class)
|
20
|
+
raise ArgumentError, "#{error_class.inspect} isn't a class"
|
21
|
+
end
|
22
|
+
unless error_class <= StandardError
|
23
|
+
raise ArgumentError, "#{error_class.inspect} isn't a rescuable class"
|
24
|
+
end
|
25
25
|
end
|
26
26
|
|
27
27
|
# Returns true if +exception+ is an instance of the class to be mapped, false otherwise.
|
28
28
|
def match?(exception)
|
29
|
-
exception.is_a?(
|
29
|
+
exception.is_a?(error_class)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
@@ -41,16 +41,20 @@ module Jsapi
|
|
41
41
|
##
|
42
42
|
# :attr_reader: schema
|
43
43
|
# The Schema of the response.
|
44
|
-
attribute :schema,
|
44
|
+
attribute :schema, read_only: true
|
45
45
|
|
46
46
|
def initialize(keywords = {})
|
47
47
|
keywords = keywords.dup
|
48
|
-
super(
|
49
|
-
|
48
|
+
super(
|
49
|
+
keywords.extract!(
|
50
|
+
:content_type, :description, :examples, :headers,
|
51
|
+
:links, :locale, :openapi_extensions
|
52
|
+
)
|
53
|
+
)
|
50
54
|
add_example(value: keywords.delete(:example)) if keywords.key?(:example)
|
51
55
|
keywords[:ref] = keywords.delete(:schema) if keywords.key?(:schema)
|
52
56
|
|
53
|
-
@schema = Schema.new(
|
57
|
+
@schema = Schema.new(keywords)
|
54
58
|
end
|
55
59
|
|
56
60
|
# Returns a hash representing the \OpenAPI response object.
|
@@ -7,17 +7,19 @@ module Jsapi
|
|
7
7
|
##
|
8
8
|
# :attr: items
|
9
9
|
# The Schema defining the kind of items.
|
10
|
-
attribute :items, Schema
|
10
|
+
attribute :items, Schema
|
11
11
|
|
12
12
|
##
|
13
13
|
# :attr: max_items
|
14
14
|
# The maximum length of an array.
|
15
|
-
attribute :max_items
|
15
|
+
attribute :max_items
|
16
16
|
|
17
17
|
##
|
18
18
|
# :attr: min_items
|
19
19
|
# The minimum length of an array.
|
20
|
-
attribute :min_items
|
20
|
+
attribute :min_items
|
21
|
+
|
22
|
+
undef items=, max_items=, min_items=
|
21
23
|
|
22
24
|
def items=(keywords = {}) # :nodoc:
|
23
25
|
if keywords.key?(:schema)
|
@@ -24,12 +24,12 @@ module Jsapi
|
|
24
24
|
##
|
25
25
|
# :attr: enum
|
26
26
|
# The allowed values.
|
27
|
-
attribute :enum
|
27
|
+
attribute :enum
|
28
28
|
|
29
29
|
##
|
30
30
|
# :attr: examples
|
31
31
|
# The samples matching the schema.
|
32
|
-
attribute :examples, [
|
32
|
+
attribute :examples, []
|
33
33
|
|
34
34
|
##
|
35
35
|
# :attr: external_docs
|
@@ -53,6 +53,8 @@ module Jsapi
|
|
53
53
|
# The validations.
|
54
54
|
attr_reader :validations
|
55
55
|
|
56
|
+
undef enum=
|
57
|
+
|
56
58
|
# Creates a new schema.
|
57
59
|
def initialize(keywords = {})
|
58
60
|
keywords = keywords.dup
|
@@ -9,16 +9,18 @@ module Jsapi
|
|
9
9
|
##
|
10
10
|
# :attr: maximum
|
11
11
|
# The (exclusive) maximum.
|
12
|
-
attribute :maximum
|
12
|
+
attribute :maximum
|
13
13
|
|
14
14
|
##
|
15
15
|
# :attr: minimum
|
16
16
|
# The (exclusive) minimum.
|
17
|
-
attribute :minimum
|
17
|
+
attribute :minimum
|
18
18
|
|
19
19
|
##
|
20
20
|
# :attr: multiple_of
|
21
|
-
attribute :multiple_of
|
21
|
+
attribute :multiple_of
|
22
|
+
|
23
|
+
undef maximum=, minimum=, multiple_of=
|
22
24
|
|
23
25
|
def maximum=(value) # :nodoc:
|
24
26
|
boundary = Boundary.from(value)
|
@@ -30,7 +30,9 @@ module Jsapi
|
|
30
30
|
##
|
31
31
|
# :attr: properties
|
32
32
|
# The properties.
|
33
|
-
attribute :properties, { String => Property },
|
33
|
+
attribute :properties, { String => Property }, default: {}
|
34
|
+
|
35
|
+
undef add_property
|
34
36
|
|
35
37
|
def add_property(name, keywords = {}) # :nodoc:
|
36
38
|
(@properties ||= {})[name.to_s] = Property.new(name, **keywords)
|
@@ -66,7 +68,7 @@ module Jsapi
|
|
66
68
|
value = property.default_value(definitions, context: context) if value.nil?
|
67
69
|
raise "#{discriminator.property_name} can't be nil" if value.nil?
|
68
70
|
|
69
|
-
schema = definitions.schema
|
71
|
+
schema = definitions.find_component(:schema, discriminator.mapping(value) || value)
|
70
72
|
raise "inheriting schema couldn't be found: #{value.inspect}" if schema.nil?
|
71
73
|
|
72
74
|
schema.resolve(definitions).resolve_schema(object, definitions, context: context)
|
@@ -14,17 +14,19 @@ module Jsapi
|
|
14
14
|
##
|
15
15
|
# :attr: max_length
|
16
16
|
# The maximum length of a string.
|
17
|
-
attribute :max_length
|
17
|
+
attribute :max_length
|
18
18
|
|
19
19
|
##
|
20
20
|
# :attr: min_length
|
21
21
|
# The minimum length of a string.
|
22
|
-
attribute :min_length
|
22
|
+
attribute :min_length
|
23
23
|
|
24
24
|
##
|
25
25
|
# :attr: pattern
|
26
26
|
# The regular expression a string must match.
|
27
|
-
attribute :pattern
|
27
|
+
attribute :pattern
|
28
|
+
|
29
|
+
undef max_length=, min_length=, pattern=
|
28
30
|
|
29
31
|
def max_length=(value) # :nodoc:
|
30
32
|
add_validation('max_length', Validation::MaxLength.new(value))
|
data/lib/jsapi/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsapi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Denis Göller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-09-
|
11
|
+
date: 2024-09-21 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Jsapi can be used to read requests, produce responses and create OpenAPI
|
14
14
|
documents
|