diff --git a/src/cowboy_swagger.erl b/src/cowboy_swagger.erl index 06d3bde..b4f9e00 100644 --- a/src/cowboy_swagger.erl +++ b/src/cowboy_swagger.erl @@ -92,11 +92,8 @@ validate_metadata(Metadata) -> -spec filter_cowboy_swagger_handler([trails:trail()]) -> [trails:trail()]. filter_cowboy_swagger_handler(Trails) -> F = fun(Trail) -> - case trails:handler(Trail) of - cowboy_swagger_handler -> false; - cowboy_static -> false; - _ -> true - end + MD = trails:metadata(Trail), + maps:size(maps:filter(fun is_visible/2, MD)) /= 0 end, lists:filter(F, Trails). @@ -104,6 +101,10 @@ filter_cowboy_swagger_handler(Trails) -> %% Private API. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% @private +is_visible(_Method, Metadata) -> + not maps:get(hidden, Metadata, false). + %% @private swagger_paths([], Acc) -> Acc; diff --git a/src/cowboy_swagger_handler.erl b/src/cowboy_swagger_handler.erl index f723ef4..79ba783 100644 --- a/src/cowboy_swagger_handler.erl +++ b/src/cowboy_swagger_handler.erl @@ -3,56 +3,13 @@ %%% Swagger-UI (located in `priv/swagger' folder). -module(cowboy_swagger_handler). -%% Cowboy callbacks --export([ init/3 - , rest_init/2 - , content_types_provided/2 - ]). - -%% Handlers --export([handle_get/2]). - %% Trails -behaviour(trails_handler). -export([trails/0, trails/1]). --type state() :: #{}. -type route_match() :: '_' | iodata(). -type options() :: #{server => ranch:ref(), host => route_match()}. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Cowboy Callbacks -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%% @hidden --spec init({atom(), atom()}, cowboy_req:req(), options()) -> - {upgrade, protocol, cowboy_rest}. -init(_Transport, _Req, _Opts) -> - {upgrade, protocol, cowboy_rest}. - -%% @hidden --spec rest_init(cowboy_req:req(), options()) -> - {ok, cowboy_req:req(), options()}. -rest_init(Req, Opts) -> - {ok, Req, Opts}. - -%% @hidden --spec content_types_provided(cowboy_req:req(), state()) -> - {[term()], cowboy_req:req(), state()}. -content_types_provided(Req, State) -> - {[{<<"application/json">>, handle_get}], Req, State}. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Handlers -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%% @hidden -handle_get(Req, State) -> - Server = maps:get(server, State, '_'), - HostMatch = maps:get(host, State, '_'), - Trails = trails:all(Server, HostMatch), - {cowboy_swagger:to_json(Trails), Req, State}. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Trails %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -70,26 +27,20 @@ trails(Options) -> {ok, Val} -> Val; _ -> filename:join(cowboy_swagger_priv(), "swagger") end, - Static1 = trails:trail( + Redirect = trails:trail( "/api-docs", - cowboy_static, + cowboy_swagger_redirect_handler, {file, StaticFiles ++ "/index.html"}, - #{get => #{tags => ["static-content"], description => "index.html"}}), - Static2 = trails:trail( + #{get => #{hidden => true}}), + Static = trails:trail( "/api-docs/[...]", cowboy_static, {dir, StaticFiles, [{mimetypes, cow_mimetypes, all}]}, - #{get => #{tags => ["static-content"], description => "Static Content"}}), - MD = - #{get => - #{tags => ["api-docs"], - description => "Retrives swagger's specification.", - produces => ["application/json"] - } - }, + #{get => #{hidden => true}}), + MD = #{get => #{hidden => true}}, Handler = trails:trail( - "/api-docs/swagger.json", cowboy_swagger_handler, Options, MD), - [Static1, Handler, Static2]. + "/api-docs/swagger.json", cowboy_swagger_json_handler, Options, MD), + [Redirect, Handler, Static]. %% @private -spec cowboy_swagger_priv() -> string(). diff --git a/src/cowboy_swagger_json_handler.erl b/src/cowboy_swagger_json_handler.erl new file mode 100644 index 0000000..7fbff10 --- /dev/null +++ b/src/cowboy_swagger_json_handler.erl @@ -0,0 +1,50 @@ +%%% @doc Cowboy Swagger Handler. This handler exposes a GET operation +%%% to enable that `swagger.json' can be retrieved from embedded +%%% Swagger-UI (located in `priv/swagger' folder). +-module(cowboy_swagger_json_handler). + +%% Cowboy callbacks +-export([ init/3 + , rest_init/2 + , content_types_provided/2 + ]). + +%% Handlers +-export([handle_get/2]). + +-type state() :: #{}. +-type route_match() :: '_' | iodata(). +-type options() :: #{server => ranch:ref(), host => route_match()}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Cowboy Callbacks +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% @hidden +-spec init({atom(), atom()}, cowboy_req:req(), options()) -> + {upgrade, protocol, cowboy_rest}. +init(_Transport, _Req, _Opts) -> + {upgrade, protocol, cowboy_rest}. + +%% @hidden +-spec rest_init(cowboy_req:req(), options()) -> + {ok, cowboy_req:req(), options()}. +rest_init(Req, Opts) -> + {ok, Req, Opts}. + +%% @hidden +-spec content_types_provided(cowboy_req:req(), state()) -> + {[term()], cowboy_req:req(), state()}. +content_types_provided(Req, State) -> + {[{<<"application/json">>, handle_get}], Req, State}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Handlers +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% @hidden +handle_get(Req, State) -> + Server = maps:get(server, State, '_'), + HostMatch = maps:get(host, State, '_'), + Trails = trails:all(Server, HostMatch), + {cowboy_swagger:to_json(Trails), Req, State}. diff --git a/src/cowboy_swagger_redirect_handler.erl b/src/cowboy_swagger_redirect_handler.erl new file mode 100644 index 0000000..fdfa1e6 --- /dev/null +++ b/src/cowboy_swagger_redirect_handler.erl @@ -0,0 +1,41 @@ +-module(cowboy_swagger_redirect_handler). + +%% Cowboy callbacks +-export([init/3]). + +%% Handlers +-export([resource_exists/2, previously_existed/2, moved_permanently/2]). + +-type state() :: #{}. +-type options() :: any(). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Cowboy Callbacks +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% @hidden +-spec init({atom(), atom()}, cowboy_req:req(), options()) -> + {upgrade, protocol, cowboy_rest}. +init(_Transport, _Req, _Opts) -> + {upgrade, protocol, cowboy_rest}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Handlers +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% @hidden +-spec resource_exists(Req::cowboy_req:req(), State::state()) -> + {boolean(), cowboy_req:req(), state()}. +resource_exists(Req, State) -> + {false, Req, State}. + +%% @hidden +-spec previously_existed(Req::cowboy_req:req(), State::state())-> + {boolean(), cowboy_req:req(), state()}. +previously_existed(Req, State) -> + {true, Req, State}. + +%% @hidden +-spec moved_permanently(Req::cowboy_req:req(), State::state()) -> + {{boolean(), string()}, cowboy_req:req(), state()}. +moved_permanently(Req, State) -> + {{true, "/api-docs/index.html"}, Req, State}. diff --git a/test/cowboy_swagger.coverspec b/test/cowboy_swagger.coverspec index dfc0372..3b880ed 100644 --- a/test/cowboy_swagger.coverspec +++ b/test/cowboy_swagger.coverspec @@ -3,6 +3,8 @@ incl_mods, [ cowboy_swagger, - cowboy_swagger_handler + cowboy_swagger_handler, + cowboy_swagger_redirect_handler, + cowboy_swagger_json_handler ] }. diff --git a/test/cowboy_swagger_SUITE.erl b/test/cowboy_swagger_SUITE.erl index 40b4ccc..527fab1 100644 --- a/test/cowboy_swagger_SUITE.erl +++ b/test/cowboy_swagger_SUITE.erl @@ -161,6 +161,5 @@ test_trails() -> } }, [trails:trail("/a/[:b/[:c/[:d]]]", handler1, [], Metadata), - trails:trail("/a/:b/[:c]", handler2, [], Metadata), - trails:trail("/api-docs", cowboy_swagger_handler, [], Metadata), - trails:trail("/[...]", cowboy_swagger_handler, [], Metadata)]. + trails:trail("/a/:b/[:c]", handler2, [], Metadata) | + cowboy_swagger_handler:trails()]. diff --git a/test/cowboy_swagger_handler_SUITE.erl b/test/cowboy_swagger_handler_SUITE.erl index c6a42a9..54fbd57 100644 --- a/test/cowboy_swagger_handler_SUITE.erl +++ b/test/cowboy_swagger_handler_SUITE.erl @@ -86,11 +86,12 @@ handler_test(_Config) -> <<"paths">> := ExpectedPaths} = cowboy_swagger:dec_json(Body0), %% GET index.html - ct:comment("GET /api-docs should return 200 OK with the index.html"), - #{status_code := 200, body := Body1} = + ct:comment("GET /api-docs should return 301 MOVED PERMANENTLY to " ++ + "/api-docs/index.html"), + #{status_code := 301, headers := Headers} = cowboy_swagger_test_utils:api_call(get, "/api-docs"), - {ok, Index} = file:read_file("../../priv/swagger/index.html"), - Index = Body1, + Location = {<<"location">>, <<"/api-docs/index.html">>}, + Location = lists:keyfind(<<"location">>, 1, Headers), %% GET swagger-ui.js - test /api-docs/[...] trail ct:comment("GET /api-docs/swagger-ui-js should return 200 OK"),