Search code examples
elixirplug

Plug.Conn, all plugs must receive a connection (conn) and return a connection


so I'm trying to update a schema by matching the params["state"] on a callback, In my controller, I'm doing something like this.

def create(conn, params) do
  viewer = GiftActions.get_gift_user!(params["user_id"])

  case params["state"] do
    "captured" ->
      GiftActions.validate_gift_order(viewer, params)
      Logger.info("Request is validated sending code to #{inspect(viewer.email)}")

      {:ok, id} ->
        put_status(conn, 200)
        |> json(%{data: id})

      _ -> put_status(conn, 404) |> json(%{data: ""})
  end
end

My function will handle update if params["state"] is captured, like this

def validate_gift_order(viewer, attrs) do
 {:ok, gift_order} = __MODULE__.get_gift_by_payment_id(viewer, attrs["id"])

 gift_order
 |> GiftOrders.changeset(%{state: attrs["state"]})
 |> Repo.update()
end

Everything work's fine but in the end I'm failing to return Plug.Conn. Error is saying

[error] #PID<0.5693.0> running VWeb.Endpoint (connection #PID<0.5692.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: POST /api/v1/gift-callback?user_id=#######
** (exit) an exception was raised:
    ** (RuntimeError) expected action/2 to return a Plug.Conn, all plugs must receive a connection (conn) and return a connection, got: :ok
        (viavolo 0.1.0) lib/v_web/controllers/gif_callback_controller.ex:1: VWeb.GiftCallbackController.phoenix_controller_pipeline/2
        (phoenix 1.5.9) lib/phoenix/router.ex:352: Phoenix.Router.__call__/2
        (viavolo 0.1.0) lib/viavolo_web/endpoint.ex:1: ViavoloWeb.Endpoint.plug_builder_call/2
        (viavolo 0.1.0) lib/viavolo_web/endpoint.ex:1: VWeb.Endpoint."call (overridable 3)"/2
        (viavolo 0.1.0) lib/plug/debugger.ex:136: VWeb.Endpoint."call (overridable 4)"/2
        (viavolo 0.1.0) lib/v_web/endpoint.ex:1: VWeb.Endpoint.call/2

and I'm not entirely sure why? What I'm I supposed to return here?


Solution

  • Your code is indented wrong, which makes me think you think you have another case statement. Here is the correct indentation for your code:

    def create(conn, params) do
      viewer = GiftActions.get_gift_user!(params["user_id"])
    
      case params["state"] do
        "captured" ->
          GiftActions.validate_gift_order(viewer, params)
          Logger.info("Request is validated sending code to #{inspect(viewer.email)}")
    
        {:ok, id} ->
          put_status(conn, 200)
          |> json(%{data: id})
    
        _ -> put_status(conn, 404) |> json(%{data: ""})
      end
    end
    

    So, when params["state"] is "captured", your last command is Logger.info, which doesn't return a Plug.Conn.

    It seems like you meant to have a case on validate_gift_order's return:

    def create(conn, params) do
      viewer = GiftActions.get_gift_user!(params["user_id"])
    
      case params["state"] do
        "captured" ->
          validation = GiftActions.validate_gift_order(viewer, params)
          Logger.info("Request is validated sending code to #{inspect(viewer.email)}")
    
          case validation do
            {:ok, id} ->
              put_status(conn, 200)
              |> json(%{data: id})
    
            _ -> put_status(conn, 404) |> json(%{data: ""})
          end
      end
    end