Skip to content

Commit 162cbd0

Browse files
committed
Better erros
1 parent 8d3b564 commit 162cbd0

File tree

8 files changed

+292
-77
lines changed

8 files changed

+292
-77
lines changed

examples/rack/app.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ def handle_single(request_or_notification)
2323

2424
case request_or_notification.method
2525
when 'add'
26-
params['addends'].sum
26+
addends = params.is_a?(Array) ? params : { addends: params } # Handle positional and named arguments
27+
addends.sum
28+
# params['addends'].sum -> New API
2729
when 'subtract'
2830
params['minuend'] - params['subtrahend']
2931
when 'multiply'

lib/jsonrpc/configuration.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ module JSONRPC
22
class Configuration
33
Procedure = Data.define(:allow_positional_arguments, :contract, :parameter_name)
44

5+
attr_reader :validate_procedure_signatures
6+
57
def initialize
68
@procedures = {}
79
@validate_procedure_signatures = true

lib/jsonrpc/middleware.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,12 @@ def handle_jsonrpc_request(req)
4545
rescue ParseError, InvalidRequestError => e
4646
req.env['jsonrpc.error'] = e
4747
# These errors must be returned immediately per JSON-RPC spec
48-
return json_response(200, Response.new(id: e.request_id, error: error).to_h)
48+
return json_response(200, e.to_response)
4949
end
5050

51-
validation_errors = @validator.validate(parsed) if JSONRPC.configuration.validate_procedure_signatures
51+
validation_errors = @validator.validate(parsed) #if JSONRPC.configuration.validate_procedure_signatures
5252

53-
return json_response(200, validation_errors) if validation_errors
53+
return json_response(200, validation_errors.to_response) if validation_errors
5454

5555
@app.call(req.env)
5656
end

lib/jsonrpc/parser.rb

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,11 @@ def parse_single(data)
5555

5656
method = data['method']
5757
params = data['params']
58-
id = data['id']
5958

60-
if id.nil?
61-
Notification.new(method: method, params: params)
59+
if data.key?('id')
60+
Request.new(method: method, params: params, id: data['id'])
6261
else
63-
Request.new(method: method, params: params, id: id)
62+
Notification.new(method: method, params: params)
6463
end
6564
rescue ArgumentError => e
6665
request_id = data.is_a?(Hash) ? data['id'] : nil

lib/jsonrpc/validator.rb

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@ def validate_request_params(request)
4444

4545
unless validation_result.success?
4646
return InvalidParamsError.new(
47-
request_id: request&.id,
48-
data: { method: request.method }
47+
request_id: request.is_a?(Request) ? request.id : nil,
48+
data: {
49+
method: request.method,
50+
params: validation_result.errors.to_h
51+
}
4952
)
5053
end
5154

@@ -56,7 +59,7 @@ def validate_request_params(request)
5659
puts e.backtrace.join("\n")
5760
end
5861

59-
InternalError.new(request_id: request&.id)
62+
InternalError.new(request_id: request.is_a?(Request) ? request.id : nil)
6063
end
6164

6265
def prepare_params_for_validation(request, procedure)
@@ -85,7 +88,7 @@ def handle_positional_arguments(request, procedure)
8588
else
8689
# Invalid params type (not Array, Hash, or nil)
8790
InvalidParamsError.new(
88-
request_id: request&.id,
91+
request_id: request.is_a?(Request) ? request.id : nil,
8992
data: { method: request.method }
9093
)
9194
end
@@ -101,13 +104,13 @@ def handle_named_arguments(request, procedure)
101104
when Array
102105
# Positional arguments not allowed for this procedure
103106
InvalidParamsError.new(
104-
request_id: request&.id,
107+
request_id: request.is_a?(Request) ? request.id : nil,
105108
data: { method: request.method }
106109
)
107110
else
108111
# Invalid params type
109112
InvalidParamsError.new(
110-
request_id: request&.id,
113+
request_id: request.is_a?(Request) ? request.id : nil,
111114
data: { method: request.method }
112115
)
113116
end

spec/jsonrpc/middleware_spec.rb

Lines changed: 108 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,13 @@
8585
jsonrpc: '2.0',
8686
error: {
8787
code: -32_602,
88-
message: 'Invalid params'
88+
message: 'Invalid method parameter(s).',
89+
data: {
90+
method: 'add',
91+
params: {
92+
addends: ['is missing']
93+
}
94+
}
8995
},
9096
id: 'req-named-for-positioned'
9197
)
@@ -106,7 +112,8 @@
106112
jsonrpc: '2.0',
107113
error: {
108114
code: -32_602,
109-
message: 'Invalid params'
115+
message: 'Invalid method parameter(s).',
116+
data: { method: 'divide' }
110117
},
111118
id: 'req-positional-for-named'
112119
)
@@ -127,15 +134,21 @@
127134
jsonrpc: '2.0',
128135
error: {
129136
code: -32_602,
130-
message: 'Invalid params'
137+
message: 'Invalid method parameter(s).',
138+
data: {
139+
method: 'divide',
140+
params: {
141+
dividend: ['must be an integer']
142+
}
143+
}
131144
},
132145
id: 'req-invalid-params-type'
133146
)
134147
end
135148
end
136149

137150
context 'when processing a JSON-RPC request with an invalid parameter structure' do
138-
it 'returns HTTP 200 OK with a JSON-RPC invalid params error' do
151+
it 'returns HTTP 200 OK with a JSON-RPC invalid request error' do
139152
post_jsonrpc_request(
140153
jsonrpc: '2.0',
141154
method: 'divide',
@@ -147,8 +160,9 @@
147160
expect_json(
148161
jsonrpc: '2.0',
149162
error: {
150-
code: -32_602,
151-
message: 'Invalid params'
163+
code: -32_600,
164+
data: { details: 'Params must be an object, array, or omitted' },
165+
message: 'The JSON payload was valid JSON, but not a valid JSON-RPC Request object.',
152166
},
153167
id: 'req-invalid-params-structure'
154168
)
@@ -169,7 +183,14 @@
169183
jsonrpc: '2.0',
170184
error: {
171185
code: -32_602,
172-
message: 'Invalid params'
186+
message: 'Invalid method parameter(s).',
187+
data: {
188+
method: 'divide',
189+
params: {
190+
dividend: ['is missing'],
191+
divisor: ['is missing']
192+
}
193+
}
173194
},
174195
id: 'req-missing-params'
175196
)
@@ -185,8 +206,12 @@
185206
jsonrpc: '2.0',
186207
error: {
187208
code: -32_700,
188-
message: 'Parse error'
189-
}
209+
data: {
210+
details: 'unexpected end of input at line 1 column 1'
211+
},
212+
message: 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.'
213+
},
214+
id: nil
190215
)
191216
expect_status(200)
192217
end
@@ -201,8 +226,12 @@
201226
jsonrpc: '2.0',
202227
error: {
203228
code: -32_700,
204-
message: 'Parse error'
205-
}
229+
data: {
230+
details: "unexpected character: 'Taxation is theft.' at line 1 column 1"
231+
},
232+
message: 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.'
233+
},
234+
id: nil
206235
)
207236
end
208237
end
@@ -221,7 +250,10 @@
221250
jsonrpc: '2.0',
222251
error: {
223252
code: -32_600,
224-
message: 'Invalid request'
253+
data: {
254+
details: 'Method must be a string'
255+
},
256+
message: 'The JSON payload was valid JSON, but not a valid JSON-RPC Request object.'
225257
},
226258
id: 'req-valid-parse-error'
227259
)
@@ -241,7 +273,10 @@
241273
jsonrpc: '2.0',
242274
error: {
243275
code: -32_600,
244-
message: 'Invalid request'
276+
data: {
277+
details: "Missing 'jsonrpc' property"
278+
},
279+
message: 'The JSON payload was valid JSON, but not a valid JSON-RPC Request object.'
245280
},
246281
id: 'req-missing-jsonrpc-attribute'
247282
)
@@ -262,7 +297,10 @@
262297
jsonrpc: '2.0',
263298
error: {
264299
code: -32_600,
265-
message: 'Invalid request'
300+
data: {
301+
details: "Missing 'jsonrpc' property"
302+
},
303+
message: 'The JSON payload was valid JSON, but not a valid JSON-RPC Request object.'
266304
},
267305
id: 'req-invalid-jsonrpc-attribute'
268306
)
@@ -282,7 +320,10 @@
282320
jsonrpc: '2.0',
283321
error: {
284322
code: -32_600,
285-
message: 'Invalid request'
323+
data: {
324+
details: "Missing 'method' property"
325+
},
326+
message: 'The JSON payload was valid JSON, but not a valid JSON-RPC Request object.'
286327
},
287328
id: 'req-missing-method-attribute'
288329
)
@@ -303,7 +344,10 @@
303344
jsonrpc: '2.0',
304345
error: {
305346
code: -32_601,
306-
message: 'Method not found'
347+
data: {
348+
method: 'spoon'
349+
},
350+
message: 'The requested RPC method does not exist or is not supported.'
307351
},
308352
id: 'req-method-not-found'
309353
)
@@ -324,34 +368,42 @@
324368
expect_json(
325369
jsonrpc: '2.0',
326370
error: {
327-
code: -32_000, # Application-defined error code
328-
message: "Can't divide by 0"
371+
code: -32_602,
372+
message: 'Invalid method parameter(s).',
373+
data: {
374+
method: 'divide',
375+
params: {
376+
divisor: ["can't be 0"]
377+
}
378+
}
329379
},
330380
id: 'req-division-by-zero'
331381
)
332382
end
333383
end
334384

335-
context 'when processing a JSON-RPC request that causes an unexpected exception' do
336-
it 'returns HTTP 200 OK with a JSON-RPC internal error' do
337-
post_jsonrpc_request(
338-
jsonrpc: '2.0',
339-
method: 'add',
340-
params: [1, 2, 3, 'NaN'], # This trigger an error
341-
id: 'req-internal-error'
342-
)
343-
344-
expect_status(200)
345-
expect_json(
346-
jsonrpc: '2.0',
347-
error: {
348-
code: -32_603,
349-
message: 'Internal error'
350-
},
351-
id: 'req-internal-error'
352-
)
353-
end
354-
end
385+
# TODO: This no longer raises an interal error
386+
#
387+
# context 'when processing a JSON-RPC request that causes an unexpected exception' do
388+
# it 'returns HTTP 200 OK with a JSON-RPC internal error' do
389+
# post_jsonrpc_request(
390+
# jsonrpc: '2.0',
391+
# method: 'add',
392+
# params: [1, 2, 3, 'NaN'], # This triggers an error
393+
# id: 'req-internal-error'
394+
# )
395+
#
396+
# expect_status(200)
397+
# expect_json(
398+
# jsonrpc: '2.0',
399+
# error: {
400+
# code: -32_603,
401+
# message: 'Internal error'
402+
# },
403+
# id: 'req-internal-error'
404+
# )
405+
# end
406+
# end
355407

356408
# Batch Requests
357409
context 'when processing a valid JSON-RPC batch request' do
@@ -521,6 +573,19 @@
521573

522574
expect_status(200)
523575
expect_json(
576+
# {
577+
# "jsonrpc" => "2.0",
578+
# "id" => "batch-partial-2",
579+
# "error" => {
580+
# "code" => -32600,
581+
# "message" => "The JSON payload was valid JSON, but not a valid JSON-RPC Request object.",
582+
# "data" => {
583+
# "index" => 1,
584+
# "details" => "Missing 'jsonrpc' property"
585+
# }
586+
# }
587+
# }
588+
524589
[
525590
{
526591
jsonrpc: '2.0',
@@ -531,7 +596,11 @@
531596
jsonrpc: '2.0',
532597
error: {
533598
code: -32_600,
534-
message: 'Invalid request'
599+
data: {
600+
details: "Missing 'jsonrpc' property",
601+
index: 1
602+
},
603+
message: 'The JSON payload was valid JSON, but not a valid JSON-RPC Request object.'
535604
},
536605
id: 'batch-partial-2'
537606
}

0 commit comments

Comments
 (0)