jsapi 1.4 → 2.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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jsapi/controller/actions/class_methods.rb +61 -0
  3. data/lib/jsapi/controller/actions.rb +13 -0
  4. data/lib/jsapi/controller/authentication/class_methods.rb +65 -0
  5. data/lib/jsapi/controller/authentication/credentials/api_key.rb +24 -0
  6. data/lib/jsapi/controller/authentication/credentials/http/base.rb +25 -0
  7. data/lib/jsapi/controller/authentication/credentials/http/basic.rb +34 -0
  8. data/lib/jsapi/controller/authentication/credentials/http/bearer.rb +30 -0
  9. data/lib/jsapi/controller/authentication/credentials/http.rb +5 -0
  10. data/lib/jsapi/controller/authentication/credentials.rb +38 -0
  11. data/lib/jsapi/controller/authentication.rb +70 -0
  12. data/lib/jsapi/controller/base.rb +5 -4
  13. data/lib/jsapi/controller/methods/callbacks/callback.rb +80 -0
  14. data/lib/jsapi/controller/methods/callbacks/class_methods.rb +62 -0
  15. data/lib/jsapi/controller/methods/callbacks.rb +54 -0
  16. data/lib/jsapi/controller/methods.rb +209 -116
  17. data/lib/jsapi/controller/parameters.rb +24 -20
  18. data/lib/jsapi/controller/response.rb +71 -39
  19. data/lib/jsapi/controller.rb +2 -1
  20. data/lib/jsapi/dsl/base.rb +38 -5
  21. data/lib/jsapi/dsl/class_methods.rb +2 -2
  22. data/lib/jsapi/dsl/definitions.rb +41 -27
  23. data/lib/jsapi/dsl/operation.rb +10 -109
  24. data/lib/jsapi/dsl/parameter.rb +1 -1
  25. data/lib/jsapi/dsl/path.rb +41 -18
  26. data/lib/jsapi/dsl/request_body.rb +1 -1
  27. data/lib/jsapi/dsl/response.rb +9 -6
  28. data/lib/jsapi/dsl/schema.rb +11 -5
  29. data/lib/jsapi/dsl/shared_operation_methods.rb +140 -0
  30. data/lib/jsapi/dsl.rb +1 -2
  31. data/lib/jsapi/json/array.rb +2 -2
  32. data/lib/jsapi/json/object.rb +6 -6
  33. data/lib/jsapi/json.rb +4 -6
  34. data/lib/jsapi/media/range.rb +102 -0
  35. data/lib/jsapi/media/type.rb +70 -0
  36. data/lib/jsapi/media/type_and_subtype.rb +38 -0
  37. data/lib/jsapi/media.rb +9 -0
  38. data/lib/jsapi/messages.rb +19 -0
  39. data/lib/jsapi/meta/callback/base.rb +63 -8
  40. data/lib/jsapi/meta/content.rb +59 -0
  41. data/lib/jsapi/meta/definitions.rb +299 -153
  42. data/lib/jsapi/meta/example/base.rb +41 -8
  43. data/lib/jsapi/meta/existence.rb +4 -2
  44. data/lib/jsapi/meta/header/base.rb +4 -2
  45. data/lib/jsapi/meta/info.rb +3 -1
  46. data/lib/jsapi/meta/license.rb +11 -5
  47. data/lib/jsapi/meta/model/attributes/class_methods.rb +150 -0
  48. data/lib/jsapi/meta/model/attributes/frozen_error.rb +16 -0
  49. data/lib/jsapi/meta/model/attributes/type_caster.rb +56 -0
  50. data/lib/jsapi/meta/model/attributes.rb +24 -118
  51. data/lib/jsapi/meta/model/base.rb +2 -5
  52. data/lib/jsapi/meta/model/reference.rb +46 -10
  53. data/lib/jsapi/meta/model/wrappable.rb +23 -0
  54. data/lib/jsapi/meta/model/wrapper.rb +26 -0
  55. data/lib/jsapi/meta/model.rb +2 -1
  56. data/lib/jsapi/meta/oauth_flow.rb +1 -1
  57. data/lib/jsapi/meta/openapi/extensions.rb +5 -6
  58. data/lib/jsapi/meta/openapi/version.rb +16 -4
  59. data/lib/jsapi/meta/operation.rb +177 -71
  60. data/lib/jsapi/meta/parameter/base.rb +10 -6
  61. data/lib/jsapi/meta/parameter/wrapper.rb +13 -0
  62. data/lib/jsapi/meta/parameter.rb +3 -0
  63. data/lib/jsapi/meta/path.rb +59 -13
  64. data/lib/jsapi/meta/pathname.rb +6 -3
  65. data/lib/jsapi/meta/property.rb +10 -0
  66. data/lib/jsapi/meta/request_body/base.rb +69 -32
  67. data/lib/jsapi/meta/request_body/wrapper.rb +13 -0
  68. data/lib/jsapi/meta/request_body.rb +3 -0
  69. data/lib/jsapi/meta/rescue_handler.rb +18 -17
  70. data/lib/jsapi/meta/response/base.rb +82 -58
  71. data/lib/jsapi/meta/response/reference.rb +11 -1
  72. data/lib/jsapi/meta/response/wrapper.rb +26 -0
  73. data/lib/jsapi/meta/response.rb +3 -0
  74. data/lib/jsapi/meta/schema/additional_properties.rb +8 -0
  75. data/lib/jsapi/meta/schema/array.rb +20 -8
  76. data/lib/jsapi/meta/schema/base.rb +10 -9
  77. data/lib/jsapi/meta/schema/boundary.rb +1 -0
  78. data/lib/jsapi/meta/schema/numeric.rb +26 -20
  79. data/lib/jsapi/meta/schema/object.rb +60 -44
  80. data/lib/jsapi/meta/schema/reference.rb +1 -8
  81. data/lib/jsapi/meta/schema/string.rb +12 -6
  82. data/lib/jsapi/meta/schema/wrapper.rb +31 -0
  83. data/lib/jsapi/meta/schema.rb +22 -9
  84. data/lib/jsapi/meta/security_requirement.rb +2 -2
  85. data/lib/jsapi/meta/security_scheme/api_key.rb +5 -2
  86. data/lib/jsapi/meta/security_scheme/base.rb +7 -5
  87. data/lib/jsapi/meta/security_scheme/http/basic.rb +5 -7
  88. data/lib/jsapi/meta/security_scheme/http/bearer.rb +5 -5
  89. data/lib/jsapi/meta/security_scheme/http/other.rb +1 -3
  90. data/lib/jsapi/meta/security_scheme/mutual_tls.rb +1 -3
  91. data/lib/jsapi/meta/security_scheme/oauth2.rb +18 -13
  92. data/lib/jsapi/meta/security_scheme/open_id_connect.rb +4 -4
  93. data/lib/jsapi/meta/security_scheme.rb +4 -4
  94. data/lib/jsapi/meta/server.rb +4 -2
  95. data/lib/jsapi/meta/tag.rb +9 -3
  96. data/lib/jsapi/meta.rb +2 -1
  97. data/lib/jsapi/model/base.rb +1 -1
  98. data/lib/jsapi/status/base.rb +35 -0
  99. data/lib/jsapi/status/code.rb +113 -0
  100. data/lib/jsapi/status/default.rb +16 -0
  101. data/lib/jsapi/status/range.rb +35 -0
  102. data/lib/jsapi/status.rb +37 -0
  103. data/lib/jsapi/version.rb +1 -1
  104. data/lib/jsapi.rb +3 -3
  105. metadata +36 -10
  106. data/lib/jsapi/controller/parameters_invalid.rb +0 -27
  107. data/lib/jsapi/dsl/callback.rb +0 -21
  108. data/lib/jsapi/dsl/error.rb +0 -36
  109. data/lib/jsapi/invalid_argument_error.rb +0 -12
  110. data/lib/jsapi/invalid_value_error.rb +0 -12
  111. data/lib/jsapi/invalid_value_helper.rb +0 -17
  112. data/lib/jsapi/meta/model/type_caster.rb +0 -50
  113. data/lib/jsapi/meta/schema/delegator.rb +0 -26
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module DSL
5
+ module SharedOperationMethods
6
+ # Specifies the model class to access top-level parameters by.
7
+ #
8
+ # model Foo do
9
+ # def bar
10
+ # # ...
11
+ # end
12
+ # end
13
+ #
14
+ # +klass+ can be any subclass of Model::Base. If block is given, an anonymous
15
+ # class is created that inherits either from +klass+ or Model::Base.
16
+ #
17
+ # See Meta::Operation#model and Meta::Path#model for further information.
18
+ def model(klass = nil, &block)
19
+ if block
20
+ klass = Class.new(klass || Model::Base)
21
+ klass.class_eval(&block)
22
+ end
23
+ @meta_model.model = klass
24
+ end
25
+
26
+ # Specifies a parameter.
27
+ #
28
+ # parameter 'foo', type: 'string'
29
+ #
30
+ # parameter 'foo', type: 'object' do
31
+ # property 'bar', type: 'string'
32
+ # end
33
+ #
34
+ # Refers a resuable parameter if the +:ref+ keyword is specified.
35
+ #
36
+ # parameter ref: 'foo'
37
+ #
38
+ # Refers the reusable parameter with the same name if neither any
39
+ # keywords nor a block is specified.
40
+ #
41
+ # parameter 'foo'
42
+ #
43
+ # See Meta::Operation#parameters and Meta::Path#parameters for further
44
+ # information.
45
+ def parameter(name = nil, **keywords, &block)
46
+ define('parameter', name&.inspect) do
47
+ name = keywords[:ref] if name.nil?
48
+ keywords = { ref: name } unless keywords.any? || block
49
+
50
+ @meta_model.add_parameter(name, keywords).tap do |parameter_model|
51
+ Parameter.new(parameter_model, &block) if block
52
+ end
53
+ end
54
+ end
55
+
56
+ ##
57
+ # :call-seq:
58
+ # request_body(**keywords, &block)
59
+ # request_body(name)
60
+ #
61
+ # Specifies a request body.
62
+ #
63
+ # request_body type: 'object' do
64
+ # property 'foo', type: 'string'
65
+ # end
66
+ #
67
+ # Refers a resuable request body if the +:ref+ keyword is specified.
68
+ #
69
+ # request_body ref: 'foo'
70
+ #
71
+ # Refers the reusable request body with the same name if neither any
72
+ # keywords nor a block is specified.
73
+ #
74
+ # request_body 'foo'
75
+ #
76
+ # See Meta::Operation#request_body and Meta::Path#request_body for
77
+ # further information.
78
+ def request_body(name = nil, **keywords, &block)
79
+ define('request body') do
80
+ raise Error, "name can't be specified together with keywords or a block" \
81
+ if name && (keywords.any? || block)
82
+
83
+ keywords = { ref: name } if name
84
+ @meta_model.request_body = keywords
85
+
86
+ @meta_model.request_body.tap do |request_body|
87
+ RequestBody.new(request_body, &block) if block
88
+ end
89
+ end
90
+ end
91
+
92
+ ##
93
+ # :call-seq:
94
+ # response(status = nil, **keywords, &block)
95
+ # response(status, name)
96
+ # response(name)
97
+ #
98
+ # Specifies a response.
99
+ #
100
+ # response 200, type: 'object' do
101
+ # property 'foo', type: 'string'
102
+ # end
103
+ #
104
+ # The default status is <code>"default"</code>.
105
+ #
106
+ # Refers a resuable response if the +:ref+ keyword is specified.
107
+ #
108
+ # response 200, ref: 'foo'
109
+ #
110
+ # Refers the reusable response with the same name if neither any
111
+ # keywords nor a block is specified.
112
+ #
113
+ # response 'foo'
114
+ #
115
+ # See Meta::Operation#responses and Meta::Path#responses for further
116
+ # information.
117
+ def response(status = nil, name = nil, **keywords, &block)
118
+ define('response', status&.inspect) do
119
+ if keywords.none? && !block
120
+ status, name = nil, status unless name
121
+ keywords = { ref: name }
122
+ elsif name
123
+ raise Error, "name can't be specified together with keywords or a block"
124
+ end
125
+
126
+ @meta_model.add_response(status, keywords).tap do |response_model|
127
+ Response.new(response_model, &block) if block
128
+ end
129
+ end
130
+ end
131
+
132
+ ##
133
+ # :method: tag
134
+ # :args: name
135
+ # Specifies a tag.
136
+ #
137
+ # tag 'foo'
138
+ end
139
+ end
140
+ end
data/lib/jsapi/dsl.rb CHANGED
@@ -1,13 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'dsl/error'
4
3
  require_relative 'dsl/base'
5
4
  require_relative 'dsl/examples'
6
5
  require_relative 'dsl/schema'
7
6
  require_relative 'dsl/parameter'
8
7
  require_relative 'dsl/request_body'
9
8
  require_relative 'dsl/response'
10
- require_relative 'dsl/callback'
9
+ require_relative 'dsl/shared_operation_methods'
11
10
  require_relative 'dsl/operation'
12
11
  require_relative 'dsl/path'
13
12
  require_relative 'dsl/definitions'
@@ -4,10 +4,10 @@ module Jsapi
4
4
  module JSON
5
5
  # Represents a JSON array.
6
6
  class Array < Value
7
- def initialize(array, schema, definitions, context: nil)
7
+ def initialize(array, schema, context: nil)
8
8
  super(schema)
9
9
  @json_values = Array(array).map do |item|
10
- JSON.wrap(item, schema.items, definitions, context: context)
10
+ JSON.wrap(item, schema.items, context: context)
11
11
  end
12
12
  end
13
13
 
@@ -10,20 +10,20 @@ module Jsapi
10
10
 
11
11
  attr_reader :raw_additional_attributes, :raw_attributes
12
12
 
13
- def initialize(hash, schema, definitions, context: nil)
14
- schema = schema.resolve_schema(hash, definitions, context: context)
15
- properties = schema.resolve_properties(definitions, context: context)
13
+ def initialize(hash, schema, context: nil)
14
+ schema = schema.resolve_schema(hash, context: context)
15
+ properties = schema.resolve_properties(context: context)
16
16
 
17
17
  @raw_attributes = properties.transform_values do |property|
18
- JSON.wrap(hash[property.name], property.schema, definitions, context: context)
18
+ JSON.wrap(hash[property.name], property.schema, context: context)
19
19
  end
20
20
 
21
21
  @raw_additional_attributes =
22
22
  if (additional_properties = schema.additional_properties)
23
- additional_properties_schema = additional_properties.schema.resolve(definitions)
23
+ additional_properties_schema = additional_properties.schema
24
24
 
25
25
  hash.except(*properties.keys).transform_values do |value|
26
- JSON.wrap(value, additional_properties_schema, definitions, context: context)
26
+ JSON.wrap(value, additional_properties_schema, context: context)
27
27
  end
28
28
  end || {}
29
29
 
data/lib/jsapi/json.rb CHANGED
@@ -13,15 +13,13 @@ module Jsapi
13
13
  # Provides a DOM for JSON values.
14
14
  module JSON
15
15
  class << self
16
- def wrap(object, schema, definitions = nil, context: nil)
17
- schema = schema.resolve(definitions) unless definitions.nil?
18
-
19
- object = schema.default_value(definitions, context: context) if object.nil?
16
+ def wrap(object, schema, context: nil)
17
+ object = schema.default_value(context: context) if object.nil?
20
18
  return Null.new(schema) if object.nil?
21
19
 
22
20
  case schema.type
23
21
  when 'array'
24
- Array.new(object, schema, definitions, context: context)
22
+ Array.new(object, schema, context: context)
25
23
  when 'boolean'
26
24
  Boolean.new(object, schema)
27
25
  when 'integer'
@@ -29,7 +27,7 @@ module Jsapi
29
27
  when 'number'
30
28
  Number.new(object, schema)
31
29
  when 'object'
32
- Object.new(object, schema, definitions, context: context)
30
+ Object.new(object, schema, context: context)
33
31
  when 'string'
34
32
  String.new(object, schema)
35
33
  else
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'type_and_subtype'
4
+
5
+ module Jsapi
6
+ module Media
7
+ # Represents a media range.
8
+ class Range
9
+ include Comparable
10
+ include TypeAndSubtype
11
+
12
+ # Range for all types (<code>"\*/\*"</code>).
13
+ ALL = Range.new('*', '*')
14
+
15
+ # Range for \JSON type (<code>"application/json"</code>).
16
+ APPLICATION_JSON = Range.new('application', 'json')
17
+
18
+ class << self
19
+ # Transforms +value+ to an instance of this class.
20
+ #
21
+ # Raises an +ArgumentError+ when +value+ could not be transformed.
22
+ def from(value)
23
+ media_range = try_from(value)
24
+ return media_range unless media_range.nil?
25
+
26
+ raise ArgumentError, "invalid media range: #{value.inspect}"
27
+ end
28
+
29
+ # Reduces the given collection of media ranges by removing media ranges
30
+ # that are already covered by another media range.
31
+ def reduce(media_ranges)
32
+ media_ranges.each_with_object([]) do |media_range, memo|
33
+ media_range = from(media_range)
34
+ if memo.none? { |other| other.cover?(media_range) }
35
+ memo.delete_if { |other| media_range.cover?(other) }
36
+ memo << media_range
37
+ end
38
+ end.sort
39
+ end
40
+
41
+ # Tries to transform +value+ to an instance of this class.
42
+ #
43
+ # Returns nil if +value+ could not be transformed.
44
+ def try_from(value)
45
+ return value if value.is_a?(Range)
46
+
47
+ type_and_subtype = pattern.match(value.to_s)&.captures
48
+ new(*type_and_subtype) if type_and_subtype&.count == 2
49
+ end
50
+
51
+ private
52
+
53
+ def pattern
54
+ @pattern ||= begin
55
+ name = '[0-9a-zA-Z-]+'
56
+ %r{(\*|#{name})/(\*|(?:#{name}(?:\.#{name})?(?:\+#{name})?))}.freeze
57
+ end
58
+ end
59
+ end
60
+
61
+ # Compares it with +other+ by +priority+.
62
+ def <=>(other)
63
+ return unless other.is_a?(self.class)
64
+
65
+ result = priority <=> other.priority
66
+ return result unless result.zero?
67
+
68
+ result = type <=> other.type
69
+ return result unless result.zero?
70
+
71
+ subtype <=> other.subtype
72
+ end
73
+
74
+ # Returns true if it covers +other+.
75
+ def cover?(other)
76
+ return if other.nil?
77
+
78
+ other = Range.from(other)
79
+
80
+ (type == '*' || type == other.type) &&
81
+ (subtype == '*' || subtype == other.subtype)
82
+ end
83
+
84
+ # Returns true if the given media type matches the media range.
85
+ def match?(media_type)
86
+ return if media_type.nil?
87
+
88
+ media_type = Type.from(media_type)
89
+
90
+ (type == '*' || type == media_type.type) &&
91
+ (subtype == '*' || subtype == media_type.subtype)
92
+ end
93
+
94
+ alias =~ match?
95
+
96
+ # Returns the level of priority of the media range.
97
+ def priority
98
+ @priority ||= (type == '*' ? 2 : 0) + (subtype == '*' ? 1 : 0) + 1
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'type_and_subtype'
4
+
5
+ module Jsapi
6
+ module Media
7
+ # Represents a media type.
8
+ class Type
9
+ include Comparable
10
+ include TypeAndSubtype
11
+
12
+ # Media::Type for <code>"application/json"</code>.
13
+ APPLICATION_JSON = Type.new('application', 'json')
14
+
15
+ # Media::Type for <code>"application/json-seq"</code>.
16
+ APPLICATION_JSON_SEQ = Type.new('application', 'json-seq')
17
+
18
+ # Media::Type for <code>"text/plain"</code>.
19
+ TEXT_PLAIN = Type.new('text', 'plain')
20
+
21
+ class << self
22
+ # Transforms +value+ to an instance of this class.
23
+ #
24
+ # Raises an ArgumentError when +value+ could not be transformed.
25
+ def from(value)
26
+ media_type = try_from(value)
27
+ return media_type unless media_type.nil?
28
+
29
+ raise ArgumentError, "invalid media type: #{value.inspect}"
30
+ end
31
+
32
+ # Tries to transform +value+ to an instance of this class.
33
+ #
34
+ # Returns nil if +value+ could not be transformed.
35
+ def try_from(value)
36
+ return value if value.is_a?(Type)
37
+
38
+ type_and_subtype = pattern.match(value.to_s)&.captures
39
+ new(*type_and_subtype) if type_and_subtype&.count == 2
40
+ end
41
+
42
+ private
43
+
44
+ def pattern
45
+ @pattern ||= begin
46
+ name = '[0-9a-zA-Z-]+'
47
+ %r{(#{name})/(#{name}(?:\.#{name})?(?:\+#{name})?)}.freeze
48
+ end
49
+ end
50
+ end
51
+
52
+ # Compares it with +other+ by +type+ and +subtype+.
53
+ def <=>(other)
54
+ return unless other.is_a?(self.class)
55
+
56
+ result = type <=> other.type
57
+ return result unless result.zero?
58
+
59
+ subtype <=> other.subtype
60
+ end
61
+
62
+ # Returns true if it represents a JSON media type as specified by
63
+ # https://mimesniff.spec.whatwg.org/#json-mime-type.
64
+ def json?
65
+ (type.in?(%w[application text]) && subtype == 'json') ||
66
+ subtype.end_with?('+json')
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Media
5
+ module TypeAndSubtype # :nodoc:
6
+ def self.included(base)
7
+ base.attr_reader :type, :subtype
8
+ end
9
+
10
+ def initialize(type, subtype)
11
+ @type = type.downcase
12
+ @subtype = subtype.downcase
13
+ end
14
+
15
+ def ==(other)
16
+ other.is_a?(self.class) &&
17
+ type == other.type &&
18
+ subtype == other.subtype
19
+ end
20
+
21
+ alias eql? ==
22
+
23
+ def hash
24
+ @hash ||= [type, subtype].hash
25
+ end
26
+
27
+ def inspect
28
+ "#<#{self.class} #{to_s.inspect}>"
29
+ end
30
+
31
+ def to_s
32
+ @to_s ||= "#{type}/#{subtype}"
33
+ end
34
+
35
+ alias as_json to_s
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'media/range'
4
+ require_relative 'media/type'
5
+
6
+ module Jsapi
7
+ # Provides classes to deal with media types.
8
+ module Media end
9
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Messages # :nodoc:
5
+ class << self
6
+ def invalid_value(name:, value:, valid_values: [])
7
+ case valid_values.count
8
+ when 0
9
+ "#{name} must not be #{value.inspect}"
10
+ when 1
11
+ "#{name} must be #{valid_values.first.inspect}, is #{value.inspect}"
12
+ else
13
+ "#{name} must be one of #{valid_values[0..-2].map(&:inspect).join(', ')} " \
14
+ "or #{valid_values.last.inspect}, is #{value.inspect}"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -2,28 +2,83 @@
2
2
 
3
3
  module Jsapi
4
4
  module Meta
5
+ module Parameter; end
5
6
  class Operation < Model::Base; end
6
7
 
7
8
  module Callback
8
9
  # Specifies a callback. Applies to \OpenAPI 3.0 and higher.
9
10
  class Base < Model::Base
11
+ # The set of operations that can be called backed.
12
+ class Operations < Model::Base
13
+ ##
14
+ # :attr: description
15
+ # The description that applies to all operations.
16
+ attribute :description, String
17
+
18
+ ##
19
+ # :attr: operations
20
+ # The operations. Maps strings to Operation objects.
21
+ attribute :operations, { String => Operation }, accessors: %i[reader writer]
22
+
23
+ ##
24
+ # :attr: parameters
25
+ # The parameters that apply for all operations. Maps parameter names
26
+ # to Parameter objects or references.
27
+ attribute :parameters, { String => Parameter }, accessors: %i[reader writer]
28
+
29
+ ##
30
+ # :attr: summary
31
+ # The short summary that applies to all operations.
32
+ attribute :summary, String
33
+
34
+ def add_operation(method = nil, keywords = {}) # :nodoc:
35
+ try_modify_attribute!(:operations) do
36
+ method, keywords = nil, method if method.is_a?(Hash)
37
+ method = 'get' if method.nil?
38
+
39
+ (@operations ||= {})[method] = Operation.new(nil, keywords.merge(method: method))
40
+ end
41
+ end
42
+
43
+ def add_parameter(name, keywords = {}) # :nodoc:
44
+ try_modify_attribute!(:parameters) do
45
+ name = name.to_s
46
+
47
+ (@parameters ||= {})[name] = Parameter.new(name, keywords)
48
+ end
49
+ end
50
+
51
+ # Returns a hash representing the \OpenAPI path item object that describes
52
+ # the operations.
53
+ def to_openapi(version, definitions)
54
+ OpenAPI::PathItem.new(
55
+ operations.values,
56
+ description: description,
57
+ summary: summary,
58
+ parameters: parameters
59
+ ).to_openapi(version, definitions)
60
+ end
61
+ end
62
+
10
63
  ##
11
- # :attr: operations
12
- attribute :operations, { String => Operation }, accessors: %i[reader writer]
64
+ # :attr: expressions
65
+ attribute :expressions, { String => Operations }, accessors: %i[reader writer]
13
66
 
14
- # Adds a callback operation.
67
+ # Adds an expression.
15
68
  #
16
69
  # Raises an +ArgumentError+ if +expression+ is blank.
17
- def add_operation(expression, keywords = {})
18
- raise ArgumentError, "expression can't be blank" if expression.blank?
70
+ def add_expression(expression, keywords = {})
71
+ try_modify_attribute!(:expressions) do
72
+ raise ArgumentError, "expression can't be blank" if expression.blank?
19
73
 
20
- (@operations ||= {})[expression.to_s] = Operation.new(nil, keywords)
74
+ (@expressions ||= {})[expression.to_s] = Operations.new(keywords)
75
+ end
21
76
  end
22
77
 
23
78
  # Returns a hash representing the \OpenAPI callback object.
24
79
  def to_openapi(version, definitions)
25
- operations.transform_values do |operation|
26
- OpenAPI::PathItem.new([operation]).to_openapi(version, definitions)
80
+ expressions.transform_values do |operations|
81
+ operations.to_openapi(version, definitions)
27
82
  end
28
83
  end
29
84
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ # Specifies the content of a request body or response.
6
+ class Content < Model::Base
7
+ include OpenAPI::Extensions
8
+
9
+ class Wrapper < Model::Wrapper
10
+ def schema
11
+ @schema ||= Schema.wrap(super, definitions)
12
+ end
13
+ end
14
+
15
+ include Model::Wrappable
16
+
17
+ delegate_missing_to :schema
18
+
19
+ ##
20
+ # :attr: examples
21
+ # The examples. Maps example names to Example objects or references.
22
+ attribute :examples, { String => Example }, default_key: 'default'
23
+
24
+ ##
25
+ # :attr_reader: schema
26
+ # The Schema of the content.
27
+ attribute :schema, accessors: %i[reader]
28
+
29
+ def initialize(keywords = {})
30
+ keywords = keywords.dup
31
+ super(keywords.extract!(:examples, :openapi_extensions))
32
+
33
+ add_example(value: keywords.delete(:example)) if keywords.key?(:example)
34
+ keywords[:ref] = keywords.delete(:schema) if keywords.key?(:schema)
35
+
36
+ @schema = Schema.new(keywords)
37
+ end
38
+
39
+ # Returns a hash representing the \OpenAPI media type object describing
40
+ # the content. Applies to \OpenAPI 3.0 and higher.
41
+ def to_openapi(version, media_type = nil)
42
+ version = OpenAPI::Version.from(version)
43
+
44
+ with_openapi_extensions(
45
+ **if media_type == Media::Type::APPLICATION_JSON_SEQ &&
46
+ schema.array? && version >= OpenAPI::V3_2
47
+ { itemSchema: schema.items.to_openapi(version) }
48
+ else
49
+ { schema: schema.to_openapi(version) }
50
+ end,
51
+ examples:
52
+ examples.transform_values do |example|
53
+ example.to_openapi(version)
54
+ end.presence
55
+ )
56
+ end
57
+ end
58
+ end
59
+ end