Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix #38] Add redirect for /api-docs ... #41

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) ->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to Xref:

cowboy_swagger:filter_cowboy_swagger_handler/1 calls undefined function maps:filter/2

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gadgetci you really should start using Erlang/OTP 18.x

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to Dialyzer:

Call to missing or unexported function maps:filter/2

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) ->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can safely remove this function

{ok, Req, Opts}.

%% @hidden
-spec content_types_provided(cowboy_req:req(), state()) ->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is probably useless. Can you try removing it to see if everything still works, please?

{[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