xerions / phoenix_swagger

Swagger integration to Phoenix framework
Mozilla Public License 2.0
691 stars 181 forks source link

Nested phoenix resources #245

Open char0n opened 4 years ago

char0n commented 4 years ago

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

char0n commented 4 years ago

The only workaround so far I could come up with is not using nested resources, but rather define urls in router statically and use defdelegate to keep backward compatibility with nested resources if utilized again in the future:

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

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