package service import ( "bitbucket.org/skeris/heruvym/dal" "bitbucket.org/skeris/heruvym/jwt_adapter" "bitbucket.org/skeris/heruvym/model" "bitbucket.org/skeris/heruvym/tools" rAL "bitbucket.org/skeris/profile/dal" "context" "encoding/json" "errors" "github.com/themakers/hlog" "net/http" ) type Heruvym struct { logger hlog.Logger dal *dal.DAL ral rAL.LayerMongoDb } func New(dataAccessLayer *dal.DAL, ral rAL.LayerMongoDb, log hlog.Logger) *Heruvym { return &Heruvym{ logger: log.Module("Service"), dal: dataAccessLayer, ral: ral, } } func (h Heruvym) Register(m *http.ServeMux) *http.ServeMux { m.HandleFunc("/create", h.CreateTicket) m.HandleFunc("/subscribe", tools.SseWrapper(h.GetList)) m.HandleFunc("/ticket", tools.SseWrapper(h.Subscribe)) m.HandleFunc("/send", tools.HandlerWrapper(h.PutMessage)) m.HandleFunc("/getTickets", tools.HandlerWrapper(h.GetTickets)) m.HandleFunc("/getMessages", tools.HandlerWrapper(h.GetMessages)) m.HandleFunc("/pick", tools.HandlerWrapper(h.Pick)) m.HandleFunc("/delegate", tools.HandlerWrapper(h.Delegate)) m.HandleFunc("/vote", tools.HandlerWrapper(h.Vote)) m.HandleFunc("/close", tools.HandlerWrapper(h.CloseTicket)) return m } type CreateTicketReq struct { Title string `json:"Title"` Message string `json:"Message"` } type CreateTicketResp struct { Ticket string `json:"Ticket"` } func (h *Heruvym) CreateTicket(w http.ResponseWriter, r *http.Request) { defer func() { if err := r.Body.Close(); err != nil { h.logger.Emit(ErrorClose{ Err: err, }) } }() var request CreateTicketReq if err := json.NewDecoder(r.Body).Decode(&request); err != nil { http.Error(w, "Invalid json", http.StatusBadRequest) return } if request.Title == "" { http.Error(w, "No Title", http.StatusNoContent) return } if request.Message == "" { http.Error(w, "No Message", http.StatusNoContent) return } ctx := r.Context() session := jwt_adapter.Get(ctx) if session == nil { http.Error(w, "No session", http.StatusMethodNotAllowed) return } ticketID, err := h.dal.CreateTicket( ctx, session.User, session.ID, request.Title, request.Message, []string{}, ) if err != nil { http.Error(w, "CannotCreateTicket", http.StatusInternalServerError) return } if _, err := h.dal.PutMessage(ctx, request.Message, session.User, session.Session, ticketID, []string{}, ); err != nil { http.Error(w, "CannotCreateMessage", http.StatusInternalServerError) return } response, err := json.Marshal(CreateTicketResp{Ticket: ticketID}) if err != nil { h.logger.Emit(ErrorMarshal{ Err: err, }) http.Error(w, "CannotMarshalMessage", http.StatusInternalServerError) return } if _, err := w.Write(response); err != nil { h.logger.Emit(ErrorMarshal{ Err: err, }) return } } var _ tools.DataEmitter = (&Heruvym{}).GetList func (h *Heruvym) GetList(ctx context.Context) chan interface{} { sess := jwt_adapter.Get(ctx) output := make(chan interface{}) if sess.User == "" { go h.unauthorizedTickets(ctx, sess.Session, output) } else { role, err := h.ral.GetProfileRole(ctx, sess.User) if err != nil { go h.hasNoRole(output) } if role == "admin" || role == "manager" { go h.allTickets(ctx, output) } else { go h.userTickets(ctx, sess.User, output) } } return output } func (h *Heruvym) allTickets(ctx context.Context, output chan interface{}) { defer close(output) if err := h.dal.WatchActiveTickets(ctx, func(ticket model.Ticket) error { output <- ticket return nil }); err != nil { output <- errors.New("cannot watch all tickets") } } func (h *Heruvym) userTickets(ctx context.Context, userID string, output chan interface{}) { defer close(output) if err := h.dal.YieldUserTickets(ctx, userID, func(ticket model.Ticket) error { output <- ticket return nil }); err != nil { output <- errors.New("cannot get tickets") } if err := h.dal.WatchTickets(ctx, userID, func(ticket model.Ticket) error { output <- ticket return nil }); err != nil { output <- errors.New("cannot watch tickets") } } func (h *Heruvym) hasNoRole(output chan interface{}) { defer close(output) output <- errors.New("no role in profile") } func (h *Heruvym) unauthorizedTickets(ctx context.Context, sess string, output chan interface{}) { defer close(output) tickets, err := h.dal.GetTickets4Sess(ctx, sess) if err != nil { output <- errors.New("no tickets for session") } for _, ticket := range tickets { output <- ticket } } type ReqPutMessage struct { Message string `json:"message"` TicketID string `json:"ticket"` Files []string `json:"files"` Lang string `json:"lang"` } func (h *Heruvym) PutMessage( ctx context.Context, request ReqPutMessage, ) (interface{}, int) { sess := jwt_adapter.Get(ctx) message, err := h.dal.PutMessage( ctx, request.Message, sess.User, sess.Session, request.TicketID, request.Files, ) if err != nil { return errors.New("can not put message"), http.StatusInternalServerError } if err := h.dal.UpdateTopMessage(ctx, request.TicketID, message); err != nil { return errors.New("can not update ticket"), http.StatusInternalServerError } return nil, http.StatusOK } var _ tools.DataEmitter = (&Heruvym{}).Subscribe func (h *Heruvym) Subscribe(ctx context.Context) chan interface{} { sess := jwt_adapter.Get(ctx) url := ctx.Value(tools.ContextURLKey).([]string) ticketID := url[len(url)-1] output := make(chan interface{}) if sess.User == "" { go func() { ticket, err := h.dal.GetTicket4Sess(ctx, ticketID, sess.Session) if err != nil || ticket == nil { output <- errors.New("no tickets 4 session") return } if err := h.dal.YieldMessages(ctx, ticketID, func(message model.Message) error { output <- message if err := h.dal.SetShown(ctx, message.ID, sess.Session); err != nil { output <- errors.New("cannot read message") return err } return nil }); err != nil { output <- errors.New("cannot read messages") } if err := h.dal.WatchMessages(ctx, ticketID, func(message model.Message) error { output <- message if err := h.dal.SetShown(ctx, message.ID, sess.Session); err != nil { output <- errors.New("cannot read message") return err } return nil }); err != nil { output <- errors.New("cannot read messages") } }() } else { role, err := h.ral.GetProfileRole(ctx, sess.User) if err != nil { go h.hasNoRole(output) } if role == "admin" || role == "manager" { go func() { if err := h.dal.YieldMessages(ctx, ticketID, func(message model.Message) error { output <- message if err := h.dal.SetShown(ctx, message.ID, sess.User); err != nil { output <- errors.New("cannot read message") return err } return nil }); err != nil { output <- errors.New("cannot read messages") } if err := h.dal.WatchMessages(ctx, ticketID, func(message model.Message) error { output <- message if err := h.dal.SetShown(ctx, message.ID, sess.Session); err != nil { output <- errors.New("cannot read message") return err } return nil }); err != nil { output <- errors.New("cannot read messages") } }() } else { go func() { ticket, err := h.dal.GetTicket4User(ctx, ticketID, sess.User) if err != nil || ticket == nil { output <- errors.New("no tickets 4 user") return } if err := h.dal.YieldMessages(ctx, ticketID, func(message model.Message) error { output <- message if err := h.dal.SetShown(ctx, message.ID, sess.User); err != nil { output <- errors.New("cannot read message") return err } return nil }); err != nil { output <- errors.New("cannot read messages") } if err := h.dal.WatchMessages(ctx, ticketID, func(message model.Message) error { output <- message if err := h.dal.SetShown(ctx, message.ID, sess.Session); err != nil { output <- errors.New("cannot read message") return err } return nil }); err != nil { output <- errors.New("cannot read messages") } }() } } return output } func (h *Heruvym) handleOwnMessages(output chan interface{}) { defer close(output) } type GetTicketsReq struct { Amount int64 `json:"amt"` Page int64 `json:"page"` Search string `json:"srch"` Status string `json:"status"` } func (h *Heruvym) GetTickets( ctx context.Context, request GetTicketsReq) ([]model.Ticket, int) { result, err := h.dal.GetTicketPage(ctx, request.Status, request.Search, request.Amount, request.Page, ) if err != nil { return nil, http.StatusNoContent } return *result, http.StatusOK } type GetMessagesReq struct { Amount int64 `json:"amt"` Page int64 `json:"page"` Search string `json:"srch"` TicketID string `json:"ticket"` } func (h *Heruvym) GetMessages( ctx context.Context, request GetMessagesReq) ([]model.Message, int) { result, err := h.dal.GetMessagesPage(ctx, request.Search, request.TicketID, request.Amount, request.Page, ) if err != nil { return nil, http.StatusNoContent } return result, http.StatusOK } type CloseTicketReq struct { TicketID string `json:"ticket"` } func (h *Heruvym) CloseTicket(ctx context.Context, req CloseTicketReq) (error, int) { if err := h.dal.SetTicketStatus(ctx, req.TicketID, model.StateClose); err != nil { return err, http.StatusBadRequest } return nil, http.StatusOK } type VoteReq struct { TicketID string `json:"ticket"` Rate int `json:"rate"` } func (h *Heruvym) Vote(ctx context.Context, req VoteReq) (error, int) { if err := h.dal.SetRate(ctx, req.TicketID, req.Rate); err != nil { return err, http.StatusBadRequest } return nil, http.StatusOK } type PickReq struct { TicketID string `json:"ticket"` } func (h *Heruvym) Pick(ctx context.Context, req PickReq) (error, int) { sess := jwt_adapter.Get(ctx) if err := h.dal.SetAnswerer(ctx, req.TicketID, sess.User); err != nil { return err, http.StatusBadRequest } return nil, http.StatusOK } type DelegateReq struct { TicketID string `json:"ticket"` AnswererID string `json:"answerer"` } func (h *Heruvym) Delegate(ctx context.Context, req DelegateReq) (error, int) { if err := h.dal.SetAnswerer(ctx, req.TicketID, req.AnswererID); err != nil { return err, http.StatusBadRequest } return nil, http.StatusOK }