lib/line_drive/incoming/handler.ex

defmodule LineDrive.Incoming.Handler do
  @moduledoc """
  This plug handles incoming webhook POSTs from Pipedrive and converts them into
  events published via the Registry.

  To subscribe to a particular event (such as updated deals in the following
  example), use:
  `Registry.register(Registry.LineDriveEvents, :updated_deal, [])`

  You will then need to implement a `handle_info({:updated_deal, payload})`,
  where the payload varies by event type. See
  https://pipedrive.readme.io/docs/guide-for-webhooks for more information about
  possible event types and the expected payloads for each.
  """

  use LineDrive.Incoming.DealHandler
  use LineDrive.Incoming.PersonHandler
  use Plug.Router

  require Logger

  plug(:match)
  plug(:dispatch)

  plug(Plug.Parsers,
    parsers: [:json],
    json_decoder: Jason
  )

  def init(opts), do: opts

  def call(conn, opts) do
    auth_fn = Keyword.fetch!(opts, :auth_fn)
    auth_opts = if is_function(auth_fn), do: auth_fn.(), else: opts
    conn = Plug.BasicAuth.basic_auth(conn, auth_opts)

    super(conn, opts)
  end

  post "/webhook" do
    conn.body_params
    |> process_event()
    |> notify_subscribers()

    send_resp(conn, 200, "")
  end

  match _ do
    send_resp(conn, 404, "")
  end

  def process_event(%{"event" => event_type} = payload) do
    transform_event(event_type, payload)
  end

  def transform_event(_, payload) do
    Logger.warn("Unhandled incoming event: #{inspect(payload)}")

    nil
  end

  defp notify_subscribers(nil), do: nil

  defp notify_subscribers({event_type, message}) do
    Logger.warn("Dispatching event: #{inspect(event_type)}")

    Registry.dispatch(Registry.LineDriveEvents, event_type, fn entries ->
      for {pid, _} <- entries, do: send(pid, {event_type, message})
    end)
  end
end