Skip to content
This repository has been archived by the owner on Jun 30, 2021. It is now read-only.

Commit

Permalink
Supports passing exchange pair's rate as string (#1000)
Browse files Browse the repository at this point in the history
* Fix exchange pair not accepting rate as string

* Update OpenAPI specs
  • Loading branch information
unnawut authored May 14, 2019
1 parent 67a248c commit 06f6ee0
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ defmodule AdminAPI.V1.ExchangePairController do

{:error, code} ->
handle_error(conn, code)

{:error, code, description} ->
handle_error(conn, code, description)
end
end

Expand All @@ -98,6 +101,9 @@ defmodule AdminAPI.V1.ExchangePairController do

{:error, code} ->
handle_error(conn, code)

{:error, code, description} ->
handle_error(conn, code, description)
end
end

Expand Down
18 changes: 16 additions & 2 deletions apps/admin_api/priv/spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -5621,7 +5621,14 @@
"type": "string"
},
"rate": {
"type": "number"
"oneOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"sync_opposite": {
"type": "boolean"
Expand Down Expand Up @@ -6088,7 +6095,14 @@
"type": "string"
},
"rate": {
"type": "number"
"oneOf": [
{
"type": "string"
},
{
"type": "number"
}
]
}
},
"required": [
Expand Down
8 changes: 6 additions & 2 deletions apps/admin_api/priv/spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1438,7 +1438,9 @@ paths:
to_token_id:
type: string
rate:
type: number
oneOf:
- type: string
- type: number
sync_opposite:
type: boolean
required:
Expand Down Expand Up @@ -1472,7 +1474,9 @@ paths:
id:
type: string
rate:
type: number
oneOf:
- type: string
- type: number
required:
- id
example:
Expand Down
8 changes: 6 additions & 2 deletions apps/admin_api/priv/swagger/exchange_pair/request_bodies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ ExchangePairCreateBody:
to_token_id:
type: string
rate:
type: number
oneOf:
- type: string
- type: number
sync_opposite:
type: boolean
required:
Expand All @@ -45,7 +47,9 @@ ExchangePairUpdateBody:
id:
type: string
rate:
type: number
oneOf:
- type: string
- type: number
required:
- id
example:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,21 @@ defmodule AdminAPI.V1.ExchangePairControllerTest do
assert opposite["opposite_exchange_pair"]["id"] == pair["id"]
end

test_with_auths "accepts the rate attribute as string" do
request_data = %{sync_opposite: true} |> insert_params() |> Map.put(:rate, "3.14159")
response = request("/exchange_pair.create", request_data)

assert response["success"] == true
assert response["data"]["object"] == "list"

pair = Enum.at(response["data"]["data"], 0)

assert pair["object"] == "exchange_pair"
assert pair["from_token_id"] == request_data.from_token_id
assert pair["to_token_id"] == request_data.to_token_id
assert pair["rate"] == 3.14159
end

test_with_auths "returns client:invalid_parameter error if given an exchange rate of 0" do
request_data = insert_params(%{rate: 0})
response = request("/exchange_pair.create", request_data)
Expand Down Expand Up @@ -236,6 +251,18 @@ defmodule AdminAPI.V1.ExchangePairControllerTest do
"Invalid parameter provided. `rate` can't be blank."
end

test_with_auths "returns client:invalid_parameter error if rate is not parsable" do
request_data = insert_params(%{rate: "1.2345rate"})
response = request("/exchange_pair.create", request_data)

assert response["success"] == false
assert response["data"]["object"] == "error"
assert response["data"]["code"] == "client:invalid_parameter"

assert response["data"]["description"] ==
"Invalid parameter provided. `rate` cannot be parsed. Got: \"1.2345rate\""
end

test_with_auths "returns client:invalid_parameter error if from_token_id is not provided" do
request_data = insert_params(%{from_token_id: nil})
response = request("/exchange_pair.create", request_data)
Expand Down Expand Up @@ -397,6 +424,23 @@ defmodule AdminAPI.V1.ExchangePairControllerTest do
assert opposite["opposite_exchange_pair"]["id"] == pair["id"]
end

test_with_auths "accepts the rate attribute as string" do
exchange_pair = :exchange_pair |> insert() |> Repo.preload([:from_token, :to_token])

assert exchange_pair.rate != "999.99"

# Prepare the update data while keeping only id the same
request_data = %{
id: exchange_pair.id,
rate: "999.99"
}

response = request("/exchange_pair.update", request_data)

assert response["success"] == true
hd(response["data"]["data"])["rate"] == 999.99
end

test_with_auths "reverts and returns error if sync_opposite: true but opposite pair is not found" do
exchange_pair =
:exchange_pair
Expand Down Expand Up @@ -463,6 +507,18 @@ defmodule AdminAPI.V1.ExchangePairControllerTest do
"Invalid parameter provided. `rate` must be greater than 0."
end

test_with_auths "returns client:invalid_parameter error if rate is not parsable" do
pair = :exchange_pair |> insert() |> Repo.preload([:from_token, :to_token])
response = request("/exchange_pair.update", %{id: pair.id, rate: "3.14159pi"})

assert response["success"] == false
assert response["data"]["object"] == "error"
assert response["data"]["code"] == "client:invalid_parameter"

assert response["data"]["description"] ==
"Invalid parameter provided. `rate` cannot be parsed. Got: \"3.14159pi\""
end

defp assert_update_logs(logs, originator, target) do
assert Enum.count(logs) == 1

Expand Down
35 changes: 33 additions & 2 deletions apps/ewallet/lib/ewallet/gates/exchange_pair_gate.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@ defmodule EWallet.ExchangePairGate do
@doc """
Inserts an exchange pair.
"""
@spec insert(map()) :: {:ok, [%ExchangePair{}]} | {:error, atom() | Ecto.Changeset.t()}
@spec insert(map()) ::
{:ok, [%ExchangePair{}]}
| {:error, atom() | Ecto.Changeset.t()}
| {:error, atom(), String.t()}
def insert(%{"rate" => rate} = attrs) when is_binary(rate) do
case cast_rate(attrs) do
{:ok, casted} -> insert(casted)
error -> error
end
end

def insert(attrs) do
Repo.transaction(fn ->
with {:ok, direct} <- insert(:direct, attrs),
Expand Down Expand Up @@ -68,7 +78,16 @@ defmodule EWallet.ExchangePairGate do
Updates an exchange pair.
"""
@spec update(String.t(), map()) ::
{:ok, [%ExchangePair{}]} | {:error, atom() | Ecto.Changeset.t()}
{:ok, [%ExchangePair{}]}
| {:error, atom() | Ecto.Changeset.t()}
| {:error, atom(), String.t()}
def update(id, %{"rate" => rate} = attrs) when is_binary(rate) do
case cast_rate(attrs) do
{:ok, casted} -> update(id, casted)
error -> error
end
end

def update(id, attrs) do
Repo.transaction(fn ->
with {:ok, direct} <- update(:direct, id, attrs),
Expand Down Expand Up @@ -119,6 +138,18 @@ defmodule EWallet.ExchangePairGate do

defp update(:opposite, _, _), do: {:ok, nil}

defp cast_rate(%{"rate" => rate} = attrs) do
case Float.parse(rate) do
# Only a succesful parsing without any binary remainder shall proceed
{parsed, ""} ->
{:ok, Map.put(attrs, "rate", parsed)}

_ ->
{:error, :invalid_parameter,
"Invalid parameter provided. `rate` cannot be parsed. Got: #{inspect(rate)}"}
end
end

@doc """
Deletes an exchange pair.
"""
Expand Down
37 changes: 37 additions & 0 deletions apps/ewallet/test/ewallet/gates/exchange_pair_gate_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,43 @@ defmodule EWallet.ExchangePairGateTest do
assert Enum.at(pairs, 1).rate == 1 / 2.0
end

test "accepts the rate as string" do
eth = insert(:token)
omg = insert(:token)

{res, pairs} =
ExchangePairGate.insert(%{
"rate" => "3.14159",
"from_token_id" => eth.id,
"to_token_id" => omg.id,
"sync_opposite" => true,
"originator" => %System{}
})

assert res == :ok
assert hd(pairs).rate == 3.14159
end

test "returns error if the string rate cannot be parsed" do
eth = insert(:token)
omg = insert(:token)

{res, error, description} =
ExchangePairGate.insert(%{
"rate" => "not a rate",
"from_token_id" => eth.id,
"to_token_id" => omg.id,
"sync_opposite" => true,
"originator" => %System{}
})

assert res == :error
assert error == :invalid_parameter

assert description ==
"Invalid parameter provided. `rate` cannot be parsed. Got: \"not a rate\""
end

test "rollbacks if an error occurred along the way" do
eth = insert(:token)
omg = insert(:token)
Expand Down

0 comments on commit 06f6ee0

Please sign in to comment.