jrpc 2.1.0 → 2.2.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: c257edbf0909e62cffe47dc7b8a79e928b4b4c24f7166bbbbec3b7141d01270f
4
- data.tar.gz: 671298a50f68328de11586e0e80eeba0ae18814ce79827b57dcd54699605c20c
3
+ metadata.gz: 84a616f81dad776fc7efea4d4e8fd778a3e2d5b42568118f0206f44f9b4e1b10
4
+ data.tar.gz: b8e8a2d8468d5e713e9015faccea22370775b99e476f17b4962cc0ebdc504d35
5
5
  SHA512:
6
- metadata.gz: 807cca67ef7ae784989daa7cf89797d9e255f77087822d1c1cae607839d02b2aed75916e5b1e75c47a11bfba5526eccd8df257fd1e36cf38029ba02e137ded28
7
- data.tar.gz: c987f2beba104d457137aeaa74821db2fb6835dee9161c39c50f1e409f254efc924c8c23178cc43e07867e083d7c11ebfd1dbf8f3a70d617c652380267abc224
6
+ metadata.gz: a58ecaa54dcaafae9871d5c7264e03adc57946859b75b72112a79bc7595df81495e5a4910fe69da28b66bf1b82c0cdb3026a5ccc0ea4f6786e81c50fea030413
7
+ data.tar.gz: abc7911d9cc8ffac7481decc11e01fc7524b1ce800c65c5bf7652c3567adad66ceedae7c63353f6f160a123957528254434807e58911dbaa61c221e3ee30727f
data/CHANGELOG.md CHANGED
@@ -1,6 +1,23 @@
1
1
  # Changelog
2
2
 
3
- ### Unreleased
3
+ ### 2.2.0
4
+
5
+ **New**
6
+
7
+ * `JRPC::Errors::ServerError#data` — the JSON-RPC `error.data` member is now carried
8
+ onto the raised exception instead of being dropped. `Message.error_to_exception`
9
+ reads `error['data']` and passes it to every `ServerError` subclass built from a peer
10
+ error object, verbatim and untyped (String, Hash, Array, … — whatever the peer sent),
11
+ `nil` when omitted. It is the only machine-readable detail an error object carries
12
+ beyond `code`, and servers use it to say *which* param was invalid or *which* id
13
+ conflicted. `MalformedResponseError` is raised locally rather than mapped from a peer
14
+ error object, so its `data` is always `nil`.
15
+ `JRPC::Transport::Test` emits a `data` member when a handler raises an error that
16
+ carries one, so the round trip is testable. Backwards compatible: on those classes
17
+ `data:` is a keyword with a `nil` default, and the wire frame is unchanged when there
18
+ is no data — the member is omitted, never emitted as `null`.
19
+
20
+ ### 2.1.0
4
21
 
5
22
  **New**
6
23
 
data/Gemfile CHANGED
@@ -5,16 +5,15 @@ source 'https://rubygems.org'
5
5
  # Specify your gem's dependencies in jrpc.gemspec
6
6
  gemspec
7
7
 
8
- gem 'bundler'
9
8
  gem 'rake', '~> 13.0'
10
9
  gem 'rspec', '~> 3.0'
11
10
 
12
11
  # Provides a real Fiber.scheduler for the fiber-caller specs (SharedClient §9.8).
13
12
  gem 'async', '~> 2.0'
14
13
 
15
- gem 'rubocop', '~> 1.21'
16
- gem 'rubocop-performance'
17
- gem 'rubocop-rspec'
14
+ gem 'rubocop', '~> 1.90.0'
15
+ gem 'rubocop-performance', '~> 1.27.0'
16
+ gem 'rubocop-rspec', '~> 3.10.2'
18
17
  gem 'rubocop-rake', '~> 0.7.1'
19
18
 
20
19
  gem 'simplecov', '~> 0.22', require: false
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/jrpc.svg)](https://rubygems.org/gems/jrpc)
4
4
  [![CI](https://github.com/didww/jrpc/actions/workflows/ci.yml/badge.svg)](https://github.com/didww/jrpc/actions/workflows/ci.yml)
5
5
 
6
- A JSON-RPC 2.0 client for Ruby, over TCP, with netstring framing.
6
+ A JSON-RPC v2 client for Ruby, over TCP, with netstring framing.
7
7
 
8
8
  JRPC ships two clients with sharp, separate responsibilities:
9
9
 
@@ -133,6 +133,7 @@ Errors::Error (RuntimeError)
133
133
  ├── Errors::Timeout # message TTL elapsed, or SimpleClient read/write/connect timeout
134
134
  └── Errors::ServerError # peer returned an error, or the response was unusable
135
135
  attr_reader :code # nil for malformed responses
136
+ attr_reader :data # the error object's `data` member, nil when absent
136
137
  ├── Errors::ParseError # -32700
137
138
  ├── Errors::InvalidRequest # -32600
138
139
  ├── Errors::MethodNotFound # -32601
@@ -145,11 +146,17 @@ Errors::Error (RuntimeError)
145
146
 
146
147
  `MalformedResponseError` is a `ServerError`, not a `ClientError`: a malformed response is the peer's fault.
147
148
 
149
+ `#data` carries the error object's optional `data` member through untouched — a String,
150
+ Hash, Array or number, whatever the peer sent, and `nil` when it sent none. It is where a
151
+ server says *which* param was invalid or *which* id conflicted, so log it alongside the
152
+ code. `MalformedResponseError` is raised locally rather than built from a peer error
153
+ object, so its `data` is always `nil`.
154
+
148
155
  ```ruby
149
156
  begin
150
157
  client.request(:do_thing, [1, 2])
151
158
  rescue JRPC::Errors::ServerError => e
152
- warn "rpc error #{e.code}: #{e.message}"
159
+ warn "rpc error #{e.code}: #{e.message} #{e.data.inspect}"
153
160
  rescue JRPC::Errors::Timeout
154
161
  warn "timed out"
155
162
  rescue JRPC::Errors::ConnectionError => e
data/lib/jrpc/errors.rb CHANGED
@@ -11,15 +11,22 @@ module JRPC
11
11
 
12
12
  class Timeout < Error; end
13
13
 
14
+ # A JSON-RPC error object the peer sent back, carrying its `code` and its optional
15
+ # `data` member verbatim. Per the spec `data` is "a primitive or structured value"
16
+ # defined by the server, so it arrives as whatever JSON.parse produced — String,
17
+ # Hash, Array, Numeric — or nil when the peer omitted it.
14
18
  class ServerError < Error
15
- attr_reader :code
19
+ attr_reader :code, :data
16
20
 
17
- def initialize(message, code: nil)
21
+ def initialize(message, code: nil, data: nil)
18
22
  @code = code
23
+ @data = data
19
24
  super(message)
20
25
  end
21
26
  end
22
27
 
28
+ # Raised locally when a frame is unparseable or violates the envelope rules, so it
29
+ # never corresponds to a peer error object: no code, no data.
23
30
  class MalformedResponseError < ServerError
24
31
  def initialize(message)
25
32
  super(message, code: nil)
@@ -27,32 +34,32 @@ module JRPC
27
34
  end
28
35
 
29
36
  class ParseError < ServerError
30
- def initialize(message)
31
- super(message, code: -32_700)
37
+ def initialize(message, data: nil)
38
+ super(message, code: -32_700, data: data)
32
39
  end
33
40
  end
34
41
 
35
42
  class InvalidRequest < ServerError
36
- def initialize(message)
37
- super(message, code: -32_600)
43
+ def initialize(message, data: nil)
44
+ super(message, code: -32_600, data: data)
38
45
  end
39
46
  end
40
47
 
41
48
  class MethodNotFound < ServerError
42
- def initialize(message)
43
- super(message, code: -32_601)
49
+ def initialize(message, data: nil)
50
+ super(message, code: -32_601, data: data)
44
51
  end
45
52
  end
46
53
 
47
54
  class InvalidParams < ServerError
48
- def initialize(message)
49
- super(message, code: -32_602)
55
+ def initialize(message, data: nil)
56
+ super(message, code: -32_602, data: data)
50
57
  end
51
58
  end
52
59
 
53
60
  class InternalError < ServerError
54
- def initialize(message)
55
- super(message, code: -32_603)
61
+ def initialize(message, data: nil)
62
+ super(message, code: -32_603, data: data)
56
63
  end
57
64
  end
58
65
 
data/lib/jrpc/message.rb CHANGED
@@ -49,17 +49,21 @@ module JRPC
49
49
  end
50
50
  end
51
51
 
52
+ # `error.data` is optional per the spec and is passed through untouched: servers use
53
+ # it to say *which* param was invalid, *which* id conflicted, and so on — the only
54
+ # machine-readable detail an error object carries beyond the code.
52
55
  def self.error_to_exception(error_hash)
53
56
  code = error_hash['code']
54
57
  message = error_hash['message']
58
+ data = error_hash['data']
55
59
  case code
56
- when -32_700 then Errors::ParseError.new(message)
57
- when -32_600 then Errors::InvalidRequest.new(message)
58
- when -32_601 then Errors::MethodNotFound.new(message)
59
- when -32_602 then Errors::InvalidParams.new(message)
60
- when -32_603 then Errors::InternalError.new(message)
61
- when -32_099..-32_000 then Errors::InternalServerError.new(message, code: code)
62
- else Errors::UnknownError.new(message, code: code)
60
+ when -32_700 then Errors::ParseError.new(message, data: data)
61
+ when -32_600 then Errors::InvalidRequest.new(message, data: data)
62
+ when -32_601 then Errors::MethodNotFound.new(message, data: data)
63
+ when -32_602 then Errors::InvalidParams.new(message, data: data)
64
+ when -32_603 then Errors::InternalError.new(message, data: data)
65
+ when -32_099..-32_000 then Errors::InternalServerError.new(message, code: code, data: data)
66
+ else Errors::UnknownError.new(message, code: code, data: data)
63
67
  end
64
68
  end
65
69
 
@@ -282,12 +282,14 @@ module JRPC
282
282
  JSON.generate({ 'jsonrpc' => JRPC::JSON_RPC_VERSION, 'id' => id, 'result' => result })
283
283
  end
284
284
 
285
+ # `data` is emitted only when the raised error carries one, so a handler that
286
+ # ignores it produces the same frame as before.
285
287
  def error_response(id, error)
286
288
  code = error.respond_to?(:code) && error.code.is_a?(Integer) ? error.code : -32_000
287
- JSON.generate(
288
- { 'jsonrpc' => JRPC::JSON_RPC_VERSION, 'id' => id,
289
- 'error' => { 'code' => code, 'message' => error.message } }
290
- )
289
+ data = error.respond_to?(:data) ? error.data : nil
290
+ err = { 'code' => code, 'message' => error.message }
291
+ err['data'] = data unless data.nil?
292
+ JSON.generate({ 'jsonrpc' => JRPC::JSON_RPC_VERSION, 'id' => id, 'error' => err })
291
293
  end
292
294
 
293
295
  def enqueue(entry)
data/lib/jrpc/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JRPC
4
- VERSION = '2.1.0'
4
+ VERSION = '2.2.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jrpc
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Talakevich