jsapi 1.2 → 1.4

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jsapi/configuration.rb +2 -2
  3. data/lib/jsapi/controller/methods.rb +17 -4
  4. data/lib/jsapi/controller/parameters.rb +26 -13
  5. data/lib/jsapi/controller/response.rb +29 -5
  6. data/lib/jsapi/dsl/class_methods.rb +11 -0
  7. data/lib/jsapi/dsl/definitions.rb +14 -0
  8. data/lib/jsapi/dsl/path.rb +43 -0
  9. data/lib/jsapi/dsl.rb +1 -0
  10. data/lib/jsapi/meta/callback/base.rb +1 -1
  11. data/lib/jsapi/meta/definitions.rb +98 -24
  12. data/lib/jsapi/meta/example/base.rb +4 -6
  13. data/lib/jsapi/meta/link/base.rb +4 -2
  14. data/lib/jsapi/meta/oauth_flow.rb +11 -2
  15. data/lib/jsapi/meta/openapi/path_item.rb +68 -0
  16. data/lib/jsapi/meta/openapi/version.rb +6 -0
  17. data/lib/jsapi/meta/openapi.rb +2 -0
  18. data/lib/jsapi/meta/operation.rb +29 -17
  19. data/lib/jsapi/meta/parameter/base.rb +82 -59
  20. data/lib/jsapi/meta/path.rb +56 -0
  21. data/lib/jsapi/meta/pathname.rb +60 -0
  22. data/lib/jsapi/meta/request_body/base.rb +3 -2
  23. data/lib/jsapi/meta/response/base.rb +26 -8
  24. data/lib/jsapi/meta/schema/base.rb +1 -1
  25. data/lib/jsapi/meta/schema/discriminator.rb +14 -4
  26. data/lib/jsapi/meta/schema/object.rb +28 -9
  27. data/lib/jsapi/meta/schema/validation/maximum.rb +1 -1
  28. data/lib/jsapi/meta/schema/validation/minimum.rb +1 -1
  29. data/lib/jsapi/meta/security_scheme/api_key.rb +4 -5
  30. data/lib/jsapi/meta/security_scheme/base.rb +16 -0
  31. data/lib/jsapi/meta/security_scheme/http/basic.rb +3 -10
  32. data/lib/jsapi/meta/security_scheme/http/bearer.rb +8 -8
  33. data/lib/jsapi/meta/security_scheme/http/other.rb +3 -5
  34. data/lib/jsapi/meta/security_scheme/mutual_tls.rb +23 -0
  35. data/lib/jsapi/meta/security_scheme/oauth2.rb +29 -13
  36. data/lib/jsapi/meta/security_scheme/open_id_connect.rb +5 -8
  37. data/lib/jsapi/meta/security_scheme.rb +3 -0
  38. data/lib/jsapi/meta/server.rb +9 -1
  39. data/lib/jsapi/meta/tag.rb +34 -4
  40. data/lib/jsapi/meta.rb +2 -0
  41. data/lib/jsapi/version.rb +1 -1
  42. metadata +7 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 40ceeb6c8774b09bfb3fbfe2bc4550444a179158b43cac27d6f9923cead5971c
4
- data.tar.gz: f6cc387571c73b93cdcc121c1c46267424a46362f4c400dee8bd53ec2e0af561
3
+ metadata.gz: 87f305e41facfb84c96e5e2406b636aa9e10a27545a25d42a5029f4d4dd93488
4
+ data.tar.gz: 6723179cd4981c590a4e8f0c45808d9b69a0436be48e1e89f06c7ef0a4b56ddb
5
5
  SHA512:
6
- metadata.gz: 437497757705da6bfeddab26b70ab0e4df3b7abe11f78b4433f6b5576b783f8d576c0c3b2e6612b4e941d58136e965b705e5db37176f59ee672c58bf64e5bb42
7
- data.tar.gz: d25820eec66069cba5bb25eb2a0c1cb262835a57ec60eef94199ab247332aa296ba5228ebf0a61c1bbc80c9b350aa19cfd2339cb11188c260973f3607cd6da5f
6
+ metadata.gz: 7ddefbaaa678ed70e29055dbe4452ee0f4f738fa2386e8e3f5dbaa42e682ca973fa0bd04e5c6b22b2570bfd290fd4fcfbb42c8f7e50650ffda50d0cfbb160cc7
7
+ data.tar.gz: 3fc210ea9c236f319f1c94dcb741d7d910eb7edb90020aafcbbee2a033bd232a9bb88dd26fc306869f7c3be715487f4655a517e5968f63461a6bbad57074099a
@@ -4,11 +4,11 @@ module Jsapi
4
4
  # Holds the \Jsapi configuration.
5
5
  class Configuration
6
6
  # The path where the API definitions are located relative to +Rails.root+.
7
- # The default is <code>"app/api_defs"</code>.
7
+ # The default is <code>"jsapi/api_defs"</code>.
8
8
  attr_accessor :api_defs_path
9
9
 
10
10
  def initialize # :nodoc:
11
- @api_defs_path = 'app/api_defs'
11
+ @api_defs_path = 'jsapi/api_defs'
12
12
  end
13
13
 
14
14
  # Returns the absolute +Pathname+ for +args+ within +api_defs_path+.
@@ -15,7 +15,9 @@ module Jsapi
15
15
  # Performs an API operation by calling the given block. The request parameters are
16
16
  # passed as an instance of the operation's model class to the block. The object
17
17
  # returned by the block is implicitly rendered according to the appropriate +response+
18
- # specification when the content type is a JSON MIME type.
18
+ # specification when the content type is a JSON MIME type. When content type is
19
+ # <code>application/json-seq</code>, the object returned by the block is streamed in
20
+ # JSON sequence text format.
19
21
  #
20
22
  # api_operation('foo') do |api_params|
21
23
  # # ...
@@ -153,18 +155,29 @@ module Jsapi
153
155
  end
154
156
 
155
157
  # Write response
156
- return unless response_model.json_type?
158
+ return unless response_model.json_type? || response_model.json_seq_type?
157
159
 
158
160
  response = Response.new(result, response_model, definitions, omit: omit)
159
161
  self.content_type = response_model.content_type
160
- render(json: response, status: status)
162
+
163
+ if response_model.json_seq_type?
164
+ self.response.status = status
165
+
166
+ self.response.stream.tap do |stream|
167
+ response.write_json_seq_to(stream)
168
+ ensure
169
+ stream.close
170
+ end
171
+ else
172
+ render(json: response, status: status)
173
+ end
161
174
  end
162
175
 
163
176
  def _api_params(operation, definitions, strong:)
164
177
  (operation.model || Model::Base).new(
165
178
  Parameters.new(
166
179
  params.except(:action, :controller, :format).permit!,
167
- request.headers,
180
+ request,
168
181
  operation,
169
182
  definitions,
170
183
  strong: strong
@@ -14,18 +14,30 @@ module Jsapi
14
14
  # If +strong+ is true+ parameters that can be mapped are accepted only. That means that
15
15
  # the instance created is invalid if +params+ contains any parameters that can't be
16
16
  # mapped to a parameter or a request body property of +operation+.
17
- def initialize(params, headers, operation, definitions, strong: false)
18
- @params = params.to_h
19
- @strong = strong == true
17
+ def initialize(params, request, operation, definitions, strong: false)
18
+ params = params.to_h
19
+ unassigned_params = params.dup
20
+
21
+ @params_to_be_validated = strong == true ? params.dup : {}
20
22
  @raw_attributes = {}
21
- @raw_additional_attributes = {}
22
23
 
23
24
  # Parameters
24
- operation.parameters.each do |name, parameter_model|
25
- parameter_model = parameter_model.resolve(definitions)
26
-
25
+ operation.resolved_parameters(definitions).each do |name, parameter_model|
27
26
  @raw_attributes[name] = JSON.wrap(
28
- parameter_model.in == 'header' ? headers[name] : @params[name],
27
+ case parameter_model.in
28
+ when 'header'
29
+ request.headers[name]
30
+ when 'querystring'
31
+ query_params = request.query_parameters
32
+ keys = query_params.keys
33
+
34
+ unassigned_params.except!(*keys)
35
+ @params_to_be_validated.except!(*keys)
36
+
37
+ parameter_model.object? ? params.slice(*keys) : query_params.to_query
38
+ else
39
+ unassigned_params.delete(name)
40
+ end,
29
41
  parameter_model.schema.resolve(definitions),
30
42
  definitions,
31
43
  context: :request
@@ -37,23 +49,24 @@ module Jsapi
37
49
  &.schema&.resolve(definitions)
38
50
  if request_body_schema&.object?
39
51
  request_body = JSON.wrap(
40
- @params.except(*operation.parameters.keys),
52
+ unassigned_params,
41
53
  request_body_schema,
42
54
  definitions,
43
55
  context: :request
44
56
  )
45
57
  @raw_attributes.merge!(request_body.raw_attributes)
46
58
  @raw_additional_attributes = request_body.raw_additional_attributes
59
+ @params_to_be_validated.except!(*@raw_additional_attributes.keys)
60
+ else
61
+ @raw_additional_attributes = {}
47
62
  end
48
63
  end
49
64
 
50
65
  # Validates the request parameters. Returns true if the parameters are valid, false
51
66
  # otherwise. Detected errors are added to +errors+.
52
67
  def validate(errors)
53
- [
54
- validate_attributes(errors),
55
- !@strong || validate_parameters(@params, attributes, errors)
56
- ].all?
68
+ validate_attributes(errors) &&
69
+ validate_parameters(@params_to_be_validated, attributes, errors)
57
70
  end
58
71
 
59
72
  private
@@ -50,11 +50,27 @@ module Jsapi
50
50
  # Returns the \JSON representation of the response as a string.
51
51
  def to_json(*)
52
52
  schema = @response.schema.resolve(@definitions)
53
- if @response.locale
54
- I18n.with_locale(@response.locale) { jsonify(@object, schema) }
55
- else
56
- jsonify(@object, schema)
57
- end.to_json
53
+ with_locale { jsonify(@object, schema) }.to_json
54
+ end
55
+
56
+ # Writes the response in \JSON sequence text format to +stream+.
57
+ def write_json_seq_to(stream)
58
+ schema = @response.schema.resolve(@definitions)
59
+ with_locale do
60
+ items, item_schema =
61
+ if schema.array? && @object.respond_to?(:each)
62
+ [@object, schema.items.resolve(@definitions)]
63
+ else
64
+ [[@object], schema]
65
+ end
66
+
67
+ items.each do |item|
68
+ stream.write("\u001E") # Record separator (see RFC 7464)
69
+ stream.write(jsonify(item, item_schema).to_json)
70
+ stream.write("\n")
71
+ end
72
+ end
73
+ nil
58
74
  end
59
75
 
60
76
  private
@@ -138,6 +154,14 @@ module Jsapi
138
154
 
139
155
  properties.presence
140
156
  end
157
+
158
+ def with_locale(&block)
159
+ if @response.locale
160
+ I18n.with_locale(@response.locale, &block)
161
+ else
162
+ yield
163
+ end
164
+ end
141
165
  end
142
166
  end
143
167
  end
@@ -147,6 +147,17 @@ module Jsapi
147
147
  api_definitions { parameter(name, **keywords, &block) }
148
148
  end
149
149
 
150
+ # Groups operations by path.
151
+ #
152
+ # api_path 'api' do
153
+ # operation 'foo'
154
+ # operation 'bar'
155
+ # end
156
+ #
157
+ def api_path(name, &block)
158
+ api_definitions { path(name, &block) }
159
+ end
160
+
150
161
  # Defines a reusable request body.
151
162
  #
152
163
  # api_request_body 'foo', type: 'string'
@@ -155,6 +155,20 @@ module Jsapi
155
155
  end
156
156
  end
157
157
 
158
+ # Groups operations by path.
159
+ #
160
+ # path 'api' do
161
+ # operation 'foo'
162
+ # operation 'bar'
163
+ # end
164
+ #
165
+ def path(name = nil, &block)
166
+ define('path', name&.inspect) do
167
+ path_model = @meta_model.add_path(name)
168
+ Path.new(path_model, &block) if block
169
+ end
170
+ end
171
+
158
172
  # Specifies a reusable request body.
159
173
  #
160
174
  # request_body 'foo', type: 'string'
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module DSL
5
+ class Path < Base
6
+ # Specifies an operation within the current path.
7
+ #
8
+ # operation 'foo' do
9
+ # parameter 'bar', type: 'string'
10
+ # response do
11
+ # property 'foo', type: 'string'
12
+ # end
13
+ # end
14
+ #
15
+ def operation(name = nil, **keywords, &block)
16
+ define('operation', name&.inspect) do
17
+ operation_model = @meta_model.owner.add_operation(name, @meta_model.name, keywords)
18
+ Operation.new(operation_model, &block) if block
19
+ end
20
+ end
21
+
22
+ # Specifies a parameter applicable for all operations in this path.
23
+ #
24
+ # parameter 'foo', type: 'string'
25
+ #
26
+ # See Meta::Path#parameters for further information.
27
+ def parameter(name, **keywords, &block)
28
+ define('parameter', name.inspect) do
29
+ parameter_model = @meta_model.add_parameter(name, keywords)
30
+ Parameter.new(parameter_model, &block) if block
31
+ end
32
+ end
33
+
34
+ # Specifies a nested path.
35
+ def path(name = nil, &block)
36
+ define('path', name&.inspect) do
37
+ path_model = @meta_model.owner.add_path(@meta_model.name + name.to_s)
38
+ Path.new(path_model, &block) if block
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
data/lib/jsapi/dsl.rb CHANGED
@@ -9,6 +9,7 @@ require_relative 'dsl/request_body'
9
9
  require_relative 'dsl/response'
10
10
  require_relative 'dsl/callback'
11
11
  require_relative 'dsl/operation'
12
+ require_relative 'dsl/path'
12
13
  require_relative 'dsl/definitions'
13
14
  require_relative 'dsl/class_methods'
14
15
 
@@ -23,7 +23,7 @@ module Jsapi
23
23
  # Returns a hash representing the \OpenAPI callback object.
24
24
  def to_openapi(version, definitions)
25
25
  operations.transform_values do |operation|
26
- { operation.method => operation.to_openapi(version, definitions) }
26
+ OpenAPI::PathItem.new([operation]).to_openapi(version, definitions)
27
27
  end
28
28
  end
29
29
  end
@@ -8,7 +8,7 @@ module Jsapi
8
8
  ##
9
9
  # :attr: base_path
10
10
  # The base path of the API. Applies to \OpenAPI 2.0.
11
- attribute :base_path, String
11
+ attribute :base_path, Pathname
12
12
 
13
13
  ##
14
14
  # :attr: callbacks
@@ -65,6 +65,11 @@ module Jsapi
65
65
  # The reusable Parameter objects.
66
66
  attribute :parameters, { String => Parameter }, accessors: %i[reader writer]
67
67
 
68
+ ##
69
+ # :attr: paths
70
+ # The Path objects.
71
+ attribute :paths, { Pathname => Path }, accessors: %i[reader writer]
72
+
68
73
  ##
69
74
  # :attr: rescue_handlers
70
75
  # The RescueHandler objects.
@@ -138,15 +143,27 @@ module Jsapi
138
143
  @parent&.inherited(self)
139
144
  end
140
145
 
141
- def add_operation(name = nil, keywords = {}) # :nodoc:
146
+ def add_operation(name, parent_path = nil, keywords = {}) # :nodoc:
147
+ parent_path, keywords = nil, parent_path if parent_path.is_a?(Hash)
148
+
142
149
  name = name.nil? ? default_operation_name : name.to_s
143
- keywords = keywords.reverse_merge(path: default_operation_path)
144
- (@operations ||= {})[name] = Operation.new(name, keywords)
150
+ parent_path ||= default_operation_name unless keywords[:path].present?
151
+
152
+ (@operations ||= {})[name] = Operation.new(name, parent_path, keywords)
145
153
  end
146
154
 
147
155
  def add_parameter(name, keywords = {}) # :nodoc:
148
156
  name = name.to_s
149
- (@parameters ||= {})[name] = Parameter.new(name, keywords)
157
+
158
+ Parameter.new(name, keywords).tap do |parameter|
159
+ (@parameters ||= {})[name] = parameter
160
+ attribute_changed(:parameters)
161
+ end
162
+ end
163
+
164
+ def add_path(name, keywords = {}) # :nodoc:
165
+ pathname = Pathname.from(name)
166
+ (@paths ||= {})[pathname] ||= Path.new(pathname, self, keywords)
150
167
  end
151
168
 
152
169
  # Returns an array containing itself and all of the +Definitions+ instances
@@ -207,6 +224,14 @@ module Jsapi
207
224
  self
208
225
  end
209
226
 
227
+ # Resets the memoized parameters for the given path.
228
+ def invalidate_path_parameters(pathname)
229
+ pathname = Pathname.from(pathname)
230
+
231
+ @path_parameters&.delete(pathname)
232
+ each_descendant { |descendant| descendant.invalidate_path_parameters(pathname) }
233
+ end
234
+
210
235
  # Returns a hash representing the \JSON \Schema document for +name+.
211
236
  def json_schema_document(name)
212
237
  find_schema(name)&.to_json_schema&.tap do |json_schema_document|
@@ -228,13 +253,18 @@ module Jsapi
228
253
  version = OpenAPI::Version.from(version)
229
254
  operations = objects[:operations].values
230
255
 
231
- openapi_paths =
232
- operations.group_by { |operation| operation.path || default_operation_path }
233
- .transform_values do |operations_by_path|
234
- operations_by_path.index_by(&:method).transform_values do |operation|
235
- operation.to_openapi(version, self)
236
- end
237
- end.presence
256
+ openapi_paths = operations.group_by(&:full_path).to_h do |key, value|
257
+ [
258
+ key.to_s,
259
+ OpenAPI::PathItem.new(
260
+ value,
261
+ description: path_description(key),
262
+ parameters: path_parameters(key),
263
+ summary: path_summary(key),
264
+ servers: path_servers(key)
265
+ ).to_openapi(version, self)
266
+ ]
267
+ end.presence
238
268
 
239
269
  openapi_objects =
240
270
  if version.major == 2
@@ -254,7 +284,7 @@ module Jsapi
254
284
  swagger: '2.0',
255
285
  info: openapi_objects[:info],
256
286
  host: openapi_objects[:host] || uri&.hostname,
257
- basePath: openapi_objects[:base_path] || uri&.path,
287
+ basePath: openapi_objects[:base_path]&.to_s || uri&.path,
258
288
  schemes: openapi_objects[:schemes] || Array(uri&.scheme).presence,
259
289
  consumes: operations.filter_map do |operation|
260
290
  operation.consumes(self)
@@ -271,10 +301,16 @@ module Jsapi
271
301
  else
272
302
  {
273
303
  # Order according to the OpenAPI specification 3.x
274
- openapi: version.minor.zero? ? '3.0.3' : '3.1.1',
304
+ openapi:
305
+ case version.minor
306
+ when 0 then '3.0.3'
307
+ when 1 then '3.1.1'
308
+ when 2 then '3.2.0'
309
+ end,
275
310
  info: openapi_objects[:info],
276
- servers: openapi_objects[:servers] ||
277
- [default_server&.to_openapi].compact.presence,
311
+ servers:
312
+ openapi_objects[:servers] ||
313
+ [default_server&.to_openapi(version)].compact.presence,
278
314
  paths: openapi_paths,
279
315
  components: {
280
316
  schemas: openapi_objects[:schemas],
@@ -296,6 +332,44 @@ module Jsapi
296
332
  )
297
333
  end
298
334
 
335
+ ##
336
+ # :method: path_description
337
+ # :args: pathname
338
+ # Returns the most accurate description for the given path.
339
+
340
+ ##
341
+ # :method: path_servers
342
+ # :args: pathname
343
+ # Returns the most accurate Server objects for the given path.
344
+
345
+ ##
346
+ # :method: path_summary
347
+ # :args: pathname
348
+ # Returns the most accurate summary for the given path.
349
+
350
+ %i[description servers summary].each do |name|
351
+ define_method(:"path_#{name}") do |arg|
352
+ Pathname.from(arg).ancestors.each do |pathname|
353
+ ancestors.each do |ancestor|
354
+ value = ancestor.path(pathname)&.public_send(name)
355
+ return value if value.present?
356
+ end
357
+ end
358
+ nil
359
+ end
360
+ end
361
+
362
+ # Returns a hash containing the Parameter objects that are applicable to all
363
+ # operations in the given path.
364
+ # :args: pathname
365
+ def path_parameters(arg)
366
+ arg = Pathname.from(arg || '')
367
+
368
+ (@path_parameters ||= {})[arg] ||= arg.ancestors.flat_map do |pathname|
369
+ ancestors.filter_map { |ancestor| ancestor.path(pathname)&.parameters }
370
+ end.reduce(&:reverse_merge) || {}
371
+ end
372
+
299
373
  # Returns the first RescueHandler to handle +exception+, or nil if no one could be found.
300
374
  def rescue_handler_for(exception)
301
375
  objects[:rescue_handlers].find { |r| r.match?(exception) }
@@ -334,15 +408,14 @@ module Jsapi
334
408
  def invalidate_ancestors
335
409
  @ancestors = nil
336
410
  @objects = nil
337
- @children&.each(&:invalidate_ancestors)
338
- @dependent_definitions&.each(&:invalidate_ancestors)
411
+ @path_parameters = nil
412
+ each_descendant(&:invalidate_ancestors)
339
413
  end
340
414
 
341
415
  # Invalidates cached objects.
342
416
  def invalidate_objects
343
417
  @objects = nil
344
- @children&.each(&:invalidate_objects)
345
- @dependent_definitions&.each(&:invalidate_objects)
418
+ each_descendant(&:invalidate_objects)
346
419
  end
347
420
 
348
421
  private
@@ -361,10 +434,6 @@ module Jsapi
361
434
  end
362
435
  end
363
436
 
364
- def default_operation_path
365
- @default_operation_path ||= "/#{default_operation_name}"
366
- end
367
-
368
437
  def default_server
369
438
  @default_server ||=
370
439
  if (name = @owner.try(:name))
@@ -376,6 +445,11 @@ module Jsapi
376
445
  end
377
446
  end
378
447
 
448
+ def each_descendant(&block)
449
+ [*@children, *dependent_definitions].each(&block)
450
+ nil
451
+ end
452
+
379
453
  def objects
380
454
  @objects ||= ancestors.each_with_object({}) do |ancestor, objects|
381
455
  self.class.attribute_names.each do |key|
@@ -29,13 +29,11 @@ module Jsapi
29
29
 
30
30
  # Returns a hash representing the \OpenAPI example object.
31
31
  def to_openapi(*)
32
- with_openapi_extensions(summary: summary, description: description).tap do |result|
33
- if external?
34
- result[:external_value] = value
35
- else
36
- result[:value] = value
32
+ with_openapi_extensions(
33
+ { summary: summary, description: description }.tap do |result|
34
+ result[external? ? :externalValue : :value] = value
37
35
  end
38
- end
36
+ )
39
37
  end
40
38
  end
41
39
  end
@@ -33,13 +33,15 @@ module Jsapi
33
33
  attribute :server, Server
34
34
 
35
35
  # Returns a hash representing the \OpenAPI link object.
36
- def to_openapi(*)
36
+ def to_openapi(version, *)
37
+ version = OpenAPI::Version.from(version)
38
+
37
39
  with_openapi_extensions(
38
40
  operationId: operation_id,
39
41
  parameters: parameters.presence,
40
42
  requestBody: request_body,
41
43
  description: description,
42
- server: server&.to_openapi
44
+ server: server&.to_openapi(version)
43
45
  )
44
46
  end
45
47
  end
@@ -18,12 +18,20 @@ module Jsapi
18
18
  # The authorization URL to be used for the flow.
19
19
  attribute :authorization_url, String
20
20
 
21
+ ##
22
+ # :attr: device_authorization_url
23
+ # The device authorization URL to be used for the flow.
24
+ #
25
+ # Note that the device authorization URL was introduced with \OpenAPI 3.2.
26
+ # It is omitted when generating an \OpenAPI document with a lower version.
27
+ attribute :device_authorization_url, String
28
+
21
29
  ##
22
30
  # :attr: refresh_url
23
31
  # The refresh URL to be used for the flow.
24
32
  #
25
33
  # Note that the refresh URL was introduced with \OpenAPI 3.0. It is
26
- # skipped when generating an \OpenAPI 2.0 document.
34
+ # omitted when generating an \OpenAPI document with a lower version.
27
35
  attribute :refresh_url, String
28
36
 
29
37
  ##
@@ -42,8 +50,9 @@ module Jsapi
42
50
 
43
51
  with_openapi_extensions(
44
52
  authorizationUrl: authorization_url,
53
+ deviceAuthorizationUrl: (device_authorization_url if version >= OpenAPI::V3_2),
45
54
  tokenUrl: token_url,
46
- refreshUrl: (refresh_url if version.major > 2),
55
+ refreshUrl: (refresh_url if version >= OpenAPI::V3_0),
47
56
  scopes: scopes.transform_values(&:description)
48
57
  )
49
58
  end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ module OpenAPI
6
+ class PathItem # :nodoc:
7
+ def initialize(operations, keywords = {})
8
+ @operations = operations
9
+ @summary = keywords[:summary]
10
+ @description = keywords[:description]
11
+ @servers = keywords[:servers]
12
+ @parameters = keywords[:parameters]
13
+ end
14
+
15
+ def to_openapi(version, definitions)
16
+ version = OpenAPI::Version.from(version)
17
+
18
+ {}.tap do |fields|
19
+ if version >= OpenAPI::V3_0
20
+ fields[:summary] = @summary if @summary.present?
21
+ fields[:description] = @description if @description.present?
22
+ end
23
+
24
+ # Operations
25
+ @operations&.each do |operation|
26
+ method = operation.method
27
+ standardized_method = method.downcase
28
+
29
+ if standard_method?(standardized_method, version)
30
+ fields[standardized_method] = operation.to_openapi(version, definitions)
31
+ elsif version >= OpenAPI::V3_2
32
+ additional_operations = fields[:additionalOperations] ||= {}
33
+ additional_operations[method] = operation.to_openapi(version, definitions)
34
+ end
35
+ end
36
+
37
+ # Servers
38
+ if version >= OpenAPI::V3_0 && @servers.present?
39
+ fields[:servers] = @servers.map { |server| server.to_openapi(version) }
40
+ end
41
+
42
+ # Parameters
43
+ if @parameters.present?
44
+ fields[:parameters] = @parameters.values.map do |parameter|
45
+ parameter.to_openapi(version, definitions)
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def standard_method?(method, version)
54
+ case method
55
+ when 'delete', 'get', 'head', 'options', 'patch', 'post', 'put'
56
+ true
57
+ when 'trace'
58
+ version >= OpenAPI::V3_0
59
+ when 'query'
60
+ version >= OpenAPI::V3_2
61
+ else
62
+ false
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -19,6 +19,8 @@ module Jsapi
19
19
  new(3, 0)
20
20
  when '3.1'
21
21
  new(3, 1)
22
+ when '3.2'
23
+ new(3, 2)
22
24
  else
23
25
  raise ArgumentError, "unsupported OpenAPI version: #{version.inspect}"
24
26
  end
@@ -46,6 +48,10 @@ module Jsapi
46
48
  minor <=> other.minor
47
49
  end
48
50
 
51
+ def inspect
52
+ "<#{self.class.name} #{self}>"
53
+ end
54
+
49
55
  def to_s # :nodoc:
50
56
  "#{major}.#{minor}"
51
57
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'openapi/extensions'
4
4
  require_relative 'openapi/version'
5
+ require_relative 'openapi/path_item'
5
6
 
6
7
  module Jsapi
7
8
  module Meta
@@ -9,6 +10,7 @@ module Jsapi
9
10
  V2_0 = Version.new(2, 0)
10
11
  V3_0 = Version.new(3, 0)
11
12
  V3_1 = Version.new(3, 1)
13
+ V3_2 = Version.new(3, 2)
12
14
  end
13
15
  end
14
16
  end