jsapi 0.2.0 → 0.3.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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jsapi/controller/methods.rb +4 -3
  3. data/lib/jsapi/controller/parameters.rb +2 -5
  4. data/lib/jsapi/controller/response.rb +46 -24
  5. data/lib/jsapi/json/object.rb +5 -8
  6. data/lib/jsapi/meta/example/model.rb +3 -4
  7. data/lib/jsapi/meta/openapi/contact.rb +3 -5
  8. data/lib/jsapi/meta/openapi/extensions.rb +31 -0
  9. data/lib/jsapi/meta/openapi/external_documentation.rb +3 -4
  10. data/lib/jsapi/meta/openapi/info.rb +4 -2
  11. data/lib/jsapi/meta/openapi/license.rb +3 -4
  12. data/lib/jsapi/meta/openapi/link/model.rb +4 -2
  13. data/lib/jsapi/meta/openapi/oauth_flow.rb +4 -2
  14. data/lib/jsapi/meta/openapi/root.rb +36 -32
  15. data/lib/jsapi/meta/openapi/security_scheme/api_key.rb +4 -2
  16. data/lib/jsapi/meta/openapi/security_scheme/http/basic.rb +17 -12
  17. data/lib/jsapi/meta/openapi/security_scheme/http/bearer.rb +4 -2
  18. data/lib/jsapi/meta/openapi/security_scheme/http/other.rb +4 -2
  19. data/lib/jsapi/meta/openapi/security_scheme/oauth2.rb +5 -5
  20. data/lib/jsapi/meta/openapi/security_scheme/open_id_connect.rb +4 -2
  21. data/lib/jsapi/meta/openapi/server.rb +4 -2
  22. data/lib/jsapi/meta/openapi/server_variable.rb +4 -2
  23. data/lib/jsapi/meta/openapi/tag.rb +4 -2
  24. data/lib/jsapi/meta/openapi.rb +1 -0
  25. data/lib/jsapi/meta/operation.rb +6 -3
  26. data/lib/jsapi/meta/parameter/model.rb +53 -47
  27. data/lib/jsapi/meta/request_body/model.rb +5 -3
  28. data/lib/jsapi/meta/response/model.rb +27 -22
  29. data/lib/jsapi/meta/schema/additional_properties.rb +29 -0
  30. data/lib/jsapi/meta/schema/base.rb +34 -29
  31. data/lib/jsapi/meta/schema/object.rb +9 -1
  32. data/lib/jsapi/meta/schema.rb +2 -1
  33. data/lib/jsapi/version.rb +1 -1
  34. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 72aa6ad77a99ade6acb3d5c70b6521b640e012cccc7e9ab00b9d55db3062d4d6
4
- data.tar.gz: 96ec077740dde1963ca6b448662e1f4370f3bc0b54778db2d03d9447ba69aa3b
3
+ metadata.gz: 78a92c9f73f4a39ede65ad5b0b5646db67dc8b9e2b3ca444b6de32fdb75e349a
4
+ data.tar.gz: e235535541854c172f578cce7cf112ceebe880971643cb33125a76cc61bbd88a
5
5
  SHA512:
6
- metadata.gz: ec7997fbedc7c9035f9e36d5426b23502b87f4c4c654b1e02f93db711f514a8a434d371dc4761156165c439256ad3f339c7a151b1b35c8dcbf3d1e14f1fd4628
7
- data.tar.gz: 1f2952e80bf883302fd835f9425bae0bd2e7629b2746d3abec4b19a259c5abbd9869534b18eb6c69afd94a9397e13c7d86d78720f3573d92ef355b1e66b99fb0
6
+ metadata.gz: 7c139744bacb90f772bb40779a8d149daf025dd838ffc3268c35640dfe18da1934e62ffd2e319658478ef5c4d54a4f630ed76eabf60a22df641f1ce7da218150
7
+ data.tar.gz: b9e72517dea9061eaa23b5ff5631745a18769614c04d2a91ae97db2e715c30504bc5b849fdcaa89384f7fb2618e5ea639601f637b9583b5fec219bd053006f08
@@ -13,9 +13,9 @@ module Jsapi
13
13
  end
14
14
 
15
15
  # Performs an API operation by calling the given block. The request parameters are
16
- # passed as an instance of the operation's model class to the block. The object
17
- # returned by the block is implicitly rendered according to the appropriate
18
- # +response+ specification.
16
+ # passed as an instance of the operation's model class to the block. Parameter names
17
+ # are converted to snake case. The object returned by the block is implicitly rendered
18
+ # according to the appropriate +response+ specification.
19
19
  #
20
20
  # api_operation('foo') do |api_params|
21
21
  # # ...
@@ -53,6 +53,7 @@ module Jsapi
53
53
  end
54
54
 
55
55
  # Returns the request parameters as an instance of the operation's model class.
56
+ # Parameter names are converted to snake case.
56
57
  #
57
58
  # params = api_params('foo')
58
59
  #
@@ -25,15 +25,12 @@ module Jsapi
25
25
  end
26
26
  request_body = operation.request_body&.resolve(definitions)
27
27
  if request_body && request_body.schema.respond_to?(:properties)
28
- meta_models.merge!(
29
- request_body.schema.resolve_properties(:write, definitions)
30
- )
28
+ meta_models.merge!(request_body.schema.resolve_properties(:write, definitions))
31
29
  end
32
30
 
33
31
  # Wrap params
34
32
  meta_models.each do |name, meta_model|
35
- @raw_attributes[name] =
36
- JSON.wrap(params[name], meta_model.schema, definitions)
33
+ @raw_attributes[name.underscore] = JSON.wrap(params[name], meta_model.schema, definitions)
37
34
  end
38
35
  end
39
36
 
@@ -5,8 +5,8 @@ module Jsapi
5
5
  # Used to serialize a response.
6
6
  class Response
7
7
 
8
- # Creates a new instance to serialize +object+ according to +response+. References are
9
- # resolved to API components in +definitions+.
8
+ # Creates a new instance to serialize +object+ according to +response+. References
9
+ # are resolved to API components in +definitions+.
10
10
  def initialize(object, response, definitions)
11
11
  @object = object
12
12
  @response = response
@@ -37,33 +37,13 @@ module Jsapi
37
37
 
38
38
  case schema.type
39
39
  when 'array'
40
- item_schema = schema.items.resolve(@definitions)
41
- Array(object).map { |item| serialize(item, item_schema, path) }
40
+ serialize_array(object, schema, path)
42
41
  when 'integer'
43
42
  schema.convert(object.to_i)
44
43
  when 'number'
45
44
  schema.convert(object.to_f)
46
45
  when 'object'
47
- return if object.blank? # {}
48
-
49
- # Select inherriting schema on polymorphism
50
- if (discriminator = schema.discriminator)
51
- discriminator_property = schema.properties[discriminator.property_name]
52
- schema = discriminator.resolve(
53
- object.public_send(
54
- discriminator_property.source || discriminator_property.name
55
- ),
56
- @definitions
57
- )
58
- end
59
- # Serialize properties
60
- schema.resolve_properties(:read, @definitions).transform_values do |property|
61
- serialize(
62
- object.public_send(property.source || property.name),
63
- property.schema.resolve(@definitions),
64
- path.nil? ? property.name : "#{path}.#{property.name}"
65
- )
66
- end
46
+ serialize_object(object, schema, path)
67
47
  when 'string'
68
48
  schema.convert(
69
49
  case schema.format
@@ -81,6 +61,48 @@ module Jsapi
81
61
  object
82
62
  end
83
63
  end
64
+
65
+ def serialize_array(array, schema, path)
66
+ item_schema = schema.items.resolve(@definitions)
67
+ Array(array).map { |item| serialize(item, item_schema, path) }
68
+ end
69
+
70
+ def serialize_object(object, schema, path)
71
+ return if object.blank? # {}
72
+
73
+ # Select inherriting schema on polymorphism
74
+ if (discriminator = schema.discriminator)
75
+ discriminator_property = schema.properties[discriminator.property_name]
76
+ schema = discriminator.resolve(
77
+ object.public_send(discriminator_property.source || discriminator_property.name.underscore),
78
+ @definitions
79
+ )
80
+ end
81
+ # Serialize properties
82
+ properties = schema.resolve_properties(:read, @definitions).transform_values do |property|
83
+ serialize(
84
+ object.public_send(property.source || property.name.underscore),
85
+ property.schema.resolve(@definitions),
86
+ path.nil? ? property.name : "#{path}.#{property.name}"
87
+ )
88
+ end
89
+ if (additional_properties = schema.additional_properties&.resolve(@definitions))
90
+ additional_properties_schema = additional_properties.schema.resolve(@definitions)
91
+
92
+ object.public_send(additional_properties.source)&.each do |key, value|
93
+ # Don't replace the property with the same key
94
+ next if properties.key?(key = key.to_s)
95
+
96
+ # Serialize the additional property
97
+ properties[key] = serialize(
98
+ value,
99
+ additional_properties_schema,
100
+ path.nil? ? key : "#{path}.#{key}"
101
+ )
102
+ end
103
+ end
104
+ properties
105
+ end
84
106
  end
85
107
  end
86
108
  end
@@ -11,16 +11,13 @@ module Jsapi
11
11
  def initialize(attributes, schema, definitions)
12
12
  # Select inherriting schema on polymorphism
13
13
  if (discriminator = schema.discriminator)
14
- schema = discriminator.resolve(
15
- attributes[discriminator.property_name],
16
- definitions
17
- )
14
+ schema = discriminator.resolve(attributes[discriminator.property_name], definitions)
18
15
  end
19
16
  # Wrap attribute values
20
- @raw_attributes = schema.resolve_properties(:write, definitions)
21
- .transform_values do |property|
22
- JSON.wrap(attributes[property.name], property.schema, definitions)
23
- end
17
+ @raw_attributes =
18
+ schema.resolve_properties(:write, definitions).transform_values do |property|
19
+ JSON.wrap(attributes[property.name], property.schema, definitions)
20
+ end
24
21
 
25
22
  super(schema)
26
23
  end
@@ -4,6 +4,8 @@ module Jsapi
4
4
  module Meta
5
5
  module Example
6
6
  class Model < Base
7
+ include OpenAPI::Extensions
8
+
7
9
  ##
8
10
  # :attr: description
9
11
  # The optional description of the example.
@@ -26,10 +28,7 @@ module Jsapi
26
28
 
27
29
  # Returns a hash representing the \OpenAPI example object.
28
30
  def to_openapi(*)
29
- {}.tap do |hash|
30
- hash[:summary] = summary if summary.present?
31
- hash[:description] = description if description.present?
32
-
31
+ with_openapi_extensions(summary: summary, description: description).tap do |hash|
33
32
  if external?
34
33
  hash[:external_value] = value
35
34
  else
@@ -5,6 +5,8 @@ module Jsapi
5
5
  module OpenAPI
6
6
  # Represents a contact object.
7
7
  class Contact < Base
8
+ include Extensions
9
+
8
10
  ##
9
11
  # :attr: email
10
12
  # The email address of the contact.
@@ -22,11 +24,7 @@ module Jsapi
22
24
 
23
25
  # Returns a hash representing the contact object.
24
26
  def to_openapi(*)
25
- {
26
- name: name,
27
- url: url,
28
- email: email
29
- }.compact
27
+ with_openapi_extensions(name: name, url: url, email: email)
30
28
  end
31
29
  end
32
30
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ module OpenAPI
6
+ module Extensions
7
+ # Adds an \OpenAPI extension.
8
+ #
9
+ # Raises an +ArgumentError+ if +name+ is blank.
10
+ def add_openapi_extension(name, value = nil)
11
+ raise ArgumentError, "name can't be blank" if name.blank?
12
+
13
+ openapi_extensions["x-#{name}".to_sym] = value
14
+ end
15
+
16
+ # Returns a hash containing the \OpenAPI extensions.
17
+ def openapi_extensions
18
+ @openapi_extensions ||= {}
19
+ end
20
+
21
+ private
22
+
23
+ def with_openapi_extensions(keywords = {}) # :nodoc:
24
+ keywords.merge!(openapi_extensions)
25
+ keywords.compact!
26
+ keywords
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -5,6 +5,8 @@ module Jsapi
5
5
  module OpenAPI
6
6
  # Represents an external documentation object.
7
7
  class ExternalDocumentation < Base
8
+ include Extensions
9
+
8
10
  ##
9
11
  # :attr: description
10
12
  # The optional description of the external documentation.
@@ -17,10 +19,7 @@ module Jsapi
17
19
 
18
20
  # Returns a hash representing the external documentation object.
19
21
  def to_openapi(*)
20
- {
21
- url: url,
22
- description: description
23
- }.compact
22
+ with_openapi_extensions(url: url, description: description)
24
23
  end
25
24
  end
26
25
  end
@@ -5,6 +5,8 @@ module Jsapi
5
5
  module OpenAPI
6
6
  # Represents an info object.
7
7
  class Info < Base
8
+ include Extensions
9
+
8
10
  ##
9
11
  # :attr: contact
10
12
  # The optional Contact object.
@@ -37,14 +39,14 @@ module Jsapi
37
39
 
38
40
  # Returns a hash representing the info object.
39
41
  def to_openapi(*)
40
- {
42
+ with_openapi_extensions(
41
43
  title: title,
42
44
  version: version,
43
45
  description: description,
44
46
  termsOfService: terms_of_service,
45
47
  contact: contact&.to_openapi,
46
48
  license: license&.to_openapi
47
- }.compact
49
+ )
48
50
  end
49
51
  end
50
52
  end
@@ -5,6 +5,8 @@ module Jsapi
5
5
  module OpenAPI
6
6
  # Represents a license object.
7
7
  class License < Base
8
+ include Extensions
9
+
8
10
  ##
9
11
  # :attr: name
10
12
  # The name of the license.
@@ -17,10 +19,7 @@ module Jsapi
17
19
 
18
20
  # Returns a hash representing the license object.
19
21
  def to_openapi(*)
20
- {
21
- name: name,
22
- url: url
23
- }.compact
22
+ with_openapi_extensions(name: name, url: url)
24
23
  end
25
24
  end
26
25
  end
@@ -6,6 +6,8 @@ module Jsapi
6
6
  module Link
7
7
  # Represents a link object.
8
8
  class Model < Base
9
+ include Extensions
10
+
9
11
  ##
10
12
  # :attr: description
11
13
  # The optional description of the link.
@@ -33,13 +35,13 @@ module Jsapi
33
35
 
34
36
  # Returns a hash representing the link object.
35
37
  def to_openapi(*)
36
- {
38
+ with_openapi_extensions(
37
39
  operationId: operation_id,
38
40
  parameters: parameters,
39
41
  requestBody: request_body,
40
42
  description: description,
41
43
  server: server&.to_openapi
42
- }.compact
44
+ )
43
45
  end
44
46
  end
45
47
  end
@@ -5,6 +5,8 @@ module Jsapi
5
5
  module OpenAPI
6
6
  # Represents an OAuth flow object.
7
7
  class OAuthFlow < Base
8
+ include Extensions
9
+
8
10
  class Scope < Base
9
11
  ##
10
12
  # :attr: description
@@ -37,12 +39,12 @@ module Jsapi
37
39
 
38
40
  # Returns a hash representing the OAuth flow object.
39
41
  def to_openapi(version)
40
- {
42
+ with_openapi_extensions(
41
43
  authorizationUrl: authorization_url,
42
44
  tokenUrl: token_url,
43
45
  refreshUrl: (refresh_url if version.major > 2),
44
46
  scopes: scopes.transform_values(&:description)
45
- }.compact
47
+ )
46
48
  end
47
49
  end
48
50
  end
@@ -5,6 +5,8 @@ module Jsapi
5
5
  module OpenAPI
6
6
  # Represents an \OpenAPI object.
7
7
  class Root < Base
8
+ include Extensions
9
+
8
10
  ##
9
11
  # :attr: callbacks
10
12
  # The reusable Callback objects. Applies to \OpenAPI 3.0 and higher.
@@ -95,38 +97,40 @@ module Jsapi
95
97
  value.to_openapi(version)
96
98
  end
97
99
 
98
- if version.major == 2
99
- uri = servers&.first&.then { |server| URI(server.url) }
100
- {
101
- swagger: '2.0',
102
- info: info&.to_openapi,
103
- host: host || uri&.hostname,
104
- basePath: base_path || uri&.path,
105
- schemes: schemes || uri&.scheme&.then { |scheme| [scheme] },
106
- consumes: consumed_mime_types,
107
- produces: produced_mime_types,
108
- securityDefinitions: security_schemes,
109
- security: security_requirements&.map(&:to_openapi),
110
- tags: tags&.map(&:to_openapi),
111
- externalDocs: external_docs&.to_openapi
112
- }
113
- else
114
- {
115
- openapi: version.minor.zero? ? '3.0.3' : '3.1.0',
116
- info: info&.to_openapi,
117
- servers: servers&.map(&:to_openapi),
118
- components: {
119
- callbacks: callbacks&.transform_values do |callback|
120
- callback.to_openapi(version, definitions)
121
- end,
122
- links: links&.transform_values(&:to_openapi),
123
- securitySchemes: security_schemes
124
- }.compact.presence,
125
- security: security_requirements&.map(&:to_openapi),
126
- tags: tags&.map(&:to_openapi),
127
- externalDocs: external_docs&.to_openapi
128
- }
129
- end.compact
100
+ with_openapi_extensions(
101
+ if version.major == 2
102
+ uri = servers&.first&.then { |server| URI(server.url) }
103
+ {
104
+ swagger: '2.0',
105
+ info: info&.to_openapi,
106
+ host: host || uri&.hostname,
107
+ basePath: base_path || uri&.path,
108
+ schemes: schemes || uri&.scheme&.then { |scheme| [scheme] },
109
+ consumes: consumed_mime_types,
110
+ produces: produced_mime_types,
111
+ securityDefinitions: security_schemes,
112
+ security: security_requirements&.map(&:to_openapi),
113
+ tags: tags&.map(&:to_openapi),
114
+ externalDocs: external_docs&.to_openapi
115
+ }
116
+ else
117
+ {
118
+ openapi: version.minor.zero? ? '3.0.3' : '3.1.0',
119
+ info: info&.to_openapi,
120
+ servers: servers&.map(&:to_openapi),
121
+ components: {
122
+ callbacks: callbacks&.transform_values do |callback|
123
+ callback.to_openapi(version, definitions)
124
+ end,
125
+ links: links&.transform_values(&:to_openapi),
126
+ securitySchemes: security_schemes
127
+ }.compact.presence,
128
+ security: security_requirements&.map(&:to_openapi),
129
+ tags: tags&.map(&:to_openapi),
130
+ externalDocs: external_docs&.to_openapi
131
+ }
132
+ end
133
+ )
130
134
  end
131
135
  end
132
136
  end
@@ -6,6 +6,8 @@ module Jsapi
6
6
  module SecurityScheme
7
7
  # Represents a security scheme based on an API key.
8
8
  class APIKey < Base
9
+ include Extensions
10
+
9
11
  ##
10
12
  # :attr: in
11
13
  # The location of the API key. Possible values are:
@@ -24,12 +26,12 @@ module Jsapi
24
26
 
25
27
  # Returns a hash representing the security scheme object.
26
28
  def to_openapi(*)
27
- {
29
+ with_openapi_extensions(
28
30
  type: 'apiKey',
29
31
  name: name,
30
32
  in: self.in,
31
33
  description: description
32
- }.compact
34
+ )
33
35
  end
34
36
  end
35
37
  end
@@ -7,21 +7,26 @@ module Jsapi
7
7
  module HTTP
8
8
  # Represents a security scheme based on \HTTP basic authentication.
9
9
  class Basic < Base
10
+ include Extensions
11
+
10
12
  # Returns a hash representing the security scheme object.
11
13
  def to_openapi(version)
12
14
  version = OpenAPI::Version.from(version)
13
- if version.major == 2
14
- {
15
- type: 'basic',
16
- description: description
17
- }
18
- else
19
- {
20
- type: 'http',
21
- scheme: 'basic',
22
- description: description
23
- }
24
- end.compact
15
+
16
+ with_openapi_extensions(
17
+ if version.major == 2
18
+ {
19
+ type: 'basic',
20
+ description: description
21
+ }
22
+ else
23
+ {
24
+ type: 'http',
25
+ scheme: 'basic',
26
+ description: description
27
+ }
28
+ end
29
+ )
25
30
  end
26
31
  end
27
32
  end
@@ -11,6 +11,8 @@ module Jsapi
11
11
  # Thus, a security scheme of this class is skipped when generating
12
12
  # an \OpenAPI 2.0 document.
13
13
  class Bearer < Base
14
+ include Extensions
15
+
14
16
  ##
15
17
  # :attr: bearer_format
16
18
  # The optional format of the bearer token.
@@ -22,12 +24,12 @@ module Jsapi
22
24
  version = OpenAPI::Version.from(version)
23
25
  return if version.major == 2
24
26
 
25
- {
27
+ with_openapi_extensions(
26
28
  type: 'http',
27
29
  scheme: 'bearer',
28
30
  bearerFormat: bearer_format,
29
31
  description: description
30
- }.compact
32
+ )
31
33
  end
32
34
  end
33
35
  end
@@ -12,6 +12,8 @@ module Jsapi
12
12
  # a security scheme of this class is skipped when generating an
13
13
  # \OpenAPI 2.0 document.
14
14
  class Other < Base
15
+ include Extensions
16
+
15
17
  ##
16
18
  # :attr: scheme
17
19
  # The mandatory \HTTP authentication scheme.
@@ -23,11 +25,11 @@ module Jsapi
23
25
  version = OpenAPI::Version.from(version)
24
26
  return if version.major == 2
25
27
 
26
- {
28
+ with_openapi_extensions(
27
29
  type: 'http',
28
30
  scheme: scheme,
29
31
  description: description
30
- }.compact
32
+ )
31
33
  end
32
34
  end
33
35
  end
@@ -6,6 +6,8 @@ module Jsapi
6
6
  module SecurityScheme
7
7
  # Represents a security scheme based on \OAuth2.
8
8
  class OAuth2 < Base
9
+ include Extensions
10
+
9
11
  ##
10
12
  # :attr: oauth_flows
11
13
  # The hash containing the OAuth flows. Possible keys are:
@@ -22,10 +24,8 @@ module Jsapi
22
24
  # Returns a hash representing the security scheme object.
23
25
  def to_openapi(version)
24
26
  version = Version.from(version)
25
- {
26
- type: 'oauth2',
27
- description: description
28
- }.tap do |h|
27
+
28
+ with_openapi_extensions(type: 'oauth2', description: description).tap do |h|
29
29
  if oauth_flows&.any?
30
30
  if version.major == 2
31
31
  key, oauth_flow = oauth_flows.first
@@ -37,7 +37,7 @@ module Jsapi
37
37
  end
38
38
  end
39
39
  end
40
- end.compact
40
+ end
41
41
  end
42
42
  end
43
43
  end
@@ -9,6 +9,8 @@ module Jsapi
9
9
  # OpenID Connect was introduced with \OpenAPI 3.0. Thus, a security scheme of
10
10
  # this class is skipped when generating an \OpenAPI 2.0 document.
11
11
  class OpenIDConnect < Base
12
+ include Extensions
13
+
12
14
  ##
13
15
  # :attr: open_id_connect_url
14
16
  attribute :open_id_connect_url, String
@@ -19,11 +21,11 @@ module Jsapi
19
21
  version = Version.from(version)
20
22
  return if version.major == 2
21
23
 
22
- {
24
+ with_openapi_extensions(
23
25
  type: 'openIdConnect',
24
26
  openIdConnectUrl: open_id_connect_url,
25
27
  description: description
26
- }.compact
28
+ )
27
29
  end
28
30
  end
29
31
  end
@@ -5,6 +5,8 @@ module Jsapi
5
5
  module OpenAPI
6
6
  # Represents a server object.
7
7
  class Server < Base
8
+ include Extensions
9
+
8
10
  ##
9
11
  # :attr: description
10
12
  # The optional description of the server.
@@ -22,11 +24,11 @@ module Jsapi
22
24
 
23
25
  # Returns a hash representing the server object.
24
26
  def to_openapi(*)
25
- {
27
+ with_openapi_extensions(
26
28
  url: url,
27
29
  description: description,
28
30
  variables: variables&.transform_values(&:to_openapi)
29
- }.compact
31
+ )
30
32
  end
31
33
  end
32
34
  end
@@ -5,6 +5,8 @@ module Jsapi
5
5
  module OpenAPI
6
6
  # Represents a server variable object.
7
7
  class ServerVariable < Base
8
+ include Extensions
9
+
8
10
  ##
9
11
  # :attr: default
10
12
  # The default value of the server variable.
@@ -22,11 +24,11 @@ module Jsapi
22
24
 
23
25
  # Returns a hash representing the server variable object.
24
26
  def to_openapi(*)
25
- {
27
+ with_openapi_extensions(
26
28
  default: default,
27
29
  enum: enum.presence, # must not be empty
28
30
  description: description
29
- }.compact
31
+ )
30
32
  end
31
33
  end
32
34
  end
@@ -5,6 +5,8 @@ module Jsapi
5
5
  module OpenAPI
6
6
  # Represents a tag object.
7
7
  class Tag < Base
8
+ include Extensions
9
+
8
10
  ##
9
11
  # :attr: description
10
12
  # The description of the tag.
@@ -22,11 +24,11 @@ module Jsapi
22
24
 
23
25
  # Returns a hash representing the tag object.
24
26
  def to_openapi(*)
25
- {
27
+ with_openapi_extensions(
26
28
  name: name,
27
29
  description: description,
28
30
  externalDocs: external_docs&.to_openapi
29
- }.compact
31
+ )
30
32
  end
31
33
  end
32
34
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'openapi/extensions'
3
4
  require_relative 'openapi/version'
4
5
  require_relative 'openapi/callback'
5
6
  require_relative 'openapi/contact'
@@ -3,6 +3,8 @@
3
3
  module Jsapi
4
4
  module Meta
5
5
  class Operation < Base
6
+ include OpenAPI::Extensions
7
+
6
8
  ##
7
9
  # :attr: callbacks
8
10
  # The optional callbacks. Applies to \OpenAPI 3.0 and higher.
@@ -133,7 +135,8 @@ module Jsapi
133
135
  # Returns a hash representing the \OpenAPI operation object.
134
136
  def to_openapi(version, definitions)
135
137
  version = OpenAPI::Version.from(version)
136
- {
138
+
139
+ with_openapi_extensions(
137
140
  operationId: name,
138
141
  tags: tags,
139
142
  summary: summary,
@@ -141,7 +144,7 @@ module Jsapi
141
144
  externalDocs: external_docs&.to_openapi,
142
145
  deprecated: deprecated?.presence,
143
146
  security: security_requirements&.map(&:to_openapi)
144
- }.tap do |hash|
147
+ ).tap do |hash|
145
148
  if version.major == 2
146
149
  hash[:consumes] = consumed_mime_types if consumed_mime_types
147
150
  hash[:produces] = produced_mime_types if produced_mime_types
@@ -170,7 +173,7 @@ module Jsapi
170
173
  callback.to_openapi(version, definitions)
171
174
  end
172
175
  end
173
- end.compact
176
+ end
174
177
  end
175
178
  end
176
179
  end
@@ -4,6 +4,8 @@ module Jsapi
4
4
  module Meta
5
5
  module Parameter
6
6
  class Model < Base
7
+ include OpenAPI::Extensions
8
+
7
9
  ##
8
10
  # :attr: deprecated
9
11
  # Specifies whether or not the parameter is deprecated.
@@ -87,32 +89,34 @@ module Jsapi
87
89
  else
88
90
  parameter_name = schema.array? ? "#{name}[]" : name
89
91
  [
90
- if version.major == 2
91
- {
92
- name: parameter_name,
93
- in: self.in,
94
- description: description,
95
- required: required?.presence,
96
- allowEmptyValue: allow_empty_value?.presence,
97
- collectionFormat: ('multi' if schema.array?)
98
- }.merge(schema.to_openapi(version))
99
- else
100
- {
101
- name: parameter_name,
102
- in: self.in,
103
- description: description,
104
- required: required?.presence,
105
- allowEmptyValue: allow_empty_value?.presence,
106
- deprecated: deprecated?.presence,
107
- schema: schema.to_openapi(version),
108
- examples: examples&.transform_values(&:to_openapi)
109
-
110
- # NOTE: collectionFormat is replaced by style and explode.
111
- # The default values for query parameters are:
112
- # - style: 'form'
113
- # - explode: true
114
- }
115
- end.compact
92
+ with_openapi_extensions(
93
+ if version.major == 2
94
+ {
95
+ name: parameter_name,
96
+ in: self.in,
97
+ description: description,
98
+ required: required?.presence,
99
+ allowEmptyValue: allow_empty_value?.presence,
100
+ collectionFormat: ('multi' if schema.array?)
101
+ }.merge(schema.to_openapi(version))
102
+ else
103
+ {
104
+ name: parameter_name,
105
+ in: self.in,
106
+ description: description,
107
+ required: required?.presence,
108
+ allowEmptyValue: allow_empty_value?.presence,
109
+ deprecated: deprecated?.presence,
110
+ schema: schema.to_openapi(version),
111
+ examples: examples&.transform_values(&:to_openapi)
112
+
113
+ # NOTE: collectionFormat is replaced by style and explode.
114
+ # The default values for query parameters are:
115
+ # - style: 'form'
116
+ # - explode: true
117
+ }
118
+ end
119
+ )
116
120
  ]
117
121
  end
118
122
  end
@@ -141,27 +145,29 @@ module Jsapi
141
145
  description = property_schema.description
142
146
  allow_empty_value = property.schema.existence <= Existence::ALLOW_EMPTY
143
147
  [
144
- if version.major == 2
145
- {
146
- name: parameter_name,
147
- in: self.in,
148
- description: description,
149
- required: required,
150
- allowEmptyValue: allow_empty_value.presence,
151
- collectionFormat: ('multi' if property_schema.array?)
152
- }.merge(property_schema.to_openapi(version))
153
- else
154
- {
155
- name: parameter_name,
156
- in: self.in,
157
- description: description,
158
- required: required,
159
- allowEmptyValue: allow_empty_value.presence,
160
- deprecated: deprecated,
161
- schema: property_schema.to_openapi(version).except(:deprecated),
162
- examples: examples&.transform_values(&:to_openapi)
163
- }
164
- end.compact
148
+ with_openapi_extensions(
149
+ if version.major == 2
150
+ {
151
+ name: parameter_name,
152
+ in: self.in,
153
+ description: description,
154
+ required: required,
155
+ allowEmptyValue: allow_empty_value.presence,
156
+ collectionFormat: ('multi' if property_schema.array?)
157
+ }.merge(property_schema.to_openapi(version))
158
+ else
159
+ {
160
+ name: parameter_name,
161
+ in: self.in,
162
+ description: description,
163
+ required: required,
164
+ allowEmptyValue: allow_empty_value.presence,
165
+ deprecated: deprecated,
166
+ schema: property_schema.to_openapi(version).except(:deprecated),
167
+ examples: examples&.transform_values(&:to_openapi)
168
+ }
169
+ end
170
+ )
165
171
  ]
166
172
  end
167
173
  end
@@ -4,6 +4,8 @@ module Jsapi
4
4
  module Meta
5
5
  module RequestBody
6
6
  class Model < Base
7
+ include OpenAPI::Extensions
8
+
7
9
  ##
8
10
  # :attr: description
9
11
  # The optional description of the request body.
@@ -44,12 +46,12 @@ module Jsapi
44
46
  in: 'body',
45
47
  description: description,
46
48
  required: required?
47
- }.merge(schema.to_openapi('2.0')).compact
49
+ }.merge(schema.to_openapi('2.0')).merge(openapi_extensions).compact
48
50
  end
49
51
 
50
52
  # Returns a hash representing the \OpenAPI 3.x request body object.
51
53
  def to_openapi(version)
52
- {
54
+ with_openapi_extensions(
53
55
  description: description,
54
56
  content: {
55
57
  'application/json' => {
@@ -58,7 +60,7 @@ module Jsapi
58
60
  }.compact
59
61
  },
60
62
  required: required?
61
- }.compact
63
+ )
62
64
  end
63
65
  end
64
66
  end
@@ -4,6 +4,8 @@ module Jsapi
4
4
  module Meta
5
5
  module Response
6
6
  class Model < Base
7
+ include OpenAPI::Extensions
8
+
7
9
  ##
8
10
  # :attr: description
9
11
  # The optional description of the response.
@@ -44,28 +46,31 @@ module Jsapi
44
46
  # Returns a hash representing the \OpenAPI response object.
45
47
  def to_openapi(version, definitions)
46
48
  version = OpenAPI::Version.from(version)
47
- if version.major == 2
48
- {
49
- description: description,
50
- schema: schema.to_openapi(version),
51
- examples: (
52
- if (example = examples&.values&.first).present?
53
- { 'application/json' => example.resolve(definitions).value }
54
- end
55
- )
56
- }
57
- else
58
- {
59
- description: description,
60
- content: {
61
- 'application/json' => {
62
- schema: schema.to_openapi(version),
63
- examples: examples&.transform_values(&:to_openapi)
64
- }.compact
65
- },
66
- links: links&.transform_values(&:to_openapi)
67
- }
68
- end.compact
49
+
50
+ with_openapi_extensions(
51
+ if version.major == 2
52
+ {
53
+ description: description,
54
+ schema: schema.to_openapi(version),
55
+ examples: (
56
+ if (example = examples&.values&.first).present?
57
+ { 'application/json' => example.resolve(definitions).value }
58
+ end
59
+ )
60
+ }
61
+ else
62
+ {
63
+ description: description,
64
+ content: {
65
+ 'application/json' => {
66
+ schema: schema.to_openapi(version),
67
+ examples: examples&.transform_values(&:to_openapi)
68
+ }.compact
69
+ },
70
+ links: links&.transform_values(&:to_openapi)
71
+ }
72
+ end
73
+ )
69
74
  end
70
75
  end
71
76
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ module Schema
6
+ class AdditionalProperties < Meta::Base
7
+ #
8
+ # :attr: schema
9
+ # The Schema of additional properties.
10
+ attribute :schema, Schema, writer: false
11
+
12
+ ##
13
+ # :attr: source
14
+ # The method to read additional properties when serializing an object.
15
+ # The default method is +additional_properties+.
16
+ attribute :source, Symbol, default: :additional_properties
17
+
18
+ delegate_missing_to :schema
19
+
20
+ def initialize(keywords = {})
21
+ keywords = keywords.dup
22
+ super(keywords.extract!(:source))
23
+
24
+ @schema = Schema.new(keywords)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -4,6 +4,8 @@ module Jsapi
4
4
  module Meta
5
5
  module Schema
6
6
  class Base < Meta::Base
7
+ include OpenAPI::Extensions
8
+
7
9
  TYPES = %w[array boolean integer number object string].freeze # :nodoc:
8
10
 
9
11
  TYPES.each do |type|
@@ -96,37 +98,40 @@ module Jsapi
96
98
  # Returns a hash representing the \OpenAPI schema object.
97
99
  def to_openapi(version)
98
100
  version = OpenAPI::Version.from(version)
99
- if version.major == 2
100
- # OpenAPI 2.0
101
- {
102
- type: type,
103
- example: examples&.first
104
- }
105
- elsif version.minor.zero?
106
- # OpenAPI 3.0
107
- {
108
- type: type,
109
- nullable: nullable?.presence,
110
- examples: examples,
111
- deprecated: deprecated?.presence
112
- }
113
- else
114
- # OpenAPI 3.1
115
- {
116
- type: nullable? ? [type, 'null'] : type,
117
- examples: examples,
118
- deprecated: deprecated?.presence
119
- }
120
- end.tap do |hash|
121
- hash[:title] = title
122
- hash[:description] = description
123
- hash[:default] = default
124
- hash[:externalDocs] = external_docs&.to_openapi
125
101
 
126
- validations.each_value do |validation|
127
- hash.merge!(validation.to_openapi_validation(version))
102
+ with_openapi_extensions(
103
+ if version.major == 2
104
+ # OpenAPI 2.0
105
+ {
106
+ type: type,
107
+ example: examples&.first
108
+ }
109
+ elsif version.minor.zero?
110
+ # OpenAPI 3.0
111
+ {
112
+ type: type,
113
+ nullable: nullable?.presence,
114
+ examples: examples,
115
+ deprecated: deprecated?.presence
116
+ }
117
+ else
118
+ # OpenAPI 3.1
119
+ {
120
+ type: nullable? ? [type, 'null'] : type,
121
+ examples: examples,
122
+ deprecated: deprecated?.presence
123
+ }
124
+ end.tap do |hash|
125
+ hash[:title] = title
126
+ hash[:description] = description
127
+ hash[:default] = default
128
+ hash[:externalDocs] = external_docs&.to_openapi
129
+
130
+ validations.each_value do |validation|
131
+ hash.merge!(validation.to_openapi_validation(version))
132
+ end
128
133
  end
129
- end.compact
134
+ )
130
135
  end
131
136
 
132
137
  def type # :nodoc:
@@ -4,15 +4,21 @@ module Jsapi
4
4
  module Meta
5
5
  module Schema
6
6
  class Object < Base
7
+ ##
8
+ # :attr: additional_properties
9
+ # The AdditionalProperties.
10
+ attribute :additional_properties, AdditionalProperties
11
+
7
12
  ##
8
13
  # :attr: all_of_references
9
14
  attribute :all_of_references, [Reference], default: []
10
15
 
11
- alias :all_of= :all_of_references=
16
+ alias :all_of= :all_of_references= # :nodoc:
12
17
  alias :add_all_of :add_all_of_reference
13
18
 
14
19
  ##
15
20
  # :attr: discriminator
21
+ # The Discriminator.
16
22
  attribute :discriminator, Discriminator
17
23
 
18
24
  ##
@@ -47,6 +53,7 @@ module Jsapi
47
53
  super.merge(
48
54
  allOf: all_of_references.map(&:to_json_schema).presence,
49
55
  properties: properties.transform_values(&:to_json_schema),
56
+ additionalProperties: additional_properties&.to_json_schema,
50
57
  required: properties.values.select(&:required?).map(&:name)
51
58
  ).compact
52
59
  end
@@ -60,6 +67,7 @@ module Jsapi
60
67
  properties: properties.transform_values do |property|
61
68
  property.to_openapi(version)
62
69
  end,
70
+ additionalProperties: additional_properties&.to_openapi(version),
63
71
  required: properties.values.select(&:required?).map(&:name)
64
72
  ).compact
65
73
  end
@@ -4,13 +4,14 @@ require_relative 'schema/conversion'
4
4
  require_relative 'schema/boundary'
5
5
  require_relative 'schema/delegator'
6
6
  require_relative 'schema/reference'
7
- require_relative 'schema/discriminator'
8
7
  require_relative 'schema/base'
9
8
  require_relative 'schema/boolean'
10
9
  require_relative 'schema/array'
11
10
  require_relative 'schema/numeric'
12
11
  require_relative 'schema/integer'
13
12
  require_relative 'schema/number'
13
+ require_relative 'schema/additional_properties'
14
+ require_relative 'schema/discriminator'
14
15
  require_relative 'schema/object'
15
16
  require_relative 'schema/string'
16
17
  require_relative 'schema/validation'
data/lib/jsapi/version.rb CHANGED
@@ -5,6 +5,6 @@ module Jsapi
5
5
  # NOTE: See https://bundler.io/guides/creating_gem.html
6
6
 
7
7
  # The current GEM version.
8
- VERSION = '0.2.0'
8
+ VERSION = '0.3.0'
9
9
  end
10
10
  end
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.2.0
4
+ version: 0.3.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-07-05 00:00:00.000000000 Z
11
+ date: 2024-07-14 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
@@ -66,6 +66,7 @@ files:
66
66
  - lib/jsapi/meta/openapi/callback/model.rb
67
67
  - lib/jsapi/meta/openapi/callback/reference.rb
68
68
  - lib/jsapi/meta/openapi/contact.rb
69
+ - lib/jsapi/meta/openapi/extensions.rb
69
70
  - lib/jsapi/meta/openapi/external_documentation.rb
70
71
  - lib/jsapi/meta/openapi/info.rb
71
72
  - lib/jsapi/meta/openapi/license.rb
@@ -102,6 +103,7 @@ files:
102
103
  - lib/jsapi/meta/response/model.rb
103
104
  - lib/jsapi/meta/response/reference.rb
104
105
  - lib/jsapi/meta/schema.rb
106
+ - lib/jsapi/meta/schema/additional_properties.rb
105
107
  - lib/jsapi/meta/schema/array.rb
106
108
  - lib/jsapi/meta/schema/base.rb
107
109
  - lib/jsapi/meta/schema/boolean.rb