Provides the ability to create services in accordance with the JSONRPC 2.0 specification. There is no transport layer, but only tools for creating transport-independent JSONRPC 2.0 services.
If available in Hex, the package can be installed
by adding jsonrpc2_service to your list of dependencies in mix.exs:
def deps do
[
{:jsonrpc2_service, "~> 0.1.0"}
]
endServices should use JSONRPC2.Service, which allows to describe methods of service. Each method is a module which uses JSONRPC2.Service.Method.
Examples:
defmodule CalculatorService do
use JSONRPC2.Service
method "add", AddMethod
method "subtract", SubtractMethod
method "multiply", MultiplyMethod
method "divide", DivideMethod
end
defmodule AddMethod do
use JSONRPC2.Service.Method
end
# and so on...There are two possible ways to execute a request: call and cast. The first assumes the response which the service will return, the second does not. The module should implement at least one handle_call or handle_cast callback function to handle requests.
defmodule AddMethod do
use JSONRPC2Plug.Method
# It handles requests like this:
# {"id": "123", "method": "add", "params": {"x": 10, "y": 20}, "jsonrpc": "2.0"}
def handle_call(%{"x" = > x, "y" => y}, _conn) do
{:ok, x + y}
end
endThe first argument is the params data comes from JSONRPC 2.0 request. According to JSONRPC 2.0 spec, it must be either object or an array of arguments.
The second argument is the map with context data. Sometimes it could be useful to send some additional data into the service, eg. HTTP connection data (Plug.Conn)
The module implements behaviour JSONRPC2.Service.Method which consists of five callbacks: handle_call, handle_cast, validate, handle_error and, handle_exception.
This callbacks should return {:ok, term()}, {:jsonrpc2_error, term()} or {:error, term()} tuples. There are two helpers for errors: error and error! functions.
use error function to return error:
def handle_call([a, b], _) do
if b != 0 do
{:ok, a / b}
else
error(12345, "divided by zero")
end
enduse error! function to abort function execution and return error:
def handle_call([a, b], _) do
if(b == 0, do: error!(12345, "divided by zero"))
{:ok, a / b}
endThis function is for the validation of the input dataset.
import JSONRPC2.Service.Validator, only: [type: 1, required: 0]
def validate(params) do
params
|> Validator.validate("x", [type(:integer), required()])
|> Validator.validate("y", [type(:integer), required()])
|> Validator.unwrap()
endThe library has its own validator. It has 8 built-in validations: type, required, not_empty, exclude, include, len, number and format. However, you can write custom validations and extend existing ones.
Moreover, you can use any preferred validator (eg. valdi), but you should respect the following requirements: the validate function should return either {:ok, params} or {:invalid, errors}. Where errors could be any type that can be safely encoded to JSON and params is parameters to pass into handle_call or handle_cast functions.
This callbacks are used to define way how to deal with errors and exceptions. The method handler catches all errors, throws, exits and exceptions and invoke error or exception handler.
def handle_error(request, {:exit, reason}, stacktrace) do
...
error(...)
end
def handle_error(request, {:throw, reason}, stacktrace) do
...
error(...)
end
def handle_exception(request, %ArithmeticError{} = ex, stacktrace) do
...
error(...)
endDocumentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/jsonrpc2_service.