Nested phoenix resources + phoenix_swagger

Hi,

I’ve intercepted a problem that I couldn’t find solution to. It concerns nested phoenix resources and documenting them with swagger macros.

It is possible to define nested resources in your router.

# router.ex
resources "/video-channels", VideoChannelController, only: [:show] do
  resources "/videos", VideoController, only: [:index]
end

resources "/videos", VideoController, only: [:index]

By route definition like this I end up with having two index functions inside VideoController that are pattern matching different params.

# VideoController.ex

def VideoController do
  use HelloWeb, :controller
  use PhoenixSwagger 

  swagger_path :index do
    get("/api/v1/video-channels/{video_channel_id}/videos")
    description("Returns videos filtered by video channel.")
  end

  def index(conn, %{"video_channel_id" => video_channel_id} = params}) do
    ..list of videos filtered by video channel ID..
  end

  swagger_path :index do
    get("/api/v1/videos")
    description("Returns videos filtered by video channel.")
  end

  def index(conn, params) do
    ..list of videos... 
 end
end

Then during the compilation I get:

 $ mix phx.swagger.generate
Compiling 2 files (.ex)
warning: this clause cannot match because a previous clause at line 20 always matches
  */video_controller.ex:87

The problem is that both swagger_paths are using the same action :index, although they handle completely different urls. Is this scenario supported? If it’s not supported, is there some recommended workaround for nested resources then ?

Thank you

1 Like

The workaround I came up with is documented in original github issue: https://github.com/xerions/phoenix_swagger/issues/245

The solution lies in defdelegate macro and not using nested resources inside you router.

def VideoController do
  use HelloWeb, :controller
  use PhoenixSwagger 

  defdelegate index_by_video_channel(conn, params), 
    to: VideoController, 
    as: :index

  swagger_path :index_by_video_channel do
    get("/api/v1/video-channels/{video_channel_id}/videos")
    description("Returns videos filtered by video channel.")
  end

  def index(conn, %{"video_channel_id" => video_channel_id} = params}) do
    ..list of videos filtered by video channel ID..
  end

  swagger_path :index do
    get("/api/v1/videos")
    description("Returns videos filtered by video channel.")
  end

  def index(conn, params) do
    ..list of videos... 
 end
end