Skip to content

Commit

Permalink
Merge pull request #10 from inaka/cabol.3.cowboy_swagger_handler
Browse files Browse the repository at this point in the history
Cabol.3.cowboy swagger handler
  • Loading branch information
Brujo Benavides committed Aug 11, 2015
2 parents 0c951f4 + f210ae9 commit 13ea00d
Show file tree
Hide file tree
Showing 17 changed files with 450 additions and 27 deletions.
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 = apply(Module, module_info, [exports]),
[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

0 comments on commit 13ea00d

Please sign in to comment.