Current section

Files

Jump to
elli_swagger src elli_swagger_handler.erl
Raw

src/elli_swagger_handler.erl

-module(elli_swagger_handler).
-behaviour(elli_handler).
-define(NOT_FOUND_MESSAGE, "Resource Not Found").
-export([handle/2, handle_event/3]).
-callback elli_swagger_config() -> elli_swagger:elli_swagger_t().
%% elli
handle(Req, PathConfig) ->
Method = elli_request:method(Req),
Path = elli_request:path(Req),
case handle_docs_request(Method, Path, PathConfig) of
{Module, Args} ->
Module:handle(Req, Args);
Response ->
Response
end.
handle_event(_Atom, _Req, _Config) ->
ignore.
%% internal functions
handle_docs_request('GET', [<<"api-docs">>], _PathConfiguration) ->
{302, [{<<"Location">>, <<"/api-docs/index.html">>}], <<"">>};
handle_docs_request('GET', [<<"api-docs">>, <<"swagger.json">>], PathConfiguration) ->
{ok, [{<<"Content-Type">>, <<"application/json">>}], fetch_documentation_from_configuration(PathConfiguration)};
handle_docs_request('GET', [<<"api-docs">>, Filename], _PathConfiguration) ->
StringFilename = binary_to_list(Filename),
case elli_swagger_file:file_exists("swagger", StringFilename) of
true ->
{ok, [], {file, "swagger/"++StringFilename}};
false ->
{404, [], ?NOT_FOUND_MESSAGE}
end;
handle_docs_request(_Method, Path, PathConfiguration) ->
get_module_to_redirect(Path, PathConfiguration).
get_module_to_redirect(Path, PathConfiguration) ->
maps:fold(fun(Key, #{handler := CallbackHandler, arguments := Args}, Arg) ->
case length(Key) =:= length(Path)
andalso is_allowed_path(Path, Key, true) of
true -> {CallbackHandler, Args};
false -> Arg
end
end, {404, [], ?NOT_FOUND_MESSAGE}, PathConfiguration).
is_allowed_path([], [], Allowed) ->
Allowed;
is_allowed_path([KeyValue|Keys], [PathValue|Values], Allowed) ->
ShouldAllow =
case KeyValue =:= PathValue
orelse binary:match(PathValue, <<"{">>) of
nomatch -> false;
_ -> true
end,
is_allowed_path(Keys, Values, Allowed and ShouldAllow).
fetch_documentation_from_configuration(PathConfiguration) ->
SwaggerMetadata = application:get_env(elli_swagger,
swagger_metadata,
#{openapi => <<"3.0.0">>}),
BuiltDocumentation =
maps:fold(fun(Key, #{metadata := Metadata}, Acc) ->
Path = [<<"/">> | lists:join(<<"/">>, Key)],
Acc#{list_to_binary(Path) => Metadata}
end,
#{}, PathConfiguration),
jsx:encode(maps:merge(SwaggerMetadata, #{paths => BuiltDocumentation})).