Module: JsonRpcHandler

Defined in:
lib/json_rpc_handler.rb,
lib/json_rpc_handler/version.rb

Defined Under Namespace

Classes: ErrorCode, Version

Constant Summary collapse

VERSION =
'0.1.0'

Class Method Summary collapse

Class Method Details

.error_response(id:, error:) ⇒ Object



137
138
139
140
141
142
143
# File 'lib/json_rpc_handler.rb', line 137

def error_response(id:, error:)
  {
    jsonrpc: Version::V2_0,
    id: valid_id?(id) ? id : nil,
    error: error.compact,
  } unless id.nil?
end

.handle(request, &method_finder) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/json_rpc_handler.rb', line 21

def handle(request, &method_finder)
  if request.is_a? Array
    return error_response id: :unknown_id, error: {
      code: ErrorCode::InvalidRequest,
      message: 'Invalid Request',
      data: 'Request is an empty array',
    } if request.empty?

    # Handle batch requests
    responses = request.map { |req| process_request req, &method_finder }.compact

    # A single item is hoisted out of the array
    return responses.first if responses.one?

    # An empty array yields nil
    responses if responses.any?
  elsif request.is_a? Hash
    # Handle single request
    process_request request, &method_finder
  else
    error_response id: :unknown_id, error: {
      code: ErrorCode::InvalidRequest,
      message: 'Invalid Request',
      data: 'Request must be an array or a hash',
    }
  end
end

.handle_json(request_json, &method_finder) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/json_rpc_handler.rb', line 49

def handle_json(request_json, &method_finder)
  begin
    request = JSON.parse request_json, symbolize_names: true
    response = handle request, &method_finder
  rescue JSON::ParserError
    response =error_response id: :unknown_id, error: {
      code: ErrorCode::ParseError,
      message: 'Parse error',
      data: 'Invalid JSON',
    }
  end

  response.to_json if response
end

.process_request(request, &method_finder) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/json_rpc_handler.rb', line 64

def process_request(request, &method_finder)
  id = request[:id]

  error = case
    when !valid_version?(request[:jsonrpc]) then 'JSON-RPC version must be 2.0'
    when !valid_id?(request[:id]) then 'Request ID must be a string or an integer or null'
    when !valid_method_name?(request[:method]) then 'Method name must be a string and not start with "rpc."'
  end

  return error_response id: :unknown_id, error: {
    code: ErrorCode::InvalidRequest,
    message: 'Invalid Request',
    data: error,
  } if error

  method_name = request[:method]
  params = request[:params]

  unless valid_params? params
    return error_response id:, error: {
      code: ErrorCode::InvalidParams,
      message: 'Invalid params',
      data: 'Method parameters must be an array or an object or null',
    }
  end

  begin
    method = method_finder.call method_name

    if method.nil?
      return error_response id:, error: {
        code: ErrorCode::MethodNotFound,
        message: 'Method not found',
        data: method_name,
      }
    end

    result = method.call params

    success_response id:, result:
  rescue StandardError => e
    error_response id:, error: {
      code: ErrorCode::InternalError,
      message: 'Internal error',
      data: e.message,
    }
  end
end

.success_response(id:, result:) ⇒ Object



129
130
131
132
133
134
135
# File 'lib/json_rpc_handler.rb', line 129

def success_response(id:, result:)
  {
    jsonrpc: Version::V2_0,
    id:,
    result:,
  } unless id.nil?
end

.valid_id?(id) ⇒ Boolean

Returns:

  • (Boolean)


117
118
119
# File 'lib/json_rpc_handler.rb', line 117

def valid_id?(id)
  id.is_a?(String) || id.is_a?(Integer) || id.nil?
end

.valid_method_name?(method) ⇒ Boolean

Returns:

  • (Boolean)


121
122
123
# File 'lib/json_rpc_handler.rb', line 121

def valid_method_name?(method)
  method.is_a?(String) && !method.start_with?('rpc.')
end

.valid_params?(params) ⇒ Boolean

Returns:

  • (Boolean)


125
126
127
# File 'lib/json_rpc_handler.rb', line 125

def valid_params?(params)
  params.nil? || params.is_a?(Array) || params.is_a?(Hash)
end

.valid_version?(version) ⇒ Boolean

Returns:

  • (Boolean)


113
114
115
# File 'lib/json_rpc_handler.rb', line 113

def valid_version?(version)
  version == Version::V2_0
end