jsonrpc-middleware 0.1.0 → 0.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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.aiexclude +4 -0
  3. data/.claude/commands/document.md +105 -0
  4. data/.claude/docs/yard.md +602 -0
  5. data/.claude/settings.local.json +2 -1
  6. data/.env.example +5 -0
  7. data/CHANGELOG.md +22 -2
  8. data/CLAUDE.md +114 -0
  9. data/README.md +42 -102
  10. data/Rakefile +59 -1
  11. data/examples/README.md +37 -0
  12. data/examples/procedures.rb +6 -1
  13. data/examples/rack/README.md +26 -1
  14. data/examples/rack/app.rb +1 -1
  15. data/examples/rack-echo/README.md +23 -1
  16. data/examples/rack-single-file/README.md +37 -0
  17. data/examples/rack-single-file/config.ru +54 -0
  18. data/examples/rails/.gitignore +21 -0
  19. data/examples/rails/.ruby-version +1 -0
  20. data/examples/rails/Gemfile +15 -0
  21. data/examples/rails/Gemfile.lock +261 -0
  22. data/examples/rails/README.md +32 -0
  23. data/examples/rails/Rakefile +8 -0
  24. data/examples/rails/app/controllers/application_controller.rb +4 -0
  25. data/examples/rails/app/controllers/jsonrpc_controller.rb +44 -0
  26. data/examples/rails/bin/dev +4 -0
  27. data/examples/rails/bin/rails +6 -0
  28. data/examples/rails/bin/rake +6 -0
  29. data/examples/rails/bin/setup +28 -0
  30. data/examples/rails/config/application.rb +47 -0
  31. data/examples/rails/config/boot.rb +5 -0
  32. data/examples/rails/config/credentials.yml.enc +1 -0
  33. data/examples/rails/config/environment.rb +7 -0
  34. data/examples/rails/config/environments/development.rb +42 -0
  35. data/examples/rails/config/environments/production.rb +60 -0
  36. data/examples/rails/config/environments/test.rb +44 -0
  37. data/examples/rails/config/initializers/cors.rb +18 -0
  38. data/examples/rails/config/initializers/filter_parameter_logging.rb +10 -0
  39. data/examples/rails/config/initializers/inflections.rb +18 -0
  40. data/examples/rails/config/initializers/jsonrpc.rb +62 -0
  41. data/examples/rails/config/locales/en.yml +31 -0
  42. data/examples/rails/config/puma.rb +40 -0
  43. data/examples/rails/config/routes.rb +14 -0
  44. data/examples/rails/config.ru +8 -0
  45. data/examples/rails/public/robots.txt +1 -0
  46. data/examples/rails-single-file/config.ru +71 -0
  47. data/examples/sinatra-classic/Gemfile +9 -0
  48. data/examples/sinatra-classic/Gemfile.lock +95 -0
  49. data/examples/sinatra-classic/README.md +32 -0
  50. data/examples/sinatra-classic/app.rb +54 -0
  51. data/examples/sinatra-classic/config.ru +6 -0
  52. data/examples/sinatra-modular/Gemfile +9 -0
  53. data/examples/sinatra-modular/Gemfile.lock +95 -0
  54. data/examples/sinatra-modular/README.md +32 -0
  55. data/examples/sinatra-modular/app.rb +57 -0
  56. data/examples/sinatra-modular/config.ru +6 -0
  57. data/lib/jsonrpc/batch_request.rb +67 -2
  58. data/lib/jsonrpc/batch_response.rb +56 -0
  59. data/lib/jsonrpc/configuration.rb +156 -14
  60. data/lib/jsonrpc/error.rb +83 -2
  61. data/lib/jsonrpc/errors/internal_error.rb +14 -2
  62. data/lib/jsonrpc/errors/invalid_params_error.rb +13 -1
  63. data/lib/jsonrpc/errors/invalid_request_error.rb +8 -0
  64. data/lib/jsonrpc/errors/method_not_found_error.rb +8 -0
  65. data/lib/jsonrpc/errors/parse_error.rb +8 -0
  66. data/lib/jsonrpc/helpers.rb +212 -21
  67. data/lib/jsonrpc/middleware.rb +211 -5
  68. data/lib/jsonrpc/notification.rb +58 -0
  69. data/lib/jsonrpc/parser.rb +30 -0
  70. data/lib/jsonrpc/railtie.rb +57 -0
  71. data/lib/jsonrpc/request.rb +68 -1
  72. data/lib/jsonrpc/response.rb +76 -0
  73. data/lib/jsonrpc/validator.rb +67 -6
  74. data/lib/jsonrpc/version.rb +11 -1
  75. data/lib/jsonrpc.rb +53 -1
  76. metadata +49 -1
@@ -6,16 +6,28 @@ module JSONRPC
6
6
  # Raised when there was an internal JSON-RPC error.
7
7
  #
8
8
  # @example Create an internal error
9
- # error = JSONRPC::Errors::InternalError.new(data: { details: "Unexpected server error" })
9
+ # error = JSONRPC::Errors::InternalError.new(data: { details: 'Unexpected server error' })
10
10
  #
11
11
  class InternalError < Error
12
12
  # Creates a new Internal Error with code -32603
13
13
  #
14
+ # @api public
15
+ #
16
+ # @example Create an internal error
17
+ # error = JSONRPC::InternalError.new
18
+ #
19
+ # @example Create an internal error with exception details
20
+ # error = JSONRPC::InternalError.new(data: { exception: "NoMethodError" }, request_id: 1)
21
+ #
14
22
  # @param message [String] short description of the error
15
23
  # @param data [Hash, Array, String, Number, Boolean, nil] additional error information
16
24
  # @param request_id [String, Integer, nil] the request identifier
17
25
  #
18
- def initialize(message = 'Internal JSON-RPC error.', data: nil, request_id: nil)
26
+ def initialize(
27
+ message = 'Internal JSON-RPC error.',
28
+ data: nil,
29
+ request_id: nil
30
+ )
19
31
  super(
20
32
  message,
21
33
  code: -32_603,
@@ -11,11 +11,23 @@ module JSONRPC
11
11
  class InvalidParamsError < Error
12
12
  # Creates a new Invalid Params Error with code -32602
13
13
  #
14
+ # @api public
15
+ #
16
+ # @example Create an invalid params error
17
+ # error = JSONRPC::InvalidParamsError.new
18
+ #
19
+ # @example Create an invalid params error with validation details
20
+ # error = JSONRPC::InvalidParamsError.new(data: { errors: ["param a is required"] })
21
+ #
14
22
  # @param message [String] short description of the error
15
23
  # @param data [Hash, Array, String, Number, Boolean, nil] additional error information
16
24
  # @param request_id [String, Integer, nil] the request identifier
17
25
  #
18
- def initialize(message = 'Invalid method parameter(s).', data: nil, request_id: nil)
26
+ def initialize(
27
+ message = 'Invalid method parameter(s).',
28
+ data: nil,
29
+ request_id: nil
30
+ )
19
31
  super(
20
32
  message,
21
33
  code: -32_602,
@@ -11,6 +11,14 @@ module JSONRPC
11
11
  class InvalidRequestError < Error
12
12
  # Creates a new Invalid Request Error with code -32600
13
13
  #
14
+ # @api public
15
+ #
16
+ # @example Create an invalid request error
17
+ # error = JSONRPC::InvalidRequestError.new
18
+ #
19
+ # @example Create an invalid request error with custom data
20
+ # error = JSONRPC::InvalidRequestError.new(data: { field: "missing jsonrpc" })
21
+ #
14
22
  # @param message [String] short description of the error
15
23
  # @param data [Hash, Array, String, Number, Boolean, nil] additional error information
16
24
  # @param request_id [String, Integer, nil] the request identifier
@@ -11,6 +11,14 @@ module JSONRPC
11
11
  class MethodNotFoundError < Error
12
12
  # Creates a new Method Not Found Error with code -32601
13
13
  #
14
+ # @api public
15
+ #
16
+ # @example Create a method not found error
17
+ # error = JSONRPC::MethodNotFoundError.new
18
+ #
19
+ # @example Create a method not found error with method name
20
+ # error = JSONRPC::MethodNotFoundError.new(data: { method: "unknown_method" })
21
+ #
14
22
  # @param message [String] short description of the error
15
23
  # @param data [Hash, Array, String, Number, Boolean, nil] additional error information
16
24
  # @param request_id [String, Integer, nil] the request identifier
@@ -12,6 +12,14 @@ module JSONRPC
12
12
  class ParseError < Error
13
13
  # Creates a new Parse Error with code -32700
14
14
  #
15
+ # @api public
16
+ #
17
+ # @example Create a parse error with default message
18
+ # error = JSONRPC::ParseError.new
19
+ #
20
+ # @example Create a parse error with custom data
21
+ # error = JSONRPC::ParseError.new(data: { detail: "Unexpected end of input" })
22
+ #
15
23
  # @param message [String] short description of the error
16
24
  # @param data [Hash, Array, String, Number, Boolean, nil] additional error information
17
25
  #
@@ -3,32 +3,140 @@
3
3
  module JSONRPC
4
4
  # Framework-agnostic helpers for JSON-RPC
5
5
  module Helpers
6
+ # Extends the including class with ClassMethods when module is included
7
+ #
8
+ # @api public
9
+ #
10
+ # @example Include helpers in a class
11
+ # class MyController
12
+ # include JSONRPC::Helpers
13
+ # end
14
+ #
15
+ # @param base [Class] The class including this module
16
+ #
17
+ # @return [void]
18
+ #
6
19
  def self.included(base)
7
20
  base.extend(ClassMethods)
8
21
  end
9
22
 
10
23
  # Class methods for registering JSON-RPC procedure handlers
11
24
  module ClassMethods
25
+ # Registers a JSON-RPC procedure with the given method name
26
+ #
27
+ # @api public
28
+ #
29
+ # @example Register a procedure
30
+ # jsonrpc_method('add') do
31
+ # params do
32
+ # required(:a).value(:integer)
33
+ # required(:b).value(:integer)
34
+ # end
35
+ # end
36
+ #
37
+ # @param method_name [String, Symbol] the name of the method
38
+ #
39
+ # @yield Block containing the procedure definition
40
+ #
41
+ # @return [Configuration::Procedure] the registered procedure
42
+ #
12
43
  def jsonrpc_method(method_name, &)
13
44
  Configuration.instance.procedure(method_name, &)
14
45
  end
15
46
  end
16
47
 
17
- def jsonrpc_batch? = @env.key?('jsonrpc.batch')
18
- def jsonrpc_notification? = @env.key?('jsonrpc.notification')
19
- def jsonrpc_request? = @env.key?('jsonrpc.request')
48
+ # Checks if the current request is a batch request
49
+ #
50
+ # @api public
51
+ #
52
+ # @example Check if request is batch
53
+ # jsonrpc_batch? # => true/false
54
+ #
55
+ # @return [Boolean] true if current request is a batch
56
+ #
57
+ def jsonrpc_batch? = env.key?('jsonrpc.batch')
20
58
 
21
- # Get the current JSON-RPC request object
22
- def jsonrpc_batch = @env['jsonrpc.batch']
23
- def jsonrpc_request = @env['jsonrpc.request']
24
- def jsonrpc_notification = @env['jsonrpc.notification']
59
+ # Checks if the current request is a notification
60
+ #
61
+ # @api public
62
+ #
63
+ # @example Check if request is notification
64
+ # jsonrpc_notification? # => true/false
65
+ #
66
+ # @return [Boolean] true if current request is a notification
67
+ #
68
+ def jsonrpc_notification? = env.key?('jsonrpc.notification')
25
69
 
26
- # Create a JSON-RPC response
70
+ # Checks if the current request is a regular request
71
+ #
72
+ # @api public
73
+ #
74
+ # @example Check if request is regular request
75
+ # jsonrpc_request? # => true/false
76
+ #
77
+ # @return [Boolean] true if current request is a regular request
78
+ #
79
+ def jsonrpc_request? = env.key?('jsonrpc.request')
80
+
81
+ # Gets the current JSON-RPC batch request object
82
+ #
83
+ # @api public
84
+ #
85
+ # @example Get current batch
86
+ # batch = jsonrpc_batch
87
+ #
88
+ # @return [BatchRequest, nil] the current batch request or nil
89
+ #
90
+ def jsonrpc_batch = env['jsonrpc.batch']
91
+
92
+ # Gets the current JSON-RPC request object
93
+ #
94
+ # @api public
95
+ #
96
+ # @example Get current request
97
+ # request = jsonrpc_request
98
+ #
99
+ # @return [Request, nil] the current request or nil
100
+ #
101
+ def jsonrpc_request = env['jsonrpc.request']
102
+
103
+ # Gets the current JSON-RPC notification object
104
+ #
105
+ # @api public
106
+ #
107
+ # @example Get current notification
108
+ # notification = jsonrpc_notification
109
+ #
110
+ # @return [Notification, nil] the current notification or nil
111
+ #
112
+ def jsonrpc_notification = env['jsonrpc.notification']
113
+
114
+ # Creates a JSON-RPC response
115
+ #
116
+ # @api public
117
+ #
118
+ # @example Create a successful response
119
+ # jsonrpc_response(42) # => [200, headers, body]
120
+ #
121
+ # @param result [Object] the result to return
122
+ #
123
+ # @return [Array] Rack response tuple
124
+ #
27
125
  def jsonrpc_response(result)
28
126
  [200, { 'content-type' => 'application/json' }, [Response.new(id: jsonrpc_request.id, result: result).to_json]]
29
127
  end
30
128
 
31
- # Create a JSON-RPC response
129
+ # Creates a JSON-RPC batch response
130
+ #
131
+ # @api public
132
+ #
133
+ # @example Create a batch response
134
+ # jsonrpc_batch_response([response1, response2]) # => [200, headers, body]
135
+ #
136
+ # @param responses [Array] array of responses
137
+ #
138
+ # @return [Array] Rack response tuple
139
+ #
32
140
  def jsonrpc_batch_response(responses)
33
141
  # If batch contained only notifications, responses will be empty or contain only nils
34
142
  return [204, {}, []] if responses.compact.empty?
@@ -36,48 +144,131 @@ module JSONRPC
36
144
  [200, { 'content-type' => 'application/json' }, [responses.to_json]]
37
145
  end
38
146
 
147
+ # Creates a response for a notification (no content)
148
+ #
149
+ # @api public
150
+ #
151
+ # @example Create notification response
152
+ # jsonrpc_notification_response # => [204, {}, []]
153
+ #
154
+ # @return [Array] Rack response tuple with 204 status
155
+ #
39
156
  def jsonrpc_notification_response
40
157
  [204, {}, []]
41
158
  end
42
159
 
43
- # Create a JSON-RPC error response
160
+ # Creates a JSON-RPC error response
161
+ #
162
+ # @api public
163
+ #
164
+ # @example Create error response
165
+ # error = JSONRPC::Error.new(code: -32600, message: "Invalid Request")
166
+ # jsonrpc_error(error) # => JSON string
167
+ #
168
+ # @param error [Error] the error object
169
+ #
170
+ # @return [String] JSON-formatted error response
171
+ #
44
172
  def jsonrpc_error(error)
45
173
  Response.new(id: jsonrpc_request.id, error: error).to_json
46
174
  end
47
175
 
48
- # Get the current JSON-RPC request params object
176
+ # Gets the current JSON-RPC request params object
177
+ #
178
+ # @api public
179
+ #
180
+ # @example Get request parameters
181
+ # params = jsonrpc_params # => [1, 2, 3] or {"a": 1, "b": 2}
182
+ #
183
+ # @return [Array, Hash, nil] the request parameters
184
+ #
49
185
  def jsonrpc_params
50
186
  jsonrpc_request.params
51
187
  end
52
188
 
53
- # Create a Parse error (-32700)
54
- # Used when invalid JSON was received by the server
189
+ # Creates a Parse error (-32700) for invalid JSON
190
+ #
191
+ # @api public
192
+ #
193
+ # @example Create parse error
194
+ # jsonrpc_parse_error # => JSON error response
195
+ #
196
+ # @param data [Object, nil] additional error data
197
+ #
198
+ # @return [String] JSON-formatted error response
199
+ #
55
200
  def jsonrpc_parse_error(data: nil)
56
201
  jsonrpc_error(ParseError.new(data: data))
57
202
  end
58
203
 
59
- # Create an Invalid Request error (-32600)
60
- # Used when the JSON sent is not a valid Request object
204
+ # Creates an Invalid Request error (-32600) for invalid Request objects
205
+ #
206
+ # @api public
207
+ #
208
+ # @example Create invalid request error
209
+ # jsonrpc_invalid_request_error # => JSON error response
210
+ #
211
+ # @param data [Object, nil] additional error data
212
+ #
213
+ # @return [String] JSON-formatted error response
214
+ #
61
215
  def jsonrpc_invalid_request_error(data: nil)
62
216
  jsonrpc_error(InvalidRequestError.new(data: data))
63
217
  end
64
218
 
65
- # Create a Method not found error (-32601)
66
- # Used when the method does not exist / is not available
219
+ # Creates a Method not found error (-32601) for missing methods
220
+ #
221
+ # @api public
222
+ #
223
+ # @example Create method not found error
224
+ # jsonrpc_method_not_found_error # => JSON error response
225
+ #
226
+ # @param data [Object, nil] additional error data
227
+ #
228
+ # @return [String] JSON-formatted error response
229
+ #
67
230
  def jsonrpc_method_not_found_error(data: nil)
68
231
  jsonrpc_error(MethodNotFoundError.new(data: data))
69
232
  end
70
233
 
71
- # Create an Invalid params error (-32602)
72
- # Used when invalid method parameter(s) were received
234
+ # Creates an Invalid params error (-32602) for invalid parameters
235
+ #
236
+ # @api public
237
+ #
238
+ # @example Create invalid params error
239
+ # jsonrpc_invalid_params_error # => JSON error response
240
+ #
241
+ # @param data [Object, nil] additional error data
242
+ #
243
+ # @return [String] JSON-formatted error response
244
+ #
73
245
  def jsonrpc_invalid_params_error(data: nil)
74
246
  jsonrpc_error(InvalidParamsError.new(data: data))
75
247
  end
76
248
 
77
- # Create an Internal error (-32603)
78
- # Used for implementation-defined server errors
249
+ # Creates an Internal error (-32603) for server errors
250
+ #
251
+ # @api public
252
+ #
253
+ # @example Create internal error
254
+ # jsonrpc_internal_error # => JSON error response
255
+ #
256
+ # @param data [Object, nil] additional error data
257
+ #
258
+ # @return [String] JSON-formatted error response
259
+ #
79
260
  def jsonrpc_internal_error(data: nil)
80
261
  jsonrpc_error(InternalError.new(data: data))
81
262
  end
263
+
264
+ # Gets the Rack environment hash from @env or the Rails Request
265
+ #
266
+ # @api private
267
+ #
268
+ # @return [Hash] the Rack environment hash
269
+ #
270
+ def env
271
+ @env ||= request.env
272
+ end
82
273
  end
83
274
  end