README.md

# JSONRPC2.Service

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.

## Installation

If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `jsonrpc2_service` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:jsonrpc2_service, "~> 0.1.0"}
  ]
end
```

## How to use

### Defining Services

Services should use `JSONRPC2.Service`, which allows to describe methods of service. Each method is a module which uses `JSONRPC2.Service.Method`.

Examples:

```elixir
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...
```

### Defining Methods

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.

```elixir
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
end
```

The 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`.

#### `handle_call` and `handle_cast` callbacks

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:

```elixir
def handle_call([a, b], _) do
  if b != 0 do
    {:ok, a / b}
  else
    error(12345, "divided by zero")
  end
end
```

use `error!` function to abort function execution and return error:

```elixir
def handle_call([a, b], _) do
  if(b == 0, do: error!(12345, "divided by zero"))

  {:ok, a / b}
end
```

#### `validate` callback

This function is for the validation of the input dataset.

```elixir
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()
end
```

The 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.

#### `handle_error` and `handle_exception` callbacks

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.

```elixir
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(...)
end
```

Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at <https://hexdocs.pm/jsonrpc2_service>.