Files
EventHubBack/src/handlers/swagger_docs_handler.erl

134 lines
5.1 KiB
Erlang
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
%%%-------------------------------------------------------------------
%%% @doc Обработчик документации Swagger.
%%%
%%% Раздаёт Swagger UI и спецификации OpenAPI для
%%% административного и клиентского API.
%%%
%%% GET / индексная страница с выбором API
%%% GET /admin/ Swagger UI для административного API
%%% GET /admin/swagger.json OpenAPI-спецификация (admin)
%%% GET /user/ Swagger UI для клиентского API
%%% GET /user/swagger.json OpenAPI-спецификация (user)
%%% @end
%%%-------------------------------------------------------------------
-module(swagger_docs_handler).
-behaviour(cowboy_handler).
-export([init/2]).
%%% cowboy_handler callback
-spec init(cowboy_req:req(), any()) -> {ok, cowboy_req:req(), any()}.
init(Req, _Opts) ->
Path = cowboy_req:path(Req),
handle(Path, Req).
%%--------------------------------------------------------------------
%% Роутинг путей
%%--------------------------------------------------------------------
-spec handle(binary(), cowboy_req:req()) -> {ok, cowboy_req:req(), any()}.
handle(<<"/">>, Req) ->
serve_index(Req);
handle(<<"/admin">>, Req) ->
redirect_to_slash(<<"/admin/">>, Req);
handle(<<"/admin/">>, Req) ->
serve_ui(admin, Req);
handle(<<"/admin/swagger.json">>, Req) ->
serve_json(admin, Req);
handle(<<"/user">>, Req) ->
redirect_to_slash(<<"/user/">>, Req);
handle(<<"/user/">>, Req) ->
serve_ui(user, Req);
handle(<<"/user/swagger.json">>, Req) ->
serve_json(user, Req);
handle(_, Req) ->
cowboy_req:reply(404, #{}, <<"Not Found">>, Req),
{ok, [], []}.
%%--------------------------------------------------------------------
%% Главная страница
%%--------------------------------------------------------------------
-spec serve_index(cowboy_req:req()) -> {ok, cowboy_req:req(), any()}.
serve_index(Req) ->
Html = <<"<!DOCTYPE html>
<html>
<head><title>EventHub API Docs</title></head>
<body>
<h1>EventHub API Documentation</h1>
<ul>
<li><a href=\"/admin/\">Admin API</a></li>
<li><a href=\"/user/\">User API</a></li>
</ul>
</body>
</html>">>,
cowboy_req:reply(200, #{<<"content-type">> => <<"text/html">>}, Html, Req),
{ok, Html, []}.
%%--------------------------------------------------------------------
%% Swagger UI
%%--------------------------------------------------------------------
-spec serve_ui(admin | user, cowboy_req:req()) -> {ok, cowboy_req:req(), any()}.
serve_ui(Api, Req) ->
{Title, SpecUrl} = case Api of
admin -> {<<"EventHub Admin API">>, <<"/admin/swagger.json">>};
user -> {<<"EventHub User API">>, <<"/user/swagger.json">>}
end,
Html = iolist_to_binary([
"<!DOCTYPE html><html><head><title>", Title,
"</title><link rel=\"stylesheet\" href=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui.css\">",
"</head><body><div id=\"swagger-ui\"></div>",
"<script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js\"></script>",
"<script>window.onload=function(){SwaggerUIBundle({url:'", SpecUrl,
"',dom_id:'#swagger-ui',presets:[SwaggerUIBundle.presets.apis,SwaggerUIBundle.SwaggerUIStandalonePreset],layout:'BaseLayout'});}</script>",
"</body></html>"
]),
cowboy_req:reply(200, #{<<"content-type">> => <<"text/html">>}, Html, Req),
{ok, Html, []}.
%%--------------------------------------------------------------------
%% OpenAPI JSON
%%--------------------------------------------------------------------
-spec serve_json(admin | user, cowboy_req:req()) -> {ok, cowboy_req:req(), any()}.
serve_json(Api, Req) ->
Trails = case Api of
admin -> trails:admin();
user -> trails:user()
end,
OpenApi = #{
openapi => <<"3.0.3">>,
info => #{
title => case Api of
admin -> <<"EventHub Admin API">>;
user -> <<"EventHub User API">>
end,
version => <<"1.0.0">>
},
servers => [#{url => <<"http://localhost:8445">>, description => <<"API server">>}],
paths => build_paths(Trails)
},
Json = jsx:encode(OpenApi),
cowboy_req:reply(200, #{<<"content-type">> => <<"application/json">>}, Json, Req),
{ok, Json, []}.
%%--------------------------------------------------------------------
%% Вспомогательные функции
%%--------------------------------------------------------------------
-spec build_paths([map()]) -> map().
build_paths(Trails) ->
lists:foldl(fun(Trail, Acc) ->
Path = maps:get(path, Trail, <<"/">>),
Method0 = maps:get(method, Trail, <<"get">>),
Method = string:lowercase(Method0),
TrailData = maps:without([path, method], Trail),
PathItem = #{Method => TrailData},
maps:merge_with(fun(_, V1, V2) -> maps:merge(V1, V2) end, Acc, #{Path => PathItem})
end, #{}, Trails).
-spec redirect_to_slash(binary(), cowboy_req:req()) -> {ok, cowboy_req:req(), any()}.
redirect_to_slash(Location, Req) ->
cowboy_req:reply(301, #{<<"location">> => Location}, <<>>, Req),
{ok, [], []}.