jsapi 0.7.3 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f0b8caebf611ba584ebd5963fefb3e7f4600f3cdda59eeda99049225f4a17fff
4
- data.tar.gz: 94db4048e68964bc70e64068631a3ae01b9ef7434fab23976e69b6368f809d37
3
+ metadata.gz: 41bcc8f72dc668c6df55dfc73e6e2c9cc6fde569ebe008e503586100e0b3bba1
4
+ data.tar.gz: 9667a2661824e0d3a47675e835f3372cfd22bff71ab8c7bb3bde3c0e56cc78be
5
5
  SHA512:
6
- metadata.gz: 144e852da54524999df466dbdf078227cae7d5e68d3155fbc3730b298072d696a39cea1c786513c34862bcb71a14b163bd202b9c203e4736598cb0fddc28813b
7
- data.tar.gz: 9d76e40aa500597eec1a5a7dc02b794ccde643f603227455ccbcea76a6e074769c246335a1e554fa19bc6d06cf8cf36ad030147a651de58f148b3c4db5c12322
6
+ metadata.gz: 576422bac22ee56e77ab160e50033a78ba1f02039fd5a7686583d0778f049e331892bb5d7692dcdc428c9b250706f13bf6e585db4472d40e31d63537116d9f37
7
+ data.tar.gz: e3ef8964a2a55d6f245a8982e7dcad76f564d066af119eec43edb1b5bc579f1771ce87e747924e5cd2d6b650e07e44515ff7e722860a4c4ee8c6fa7e8b0360cb
@@ -3,7 +3,7 @@
3
3
  module Jsapi
4
4
  module Controller
5
5
  # Used by Methods#api_operation! to produce an error response.
6
- class ErrorResult
6
+ class Error
7
7
  delegate_missing_to :@exception
8
8
 
9
9
  # The HTTP status code of the error response to be produced.
@@ -27,14 +27,18 @@ module Jsapi
27
27
  # that the model passed to the block is invalid if there are any request parameters
28
28
  # that can't be mapped to a parameter or a request body property of the operation.
29
29
  #
30
- # The +:omit+ option specifies on which conditions properties are omitted.
30
+ # The +:omit+ option specifies on which conditions properties are omitted in responses.
31
31
  # Possible values are:
32
32
  #
33
33
  # - +:empty+ - All of the properties whose value is empty are omitted.
34
34
  # - +:nil+ - All of the properties whose value is +nil+ are omitted.
35
35
  #
36
- # Raises an InvalidArgumentError when the value of +:omit+ is invalid.
37
- def api_operation(operation_name = nil, omit: nil, status: nil, strong: false, &block)
36
+ # Raises an +ArgumentError+ when +:omit+ is other than +:empty+, +:nil+ or +nil+.
37
+ def api_operation(operation_name = nil,
38
+ omit: nil,
39
+ status: nil,
40
+ strong: false,
41
+ &block)
38
42
  _api_operation(
39
43
  operation_name,
40
44
  bang: false,
@@ -52,7 +56,11 @@ module Jsapi
52
56
  # # ...
53
57
  # end
54
58
  #
55
- def api_operation!(operation_name = nil, omit: nil, status: nil, strong: false, &block)
59
+ def api_operation!(operation_name = nil,
60
+ omit: nil,
61
+ status: nil,
62
+ strong: false,
63
+ &block)
56
64
  _api_operation(
57
65
  operation_name,
58
66
  bang: true,
@@ -97,13 +105,13 @@ module Jsapi
97
105
  # - +:empty+ - All of the properties whose value is empty are omitted.
98
106
  # - +:nil+ - All of the properties whose value is +nil+ are omitted.
99
107
  #
100
- # Raises an InvalidArgumentError when the value of +:omit+ is invalid.
108
+ # Raises an +ArgumentError+ when +:omit+ is other than +:empty+, +:nil+ or +nil+.
101
109
  def api_response(result, operation_name = nil, omit: nil, status: nil)
102
110
  definitions = api_definitions
103
111
  operation = _find_api_operation(operation_name, definitions)
104
- response = _api_response(operation, status, definitions)
112
+ response_model = _api_response(operation, status, definitions)
105
113
 
106
- Response.new(result, response, api_definitions, omit: omit)
114
+ Response.new(result, response_model, api_definitions, omit: omit)
107
115
  end
108
116
 
109
117
  private
@@ -111,40 +119,43 @@ module Jsapi
111
119
  def _api_operation(operation_name, bang:, omit:, status:, strong:, &block)
112
120
  definitions = api_definitions
113
121
  operation = _find_api_operation(operation_name, definitions)
114
- response = _api_response(operation, status, definitions)
115
-
116
- if block
117
- params = _api_params(operation, definitions, strong: strong)
118
- result = begin
119
- raise ParametersInvalid.new(params) if bang && params.invalid?
120
-
121
- block.call(params)
122
- rescue StandardError => e
123
- # Lookup a rescue handler
124
- rescue_handler = definitions.rescue_handler_for(e)
125
- raise e if rescue_handler.nil?
126
-
127
- # Change the HTTP status code and response schema
128
- status = rescue_handler.status
129
- response = operation.response(status)&.resolve(definitions)
130
- raise e if response.nil?
131
-
132
- # Call on_rescue callbacks
133
- definitions.on_rescue_callbacks.each do |callback|
134
- if callback.respond_to?(:call)
135
- callback.call(e)
136
- else
137
- send(callback, e)
138
- end
139
- end
140
122
 
141
- ErrorResult.new(e, status: status)
123
+ # Perform operation
124
+ response_model = _api_response(operation, status, definitions)
125
+ head(status) && return unless block
126
+
127
+ params = _api_params(operation, definitions, strong: strong)
128
+
129
+ result = begin
130
+ raise ParametersInvalid.new(params) if bang && params.invalid?
131
+
132
+ block.call(params)
133
+ rescue StandardError => e
134
+ # Lookup a rescue handler
135
+ rescue_handler = definitions.rescue_handler_for(e)
136
+ raise e if rescue_handler.nil?
137
+
138
+ # Change the HTTP status code and response model
139
+ status = rescue_handler.status
140
+ response_model = operation.response(status)&.resolve(definitions)
141
+ raise e if response_model.nil?
142
+
143
+ # Call on_rescue callbacks
144
+ definitions.on_rescue_callbacks.each do |callback|
145
+ if callback.respond_to?(:call)
146
+ callback.call(e)
147
+ else
148
+ send(callback, e)
149
+ end
142
150
  end
143
- render(json: Response.new(result, response, definitions, omit: omit), status: status)
144
- else
145
- head(status)
151
+
152
+ Error.new(e, status: status)
146
153
  end
147
- self.content_type = response.content_type
154
+ response = Response.new(result, response_model, definitions, omit: omit)
155
+
156
+ # Write response
157
+ self.content_type = response_model.content_type
158
+ render(json: response, status: status)
148
159
  end
149
160
 
150
161
  def _api_params(operation, definitions, strong:)
@@ -27,7 +27,8 @@ module Jsapi
27
27
  @raw_attributes[name] = JSON.wrap(
28
28
  parameter_model.in == 'header' ? headers[name] : @params[name],
29
29
  parameter_model.schema.resolve(definitions),
30
- definitions
30
+ definitions,
31
+ context: :request
31
32
  )
32
33
  end
33
34
 
@@ -38,7 +39,8 @@ module Jsapi
38
39
  request_body = JSON.wrap(
39
40
  @params.except(*operation.parameters.keys),
40
41
  request_body_schema,
41
- definitions
42
+ definitions,
43
+ context: :request
42
44
  )
43
45
  @raw_attributes.merge!(request_body.raw_attributes)
44
46
  @raw_additional_attributes = request_body.raw_additional_attributes
@@ -2,10 +2,20 @@
2
2
 
3
3
  module Jsapi
4
4
  module Controller
5
- # Used to serialize a response.
5
+ # Used to jsonify a response.
6
6
  class Response
7
+ class JsonifyError < RuntimeError # :nodoc:
8
+ def message
9
+ [@path&.delete_prefix('.') || 'response body', super].join(' ')
10
+ end
11
+
12
+ def prepend(origin)
13
+ @path = "#{origin}#{@path}"
14
+ self
15
+ end
16
+ end
7
17
 
8
- # Creates a new instance to serialize +object+ according to +response+. References
18
+ # Creates a new instance to jsonify +object+ according to +response+. References
9
19
  # are resolved to API components in +definitions+.
10
20
  #
11
21
  # The +:omit+ option specifies on which conditions properties are omitted.
@@ -14,111 +24,119 @@ module Jsapi
14
24
  # - +:empty+ - All of the properties whose value is empty are omitted.
15
25
  # - +:nil+ - All of the properties whose value is +nil+ are omitted.
16
26
  #
17
- # Raises an InvalidArgumentError when the value of +:omit+ is invalid.
27
+ # Raises an +ArgumentError+ when +:omit+ is other than +:empty+, +:nil+ or +nil+.
18
28
  def initialize(object, response, definitions, omit: nil)
19
- if [:empty, :nil, nil].exclude?(omit)
20
- raise InvalidArgumentError.new('omit', omit, valid_values: %i[empty nil])
21
- end
22
-
23
29
  @object = object
24
30
  @response = response
25
31
  @definitions = definitions
26
- @omit = omit
32
+
33
+ @omittable_check =
34
+ case omit
35
+ when nil
36
+ nil
37
+ when :nil
38
+ ->(value, schema) { schema.omittable? && value.nil? }
39
+ when :empty
40
+ ->(value, schema) { schema.omittable? && value.try(:empty?) }
41
+ else
42
+ raise InvalidArgumentError.new('omit', omit, valid_values: %i[empty nil])
43
+ end
27
44
  end
28
45
 
29
46
  def inspect # :nodoc:
30
47
  "#<#{self.class.name} #{@object.inspect}>"
31
48
  end
32
49
 
33
- # Returns the JSON representation of the response as a +String+.
50
+ # Returns the \JSON representation of the response as a string.
34
51
  def to_json(*)
35
52
  schema = @response.schema.resolve(@definitions)
36
53
  if @response.locale
37
- I18n.with_locale(@response.locale) do
38
- serialize(@object, schema)
39
- end
54
+ I18n.with_locale(@response.locale) { jsonify(@object, schema) }
40
55
  else
41
- serialize(@object, schema)
56
+ jsonify(@object, schema)
42
57
  end.to_json
43
58
  end
44
59
 
45
60
  private
46
61
 
47
- def serialize(object, schema, path = nil)
62
+ def jsonify(object, schema)
48
63
  object = schema.default_value(@definitions, context: :response) if object.nil?
49
- return if object.nil? && schema.nullable?
50
-
51
- raise "#{path || 'response'} can't be nil" if object.nil?
52
-
53
- case schema.type
54
- when 'array'
55
- serialize_array(object, schema, path)
56
- when 'integer'
57
- schema.convert(object.to_i)
58
- when 'number'
59
- schema.convert(object.to_f)
60
- when 'object'
61
- serialize_object(object, schema, path)
62
- when 'string'
63
- schema.convert(
64
- case schema.format
65
- when 'date'
66
- object.to_date
67
- when 'date-time'
68
- object.to_datetime
69
- when 'duration'
70
- object.iso8601
71
- else
72
- object.to_s
73
- end
74
- )
64
+
65
+ if object.nil?
66
+ raise JsonifyError, "can't be nil" unless schema.nullable?
75
67
  else
76
- object
68
+ case schema.type
69
+ when 'array'
70
+ jsonify_array(object, schema)
71
+ when 'boolean'
72
+ object
73
+ when 'integer'
74
+ schema.convert(object.to_i)
75
+ when 'number'
76
+ schema.convert(object.to_f)
77
+ when 'object'
78
+ jsonify_object(object, schema)
79
+ when 'string'
80
+ schema.convert(
81
+ case schema.format
82
+ when 'date'
83
+ object.to_date
84
+ when 'date-time'
85
+ object.to_datetime
86
+ when 'duration'
87
+ object.iso8601
88
+ else
89
+ object.to_s
90
+ end
91
+ )
92
+ else
93
+ raise JsonifyError, "has an invalid type: #{schema.type.inspect}"
94
+ end
77
95
  end
78
96
  end
79
97
 
80
- def serialize_array(array, schema, path)
98
+ def jsonify_array(array, schema)
81
99
  item_schema = schema.items.resolve(@definitions)
82
- Array(array).map { |item| serialize(item, item_schema, path) }
100
+ index = 0
101
+
102
+ Array(array).map do |item|
103
+ item = jsonify(item, item_schema)
104
+ index += 1
105
+ item
106
+ rescue JsonifyError => e
107
+ raise e.prepend("[#{index}]")
108
+ end
83
109
  end
84
110
 
85
- def serialize_object(object, schema, path)
111
+ def jsonify_object(object, schema)
86
112
  schema = schema.resolve_schema(object, @definitions, context: :response)
87
113
  properties = {}
88
114
 
89
- # Serialize properties
90
- schema.resolve_properties(@definitions, context: :response).each do |key, property|
115
+ # Add properties
116
+ schema.resolve_properties(@definitions, context: :response).each_value do |property|
91
117
  property_schema = property.schema.resolve(@definitions)
92
118
  property_value = property.reader.call(object)
93
119
  property_value = property_schema.default if property_value.nil?
120
+ next if @omittable_check&.call(property_value, property_schema)
94
121
 
95
- next if ((@omit == :empty && property_value.try(:empty?)) ||
96
- (@omit == :nil && property_value.nil?)) &&
97
- property_schema.omittable?
98
-
99
- properties[key] = serialize(
100
- property_value,
101
- property_schema,
102
- path.nil? ? property.name : "#{path}.#{property.name}"
103
- )
122
+ properties[property.name] = jsonify(property_value, property_schema)
123
+ rescue JsonifyError => e
124
+ raise e.prepend(".#{property.name}")
104
125
  end
105
- # Serialize additional properties
126
+ # Add additional properties
106
127
  if (additional_properties = schema.additional_properties)
107
128
  additional_properties_schema = additional_properties.schema.resolve(@definitions)
108
129
 
109
130
  additional_properties.source.call(object)&.each do |key, value|
110
- # Don't replace the property with the same key
111
131
  next if properties.key?(key = key.to_s)
112
132
 
113
- properties[key] = serialize(
114
- value,
115
- additional_properties_schema,
116
- path.nil? ? key : "#{path}.#{key}"
117
- )
133
+ properties[key] = jsonify(value, additional_properties_schema)
134
+ rescue JsonifyError => e
135
+ raise e.prepend(".#{key}")
118
136
  end
119
137
  end
120
- # Return properties if present, otherwise nil
121
- properties if properties.present?
138
+
139
+ properties.presence
122
140
  end
123
141
  end
124
142
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'controller/parameters_invalid'
4
- require_relative 'controller/error_result'
4
+ require_relative 'controller/error'
5
5
  require_relative 'controller/parameters'
6
6
  require_relative 'controller/response'
7
7
  require_relative 'controller/methods'
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Jsapi
4
- module InvalidValueHelper
4
+ module InvalidValueHelper # :nodoc:
5
5
  def build_message(name, value, valid_values)
6
6
  case valid_values.count
7
7
  when 0
@@ -4,30 +4,30 @@ module Jsapi
4
4
  module JSON
5
5
  # Represents a JSON array.
6
6
  class Array < Value
7
- def initialize(elements, schema, definitions)
7
+ def initialize(array, schema, definitions, context: nil)
8
8
  super(schema)
9
- @elements = Array(elements).map do |element|
10
- JSON.wrap(element, schema.items, definitions)
9
+ @json_values = Array(array).map do |item|
10
+ JSON.wrap(item, schema.items, definitions, context: context)
11
11
  end
12
12
  end
13
13
 
14
14
  # Returns true if it contains no elements, false otherwise.
15
15
  def empty?
16
- @elements.empty?
16
+ @json_values.empty?
17
17
  end
18
18
 
19
19
  def inspect # :nodoc:
20
- "#<#{self.class.name} [#{@elements.map(&:inspect).join(', ')}]>"
20
+ "#<#{self.class.name} [#{@json_values.map(&:inspect).join(', ')}]>"
21
21
  end
22
22
 
23
23
  def validate(errors) # :nodoc:
24
24
  return false unless super
25
25
 
26
- @elements.map { |element| element.validate(errors) }.all?
26
+ @json_values.map { |element| element.validate(errors) }.all?
27
27
  end
28
28
 
29
29
  def value
30
- @value ||= @elements.map(&:value)
30
+ @value ||= @json_values.map(&:value)
31
31
  end
32
32
  end
33
33
  end
@@ -8,20 +8,20 @@ module Jsapi
8
8
 
9
9
  attr_reader :raw_additional_attributes, :raw_attributes
10
10
 
11
- def initialize(attributes, schema, definitions)
12
- schema = schema.resolve_schema(attributes, definitions, context: :request)
13
- properties = schema.resolve_properties(definitions, context: :request)
11
+ def initialize(hash, schema, definitions, context: nil)
12
+ schema = schema.resolve_schema(hash, definitions, context: context)
13
+ properties = schema.resolve_properties(definitions, context: context)
14
14
 
15
15
  @raw_attributes = properties.transform_values do |property|
16
- JSON.wrap(attributes[property.name], property.schema, definitions)
16
+ JSON.wrap(hash[property.name], property.schema, definitions, context: context)
17
17
  end
18
18
 
19
19
  @raw_additional_attributes =
20
20
  if (additional_properties = schema.additional_properties)
21
21
  additional_properties_schema = additional_properties.schema.resolve(definitions)
22
22
 
23
- attributes.except(*properties.keys).transform_values do |value|
24
- JSON.wrap(value, additional_properties_schema, definitions)
23
+ hash.except(*properties.keys).transform_values do |value|
24
+ JSON.wrap(value, additional_properties_schema, definitions, context: context)
25
25
  end
26
26
  end || {}
27
27
 
data/lib/jsapi/json.rb CHANGED
@@ -13,15 +13,15 @@ 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)
16
+ def wrap(object, schema, definitions = nil, context: nil)
17
17
  schema = schema.resolve(definitions) unless definitions.nil?
18
18
 
19
- object = schema.default_value(definitions, context: :request) if object.nil?
19
+ object = schema.default_value(definitions, context: context) if object.nil?
20
20
  return Null.new(schema) if object.nil?
21
21
 
22
22
  case schema.type
23
23
  when 'array'
24
- Array.new(object, schema, definitions)
24
+ Array.new(object, schema, definitions, context: context)
25
25
  when 'boolean'
26
26
  Boolean.new(object, schema)
27
27
  when 'integer'
@@ -29,7 +29,7 @@ module Jsapi
29
29
  when 'number'
30
30
  Number.new(object, schema)
31
31
  when 'object'
32
- Object.new(object, schema, definitions)
32
+ Object.new(object, schema, definitions, context: context)
33
33
  when 'string'
34
34
  String.new(object, schema)
35
35
  else
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.7.3'
8
+ VERSION = '0.8.0'
9
9
  end
10
10
  end
data/lib/jsapi.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'jsapi/helpers/invalid_value_helper'
3
+ require 'jsapi/invalid_value_helper'
4
4
  require 'jsapi/invalid_value_error'
5
5
  require 'jsapi/invalid_argument_error'
6
6
 
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.7.3
4
+ version: 0.8.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-25 00:00:00.000000000 Z
11
+ date: 2024-09-29 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
@@ -20,7 +20,7 @@ files:
20
20
  - lib/jsapi.rb
21
21
  - lib/jsapi/controller.rb
22
22
  - lib/jsapi/controller/base.rb
23
- - lib/jsapi/controller/error_result.rb
23
+ - lib/jsapi/controller/error.rb
24
24
  - lib/jsapi/controller/methods.rb
25
25
  - lib/jsapi/controller/parameters.rb
26
26
  - lib/jsapi/controller/parameters_invalid.rb
@@ -40,9 +40,9 @@ files:
40
40
  - lib/jsapi/dsl/request_body.rb
41
41
  - lib/jsapi/dsl/response.rb
42
42
  - lib/jsapi/dsl/schema.rb
43
- - lib/jsapi/helpers/invalid_value_helper.rb
44
43
  - lib/jsapi/invalid_argument_error.rb
45
44
  - lib/jsapi/invalid_value_error.rb
45
+ - lib/jsapi/invalid_value_helper.rb
46
46
  - lib/jsapi/json.rb
47
47
  - lib/jsapi/json/array.rb
48
48
  - lib/jsapi/json/boolean.rb