Skip to content

Commit

Permalink
[Fix #38] Add redirect for /api-docs and split cowboy_swagger_handler…
Browse files Browse the repository at this point in the history
… in two more handlers
  • Loading branch information
harenson committed Dec 3, 2015
1 parent 2054fc0 commit 7ce1839
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 70 deletions.
11 changes: 6 additions & 5 deletions src/cowboy_swagger.erl
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,19 @@ 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).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Private API.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% @private
is_visible(_Method, Metadata) ->
not maps:get(hidden, Metadata, false).

%% @private
swagger_paths([], Acc) ->
Acc;
Expand Down
65 changes: 8 additions & 57 deletions src/cowboy_swagger_handler.erl
Original file line number Diff line number Diff line change
Expand Up @@ -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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Expand All @@ -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().
Expand Down
50 changes: 50 additions & 0 deletions src/cowboy_swagger_json_handler.erl
Original file line number Diff line number Diff line change
@@ -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}.
57 changes: 57 additions & 0 deletions src/cowboy_swagger_redirect_handler.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
-module(cowboy_swagger_redirect_handler).

%% Cowboy callbacks
-export([ init/3
, rest_init/2
, content_types_provided/2
]).

%% Handlers
-export([resource_exists/2, previously_existed/2, moved_permanently/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
-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}.
4 changes: 3 additions & 1 deletion test/cowboy_swagger.coverspec
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
incl_mods,
[
cowboy_swagger,
cowboy_swagger_handler
cowboy_swagger_handler,
cowboy_swagger_redirect_handler,
cowboy_swagger_json_handler
]
}.
5 changes: 2 additions & 3 deletions test/cowboy_swagger_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -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()].
9 changes: 5 additions & 4 deletions test/cowboy_swagger_handler_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down

0 comments on commit 7ce1839

Please sign in to comment.