Files
EventHubBack/src/handlers/handler_tickets.erl

146 lines
5.3 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 Обработчик пользовательских тикетов (клиентский API).
%%%
%%% GET получить список тикетов.
%%% Администраторы видят все тикеты,
%%% обычные пользователи только свои.
%%% POST создать новый тикет об ошибке.
%%% @end
%%%-------------------------------------------------------------------
-module(handler_tickets).
-behaviour(cowboy_handler).
-export([init/2]).
-export([trails/0]).
-include("records.hrl").
%%% cowboy_handler callback
-spec init(cowboy_req:req(), any()) -> {ok, cowboy_req:req(), any()}.
init(Req, Opts) ->
handle(Req, Opts).
%%% Swagger metadata
-spec trails() -> [map()].
trails() ->
[
#{ % GET list
path => <<"/v1/tickets">>,
method => <<"GET">>,
description => <<"List tickets (admin sees all, user sees own)">>,
tags => [<<"Tickets">>],
parameters => [
#{name => <<"limit">>, in => <<"query">>, schema => #{type => integer}, description => <<"Page size">>},
#{name => <<"offset">>, in => <<"query">>, schema => #{type => integer}, description => <<"Offset">>}
],
responses => #{
200 => #{
description => <<"Array of tickets">>,
content => #{<<"application/json">> => #{schema => #{
type => array,
items => ticket_schema()
}}}
},
401 => #{description => <<"Unauthorized">>}
}
},
#{ % POST create
path => <<"/v1/tickets">>,
method => <<"POST">>,
description => <<"Create a new ticket (bug report)">>,
tags => [<<"Tickets">>],
requestBody => #{
required => true,
content => #{<<"application/json">> => #{schema => #{
type => object,
required => [<<"error_message">>],
properties => #{
error_message => #{type => string},
stacktrace => #{type => string},
context => #{type => string}
}
}}}
},
responses => #{
201 => #{description => <<"Ticket created">>},
400 => #{description => <<"Missing required fields or invalid JSON">>},
401 => #{description => <<"Unauthorized">>}
}
}
].
ticket_schema() ->
#{
type => object,
properties => #{
id => #{type => string},
reporter_id => #{type => string},
error_hash => #{type => string},
error_message => #{type => string},
stacktrace => #{type => string},
context => #{type => string},
count => #{type => integer},
first_seen => #{type => string, format => <<"date-time">>},
last_seen => #{type => string, format => <<"date-time">>},
status => #{type => string, enum => [<<"open">>, <<"in_progress">>, <<"resolved">>, <<"closed">>]},
assigned_to => #{type => string, nullable => true},
resolution_note => #{type => string, nullable => true}
}
}.
%%%===================================================================
%%% HTTP-методы
%%%===================================================================
%% @private
-spec handle(cowboy_req:req(), any()) -> {ok, cowboy_req:req(), any()}.
handle(Req, _Opts) ->
case cowboy_req:method(Req) of
<<"GET">> -> list_tickets(Req);
<<"POST">> -> create_ticket(Req);
_ -> handler_utils:send_error(Req, 405, <<"Method not allowed">>)
end.
%% @doc GET /v1/tickets — список тикетов.
-spec list_tickets(cowboy_req:req()) -> {ok, binary(), cowboy_req:req()}.
list_tickets(Req) ->
case handler_utils:auth_user(Req) of
{ok, UserId, Req1} ->
case admin_utils:is_admin(UserId) of
true ->
Tickets = core_ticket:list_all(),
handler_utils:send_json(Req1, 200, [handler_utils:ticket_to_json(T) || T <- Tickets]);
false ->
Tickets = core_ticket:list_by_user(UserId),
handler_utils:send_json(Req1, 200, [handler_utils:ticket_to_json(T) || T <- Tickets])
end;
{error, Code, Message, Req1} ->
handler_utils:send_error(Req1, Code, Message)
end.
%% @doc POST /v1/tickets — создание тикета.
-spec create_ticket(cowboy_req:req()) -> {ok, binary(), cowboy_req:req()}.
create_ticket(Req) ->
case handler_utils:auth_user(Req) of
{ok, UserId, Req1} ->
{ok, Body, Req2} = cowboy_req:read_body(Req1),
try jsx:decode(Body, [return_maps]) of
#{<<"error_message">> := _} = Data ->
TicketData = maps:merge(
#{<<"reporter_id">> => UserId, <<"status">> => <<"open">>},
Data
),
case core_ticket:create_ticket(TicketData) of
{ok, Ticket} ->
handler_utils:send_json(Req2, 201, handler_utils:ticket_to_json(Ticket));
{error, Reason} ->
handler_utils:send_error(Req2, 500, Reason)
end;
_ ->
handler_utils:send_error(Req2, 400, <<"Missing 'error_message' field">>)
catch
_:_ -> handler_utils:send_error(Req2, 400, <<"Invalid JSON">>)
end;
{error, Code, Message, Req1} ->
handler_utils:send_error(Req1, Code, Message)
end.