Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose send_query as top-level from StateQuery #10

Merged
merged 3 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ xogmios-*.tar
/tmp/
.DS_Store
.iex.exs
.elixir_ls
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

![CI Status](https://github.com/wowica/xogmios/actions/workflows/ci.yml/badge.svg)

An Elixir client for Cardano's [Ogmios](https://github.com/CardanoSolutions/ogmios).
An Elixir client for [Ogmios](https://github.com/CardanoSolutions/ogmios).

Currently supports the **Chain Synchronization** and **State Query** Ouroboros mini-protocol.
> Ogmios is a lightweight bridge interface for a Cardano node. It offers a WebSockets API that enables local clients to speak Ouroboros' mini-protocols via JSON/RPC. - https://ogmios.dev/

It currently only partially supports the **Chain Synchronization** and **State Query** mini-protocols. See [Examples](#examples) section below for information on how to use it.

## Installing

Expand All @@ -13,11 +15,13 @@ Add the dependency to `mix.exs`:
```elixir
defp deps do
[
{:xogmios, github: "wowica/xogmios", ref: "403384c"}
{:xogmios, github: "wowica/xogmios"}
]
end
```

Not yet available on Hex.

## Examples

See [ChainSyncClient](./examples/chain_sync_client.ex) and [StateQueryClient](./examples/state_query_client.ex)
Expand Down
25 changes: 8 additions & 17 deletions examples/state_query_client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,20 @@ defmodule StateQueryClient do
"""

use Xogmios, :state_query
alias Xogmios.StateQuery

@spec start_link(keyword()) :: {:ok, pid()} | {:error, any()}
def start_link(opts) do
Xogmios.start_state_link(__MODULE__, opts)
end

def get_current_epoch() do
case send_query(:get_current_epoch) do
{:ok, result} -> result
{:error, reason} -> "Something went wrong #{inspect(reason)}"
end
@spec get_current_epoch(pid() | atom()) :: {:ok, map()} | {:error, map()}
def get_current_epoch(pid \\ __MODULE__) do
StateQuery.send_query(pid, :get_current_epoch)
end

def get_era_start() do
case send_query(:get_era_start) do
{:ok, result} -> result
{:error, reason} -> "Something went wrong #{inspect(reason)}"
end
end

def get_bananas() do
case send_query(:get_bananas) do
{:ok, result} -> result
{:error, reason} -> "Something went wrong #{inspect(reason)}"
end
@spec get_era_start(pid() | atom()) :: {:ok, map()} | {:error, map()}
def get_era_start(pid \\ __MODULE__) do
StateQuery.send_query(pid, :get_era_start)
end
end
34 changes: 16 additions & 18 deletions lib/xogmios/state_query.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ defmodule Xogmios.StateQuery do
This module interfaces with the State Query protocol.
"""

alias Xogmios.StateQuery
alias Xogmios.StateQuery.Messages
alias Xogmios.StateQuery.Response
alias Xogmios.StateQuery.Server
Expand All @@ -19,13 +18,26 @@ defmodule Xogmios.StateQuery do

@allowed_queries Map.keys(@query_messages)

def fetch_query_message(query) when query in @allowed_queries,
@doc """
Sends a State Query call to the server and returns a response. This function is synchornous and takes two arguments:
1. (Optional) A process reference. If none given, it defaults to __MODULE__.
2. The query to run. It currently accepts the following values: `:get_current_epoch`, `:get_era_start`.
"""
@spec send_query(pid() | atom(), atom()) :: {:ok, any()} | {:error, any()}
def send_query(client \\ __MODULE__, query) do
with {:ok, message} <- fetch_query_message(query),
{:ok, %Response{} = response} <- call_query(client, message) do
{:ok, response.result}
end
end

defp fetch_query_message(query) when query in @allowed_queries,
do: Map.fetch(@query_messages, query)

def fetch_query_message(query),
defp fetch_query_message(query),
do: {:error, "Unsupported query #{inspect(query)}"}

def call_query(client, message) do
defp call_query(client, message) do
case GenServer.call(client, {:send_message, message}) do
{:ok, response} -> {:ok, response}
{:error, reason} -> {:error, reason}
Expand All @@ -36,20 +48,6 @@ defmodule Xogmios.StateQuery do
quote do
use GenServer

## Client API

@doc """
Sends a State Query call to the server and returns a response.
This function is synchornous.
"""
@spec send_query(term(), term()) :: {:ok, any()} | {:error, any()}
def send_query(client \\ __MODULE__, query) do
with {:ok, message} <- StateQuery.fetch_query_message(query),
{:ok, %Response{} = response} <- StateQuery.call_query(client, message) do
{:ok, response.result}
end
end

## Callbacks

@impl true
Expand Down
5 changes: 2 additions & 3 deletions lib/xogmios/state_query/messages.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
defmodule Xogmios.StateQuery.Messages do
@moduledoc """
This module returns messages for the State Query protocol
"""
@moduledoc false
# This module returns messages for the State Query protocol

alias Jason.DecodeError

Expand Down
5 changes: 2 additions & 3 deletions lib/xogmios/state_query/response.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
defmodule Xogmios.StateQuery.Response do
@moduledoc """
This module provides a common interface for responses from a State Queries
"""
@moduledoc false
# This module provides a common interface for responses from a State Queries

defstruct [:result]
end
9 changes: 4 additions & 5 deletions lib/xogmios/state_query/server.ex
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
defmodule Xogmios.StateQuery.Server do
@moduledoc """
This module implements the callbacks necessary for receiving asynchronous responses from
the WebSocket server. It acts as an synchronous interface for clients of Xogmios.StateQuery.
It uses GenServer.reply/2 to respond to GenServer.call/2 calls from Xogmios.StateQuery.send_query/2.
"""
@moduledoc false
# This module implements the callbacks necessary for receiving asynchronous responses from
# the WebSocket server. It acts as an synchronous interface for clients of Xogmios.StateQuery.
# It uses GenServer.reply/2 to respond to GenServer.call/2 calls from Xogmios.StateQuery.send_query/2.

@behaviour :websocket_client

Expand Down
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ defmodule Xogmios.MixProject do
~r/\.TestConnection/,
~r/\.TestHandler/,
~r/\.TestServer/,
Xogmios.ClientExampleA,
Xogmios.ClientExampleB
ChainSyncClient,
StateQueryClient
]
],
package: package(),
Expand Down
21 changes: 9 additions & 12 deletions test/state_query_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,28 @@ defmodule Xogmios.StateQueryTest do

defmodule DummyClient do
use Xogmios, :state_query
alias Xogmios.StateQuery

def start_link(opts) do
Xogmios.start_state_link(__MODULE__, opts)
end

def get_current_epoch() do
case send_query(:get_current_epoch) do
{:ok, result} -> result
{:error, reason} -> "Something went wrong #{inspect(reason)}"
end
def get_current_epoch(pid \\ __MODULE__) do
StateQuery.send_query(pid, :get_current_epoch)
end

def get_bananas() do
case send_query(:get_bananas) do
{:ok, result} -> result
{:error, reason} -> "Something went wrong #{inspect(reason)}"
end
def unsupported_query(pid \\ __MODULE__) do
StateQuery.send_query(pid, :unsupported_query)
end
end

test "returns current epoch" do
pid = start_supervised!({DummyClient, url: @ws_url})
assert is_pid(pid)
Process.sleep(1_000)
assert DummyClient.get_current_epoch() == 333
assert DummyClient.get_bananas() =~ "Something went wrong"
expected_epoch = 333
assert {:ok, ^expected_epoch} = DummyClient.get_current_epoch()
assert {:error, error_message} = DummyClient.unsupported_query()
assert error_message == "Unsupported query :unsupported_query"
end
end
Loading