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

Cabol.3.cowboy swagger handler #10

Merged
merged 4 commits into from
Aug 11, 2015
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
15 changes: 10 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
PROJECT = cowboy_swagger

CONFIG ?= test/test.config

DEPS = jiffy trails

dep_jiffy = git https://github.com/davisp/jiffy.git 0.14.2
dep_trails = git https://github.com/inaka/cowboy-trails.git 0.0.1
dep_trails = git https://github.com/inaka/cowboy-trails.git 0.0.2

SHELL_DEPS = sync

dep_sync = git https://github.com/inaka/sync.git 0.1.3

TEST_DEPS = xref_runner
TEST_DEPS = xref_runner mixer shotgun

dep_xref_runner = git https://github.com/inaka/xref_runner.git 0.2.2
dep_mixer = git https://github.com/inaka/mixer.git 0.1.3
dep_shotgun = git https://github.com/inaka/shotgun.git 0.1.12

PLT_APPS := trails cowboy
DIALYZER_DIRS := ebin/
Expand All @@ -20,9 +24,10 @@ DIALYZER_OPTS := --verbose --statistics -Werror_handling \

include erlang.mk

SHELL_OPTS = -s sync

# Commont Test Config
CT_DEPS = xref_runner
TEST_ERLC_OPTS += +debug_info
CT_SUITES = cowboy_swagger cowboy_swagger_handler
CT_OPTS = -cover test/cowboy_swagger.coverspec -erl_args -config ${CONFIG}

CT_OPTS = -cover test/cowboy_swagger.coverspec -erl_args
SHELL_OPTS = -s sync
2 changes: 1 addition & 1 deletion src/cowboy_swagger.app.src
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
trails
]},
{modules, []},
{mod, {cowboy_swagger, []}},
{mod, {cowboy_swagger_app, []}},
{registered, []}
]
}.
28 changes: 24 additions & 4 deletions src/cowboy_swagger.erl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
%% API
-export([to_json/1]).

%% Utilities
-export([enc_json/1, dec_json/1]).
-export([swagger_paths/1]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Types.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Expand Down Expand Up @@ -41,19 +45,35 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% @doc Returns the swagger json specification from given `trails'.
-spec to_json(Trails :: [trails:trail()]) -> iolist().
-spec to_json([trails:trail()]) -> iolist().
to_json(Trails) ->
SwaggerSpec = #{paths => swagger_paths(Trails)},
jiffy:encode(SwaggerSpec, [uescape]).
enc_json(SwaggerSpec).

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

%% @private
-spec enc_json(jiffy:json_value()) -> iolist().
enc_json(Json) ->
jiffy:encode(Json, [uescape]).

-spec dec_json(iodata()) -> jiffy:json_value().
dec_json(Data) ->
try jiffy:decode(Data, [return_maps])
catch
_:{error, _} ->
throw(bad_json)
end.

-spec swagger_paths([trails:trail()]) -> map().
swagger_paths(Trails) ->
swagger_paths(Trails, #{}).

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

%% @private
swagger_paths([], Acc) ->
Acc;
Expand Down
15 changes: 15 additions & 0 deletions src/cowboy_swagger_app.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
%%% @hidden
-module(cowboy_swagger_app).

-behaviour(application).

%% Application callbacks
-export([start/2, stop/1]).

-spec start(term(), term()) -> {error, term()} | {ok, pid()}.
start(_Type, _Args) ->
cowboy_swagger_sup:start_link().

-spec stop(term()) -> ok.
stop(_State) ->
ok.
58 changes: 58 additions & 0 deletions src/cowboy_swagger_handler.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
-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]).

-type state() :: #{}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Cowboy Callbacks
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-spec init({atom(), atom()}, cowboy_req:req(), state()) ->
{upgrade, protocol, cowboy_rest}.
init(_Transport, _Req, _Opts) ->
{upgrade, protocol, cowboy_rest}.

-spec rest_init(cowboy_req:req(), state()) ->
{ok, cowboy_req:req(), term()}.
rest_init(Req, _Opts) ->
{ok, Req, #{}}.

-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) ->
Trails = trails:all(),
{cowboy_swagger:to_json(Trails), Req, State}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Trails
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% @hidden
trails() ->
MD =
#{get =>
#{description => "Retrives swagger's specification.",
produces => ["application/json"]
}
},
[trails:trail("/api-docs/swagger.json", cowboy_swagger_handler, [], MD)].
14 changes: 14 additions & 0 deletions src/cowboy_swagger_sup.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
%%% @hidden
-module(cowboy_swagger_sup).

-behaviour(supervisor).

-export([init/1]).
-export([start_link/0]).

-spec start_link() -> {ok, pid()} | {error, term()}.
start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).

-spec init([]) -> {ok, {{one_for_one, 10, 60}, []}}.
init([]) ->
{ok, {{one_for_one, 10, 60}, []}}.
5 changes: 4 additions & 1 deletion test/cowboy_swagger.coverspec
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
%% Specific modules to include in cover.
{
incl_mods,
[cowboy_swagger]
[
cowboy_swagger,
cowboy_swagger_handler
]
}.
26 changes: 10 additions & 16 deletions test/cowboy_swagger_SUITE.erl
Original file line number Diff line number Diff line change
@@ -1,35 +1,29 @@
-module(cowboy_swagger_SUITE).

%% CT
-export([all/0, init_per_suite/1, end_per_suite/1]).
-include_lib("mixer/include/mixer.hrl").
-mixin([
{cowboy_swagger_test_utils,
[ init_per_suite/1
, end_per_suite/1
]}
]).

%% Test cases
-export([all/0]).
-export([to_json_test/1]).

-type config() :: [{atom(), term()}].

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Common test
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-spec all() -> [atom()].
all() ->
Exports = ?MODULE:module_info(exports),
[F || {F, 1} <- Exports, F /= module_info].

-spec init_per_suite(config()) -> config().
init_per_suite(Config) ->
Config.

-spec end_per_suite(config()) -> config().
end_per_suite(Config) ->
Config.
cowboy_swagger_test_utils:all(?MODULE).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Test Cases
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-spec to_json_test(config()) -> {atom(), string()}.
-spec to_json_test(cowboy_swagger_test_utils:config()) -> {atom(), string()}.
to_json_test(_Config) ->
Trails = test_trails(),
SwaggerJson = cowboy_swagger:to_json(Trails),
Expand Down
55 changes: 55 additions & 0 deletions test/cowboy_swagger_handler_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
-module(cowboy_swagger_handler_SUITE).

%% CT
-export([ all/0
, init_per_suite/1
, end_per_suite/1
]).

%% Test cases
-export([handler_test/1]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Common test
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-spec all() -> [atom()].
all() ->
cowboy_swagger_test_utils:all(?MODULE).

-spec init_per_suite(
cowboy_swagger_test_utils:config()
) -> cowboy_swagger_test_utils:config().
init_per_suite(Config) ->
shotgun:start(),
example:start(),
Config.

-spec end_per_suite(
cowboy_swagger_test_utils:config()
) -> cowboy_swagger_test_utils:config().
end_per_suite(Config) ->
shotgun:stop(),
example:stop(),
Config.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Test Cases
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-spec handler_test(cowboy_swagger_test_utils:config()) -> {atom(), string()}.
handler_test(_Config) ->
%% Expected result
Trails = trails:trails([example_echo_handler,
example_description_handler,
cowboy_swagger_handler]),
ExpectedPaths = cowboy_swagger:dec_json(
cowboy_swagger:enc_json(cowboy_swagger:swagger_paths(Trails))),

%% GET swagger.json spec
ct:comment("GET /api-docs/swagger.json should return 200 OK"),
#{status_code := 200, body := Body0} =
cowboy_swagger_test_utils:api_call(get, "/api-docs/swagger.json"),
#{<<"paths">> := ExpectedPaths} = cowboy_swagger:dec_json(Body0),

{comment, ""}.
46 changes: 46 additions & 0 deletions test/cowboy_swagger_test_utils.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
-module(cowboy_swagger_test_utils).

-export([ all/1
, init_per_suite/1
, end_per_suite/1
]).
-export([ api_call/2
, api_call/3
, api_call/4
]).

-type config() :: proplists:proplist().
-export_type([config/0]).

-spec all(atom()) -> [atom()].
all(Module) ->
ExcludedFuns = [module_info, init_per_suite, end_per_suite, group, all],
Exports = Module:module_info(exports),
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 Elvis:

Remove the dynamic function call on line 18. Only modules that define callbacks should make dynamic calls.

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 Elvis:

Remove the dynamic function call on line 18. Only modules that define callbacks should make dynamic calls.

[F || {F, 1} <- Exports, not lists:member(F, ExcludedFuns)].

-spec init_per_suite(config()) -> config().
init_per_suite(Config) ->
Config.

-spec end_per_suite(config()) -> config().
end_per_suite(Config) ->
Config.

-spec api_call(atom(), string()) -> #{}.
api_call(Method, Uri) ->
api_call(Method, Uri, #{}).

-spec api_call(atom(), string(), #{}) -> #{}.
api_call(Method, Uri, Headers) ->
api_call(Method, Uri, Headers, []).

-spec api_call(atom(), string(), #{}, iodata()) -> #{}.
api_call(Method, Uri, Headers, Body) ->
Port = application:get_env(example, http_port, 8080),
{ok, Pid} = shotgun:open("localhost", Port),
try
{ok, Response} = shotgun:request(Pid, Method, Uri, Headers, Body, #{}),
Response
after
shotgun:close(Pid)
end.
16 changes: 16 additions & 0 deletions test/example.app
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{application, example,
[
{description, "Cowboy Trails Basic Example."},
{vsn, "0.1"},
{applications,
[kernel,
stdlib,
cowboy,
trails,
cowboy_swagger
]},
{modules, []},
{mod, {example, []}},
{start_phases, [{start_trails_http, []}]}
]
}.
Loading