How to check the arguments of the auto generated Phoenix paths - phoenix-framework

No helper clause for Api.Router.Helpers.v1_user_organization_path
defined for action :show with arity 3. Please check that the function,
arity and action are correct. The following v1_user_organization_path
actions are defined under your router:
* :create
* :index
* :show
* :update
router.ex
defmodule Api.Router do
use Api.Web, :router
pipeline :api do
plug :accepts, ["json"]
end
scope "/", Api do
pipe_through :api
end
scope "/v1", Api.V1, as: :v1 do
pipe_through :api
resources "/users", UserController, only: [:create, :show, :update] do
resources "/organizations", OrganizationController, only: [:create, :update, :index, :show]
end
end
end
and when I do mix phoenix.routes I see the following v1_user_organization_path getting generated. The problem is I don't know how to use it and I don't know what I should pass into it. Is there a way I can check what this generated method accepts?
The error I get is occuring here
organization_controller.ex
def create(conn, %{"user_id" => user_id, "organization" => organization_params}) do
changeset = Organization.changeset(%Organization{}, organization_params)
case Repo.insert(changeset) do
{:ok, organization} ->
conn
|> put_status(:created)
|> put_resp_header("location", v1_user_organization_path(conn, :show, organization))
|> render("show.json", organization: organization)
{:error, changeset} ->
conn
|> put_status(:unprocessable_entity)
|> render(Api.ChangesetView, "error.json", changeset: changeset)
end
end
At put_resp_header("location", v1_user_organization_path(conn, :show, organization))

You can look at the auto generated documentation for the router helper functions using h in iex -S mix and reading the output of mix phoenix.routes for some help. For example, for the following routes:
resources "/posts", PostController do
resources "/comments", CommentController
end
I get:
iex(1)> h MyApp.Router.Helpers.post_comment_path
def post_comment_path(conn_or_endpoint, action, post_id)
def post_comment_path(conn_or_endpoint, action, post_id, params)
def post_comment_path(conn_or_endpoint, action, post_id, id, params)
$ mix phoenix.routes
post_comment_path GET /posts/:post_id/comments MyApp.CommentController :index
post_comment_path GET /posts/:post_id/comments/:id/edit MyApp.CommentController :edit
post_comment_path GET /posts/:post_id/comments/new MyApp.CommentController :new
post_comment_path GET /posts/:post_id/comments/:id MyApp.CommentController :show
post_comment_path POST /posts/:post_id/comments MyApp.CommentController :create
post_comment_path PATCH /posts/:post_id/comments/:id MyApp.CommentController :update
PUT /posts/:post_id/comments/:id MyApp.CommentController :update
post_comment_path DELETE /posts/:post_id/comments/:id MyApp.CommentController :delete
It's not clear from just the function signature which action accepts how many arguments, but if you read the output of mix phoenix.routes, you can see that :show (last column) requires a post_id and an id.
The output of h is also not completely accurate because it doesn't tell you that the arity 4 version also accepts (conn_or_endpoint, action, post_id, id) and not just (conn_or_endpoint, action, post_id, params).
I don't think there's any better auto generated documentation for the generated route functions right now in Phoenix. I usually just look at the output of mix phoenix.routes and pass in conn_or_endpoint followed by the action followed by every :var in the route, optionally followed by a params map.

Related

phoenix view module not available

My application works and api json requests and for regular html. My router.ex
defmodule MyApp.Router do
use MyApp.Web, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
end
pipeline :api do
plug :accepts, ["json"]
end
scope "/api", MyApp do
pipe_through :api # Use the default browser stack
scope "/v1", V1, as: :v1 do
resources "/users", UserController, except: [:new, :edit, :index]
end
end
scope "/", MyApp do
pipe_through :browser # Use the default browser stack
get "/confirm/:token", UserController, :confirm, as: :user_confirm
end
end
my web/controllers/v1/user_controller.ex
defmodule MyApp.V1.UserController do
use MyApp.Web, :controller
def create(conn, %{"user" => user_params}) do
...
conn
|> put_status(:created)
|> put_resp_header("location", v1_user_path(conn, :show, user))
|> render("sign_up.json", user: Map.put(user, :session, result[:session]))
...
end
and my web/controllers/user_controller.rb
defmodule MyApp.UserController do
use MyApp.Web, :controller
alias MyApp.User
def confirm(conn, %{"token" => token}) do
...
render(conn, "confirmed.html")
...
end
end
my web/views/v1/user_view.ex
defmodule MyApp.V1.UserView do
use MyApp.Web, :view
...
end
and my web/views/user_view.ex
defmodule MyApp.UserView do
use MyApp.Web, :view
end
Everything works fine until I added a route and a controller for html.
Now, when I make a request for api json, I get an error
Request: POST /api/v1/users
** (exit) an exception was raised:
** (UndefinedFunctionError) function MyApp.V1.UserView.render/2 is undefined (module MyApp.V1.UserView is not available)
But if I delete web/vews/user_view.ex, then this query works without errors.
How can you correct this error?
These types of errors can usually be resolved by running mix clean. You may also see this type of error during Live code reload in dev. It case, try restarting the Phoenix.Server, and if that does not help, run mix clean

Phoenix emits "protocol Enumerable not implemented" error when path helpers get called

For a Phoenix app, I created two routes as following:
scope "/", Greeter do
pipe_through :browser
get "/hello", HelloController, :show
get "/hello/:name", HelloController, :show
end
With them, the app can respond to the both "/hello" and "/hello/alice" paths.
But, when I use the path helper hello_path(#conn, :show, "alice") to produce "/hello/alice", Phoenix server emits this error message:
protocol Enumerable not implemented for "alice"
The cause is simple.
The first route creates two helpers hello_path/2 and hello_path/3, but the second route creates only one helper hello_path/4 because the hello_path/3 is already defined.
This hello_path/3 demands an enumerable as the third argument.
How should I avoid this error?
You can give one of the route a different name using as::
get "/hello", HelloController, :show
get "/hello/:name", HelloController, :show, as: :hello_with_name
Test:
iex(1)> import MyApp.Router.Helpers
MyApp.Router.Helpers
iex(2)> hello_path MyApp.Endpoint, :show
"/hello"
iex(3)> hello_path MyApp.Endpoint, :show, foo: "bar"
"/hello?foo=bar"
iex(4)> hello_with_name_path MyApp.Endpoint, :show, "alice"
"/hello/alice"
iex(5)> hello_with_name_path MyApp.Endpoint, :show, "alice", foo: "bar"
"/hello/alice?foo=bar"

routing resources with a wild card

I have this routing
scope "/api", MosaicApi do
pipe_through :api
# resources "/cards", CardController, except: [:new, :edit]
resources "/estimates/:product", EstimateController, except: [:new, :edit]
Initially I used the generated CardController and things worked (at least POST/create did), but now I want to generalise as Card is a product type and I have a variety of other products that need to expose the exact same CRUD operations. So I am trying to morph Card* to Estimate*
In EstimateController I now have this
defmodule Api.CardController do
use Api.Web, :controller
alias Api.Card
def create(conn, %{"product" => product}) do
conn
|> render("result.json", product: product)
end
...
What I want to do is pattern match on product to bring into scope the relevant Struct (Card, ...), but I've got stuck as the code above yields this error
undefined function Api.EstimateController.init/1 (module Api.EstimateController is not available)
Api.EstimateController.init(:create)
I'm confused as init is not mentioned in http://www.phoenixframework.org/docs/controllers at all
Other indications that things are mostly good
mix phoenix.routes
page_path GET / Api.PageController :index
estimate_path GET /api/estimates/:product Api.EstimateController :index
estimate_path GET /api/estimates/:product/:id Api.EstimateController :show
estimate_path POST /api/estimates/:product Api.EstimateController :create
The function init/1 is defined in https://github.com/phoenixframework/phoenix/blob/v1.1.4/lib/phoenix/controller/pipeline.ex#L98
def init(action) when is_atom(action) do
action
end
It looks like you are not calling use Phoenix.Controller in your EstimatesController.
Usually this is done with:
use Api.Web, :controller

Guardian and Sessions in Phoenix app

I'm using Guardian on a phoenix app but I can't use the put_session and get_session methods in controllers with Guardian
defmodule Bonsai.LedgerController do
use Bonsai.Web, :controller
alias Bonsai.{Repo, Ledger}
plug Guardian.Plug.EnsureAuthenticated, handler: Bonsai.SessionController
plug :scrub_params, "ledger" when action in [:create]
def index(conn, _params) do
put_session(conn, :sess_val, "A value") # Generates error
ledgers = Repo.all(Ledger)
render(conn, "index.json", ledgers: ledgers)
end
end
There are two issues here.
First of all, you need to fetch the session in your pipeline. This is commonly done in your router:
pipeline :browser do
#...
plug :fetch_session
#...
end
The second issue is that you are calling put_session however the result is not being used. Try this:
def index(conn, _params) do
conn = put_session(conn, :sess_val, "A value")
ledgers = Repo.all(Ledger)
render(conn, "index.json", ledgers: ledgers)
end

What's the equivalent of rake routes in Phoenix?

Ruby on Rails comes with the terminal command rake routes. Is there an equivalent in Phoenix?
The command is $ mix phoenix.routes
As #Martimatix pointed out $ mix phx.routes print all routes for the default router, here's the documentation
This is an example of routes generated for this router.ex file:
defmodule MessageApi.Router do
use MessageApi.Web, :router
pipeline :api do
plug :accepts, ["json"]
end
scope "/api", MessageApi do
pipe_through :api
resources "/messages", MessageController, except: [:new, :edit]
resources "/message_details", MessageDetailController, except: [:new, :edit]
end
end
then routes generated will be:
message_path GET /api/messages MessageApi.MessageController :index
message_path GET /api/messages/:id MessageApi.MessageController :show
message_path POST /api/messages MessageApi.MessageController :create
message_path PATCH /api/messages/:id MessageApi.MessageController :update
PUT /api/messages/:id MessageApi.MessageController :update
message_path DELETE /api/messages/:id MessageApi.MessageController :delete
message_detail_path GET /api/message_details MessageApi.MessageDetailController :index
message_detail_path GET /api/message_details/:id MessageApi.MessageDetailController :show
message_detail_path POST /api/message_details MessageApi.MessageDetailController :create
message_detail_path PATCH /api/message_details/:id MessageApi.MessageDetailController :update
PUT /api/message_details/:id MessageApi.MessageDetailController :update
message_detail_path DELETE /api/message_details/:id MessageApi.MessageDetailController :delete

Resources