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

fix: Centralize the creation of errors #18

Merged
merged 1 commit into from
Nov 22, 2023
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
2 changes: 1 addition & 1 deletion lib/boltx/bolt_protocol/message/hello_message.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ defmodule Boltx.BoltProtocol.Message.HelloMessage do
{:success, response} ->
{:ok, response}
{:failure, response} ->
{:error, response}
{:error, Boltx.Error.wrap(__MODULE__, %{code: response["code"], message: response["message"]})}
end
end
end
2 changes: 1 addition & 1 deletion lib/boltx/bolt_protocol/message/init_message.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ defmodule Boltx.BoltProtocol.Message.InitMessage do
{:success, response} ->
{:ok, response}
{:failure, response} ->
{:error, response}
{:error, Boltx.Error.wrap(__MODULE__, %{code: response["code"], message: response["message"]})}
end
end
end
2 changes: 1 addition & 1 deletion lib/boltx/bolt_protocol/message/logon_message.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ defmodule Boltx.BoltProtocol.Message.LogonMessage do
{:success, response} ->
{:ok, response}
{:failure, response} ->
{:error, response}
{:error, Boltx.Error.wrap(__MODULE__, %{code: response["code"], message: response["message"]})}
end
end
end
8 changes: 2 additions & 6 deletions lib/boltx/client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ defmodule Boltx.Client do
encode_version <- recv_packets(client, config.connect_timeout),
version <- decode_version(encode_version) do
case version do
0.0 -> {:error, %Boltx.Error{code: :version_negotiation_error}}
0.0 -> {:error, Boltx.Error.wrap(__MODULE__, :version_negotiation_error)}
_ -> {:ok, %{client | bolt_version: version}}
end
else
Expand Down Expand Up @@ -189,11 +189,7 @@ defmodule Boltx.Client do
{:ok, response} ->
case decode_messages(response, chunks) do
{:complete_chunks, binary_message} ->
message = binary_message |> decoder.()
case message do
{:ok, decoded_message} -> {:ok, decoded_message}
{:error, error_message} -> {:error, %Boltx.Error{code: error_message["code"], message: error_message["message"]}}
end
binary_message |> decoder.()
{:remaining_chunks, binary_message} -> recv_packets(client, decoder, timeout, binary_message)
end
{:error, _} = error ->
Expand Down
11 changes: 0 additions & 11 deletions lib/boltx/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ defmodule Boltx.Connection do
@moduledoc false
use DBConnection
alias Boltx.Client
alias Boltx.Internals.Error, as: BoltError

defstruct [
:client,
Expand All @@ -21,11 +20,6 @@ defmodule Boltx.Connection do
client = %Client{client | connection_id: connection_id }
state = %__MODULE__{state | client: client}
{:ok, state}
else
{:error, reason} ->
# TODO: Evaluate how to return errors
#{:error, BoltError.exception(reason, nil, :connect)}
{:error, reason}
end
end

Expand All @@ -37,11 +31,6 @@ defmodule Boltx.Connection do
with {:ok, response_hello} <- Client.message_hello(client, opts),
{:ok, _response_logon} <- Client.message_logon(client, opts) do
{:ok, response_hello}
else
{:error, reason} ->
# TODO: Evaluate how to return errors
#{:error, BoltError.exception(reason, nil, :do_init)}
{:error, reason}
end
end

Expand Down
70 changes: 42 additions & 28 deletions lib/boltx/error.ex
Original file line number Diff line number Diff line change
@@ -1,34 +1,48 @@
defmodule Boltx.Error do
@moduledoc """
represents an error message
"""
alias __MODULE__
@type t :: %__MODULE__{}

defstruct [:code, :message]

def new(%Boltx.Internals.Error{
code: code,
connection_id: cid,
function: f,
message: message,
type: t
}) do
{:error,
%Error{
code: code,
message:
"Details: #{message}; connection_id: #{inspect(cid)}, function: #{inspect(f)}, type: #{
inspect(t)
}"
}}
end

def new({:ignored, f} = _r), do: new({:error, f})
@error_map %{
"Neo.ClientError.Security.Unauthorized" => :unauthorized,
}

@type t() :: %__MODULE__{
module: module(),
code: atom(),
bolt: %{code: binary(), message: binary() | nil} | nil,
}

defexception [:module, :code, :bolt]

@spec wrap(module(), atom()) :: t()
def wrap(module, code) when is_atom(code), do: %__MODULE__{module: module, code: code}

@spec wrap(module(), binary()) :: t()
def wrap(module, code) when is_binary(code), do: wrap(module, to_atom(code))

@spec wrap(module(), map()) :: t()
def wrap(module, bolt_error) when is_map(bolt_error), do: %__MODULE__{module: module, code: bolt_error.code |> to_atom(), bolt: bolt_error}


def new({:failure, %{"code" => code, "message" => message}} = _r) do
{:error, %Error{code: code, message: message}}
@doc """
Return the code for the given error.

### Examples

iex> {:error, %Boltx.Error{} = error} = do_something()
iex> Exception.message(error)
"Unable to perform this action."


"""
@spec message(t()) :: String.t()
def message(%__MODULE__{code: code, module: module}) do
module.format_error(code)
end

def new(r), do: r
@doc """
Gets the corresponding atom based on the error code.
"""
@spec to_atom(t()) :: String.t()
def to_atom(error_message) do
Map.get(@error_map, error_message, :unknown)
end
end
34 changes: 34 additions & 0 deletions lib/boltx/error_legacy.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
defmodule Boltx.ErrorLegacy do
@moduledoc """
represents an error message
"""
alias __MODULE__
@type t :: %__MODULE__{}

defstruct [:code, :message]

def new(%Boltx.Internals.Error{
code: code,
connection_id: cid,
function: f,
message: message,
type: t
}) do
{:error,
%ErrorLegacy{
code: code,
message:
"Details: #{message}; connection_id: #{inspect(cid)}, function: #{inspect(f)}, type: #{
inspect(t)
}"
}}
end

def new({:ignored, f} = _r), do: new({:error, f})

def new({:failure, %{"code" => code, "message" => message}} = _r) do
{:error, %ErrorLegacy{code: code, message: message}}
end

def new(r), do: r
end
12 changes: 6 additions & 6 deletions lib/boltx/query.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ defmodule Boltx.Query do
See the various tests, or more examples and implementation details.

"""
alias Boltx.{QueryStatement, Response, Types, Error, Exception}
alias Boltx.{QueryStatement, Response, Types, ErrorLegacy, Exception}

@cypher_seps ~r/;(.){0,1}\n/

Expand All @@ -66,19 +66,19 @@ defmodule Boltx.Query do
end
end

@spec query(Boltx.conn(), String.t()) :: {:error, Error.t()} | {:ok, Response.t()}
@spec query(Boltx.conn(), String.t()) :: {:error, ErrorLegacy.t()} | {:ok, Response.t()}
def query(conn, statement), do: query(conn, statement, %{})

@spec query(Boltx.conn(), String.t(), map, Keyword.t()) ::
{:error, Error.t()} | {:ok, Response.t()}
{:error, ErrorLegacy.t()} | {:ok, Response.t()}
def query(conn, statement, params, opts \\ []) when is_map(params) do
case query_commit(conn, statement, params, opts) do
{:error, message} -> {:error, %Error{message: message}}
{:error, message} -> {:error, %ErrorLegacy{message: message}}
r -> r
end
rescue
e in Boltx.Exception ->
{:error, %Boltx.Error{code: e.code, message: e.message}}
{:error, %Boltx.ErrorLegacy{code: e.code, message: e.message}}
end

###########
Expand Down Expand Up @@ -112,7 +112,7 @@ defmodule Boltx.Query do
{:ok, commit!(errors, conn, statements, formatted_params, opts)}
rescue
e in [RuntimeError, DBConnection.ConnectionError] ->
{:error, Boltx.Error.new(e.message)}
{:error, Boltx.ErrorLegacy.new(e.message)}

e in Exception ->
{:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace)
Expand Down
8 changes: 4 additions & 4 deletions lib/boltx/response.ex
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ defmodule Boltx.Response do
type: nil,
bookmark: nil

alias Boltx.Error
alias Boltx.ErrorLegacy

require Logger
require Integer
Expand Down Expand Up @@ -136,7 +136,7 @@ defmodule Boltx.Response do
end

defp parse(records) do
with {err_type, error} when err_type in ~w(halt error failure)a <- Error.new(records) do
with {err_type, error} when err_type in ~w(halt error failure)a <- ErrorLegacy.new(records) do
{:error, error}
else
records ->
Expand Down Expand Up @@ -193,7 +193,7 @@ defmodule Boltx.Response do

err_msg = "UNKNOWN success type: " <> inspect(record) <> line
Logger.error(err_msg)
{:error, Boltx.Error.new(err_msg)}
{:error, Boltx.ErrorLegacy.new(err_msg)}
end

# defp parse_record(:record, %{"bookmark" => "neo4j:bookmark:v1:tx14519", "t_last" => 1, "type" => "r"}, response) do
Expand All @@ -208,7 +208,7 @@ defmodule Boltx.Response do

err_msg = "UNKNOWN `:record`: " <> inspect(record) <> line
Logger.error(err_msg)
{:error, Boltx.Error.new(err_msg)}
{:error, Boltx.ErrorLegacy.new(err_msg)}
end

defp create_results(fields, records) do
Expand Down
6 changes: 3 additions & 3 deletions lib/boltx/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule Boltx.Router do
require Logger

alias Boltx.Routing.RoutingTable
alias Boltx.{Protocol, ConnectionSupervisor, LoadBalancer, Response, Error}
alias Boltx.{Protocol, ConnectionSupervisor, LoadBalancer, Response, ErrorLegacy}

defmodule State do
@moduledoc """
Expand Down Expand Up @@ -174,7 +174,7 @@ defmodule Boltx.Router do
{:ok, %Response{} = results} <- Boltx.query(conn, query, props) do
{:ok, Response.first(results), updated_connections}
else
{:error, %Error{code: code, message: message}} ->
{:error, %ErrorLegacy{code: code, message: message}} ->
err_msg = "#{code}; #{message}"
Logger.error(err_msg)
{:error, err_msg}
Expand Down Expand Up @@ -221,7 +221,7 @@ defmodule Boltx.Router do

{:ok, table}
else
{:error, %Error{message: message}} ->
{:error, %ErrorLegacy{message: message}} ->
Logger.error(message)
{:error, message}

Expand Down
2 changes: 1 addition & 1 deletion test/boltx/connection_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ defmodule Boltx.ConnectionTest do
@tag core: true
test "connect/1 - not successful with incorrect credentials" do
opts = @opts_without_auth ++ [auth: [username: "baduser", password: "badsecret"]]
{:error, %Boltx.Error{code: "Neo.ClientError.Security.Unauthorized"}} =
{:error, %Boltx.Error{code: :unauthorized}} =
Connection.connect(opts)
end

Expand Down
2 changes: 1 addition & 1 deletion test/invalid_param_type_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defmodule Boltx.InvalidParamType.Test do
MATCH (n:Person {invalid: {an_elixir_datetime}}) RETURN TRUE
"""

{:error, %Boltx.Error{message: message}} =
{:error, %Boltx.ErrorLegacy{message: message}} =
Boltx.query(conn, cypher, %{an_elixir_tuple: {:not, :valid}})

assert String.match?(message, ~r/unable to encode value: {:not, :valid}/i)
Expand Down