jsapi 3.0 → 3.1

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: 0ec240995632c56faf46229f425a0fb65a32da97f6aa0b68ee66327a6a17ca3b
4
- data.tar.gz: 7c457efa33d29830814b66dc62ac001d813f042890b7e637e265567198251e01
3
+ metadata.gz: 3c5ea7fc42be26ca35561f17cb2503e54efd9e138abd0ec4f9b7594fb27390ca
4
+ data.tar.gz: 1248e0b2499dd3616e5e4130fae88f9d5be8bd6a07e921317e86397a92832f71
5
5
  SHA512:
6
- metadata.gz: 9a7fc689d67fe755b7351a06730ff59b65212a5764e4eeab2d2b03748954e6c635a371bbdab0615542d07e0226c96c81453e0f246f99ef007f94f1b5c6210a54
7
- data.tar.gz: 2cbe363c8e5915611b9c3e9a38c2448af1bc0ccb8353b99a6f0595c51a115dce93269b2832a1f18d915f11dd96855249261f7749ba2ca9175685deda51cacbe6
6
+ metadata.gz: 2f4af4e90d29a9b1df69ab71c5ad729eecd9d625286864c2dae078e2d6f6a43cab306fa9e999bccedeea82183bc8d5c3d4d6a977faf607fcb0236621eb3f19e2
7
+ data.tar.gz: 3b2467ea75b1c14bc99266977d7bff07ab74315178ea6d936795fc6fb1111a59d04bbd859b7ffb86bf478baca3d9e5eb4c4979f4b22c1e43f81bae5c981a36e1
@@ -117,34 +117,77 @@ module Jsapi
117
117
  ) if [nil, :empty, :nil].exclude?(omit)
118
118
 
119
119
  operation = api.find_operation!(operation_name)
120
+ result = nil
120
121
  response_definition = nil
121
122
 
122
- result = begin
123
- # Authenticate request first if Authentication is included
124
- if respond_to?(:_api_authenticated?, true)
125
- raise Unauthorized unless _api_authenticated?(operation)
123
+ bypassed = catch :bypass do
124
+ if result.nil?
125
+ # Authenticate request first if Authentication is included
126
+ if respond_to?(:_api_authenticated?, true)
127
+ raise Unauthorized unless _api_authenticated?(operation)
126
128
 
127
- _api_callback(:after_authentication, operation_name)
128
- end
129
+ _api_callback(:after_authentication, operation_name)
130
+ end
129
131
 
130
- status = Status::Code.from(status)
131
- response_definition = operation.find_response_for!(status)
132
+ status = Status::Code.from(status)
133
+ response_definition = operation.find_response_for!(status)
132
134
 
133
- api_params = _api_params(operation, strong: strong)
135
+ api_params = _api_params(operation, strong: strong)
134
136
 
135
- if bang
136
- raise ParametersInvalid.new(api_params) if api_params.invalid?
137
+ if bang
138
+ raise ParametersInvalid.new(api_params) if api_params.invalid?
137
139
 
138
- _api_callback(:after_validation, operation_name, api_params)
140
+ _api_callback(:after_validation, operation_name, api_params)
141
+ end
142
+ result = block&.call(api_params)
143
+ break if performed?
139
144
  end
140
145
 
141
- block&.call(api_params)
146
+ # Produce response
147
+ media_type, content_definition =
148
+ response_definition
149
+ .media_type_and_content_for(*_api_accept)
150
+
151
+ status_code = status&.to_i
152
+ head(status_code) && break unless content_definition
153
+
154
+ content_type = media_type.to_s
155
+ result = _api_before_rendering(operation_name, result, api_params)
156
+
157
+ if (json_media_type = media_type.json?) || media_type.sequential_json?
158
+ api_response = Response.new(
159
+ result,
160
+ content_definition,
161
+ omit: omit,
162
+ locale: response_definition.locale
163
+ )
164
+ if json_media_type
165
+ render(json: api_response, status: status_code, content_type: content_type)
166
+ else
167
+ self.content_type = content_type
168
+ response.status = status_code
169
+ begin
170
+ response.stream.tap do |stream|
171
+ api_response.write_seq_json_to(stream, media_type)
172
+ ensure
173
+ stream.close
174
+ end
175
+ rescue StandardError => e
176
+ throw :bypass, e
177
+ end
178
+ end
179
+ elsif media_type == Media::Type::TEXT_PLAIN
180
+ render(plain: result.to_s, status: status_code, content_type: content_type)
181
+ end
182
+ nil
142
183
  rescue StandardError => e
143
- # Lookup a rescue handler
184
+ raise e if result.is_a?(Error)
185
+
186
+ # Get rescue handler for the error
144
187
  rescue_handler = api.rescue_handler_for(e)
145
188
  raise e if rescue_handler.nil?
146
189
 
147
- # Replace the status code and response definition
190
+ # Set error status code and response definition
148
191
  status = rescue_handler.status_code
149
192
  response_definition = operation.find_response_for(status)
150
193
  raise e if response_definition.nil?
@@ -154,51 +197,10 @@ module Jsapi
154
197
  callback.respond_to?(:call) ? callback.call(e) : send(callback, e)
155
198
  end
156
199
 
157
- Error.new(e, status: status)
158
- end
159
- # Return if response body has already been rendered
160
- return if response_body
161
-
162
- # Produce response
163
- media_type, content_definition =
164
- response_definition
165
- .media_type_and_content_for(*_api_accept)
166
-
167
- status = status&.to_i
168
- return head(status) unless content_definition
169
-
170
- result = _api_before_rendering(operation_name, result, api_params)
171
-
172
- if media_type == Media::Type::TEXT_PLAIN
173
- render(
174
- plain: result.to_s,
175
- status: status,
176
- content_type: media_type.to_s
177
- )
178
- elsif media_type.json? || media_type.json_seq?
179
- api_response = Response.new(
180
- result,
181
- content_definition,
182
- omit: omit,
183
- locale: response_definition.locale
184
- )
185
- if media_type.json?
186
- render(
187
- json: api_response,
188
- status: status,
189
- content_type: media_type.to_s
190
- )
191
- else
192
- self.content_type = media_type.to_s
193
- response.status = status
194
-
195
- response.stream.tap do |stream|
196
- api_response.write_json_seq_to(stream)
197
- ensure
198
- stream.close
199
- end
200
- end
200
+ result = Error.new(e, status: status)
201
+ retry
201
202
  end
203
+ raise bypassed if bypassed.is_a?(Exception)
202
204
  end
203
205
  end
204
206
 
@@ -41,8 +41,9 @@ module Jsapi
41
41
  as_json.to_json
42
42
  end
43
43
 
44
- # Writes the response in \JSON sequence text format to +stream+.
45
- def write_json_seq_to(stream)
44
+ # Writes the response as sequential \JSON to +stream+.
45
+ def write_seq_json_to(stream, media_type = nil)
46
+ record_separator = "\u001E" if media_type&.subtype == 'json-seq' # see RFC 7464
46
47
  schema = @content_definition.schema
47
48
  object = @object
48
49
  object = schema.default_value(context: :response) if object.nil?
@@ -56,7 +57,7 @@ module Jsapi
56
57
  end
57
58
 
58
59
  items.each_with_index do |item, index|
59
- stream.write("\u001E") # Record separator (see RFC 7464)
60
+ stream.write(record_separator) if record_separator
60
61
  stream.write(
61
62
  item_schema.jsonify(
62
63
  item,
@@ -51,8 +51,7 @@ module Jsapi
51
51
  example_builder = nil
52
52
 
53
53
  with_openapi_extensions(
54
- **if media_type == Media::Type::APPLICATION_JSON_SEQ &&
55
- schema.array? && version >= OpenAPI::V3_2
54
+ **if version >= OpenAPI::V3_2 && media_type&.sequential_json? && schema.array?
56
55
  { itemSchema: schema.items.to_openapi(version) }
57
56
  else
58
57
  { schema: schema.to_openapi(version) }
@@ -18,7 +18,7 @@ module Jsapi
18
18
  class << self
19
19
  # Transforms +value+ to an instance of this class.
20
20
  #
21
- # Raises an +ArgumentError+ when +value+ could not be transformed.
21
+ # Raises an +ArgumentError+ if +value+ can not be transformed to a Media::Range.
22
22
  def from(value)
23
23
  media_range = try_from(value)
24
24
  return media_range unless media_range.nil?
@@ -12,16 +12,15 @@ module Jsapi
12
12
  # Media::Type for <code>"application/json"</code>.
13
13
  APPLICATION_JSON = Type.new('application', 'json')
14
14
 
15
- # Media::Type for <code>"application/json-seq"</code>.
16
- APPLICATION_JSON_SEQ = Type.new('application', 'json-seq')
17
-
18
15
  # Media::Type for <code>"text/plain"</code>.
19
16
  TEXT_PLAIN = Type.new('text', 'plain')
20
17
 
18
+ SEQ_JSON_SUBTYPE = /\A(jsonl|(|.+\+)json-seq|x-ndjson)\Z/.freeze # :nodoc:
19
+
21
20
  class << self
22
21
  # Transforms +value+ to an instance of this class.
23
22
  #
24
- # Raises an ArgumentError when +value+ could not be transformed.
23
+ # Raises an ArgumentError if +value+ can not be transformed to a Media::Type.
25
24
  def from(value)
26
25
  media_type = try_from(value)
27
26
  return media_type unless media_type.nil?
@@ -31,7 +30,7 @@ module Jsapi
31
30
 
32
31
  # Tries to transform +value+ to an instance of this class.
33
32
  #
34
- # Returns nil if +value+ could not be transformed.
33
+ # Returns nil if +value+ can not be transformed to a Media::Type.
35
34
  def try_from(value)
36
35
  return value if value.is_a?(Type)
37
36
 
@@ -59,16 +58,16 @@ module Jsapi
59
58
  subtype <=> other.subtype
60
59
  end
61
60
 
62
- # Returns true if it represents a JSON media type as specified by
61
+ # Returns true if it represents a \JSON media type as specified by
63
62
  # https://mimesniff.spec.whatwg.org/#json-mime-type.
64
63
  def json?
65
64
  (type.in?(%w[application text]) && subtype == 'json') ||
66
65
  subtype.end_with?('+json')
67
66
  end
68
67
 
69
- # Returns true if it is <code>"application/json-seq"</code>.
70
- def json_seq?
71
- type == 'application' && subtype == 'json-seq'
68
+ # Returns true if it represents a sequential \JSON media type.
69
+ def sequential_json?
70
+ type == 'application' && SEQ_JSON_SUBTYPE.match?(subtype)
72
71
  end
73
72
  end
74
73
  end
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 = '3.0'
8
+ VERSION = '3.1'
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: '3.0'
4
+ version: '3.1'
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: 2026-10-24 00:00:00.000000000 Z
11
+ date: 2026-10-31 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: denis@dmgoeller.de