fix jsonrpc serializations
This commit is contained in:
@@ -37,6 +37,7 @@ pub fn new_client(client Client) &Client {
|
||||
// These parameters control timeout and retry behavior.
|
||||
@[params]
|
||||
pub struct SendParams {
|
||||
pub:
|
||||
// Maximum time in seconds to wait for a response (default: 60)
|
||||
timeout int = 60
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import net.websocket
|
||||
// Handler is a JSON-RPC request handler that maps method names to their corresponding procedure handlers.
|
||||
// It can be used with a WebSocket server to handle incoming JSON-RPC requests.
|
||||
pub struct Handler {
|
||||
pub:
|
||||
pub mut:
|
||||
// A map where keys are method names and values are the corresponding procedure handler functions
|
||||
procedures map[string]ProcedureHandler
|
||||
}
|
||||
@@ -20,7 +20,7 @@ pub:
|
||||
// 2. Execute the procedure with the extracted parameters
|
||||
// 3. Return the result as a JSON-encoded string
|
||||
// If an error occurs during any of these steps, it should be returned.
|
||||
type ProcedureHandler = fn (payload string) !string
|
||||
pub type ProcedureHandler = fn (payload string) !string
|
||||
|
||||
// new_handler creates a new JSON-RPC handler with the specified procedure handlers.
|
||||
//
|
||||
@@ -33,6 +33,15 @@ pub fn new_handler(handler Handler) !&Handler {
|
||||
return &Handler{...handler}
|
||||
}
|
||||
|
||||
// register_procedure registers a new procedure handler for the specified method.
|
||||
//
|
||||
// Parameters:
|
||||
// - method: The name of the method to register
|
||||
// - procedure: The procedure handler function to register
|
||||
pub fn (mut handler Handler) register_procedure(method string, procedure ProcedureHandler) {
|
||||
handler.procedures[method] = procedure
|
||||
}
|
||||
|
||||
// handler is a callback function compatible with the WebSocket server's message handler interface.
|
||||
// It processes an incoming WebSocket message as a JSON-RPC request and returns the response.
|
||||
//
|
||||
@@ -58,14 +67,14 @@ pub fn (handler Handler) handler(client &websocket.Client, message string) strin
|
||||
pub fn (handler Handler) handle(message string) !string {
|
||||
// Extract the method name from the request
|
||||
method := decode_request_method(message)!
|
||||
log.info('Handling remote procedure call to method: ${method}')
|
||||
// log.info('Handling remote procedure call to method: ${method}')
|
||||
|
||||
// Look up the procedure handler for the requested method
|
||||
procedure_func := handler.procedures[method] or {
|
||||
log.error('No procedure handler for method ${method} found')
|
||||
// log.error('No procedure handler for method ${method} found')
|
||||
return method_not_found
|
||||
}
|
||||
|
||||
|
||||
// Execute the procedure handler with the request payload
|
||||
response := procedure_func(message) or { panic(err) }
|
||||
return response
|
||||
|
||||
@@ -73,7 +73,7 @@ pub mut:
|
||||
//
|
||||
// Returns:
|
||||
// - A Response object containing the error
|
||||
pub fn new_error(id string, error RPCError) Response {
|
||||
pub fn new_error(id int, error RPCError) Response {
|
||||
return Response{
|
||||
jsonrpc: jsonrpc_version
|
||||
error_: error
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
module jsonrpc
|
||||
|
||||
import json
|
||||
import x.json2
|
||||
import rand
|
||||
|
||||
@@ -20,7 +21,7 @@ pub mut:
|
||||
|
||||
// An identifier established by the client that must be included in the response
|
||||
// This is used to correlate requests with their corresponding responses
|
||||
id string @[required]
|
||||
id int @[required]
|
||||
}
|
||||
|
||||
// new_request creates a new JSON-RPC request with the specified method and parameters.
|
||||
@@ -37,7 +38,7 @@ pub fn new_request(method string, params string) Request {
|
||||
jsonrpc: jsonrpc.jsonrpc_version
|
||||
method: method
|
||||
params: params
|
||||
id: rand.uuid_v4() // Automatically generate a unique ID using UUID v4
|
||||
id: rand.i32() // Automatically generate a unique ID using UUID v4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +69,7 @@ pub fn (req Request) encode() string {
|
||||
pub fn (req Request) validate() ! {
|
||||
if req.jsonrpc == '' {
|
||||
return error('request jsonrpc version not specified')
|
||||
} else if req.id == '' {
|
||||
} else if req.id == -1 {
|
||||
return error('request id is empty')
|
||||
} else if req.method == '' {
|
||||
return error('request method is empty')
|
||||
@@ -90,7 +91,7 @@ pub mut:
|
||||
params T
|
||||
|
||||
// An identifier established by the client
|
||||
id string @[required]
|
||||
id int @[required]
|
||||
}
|
||||
|
||||
// new_request_generic creates a new generic JSON-RPC request with strongly-typed parameters.
|
||||
@@ -107,7 +108,7 @@ pub fn new_request_generic[T](method string, params T) RequestGeneric[T] {
|
||||
jsonrpc: jsonrpc.jsonrpc_version
|
||||
method: method
|
||||
params: params
|
||||
id: rand.uuid_v4()
|
||||
id: rand.i32()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,11 +120,11 @@ pub fn new_request_generic[T](method string, params T) RequestGeneric[T] {
|
||||
//
|
||||
// Returns:
|
||||
// - The ID as a string, or an error if the ID field is missing
|
||||
pub fn decode_request_id(data string) !string {
|
||||
pub fn decode_request_id(data string) !int {
|
||||
data_any := json2.raw_decode(data)!
|
||||
data_map := data_any.as_map()
|
||||
id_any := data_map['id'] or { return error('ID field not found') }
|
||||
return id_any.str()
|
||||
return id_any.int()
|
||||
}
|
||||
|
||||
// decode_request_method extracts just the method field from a JSON-RPC request string.
|
||||
@@ -149,7 +150,7 @@ pub fn decode_request_method(data string) !string {
|
||||
// Returns:
|
||||
// - A RequestGeneric object with parameters of type T, or an error if parsing fails
|
||||
pub fn decode_request_generic[T](data string) !RequestGeneric[T] {
|
||||
return json2.decode[RequestGeneric[T]](data)!
|
||||
return json.decode(RequestGeneric[T], data)!
|
||||
}
|
||||
|
||||
// encode serializes the RequestGeneric object into a JSON string.
|
||||
|
||||
@@ -5,7 +5,7 @@ fn test_new_request() {
|
||||
assert request.jsonrpc == jsonrpc.jsonrpc_version
|
||||
assert request.method == 'test_method'
|
||||
assert request.params == 'test_params'
|
||||
assert request.id != '' // Ensure the ID is generated
|
||||
assert request.id != -1 // Ensure the ID is generated
|
||||
}
|
||||
|
||||
fn test_decode_request() {
|
||||
@@ -17,7 +17,7 @@ fn test_decode_request() {
|
||||
assert request.jsonrpc == '2.0'
|
||||
assert request.method == 'test_method'
|
||||
assert request.params == 'test_params'
|
||||
assert request.id == '123'
|
||||
assert request.id == 123
|
||||
}
|
||||
|
||||
fn test_request_encode() {
|
||||
@@ -32,7 +32,7 @@ fn test_new_request_generic() {
|
||||
assert request.jsonrpc == jsonrpc.jsonrpc_version
|
||||
assert request.method == 'test_method'
|
||||
assert request.params == params
|
||||
assert request.id != '' // Ensure the ID is generated
|
||||
assert request.id != -1 // Ensure the ID is generated
|
||||
}
|
||||
|
||||
fn test_decode_request_id() {
|
||||
@@ -41,7 +41,7 @@ fn test_decode_request_id() {
|
||||
assert false, 'Failed to decode request ID: $err'
|
||||
return
|
||||
}
|
||||
assert id == '123'
|
||||
assert id == 123
|
||||
}
|
||||
|
||||
fn test_decode_request_method() {
|
||||
@@ -54,7 +54,7 @@ fn test_decode_request_method() {
|
||||
}
|
||||
|
||||
fn test_decode_request_generic() {
|
||||
data := '{"jsonrpc":"2.0","method":"test_method","params":{"key":"value"},"id":"123"}'
|
||||
data := '{"jsonrpc":"2.0","method":"test_method","params":{"key":"value"},"id":123}'
|
||||
request := decode_request_generic[map[string]string](data) or {
|
||||
assert false, 'Failed to decode generic request: $err'
|
||||
return
|
||||
@@ -62,7 +62,7 @@ fn test_decode_request_generic() {
|
||||
assert request.jsonrpc == '2.0'
|
||||
assert request.method == 'test_method'
|
||||
assert request.params == {'key': 'value'}
|
||||
assert request.id == '123'
|
||||
assert request.id == 123
|
||||
}
|
||||
|
||||
fn test_request_generic_encode() {
|
||||
|
||||
@@ -21,7 +21,7 @@ pub:
|
||||
error_ ?RPCError @[json: 'error']
|
||||
|
||||
// Must match the id of the request that generated this response
|
||||
id string @[required]
|
||||
id int @[required]
|
||||
}
|
||||
|
||||
// new_response creates a successful JSON-RPC response with the given result.
|
||||
@@ -32,7 +32,7 @@ pub:
|
||||
//
|
||||
// Returns:
|
||||
// - A Response object containing the result
|
||||
pub fn new_response(id string, result string) Response {
|
||||
pub fn new_response(id int, result string) Response {
|
||||
return Response{
|
||||
jsonrpc: jsonrpc.jsonrpc_version
|
||||
result: result
|
||||
@@ -48,7 +48,7 @@ pub fn new_response(id string, result string) Response {
|
||||
//
|
||||
// Returns:
|
||||
// - A Response object containing the error
|
||||
pub fn new_error_response(id string, error RPCError) Response {
|
||||
pub fn new_error_response(id int, error RPCError) Response {
|
||||
return Response{
|
||||
jsonrpc: jsonrpc.jsonrpc_version
|
||||
error_: error
|
||||
@@ -79,7 +79,7 @@ pub fn decode_response(data string) !Response {
|
||||
if err := raw_map['error'] {
|
||||
id_any := raw_map['id'] or {return error('Invalid JSONRPC response, no ID Field found')}
|
||||
return Response {
|
||||
id: id_any.str()
|
||||
id: id_any.int()
|
||||
jsonrpc: jsonrpc_version
|
||||
error_: json2.decode[RPCError](err.str())!
|
||||
}
|
||||
@@ -87,7 +87,7 @@ pub fn decode_response(data string) !Response {
|
||||
|
||||
// Handle successful responses
|
||||
return Response {
|
||||
id: raw_map['id'] or {return error('Invalid JSONRPC response, no ID Field found')}.str()
|
||||
id: raw_map['id'] or {return error('Invalid JSONRPC response, no ID Field found')}.int()
|
||||
jsonrpc: jsonrpc_version
|
||||
result: raw_map['result']!.str()
|
||||
}
|
||||
@@ -167,7 +167,7 @@ pub mut:
|
||||
error_ ?RPCError @[json: 'error']
|
||||
|
||||
// Must match the id of the request that generated this response
|
||||
id string @[required]
|
||||
id int @[required]
|
||||
}
|
||||
|
||||
// new_response_generic creates a successful generic JSON-RPC response with a strongly-typed result.
|
||||
@@ -178,7 +178,7 @@ pub mut:
|
||||
//
|
||||
// Returns:
|
||||
// - A ResponseGeneric object with result of type D
|
||||
pub fn new_response_generic[D](id string, result D) ResponseGeneric[D] {
|
||||
pub fn new_response_generic[D](id int, result D) ResponseGeneric[D] {
|
||||
return ResponseGeneric[D]{
|
||||
jsonrpc: jsonrpc.jsonrpc_version
|
||||
result: result
|
||||
@@ -196,7 +196,6 @@ pub fn new_response_generic[D](id string, result D) ResponseGeneric[D] {
|
||||
// - A ResponseGeneric object with result of type D, or an error if parsing fails
|
||||
pub fn decode_response_generic[D](data string) !ResponseGeneric[D] {
|
||||
// Debug output - consider removing in production
|
||||
println('respodata ${data}')
|
||||
|
||||
raw := json2.raw_decode(data)!
|
||||
raw_map := raw.as_map()
|
||||
@@ -211,7 +210,7 @@ pub fn decode_response_generic[D](data string) !ResponseGeneric[D] {
|
||||
// Handle error responses
|
||||
if err := raw_map['error'] {
|
||||
return ResponseGeneric[D] {
|
||||
id: raw_map['id'] or {return error('Invalid JSONRPC response, no ID Field found')}.str()
|
||||
id: raw_map['id'] or {return error('Invalid JSONRPC response, no ID Field found')}.int()
|
||||
jsonrpc: jsonrpc_version
|
||||
error_: json2.decode[RPCError](err.str())!
|
||||
}
|
||||
@@ -220,7 +219,7 @@ pub fn decode_response_generic[D](data string) !ResponseGeneric[D] {
|
||||
// Handle successful responses
|
||||
resp := json.decode(ResponseGeneric[D], data)!
|
||||
return ResponseGeneric[D] {
|
||||
id: raw_map['id'] or {return error('Invalid JSONRPC response, no ID Field found')}.str()
|
||||
id: raw_map['id'] or {return error('Invalid JSONRPC response, no ID Field found')}.int()
|
||||
jsonrpc: jsonrpc_version
|
||||
result: resp.result
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module jsonrpc
|
||||
|
||||
fn test_new_response() {
|
||||
response := new_response('123', 'test_result')
|
||||
response := new_response(123, 'test_result')
|
||||
assert response.jsonrpc == jsonrpc.jsonrpc_version
|
||||
assert response.id == '123'
|
||||
assert response.id == 123
|
||||
|
||||
assert response.is_result()
|
||||
assert !response.is_error() // Ensure no error is set
|
||||
@@ -20,13 +20,13 @@ fn test_new_error_response() {
|
||||
message: 'Test error'
|
||||
data: 'Error details'
|
||||
}
|
||||
response := new_error_response('123', error)
|
||||
response := new_error_response(123, error)
|
||||
assert response.jsonrpc == jsonrpc.jsonrpc_version
|
||||
|
||||
response.validate()!
|
||||
assert response.is_error()
|
||||
assert !response.is_result() // Ensure no result is set
|
||||
assert response.id == '123'
|
||||
assert response.id == 123
|
||||
|
||||
response_error := response.error()?
|
||||
assert response_error == error
|
||||
@@ -39,7 +39,7 @@ fn test_decode_response() {
|
||||
return
|
||||
}
|
||||
assert response.jsonrpc == '2.0'
|
||||
assert response.id == '123'
|
||||
assert response.id == 123
|
||||
|
||||
assert response.is_result()
|
||||
assert !response.is_error() // Ensure no error is set
|
||||
@@ -51,13 +51,13 @@ fn test_decode_response() {
|
||||
}
|
||||
|
||||
fn test_response_encode() {
|
||||
response := new_response('123', 'test_result')
|
||||
response := new_response(123, 'test_result')
|
||||
json := response.encode()
|
||||
assert json.contains('"jsonrpc"') && json.contains('"result"') && json.contains('"id"')
|
||||
}
|
||||
|
||||
fn test_response_validate() {
|
||||
response := new_response('123', 'test_result')
|
||||
response := new_response(123, 'test_result')
|
||||
response.validate() or { assert false, 'Validation failed for valid response: $err' }
|
||||
|
||||
error := RPCError{
|
||||
@@ -69,7 +69,7 @@ fn test_response_validate() {
|
||||
jsonrpc: '2.0'
|
||||
result: 'test_result'
|
||||
error_: error
|
||||
id: '123'
|
||||
id: 123
|
||||
}
|
||||
invalid_response.validate() or {
|
||||
assert err.msg().contains('Response contains both error and result.')
|
||||
@@ -82,7 +82,7 @@ fn test_response_error() {
|
||||
message: 'Test error'
|
||||
data: 'Error details'
|
||||
}
|
||||
response := new_error_response('123', error)
|
||||
response := new_error_response(123, error)
|
||||
err := response.error() or {
|
||||
assert false, 'Failed to get error: $err'
|
||||
return
|
||||
@@ -93,7 +93,7 @@ fn test_response_error() {
|
||||
}
|
||||
|
||||
fn test_response_result() {
|
||||
response := new_response('123', 'test_result')
|
||||
response := new_response(123, 'test_result')
|
||||
result := response.result() or {
|
||||
assert false, 'Failed to get result: $err'
|
||||
return
|
||||
@@ -102,9 +102,9 @@ fn test_response_result() {
|
||||
}
|
||||
|
||||
fn test_new_response_generic() {
|
||||
response := new_response_generic('123', {'key': 'value'})
|
||||
response := new_response_generic(123, {'key': 'value'})
|
||||
assert response.jsonrpc == jsonrpc.jsonrpc_version
|
||||
assert response.id == '123'
|
||||
assert response.id == 123
|
||||
|
||||
assert response.is_result()
|
||||
assert !response.is_error() // Ensure no error is set
|
||||
@@ -122,7 +122,7 @@ fn test_decode_response_generic() {
|
||||
return
|
||||
}
|
||||
assert response.jsonrpc == '2.0'
|
||||
assert response.id == '123'
|
||||
assert response.id == 123
|
||||
|
||||
assert response.is_result()
|
||||
assert !response.is_error() // Ensure no error is set
|
||||
@@ -134,13 +134,13 @@ fn test_decode_response_generic() {
|
||||
}
|
||||
|
||||
fn test_response_generic_encode() {
|
||||
response := new_response_generic('123', {'key': 'value'})
|
||||
response := new_response_generic(123, {'key': 'value'})
|
||||
json := response.encode()
|
||||
assert json.contains('"jsonrpc"') && json.contains('"result"') && json.contains('"id"')
|
||||
}
|
||||
|
||||
fn test_response_generic_validate() {
|
||||
response := new_response_generic('123', {'key': 'value'})
|
||||
response := new_response_generic(123, {'key': 'value'})
|
||||
response.validate() or { assert false, 'Validation failed for valid response: $err' }
|
||||
|
||||
error := RPCError{
|
||||
@@ -152,7 +152,7 @@ fn test_response_generic_validate() {
|
||||
jsonrpc: '2.0'
|
||||
result: {'key': 'value'}
|
||||
error_: error
|
||||
id: '123'
|
||||
id: 123
|
||||
}
|
||||
invalid_response.validate() or {
|
||||
assert err.msg().contains('Response contains both error and result.')
|
||||
@@ -168,7 +168,7 @@ fn test_response_generic_error() {
|
||||
response := ResponseGeneric[map[string]string]{
|
||||
jsonrpc: '2.0'
|
||||
error_: error
|
||||
id: '123'
|
||||
id: 123
|
||||
}
|
||||
err := response.error() or {
|
||||
assert false, 'Failed to get error: $err'
|
||||
@@ -180,7 +180,7 @@ fn test_response_generic_error() {
|
||||
}
|
||||
|
||||
fn test_response_generic_result() {
|
||||
response := new_response_generic('123', {'key': 'value'})
|
||||
response := new_response_generic(123, {'key': 'value'})
|
||||
result := response.result() or {
|
||||
assert false, 'Failed to get result: $err'
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user