Skip to content

Commit

Permalink
Message recovery client (#55)
Browse files Browse the repository at this point in the history
* Add messaging method to retrieve distinct origin_host from topics

* Add rescuer client and adapter to list undelivered messages

* Implement delete method on rescuer client
  • Loading branch information
lonamiaec authored Mar 10, 2020
1 parent 98d8cdd commit c3a487f
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 0 deletions.
5 changes: 5 additions & 0 deletions lib/postoffice/messaging.ex
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,9 @@ defmodule Postoffice.Messaging do
from(p in PublisherFailures, where: p.message_id == ^message_id)
|> Repo.all()
end

def get_topic_origin_hosts() do
from(t in Topic, distinct: true, select: t.origin_host)
|> Repo.all()
end
end
21 changes: 21 additions & 0 deletions lib/postoffice/rescuer/adapters/http.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
defmodule Postoffice.Rescuer.Adapters.Http do
require Logger

@behaviour Postoffice.Rescuer.Adapters.Impl

@impl true
def list(host) do
Logger.info("Listing undelivered messages from #{host}")
HTTPoison.get(host)
end

@impl true
def delete(host, message_id) do
build_message_path(host, message_id)
|> HTTPoison.delete()
end

defp build_message_path(host, message_id) do
host <> message_id <> "/"
end
end
6 changes: 6 additions & 0 deletions lib/postoffice/rescuer/adapters/impl.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
defmodule Postoffice.Rescuer.Adapters.Impl do
@moduledoc false

@callback list(host :: String) :: {:ok, list} | {:error, reason :: String}
@callback delete(host :: String, message_id :: Integer) :: {:ok, status :: Atom } | {:error, reason :: String}
end
52 changes: 52 additions & 0 deletions lib/postoffice/rescuer/client.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
defmodule Postoffice.Rescuer.Client do
require Logger

alias Postoffice.Rescuer.Adapters.Http

def list(host) do
case impl().list(host) do
{:ok, %HTTPoison.Response{status_code: status_code, body: body}}
when status_code in 200..299 ->
Logger.info("Succesfully listed pending messages from #{host}")
{:ok, Poison.decode!(body)}

{:ok, %HTTPoison.Response{status_code: status_code, body: body}} ->
Logger.info(
"Non successful response list pending messages from #{host} with status code: #{
status_code
}"
)

{:error, []}

{:error, %HTTPoison.Error{reason: reason}} ->
Logger.info("Error trying to list pending messages #{reason}")
{:error, []}
end
end

def delete(host, message_id) do
case impl().delete(host, message_id) do
{:ok, %HTTPoison.Response{status_code: status_code}}
when status_code in 200..299 ->
Logger.info("Successfully deleted message #{message_id} from #{host}")
{:ok, :deleted}
{:ok, %HTTPoison.Response{status_code: status_code, body: body}} ->
Logger.info(
"Non successful response deleting message from #{host} with status code: #{
status_code
}"
)

{:error, "Request status code #{status_code}"}

{:error, %HTTPoison.Error{reason: reason}} ->
Logger.info("Error trying to delete message #{host}: #{reason}")
{:error, reason}
end
end

defp impl do
Application.get_env(:postoffice, :rescuer_client, Http)
end
end
14 changes: 14 additions & 0 deletions test/postoffice/messaging_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -290,5 +290,19 @@ defmodule Postoffice.MessagingTest do
loaded_publisher_failures = Messaging.get_publisher_failures_for_message(message.id)
assert Kernel.length(loaded_publisher_failures) == 1
end

test "get_topic_origin_hosts returns unique hosts" do
_topic = Fixtures.create_topic()

_second_topic =
Fixtures.create_topic(%{
name: "second_test",
origin_host: "example.com"
})

hosts = Messaging.get_topic_origin_hosts()

assert Kernel.length(hosts) == 1
end
end
end
96 changes: 96 additions & 0 deletions test/postoffice/rescuer/client_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
defmodule Postoffice.Rescuer.ClientTest do
use ExUnit.Case

import Mox

alias Postoffice.Rescuer.Adapters.HttpMock
alias Postoffice.Rescuer.Client

@origin_host "http://fake_origin.host"
@wrong_message_id 9999
@external_message_id 1
@one_message_response "[{\"id\": 1, \"topic\": \"test\", \"payload\": {\"products\": [{\"code\": \"1234\"}, {\"code\": 2345}], \"reference\": 1234}, \"attributes\": null}]"
@two_messages_response "[{\"id\": 1, \"topic\": \"test\", \"payload\": {\"products\": [{\"code\": \"1234\"}, {\"code\": 2345}], \"reference\": 1234}, \"attributes\": null}, {\"id\": 2, \"topic\": \"test2\", \"payload\": {\"products\": [{\"code\": \"1234\"}, {\"code\": 2345}], \"reference\": 1234}, \"attributes\": null}]"

setup [:set_mox_from_context, :verify_on_exit!]

setup do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(Postoffice.Repo)
end

describe "list undelivered messages" do
test "non successful response from origin host" do
expect(HttpMock, :list, fn @origin_host ->
{:ok, %HTTPoison.Response{status_code: 300}}
end)

{:error, pending_messages} = Client.list(@origin_host)
assert pending_messages == []
end

test "origin host returns an error" do
expect(HttpMock, :list, fn @origin_host ->
{:error, %HTTPoison.Error{reason: "test error"}}
end)

{:error, pending_messages} = Client.list(@origin_host)
assert pending_messages == []
end

test "no pending messages returned" do
expect(HttpMock, :list, fn @origin_host ->
{:ok, %HTTPoison.Response{status_code: 200, body: "[]"}}
end)

{:ok, pending_messages} = Client.list(@origin_host)
assert pending_messages == []
end

test "one pending message received" do
expect(HttpMock, :list, fn @origin_host ->
{:ok, %HTTPoison.Response{status_code: 200, body: @one_message_response}}
end)

{:ok, pending_messages} = Client.list(@origin_host)
assert Kernel.length(pending_messages) == 1
end

test "two pending messages received" do
expect(HttpMock, :list, fn @origin_host ->
{:ok, %HTTPoison.Response{status_code: 200, body: @two_messages_response}}
end)

{:ok, pending_messages} = Client.list(@origin_host)
assert Kernel.length(pending_messages) == 2
end
end

describe "delete undelivered messages" do
test "trying to delete non existing message" do
expect(HttpMock, :delete, fn @origin_host, @wrong_message_id ->
{:ok, %HTTPoison.Response{status_code: 404, body: ""}}
end)

{:error, reason} = Client.delete(@origin_host, @wrong_message_id)
assert reason == "Request status code 404"
end

test "errors are handled from our side" do
expect(HttpMock, :delete, fn @origin_host, @wrong_message_id ->
{:error, %HTTPoison.Error{reason: "Something weird happened"}}
end)

{:error, reason} = Client.delete(@origin_host, @wrong_message_id)
assert reason == "Something weird happened"
end

test "messages are successfuly deleted" do
expect(HttpMock, :delete, fn @origin_host, @external_message_id ->
{:ok, %HTTPoison.Response{status_code: 204, body: ""}}
end)

{:ok, :deleted} = Client.delete(@origin_host, @external_message_id)

end
end
end
7 changes: 7 additions & 0 deletions test/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Ecto.Adapters.SQL.Sandbox.mode(Postoffice.Repo, :manual)

Mox.defmock(Postoffice.Adapters.HttpMock, for: Postoffice.Adapters.Impl)
Mox.defmock(Postoffice.Adapters.PubsubMock, for: Postoffice.Adapters.Impl)
Mox.defmock(Postoffice.Rescuer.Adapters.HttpMock, for: Postoffice.Rescuer.Adapters.Impl)

Application.put_env(
:postoffice,
Expand All @@ -15,3 +16,9 @@ Application.put_env(
:pubsub_consumer_impl,
Postoffice.Adapters.PubsubMock
)

Application.put_env(
:postoffice,
:rescuer_client,
Postoffice.Rescuer.Adapters.HttpMock
)

0 comments on commit c3a487f

Please sign in to comment.