Current section
Files
Jump to
Current section
Files
lib/bolt_sips/internals/bolt_protocol_v3.ex
defmodule Bolt.Sips.Internals.BoltProtocolV3 do
alias Bolt.Sips.Internals.BoltProtocol
alias Bolt.Sips.Internals.BoltProtocolHelper
alias Bolt.Sips.Internals.Error
@doc """
Implementation of Bolt's HELLO. It initialises the connection.
Expects a transport module (i.e. `gen_tcp`) and a `Port`. Accepts
authorisation params in the form of {username, password}.
## Options
See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.
## Examples
iex> Bolt.Sips.Internals.BoltProtocolV3.hello(:gen_tcp, port, 3, {}, [])
{:ok, info}
iex> Bolt.Sips.Internals.BoltProtocolV3.hello(:gen_tcp, port, 3, {"username", "password"}, [])
{:ok, info}
"""
@spec hello(atom(), port(), integer(), tuple(), Keyword.t()) ::
{:ok, any()} | {:error, Bolt.Sips.Internals.Error.t()}
def hello(transport, port, bolt_version, auth, options \\ [recv_timeout: 15_000]) do
BoltProtocolHelper.send_message(transport, port, bolt_version, {:hello, [auth]})
case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do
{:success, info} ->
{:ok, info}
{:failure, response} ->
{:error, Error.exception(response, port, :hello)}
other ->
{:error, Error.exception(other, port, :hello)}
end
end
@doc """
Implementation of Bolt's RUN. It closes the connection.
## Options
See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.
## Examples
iex> Bolt.Sips.Internals.BoltProtocolV3.goodbye(:gen_tcp, port, 3)
:ok
iex> Bolt.Sips.Internals.BoltProtocolV3.goodbye(:gen_tcp, port, 3)
:ok
"""
def goodbye(transport, port, bolt_version) do
BoltProtocolHelper.send_message(transport, port, bolt_version, {:goodbye, []})
try do
Port.close(port)
:ok
rescue
ArgumentError -> Error.exception("Can't close port", port, :goodbye)
end
end
@doc """
Implementation of Bolt's RUN. It passes a statement for execution on the server.
Note that this message doesn't return the statement result. For this purpose, use PULL_ALL.
In bolt >= 3, run has an additional paramters; metadata
## Options
See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.
## Example
iex> BoltProtocolV1.run(:gen_tcp, port, 1, "RETURN $num AS num", %{num: 5}, %{}, [])
{:ok, {:success, %{"fields" => ["num"]}}}
"""
@spec run(atom(), port(), integer(), String.t(), map(), Bolt.Sips.Metadata.t(), Keyword.t()) ::
{:ok, any()} | {:error, Bolt.Sips.Internals.Error.t()}
def run(transport, port, bolt_version, statement, params, metadata, options) do
BoltProtocolHelper.send_message(
transport,
port,
bolt_version,
{:run, [statement, params, metadata]}
)
case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do
{:success, _} = result ->
{:ok, result}
{:failure, response} ->
{:error, Error.exception(response, port, :run)}
%Error{} = error ->
{:error, error}
other ->
{:error, Error.exception(other, port, :run)}
end
end
@doc """
Runs a statement (most likely Cypher statement) and returns a list of the
records and a summary (Act as as a RUN + PULL_ALL).
Records are represented using PackStream's record data type. Their Elixir
representation is a Keyword with the indexes `:sig` and `:fields`.
## Options
See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.
## Examples
iex> Bolt.Sips.Internals.BoltProtocol.run_statement(:gen_tcp, port, 1, "MATCH (n) RETURN n")
[
{:success, %{"fields" => ["n"]}},
{:record, [sig: 1, fields: [1, "Example", "Labels", %{"some_attribute" => "some_value"}]]},
{:success, %{"type" => "r"}}
]
"""
@spec run_statement(
atom(),
port(),
integer(),
String.t(),
map(),
Bolt.Sips.Metadata.t(),
Keyword.t()
) ::
[
Bolt.Sips.Internals.PackStream.Message.decoded()
]
| Bolt.Sips.Internals.Error.t()
def run_statement(transport, port, bolt_version, statement, params, metadata, options) do
with {:ok, run_data} <-
run(transport, port, bolt_version, statement, params, metadata, options),
{:ok, result} <- BoltProtocol.pull_all(transport, port, bolt_version, options) do
[run_data | result]
else
{:error, %Error{} = error} ->
error
other ->
Error.exception(other, port, :run_statement)
end
end
@doc """
Implementation of Bolt's BEGIN. It opens a transaction.
## Options
See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.
## Example
iex> BoltProtocolV3.begin(:gen_tcp, port, 3, [])
{:ok, metadata}
"""
@spec begin(atom(), port(), integer(), Bolt.Sips.Metadata.t() | map(), Keyword.t()) ::
{:ok, any()} | Bolt.Sips.Internals.Error.t()
def begin(transport, port, bolt_version, metadata, options) do
BoltProtocolHelper.send_message(transport, port, bolt_version, {:begin, [metadata]})
case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do
{:success, info} ->
{:ok, info}
{:failure, response} ->
{:error, Error.exception(response, port, :begin)}
other ->
{:error, Error.exception(other, port, :begin)}
end
end
@doc """
Implementation of Bolt's COMMIT. It commits the open transaction.
## Options
See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.
## Example
iex> BoltProtocolV3.commit(:gen_tcp, port, 3, [])
:ok
"""
@spec commit(atom(), port(), integer(), Keyword.t()) ::
{:ok, any()} | Bolt.Sips.Internals.Error.t()
def commit(transport, port, bolt_version, options) do
BoltProtocolHelper.send_message(transport, port, bolt_version, {:commit, []})
case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do
{:success, info} ->
{:ok, info}
{:failure, response} ->
{:error, Error.exception(response, port, :commit)}
other ->
{:error, Error.exception(other, port, :commit)}
end
end
@doc """
Implementation of Bolt's ROLLBACK. It rollbacks the open transaction.
## Options
See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.
## Example
iex> BoltProtocolV3.rollback(:gen_tcp, port, 3, [])
:ok
"""
@spec rollback(atom(), port(), integer(), Keyword.t()) :: :ok | Bolt.Sips.Internals.Error.t()
def rollback(transport, port, bolt_version, options) do
BoltProtocolHelper.treat_simple_message(:rollback, transport, port, bolt_version, options)
end
end