API Gateway
Number Porting
Telephony
Billing
Number Details
Call Monitoring
API Gateway
Number Porting
Telephony
Billing
Number Details
Call Monitoring
Number Porting
Number Details
Number Details API Client
POST /portability_checks
{
"phone_number": "14072966265"
}
{
"phone_number": "14072966265",
"portable": true
}
Look up number's location (Number details service)
See if we have coverage
defmodule NumberDetailsApiClientTest do
test "number deserializes correctly", %{bypass: bypass} do
Bypass.expect bypass, fn conn ->
phone_number = "14072966265"
fake_response = Poison.encode!(
%{tn: phone_number, city: "ORLANDO", state: "FL"}
)
Plug.Conn.resp(conn, 200, fake_response)
end
result = NumberDetailsApiClient.lookup([phone_number])
assert result ==
%NumberDetailsResult{tn: phone_number, city: "ORLANDO", state: "FL"}
end
end
defmodule PortabilityCheckControllerTest do
test "with a portable number", %{conn: conn} do
phone_number = "14072966265"
#TODO: figure out how return the number details
conn = post conn, portability_checks(conn, :create),
phone_numbers: [%{phone_number: phone_number}]
assert json_response(conn, 200) == %{
phone_numbers: [
%{
phone_number: phone_number,
portable: true
}
]
}
end
end
-- Jose Valim
defmodule NumberService.HTTPAdapter do
def lookup(number) do
number
|> make_request
|> handle_response
end
# ...
end
defmodule NumberDetails.TestAdapter do
@orlando_number "14072966265"
def orlando_number, do: @orlando_number
def lookup(@orlando_number) do
%NumberLookupResult{
phone_number: @orlando_number,
city: "ORLANDO",
state: "FL"
}
end
end
# config/config.exs
config :porting_app, :number_details,
adapter: NumberDetails.HTTPAdapter
# config/test.exs
config :porting_app, :number_details,
adapter: NumberDetails.TestAdapter
number_details_client =
Application.get_env(:porting_app, :number_details)[:adapter]
number_details_client.lookup("14072966265")
defmodule PortabilityCheckControllerTest do
alias NumberDetails.TestAdapter, as: TestNumbers
test "with a portable number", %{conn: conn} do
# Call with preconfigured test number
phone_number = TestNumbers.orlando_number
conn = post conn, portability_checks(conn, :create),
%{phone_number: phone_number}
assert json_response(conn, 200) ==
%{ phone_number: phone_number, portable: true }
end
end
Keep Adapters in Sync - Use Structs
defmodule NumberService.HTTPAdapter do
def lookup(number) do
number
|> make_request
|> handle_response
end
#...
defp handle_response(body) do
Poison.decode!(body,
as: %NumberDetailsResult{})
end
end
defmodule NumberDetails.TestAdapter do
@orlando_number "14072966265"
def lookup(@orlando_number) do
%NumberDetailsResult{
phone_number: @orlando_number,
city: "ORLANDO",
state: "FL"
}
end
end
defmodule NumberDetailsResult do
defstruct [:phone_number, :city, :state]
end
Keep Adapters in Sync - Use Behaviours
defmodule NumberDetails.Adapter do
@callback lookup(String.t) :: NumberDetailsResult.t
end
defmodule NumberDetails.HttpAdapter do
@behaviour NumberDetails.Adapter
def lookup(numbers) do
#...
end
end
defmodule NumberDetails.TestAdapter do
@behaviour NumberDetails.Adapter
def lookup(numbers) do
#...
end
end
Create a proxy module
defmodule NumberDetails do
@behaviour NumberDetails.Adapter
def lookup(numbers) do
adapter.lookup_numbers(numbers)
end
def adapter do
Application.get_env(:porting_app, :number_details)[:adapter]
end
end
# Then in app code
NumberDetails.lookup("14072966265")
Aaron Renner
Telnyx
@bayfieldcoder
https://github.com/aaronrenner