jsonrpc-middleware 0.6.0 → 0.7.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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/.aiignore +6 -1
  3. data/.claude/agents/entire-search.md +25 -0
  4. data/.claude/agents/rbs-specialist.md +89 -0
  5. data/.claude/settings.json +84 -0
  6. data/.devcontainer/devcontainer.json +17 -0
  7. data/.dockerignore +16 -0
  8. data/.entire/.gitignore +5 -0
  9. data/.entire/settings.json +4 -0
  10. data/.rubocop.yml +26 -1
  11. data/.tool-versions +1 -1
  12. data/.yard-lint.yml +283 -0
  13. data/AGENTS.md +142 -0
  14. data/CHANGELOG.md +19 -0
  15. data/CLAUDE.md +2 -113
  16. data/Dockerfile +144 -0
  17. data/README.md +9 -17
  18. data/Rakefile +62 -11
  19. data/examples/procedures.rb +3 -1
  20. data/examples/rack/Gemfile.lock +4 -4
  21. data/examples/rack-echo/Gemfile.lock +4 -4
  22. data/examples/rails/Gemfile.lock +12 -5
  23. data/examples/rails/config/initializers/jsonrpc.rb +1 -1
  24. data/examples/rails-routing-dsl/config.ru +5 -5
  25. data/examples/rails-single-file/config.ru +1 -1
  26. data/examples/rails-single-file-routing/config.ru +1 -1
  27. data/examples/sinatra-classic/Gemfile.lock +11 -4
  28. data/examples/sinatra-modular/Gemfile.lock +11 -4
  29. data/lib/jsonrpc/batch_request.rb +8 -11
  30. data/lib/jsonrpc/batch_response.rb +6 -8
  31. data/lib/jsonrpc/configuration.rb +30 -4
  32. data/lib/jsonrpc/error.rb +7 -8
  33. data/lib/jsonrpc/errors/internal_error.rb +2 -0
  34. data/lib/jsonrpc/errors/invalid_params_error.rb +2 -0
  35. data/lib/jsonrpc/errors/invalid_request_error.rb +2 -0
  36. data/lib/jsonrpc/errors/method_not_found_error.rb +2 -0
  37. data/lib/jsonrpc/errors/parse_error.rb +2 -0
  38. data/lib/jsonrpc/helpers.rb +6 -0
  39. data/lib/jsonrpc/middleware.rb +12 -11
  40. data/lib/jsonrpc/notification.rb +7 -8
  41. data/lib/jsonrpc/parser.rb +13 -12
  42. data/lib/jsonrpc/railtie/batch_constraint.rb +1 -0
  43. data/lib/jsonrpc/railtie/mapper_extension.rb +2 -2
  44. data/lib/jsonrpc/railtie/method_constraint.rb +9 -0
  45. data/lib/jsonrpc/railtie/routes_dsl.rb +10 -15
  46. data/lib/jsonrpc/railtie.rb +2 -0
  47. data/lib/jsonrpc/response.rb +2 -2
  48. data/lib/jsonrpc/types.rb +1 -1
  49. data/lib/jsonrpc/validator.rb +14 -4
  50. data/lib/jsonrpc/version.rb +1 -1
  51. data/lib/jsonrpc.rb +3 -0
  52. data/rbs_collection.lock.yaml +476 -0
  53. data/rbs_collection.yaml +21 -0
  54. data/sig/jsonrpc/batch_request.rbs +17 -0
  55. data/sig/jsonrpc/batch_response.rbs +17 -0
  56. data/sig/jsonrpc/configuration.rbs +18 -0
  57. data/sig/jsonrpc/error.rbs +17 -0
  58. data/sig/jsonrpc/errors/internal_error.rbs +5 -0
  59. data/sig/jsonrpc/errors/invalid_params_error.rbs +5 -0
  60. data/sig/jsonrpc/errors/invalid_request_error.rbs +5 -0
  61. data/sig/jsonrpc/errors/method_not_found_error.rbs +5 -0
  62. data/sig/jsonrpc/errors/parse_error.rbs +5 -0
  63. data/sig/jsonrpc/middleware.rbs +20 -3
  64. data/sig/jsonrpc/notification.rbs +15 -0
  65. data/sig/jsonrpc/parser.rbs +7 -1
  66. data/sig/jsonrpc/request.rbs +18 -0
  67. data/sig/jsonrpc/response.rbs +19 -0
  68. data/sig/jsonrpc/validator.rbs +8 -0
  69. data/sig/jsonrpc.rbs +3 -156
  70. data/sig/multi_json.rbs +17 -0
  71. data/sig/type_definitions.rbs +11 -0
  72. data/sig/zeitwerk.rbs +10 -0
  73. metadata +34 -12
  74. data/.claude/commands/document.md +0 -105
  75. data/.claude/commands/gemfile/update.md +0 -52
  76. data/.claude/commands/test.md +0 -561
  77. data/.claude/docs/yard.md +0 -602
  78. data/.claude/settings.local.json +0 -15
  79. data/.yardstick.yml +0 -22
@@ -6,6 +6,8 @@ module JSONRPC
6
6
  # The Parser handles converting raw JSON strings into appropriate JSONRPC objects
7
7
  # based on the JSON-RPC 2.0 protocol specification.
8
8
  #
9
+ # @api public
10
+ #
9
11
  # @example Parse a request
10
12
  # parser = JSONRPC::Parser.new
11
13
  # request = parser.parse('{"jsonrpc":"2.0","method":"subtract","params":[42,23],"id":1}')
@@ -26,14 +28,13 @@ module JSONRPC
26
28
  # @example Parse a batch request
27
29
  # parser.parse('[{"jsonrpc":"2.0","method":"sum","params":[1,2],"id":"1"}]')
28
30
  #
31
+ # @raise [ParseError] if the JSON is invalid
32
+ # @raise [InvalidRequestError] if the request structure is invalid
33
+ #
29
34
  # @param json [String] the JSON-RPC 2.0 message
30
35
  #
31
36
  # @return [Request, Notification, BatchRequest] the parsed object
32
37
  #
33
- # @raise [ParseError] if the JSON is invalid
34
- #
35
- # @raise [InvalidRequestError] if the request structure is invalid
36
- #
37
38
  def parse(json)
38
39
  data = MultiJson.load(json)
39
40
 
@@ -83,12 +84,12 @@ module JSONRPC
83
84
  #
84
85
  # @api private
85
86
  #
87
+ # @raise [InvalidRequestError] if the batch is empty
88
+ #
86
89
  # @param data [Array] the array of request data
87
90
  #
88
91
  # @return [BatchRequest] the batch request
89
92
  #
90
- # @raise [InvalidRequestError] if the batch is empty
91
- #
92
93
  def parse_batch(data)
93
94
  raise InvalidRequestError.new(data: { details: 'Batch request cannot be empty' }) if data.empty?
94
95
 
@@ -114,12 +115,12 @@ module JSONRPC
114
115
  #
115
116
  # @api private
116
117
  #
118
+ # @raise [InvalidRequestError] if the request structure is invalid
119
+ #
117
120
  # @param data [Hash] the parsed JSON data for a single item
118
121
  #
119
122
  # @return [Request, Notification] the parsed request or notification
120
123
  #
121
- # @raise [InvalidRequestError] if the request structure is invalid
122
- #
123
124
  def parse_single_for_batch(data)
124
125
  validate_jsonrpc_version(data)
125
126
  validate_request_structure(data)
@@ -141,10 +142,10 @@ module JSONRPC
141
142
  #
142
143
  # @api private
143
144
  #
144
- # @param data [Hash] the request data
145
- #
146
145
  # @raise [InvalidRequestError] if the version is missing or invalid
147
146
  #
147
+ # @param data [Hash] the request data
148
+ #
148
149
  # @return [void]
149
150
  #
150
151
  def validate_jsonrpc_version(data)
@@ -170,10 +171,10 @@ module JSONRPC
170
171
  #
171
172
  # @api private
172
173
  #
173
- # @param data [Hash] the request data
174
- #
175
174
  # @raise [InvalidRequestError] if the request structure is invalid
176
175
  #
176
+ # @param data [Hash] the request data
177
+ #
177
178
  # @return [void]
178
179
  #
179
180
  def validate_request_structure(data)
@@ -13,6 +13,7 @@ module JSONRPC
13
13
  # Check if the request is a JSON-RPC batch request
14
14
  #
15
15
  # @param request [ActionDispatch::Request] The Rails request object
16
+ #
16
17
  # @return [Boolean] true if the request is a batch request, false otherwise
17
18
  #
18
19
  def matches?(request)
@@ -8,8 +8,6 @@ module JSONRPC
8
8
  module MapperExtension
9
9
  # Define JSON-RPC routes with a DSL
10
10
  #
11
- # @param path [String] the path to handle JSON-RPC requests on
12
- #
13
11
  # @example Define JSON-RPC routes
14
12
  # jsonrpc '/api/v1' do
15
13
  # # Handle batch requests
@@ -24,6 +22,8 @@ module JSONRPC
24
22
  # end
25
23
  # end
26
24
  #
25
+ # @param path [String] the path to handle JSON-RPC requests on
26
+ #
27
27
  # @return [void]
28
28
  #
29
29
  def jsonrpc(path = '/', &)
@@ -22,6 +22,7 @@ module JSONRPC
22
22
  # Check if the request matches the configured method name
23
23
  #
24
24
  # @param request [ActionDispatch::Request] The Rails request object
25
+ #
25
26
  # @return [Boolean] true if the JSON-RPC method matches, false otherwise
26
27
  #
27
28
  def matches?(request)
@@ -31,5 +32,13 @@ module JSONRPC
31
32
 
32
33
  jsonrpc_request.method == @jsonrpc_method_name
33
34
  end
35
+
36
+ # Returns a string representation of the constraint
37
+ #
38
+ # @return [String] A string showing the configured JSON-RPC method name
39
+ #
40
+ def to_s
41
+ "#<JSONRPC::MethodConstraint method=\"#{@jsonrpc_method_name}\">"
42
+ end
34
43
  end
35
44
  end
@@ -57,9 +57,6 @@ module JSONRPC
57
57
 
58
58
  # Define a JSON-RPC method route
59
59
  #
60
- # @param jsonrpc_method [String] the JSON-RPC method name
61
- # @param to [String] the Rails controller action (e.g., 'users#create')
62
- #
63
60
  # @example Map a JSON-RPC method to controller action
64
61
  # method 'user.create', to: 'users#create'
65
62
  #
@@ -68,33 +65,30 @@ module JSONRPC
68
65
  # method 'create', to: 'posts#create' # becomes posts.create
69
66
  # end
70
67
  #
68
+ # @param jsonrpc_method [String] the JSON-RPC method name
69
+ # @param to [String] the Rails controller action (e.g., 'users#create')
70
+ #
71
71
  # @return [void]
72
72
  #
73
73
  def method(jsonrpc_method, to:)
74
74
  full_method_name = build_full_method_name(jsonrpc_method)
75
75
  constraint = JSONRPC::MethodConstraint.new(full_method_name)
76
76
 
77
- @mapper.post @path_prefix, {
78
- to: to,
79
- constraints: constraint
80
- }
77
+ @mapper.post(@path_prefix, to: to, constraints: constraint)
81
78
  end
82
79
 
83
80
  # Define a route for handling JSON-RPC batch requests
84
81
  #
85
- # @param to [String] the Rails controller action (e.g., 'batches#handle')
86
- #
87
82
  # @example Map batch requests to a controller action
88
83
  # batch to: 'batches#handle'
89
84
  #
85
+ # @param to [String] the Rails controller action (e.g., 'batches#handle')
86
+ #
90
87
  # @return [void]
91
88
  def batch(to:)
92
89
  constraint = JSONRPC::BatchConstraint.new
93
90
 
94
- @mapper.post @path_prefix, {
95
- to: to,
96
- constraints: constraint
97
- }
91
+ @mapper.post(@path_prefix, to: to, constraints: constraint)
98
92
  end
99
93
 
100
94
  # Create a namespace for grouping related JSON-RPC methods
@@ -102,8 +96,6 @@ module JSONRPC
102
96
  # Namespaces can be nested to create hierarchical method names.
103
97
  # Each level of nesting adds a dot-separated prefix to the method names.
104
98
  #
105
- # @param name [String] the namespace name
106
- #
107
99
  # @example Single-level namespace
108
100
  # namespace 'posts' do
109
101
  # method 'create', to: 'posts#create' # becomes posts.create
@@ -121,6 +113,8 @@ module JSONRPC
121
113
  # end
122
114
  # end
123
115
  #
116
+ # @param name [String] the namespace name
117
+ #
124
118
  # @return [void]
125
119
  #
126
120
  def namespace(name, &)
@@ -134,6 +128,7 @@ module JSONRPC
134
128
  # Build the full method name including namespaces
135
129
  #
136
130
  # @param method_name [String] the base method name
131
+ #
137
132
  # @return [String] the full method name with namespace prefixes
138
133
  #
139
134
  def build_full_method_name(method_name)
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSONRPC
4
+ # Integrates JSONRPC middleware, routing DSL, and helpers into Rails applications.
5
+ #
4
6
  # @api private
5
7
  class Railtie < ::Rails::Railtie
6
8
  # Rails routing constraint for matching JSON-RPC method names
@@ -82,9 +82,7 @@ module JSONRPC
82
82
  # @param id [String, Integer, nil] the request identifier
83
83
  #
84
84
  # @raise [ArgumentError] if both result and error are present or both are nil
85
- #
86
85
  # @raise [ArgumentError] if error is present but not a JSONRPC::Error
87
- #
88
86
  # @raise [ArgumentError] if id is not a String, Integer, or nil
89
87
  #
90
88
 
@@ -150,5 +148,7 @@ module JSONRPC
150
148
  def to_json(*)
151
149
  MultiJson.dump(to_h, *)
152
150
  end
151
+
152
+ alias to_response to_h
153
153
  end
154
154
  end
data/lib/jsonrpc/types.rb CHANGED
@@ -8,6 +8,6 @@ module JSONRPC
8
8
  # @see https://dry-rb.org/gems/dry-types/ Dry::Types documentation
9
9
  #
10
10
  module Types
11
- include Dry.Types()
11
+ send(:include, Dry.Types()) # Uses send to fix a YARD documentation bug
12
12
  end
13
13
  end
@@ -13,6 +13,16 @@ module JSONRPC
13
13
  # error = validator.validate(request)
14
14
  #
15
15
  class Validator
16
+ # Initializes the Validator
17
+ #
18
+ # @api public
19
+ #
20
+ # @param logger [Logger] the logger instance for diagnostic output
21
+ #
22
+ def initialize(logger: JSONRPC.configuration.logger)
23
+ @logger = logger
24
+ end
25
+
16
26
  # Validates a single request, notification or a batch
17
27
  #
18
28
  # @api public
@@ -92,7 +102,7 @@ module JSONRPC
92
102
  request_id: extract_request_id(request_or_notification),
93
103
  data: {
94
104
  method: request_or_notification.method,
95
- params: validation_result.errors.to_h
105
+ params: validation_result.errors.to_h # rubocop:disable Rails/DeprecatedActiveModelErrorsMethods
96
106
  }
97
107
  )
98
108
  end
@@ -100,8 +110,8 @@ module JSONRPC
100
110
  nil
101
111
  rescue StandardError => e
102
112
  if JSONRPC.configuration.log_request_validation_errors
103
- puts "Validation error: #{e.message}"
104
- puts e.backtrace.join("\n")
113
+ @logger.error("Validation error: #{e.message}")
114
+ @logger.error(e.backtrace.join("\n"))
105
115
  end
106
116
 
107
117
  InternalError.new(request_id: extract_request_id(request_or_notification))
@@ -111,7 +121,7 @@ module JSONRPC
111
121
  #
112
122
  # @api private
113
123
  #
114
- # @param request [Request, Notification] the request
124
+ # @param request [Request, Notification] A request or notification to be validated
115
125
  # @param procedure [Configuration::Procedure] the procedure configuration
116
126
  #
117
127
  # @return [Hash, InvalidParamsError] prepared params or error
@@ -11,5 +11,5 @@ module JSONRPC
11
11
  #
12
12
  # @return [String] The current version number
13
13
  #
14
- VERSION = '0.6.0'
14
+ VERSION = '0.7.0'
15
15
  end
data/lib/jsonrpc.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'logger'
3
4
  require 'zeitwerk'
4
5
  require 'dry-struct'
5
6
  require 'dry-validation'
@@ -72,10 +73,12 @@ loader.enable_reloading
72
73
  loader.collapse("#{__dir__}/jsonrpc/errors")
73
74
  loader.collapse("#{__dir__}/jsonrpc/railtie")
74
75
 
76
+ # :nocov:
75
77
  unless defined?(Rails)
76
78
  loader.ignore("#{__dir__}/jsonrpc/railtie.rb")
77
79
  loader.ignore("#{__dir__}/jsonrpc/railtie/method_constraint.rb")
78
80
  end
81
+ # :nocov:
79
82
 
80
83
  loader.inflector.inflect('jsonrpc' => 'JSONRPC')
81
84
  loader.setup