package handler import ( "fmt" "net/http" "strings" "time" "git.kundeng.us/phoenix/textsender-models/tx0/message/scheduling" "github.com/google/uuid" "git.kundeng.us/phoenix/textsender-api/internal/app" "git.kundeng.us/phoenix/textsender-api/internal/store" ) type ScheduledMessageHandler struct { App *app.App ScheduledMessageStore store.ScheduledMessageStore } func NewScheduledMessageHandler(apiApp *app.App, str store.ScheduledMessageStore) *ScheduledMessageHandler { return &ScheduledMessageHandler{App: apiApp, ScheduledMessageStore: str} } type RequestAddScheduledMessage struct { Scheduled time.Time `json:"scheduled"` Status string `json:"status"` UserId uuid.UUID `json:"user_id"` } type AddScheduledMessageResponse struct { Message string `json:"message"` Data []scheduling.ScheduledMessage `json:"data"` } // AddScheduledMessage godoc // @Summary Add scheduled message // @Description Adds a scheduled message (requires JWT) // @Tags scheduled messages // @Accept json // @Produce json // @Security BearerAuth // @Param request body RequestAddScheduledMessage true "Data to add scheduled message" // @Success 201 {object} AddScheduledMessageResponse // @Failure 400 {object} AddScheduledMessageResponse // @Failure 500 {object} AddScheduledMessageResponse // @Router /schedule/message [post] func (s *ScheduledMessageHandler) AddScheduledMessage(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req RequestAddScheduledMessage if err := ExtractFromRequest(r, &req); err != nil { http.Error(w, "Invalid JSON: "+err.Error(), http.StatusBadRequest) } defer r.Body.Close() scheduledMessage := scheduling.ScheduledMessage{Scheduled: req.Scheduled, Status: req.Status, UserId: req.UserId} var statusCode int var resp AddScheduledMessageResponse if scheduledMessage.UserId == uuid.Nil { statusCode = http.StatusBadRequest resp.Message = "No UserId" } else { ctx := r.Context() if validStatus, err := IsStatusValid(&scheduledMessage); err == nil { if validStatus { if valid, err := isScheduledTimeValid(&scheduledMessage); err == nil && valid { if err = s.ScheduledMessageStore.CreateScheduledMessage(ctx, &scheduledMessage); err == nil { statusCode = http.StatusCreated resp.Data = append(resp.Data, scheduledMessage) resp.Message = "Successful" } else { statusCode = http.StatusInternalServerError resp.Message = err.Error() } } else { statusCode = http.StatusBadRequest resp.Message = "Invalid scheduled time" } } else { statusCode = http.StatusBadRequest resp.Message = "Invalid status" } } else { statusCode = http.StatusInternalServerError resp.Message = err.Error() } } RespondWithJSON(w, statusCode, &resp) } type GetScheduledMessageResponse struct { Message string `json:"message"` Data []scheduling.ScheduledMessage `json:"data"` } // GetScheduledMessage godoc // @Summary Get scheduled message // @Description Get a scheduled message (requires JWT) // @Tags scheduled messages // @Accept json // @Produce json // @Security BearerAuth // @Param id path string true "Contact Id" // @Param user_id path string true "User Id" // @Success 200 {object} GetScheduledMessageResponse // @Failure 400 {object} GetScheduledMessageResponse // @Failure 500 {object} GetScheduledMessageResponse // @Router /schedule/message [get] func (s *ScheduledMessageHandler) GetScheduledMessage(w http.ResponseWriter, r *http.Request) { var id, userId uuid.UUID if idParam, err := ParseQueryParams(r, "id"); err == nil { var err error id, err = uuid.Parse(*idParam) if err != nil { http.Error(w, "Error parsing Id", http.StatusBadRequest) return } } if userIdParam, err := ParseQueryParams(r, "user_id"); err == nil { userId, err = uuid.Parse(*userIdParam) if err != nil { http.Error(w, "Error parsing Id", http.StatusBadRequest) return } } var resp GetScheduledMessageResponse var statusCode int ctx := r.Context() if userId == uuid.Nil && id == uuid.Nil { http.Error(w, "Invalid query parameters", http.StatusBadRequest) return } else if userId != uuid.Nil && id != uuid.Nil { if schMsgs, err := s.ScheduledMessageStore.GetWithUserId(ctx, userId); err != nil { statusCode = http.StatusInternalServerError resp.Message = err.Error() } else { for _, schMsg := range schMsgs { if schMsg.Id == id { resp.Data = append(resp.Data, *schMsg) } } statusCode = http.StatusOK resp.Message = "Successful" } } else if id != uuid.Nil { if schMsg, err := s.ScheduledMessageStore.Get(ctx, id); err != nil { statusCode = http.StatusInternalServerError resp.Message = err.Error() } else { statusCode = http.StatusOK resp.Message = "Successful" resp.Data = append(resp.Data, *schMsg) } } else { if schMsgs, err := s.ScheduledMessageStore.GetWithUserId(ctx, userId); err != nil { statusCode = http.StatusInternalServerError resp.Message = err.Error() } else { statusCode = http.StatusOK resp.Message = "Successful" for _, schMsg := range schMsgs { resp.Data = append(resp.Data, *schMsg) } } } RespondWithJSON(w, statusCode, &resp) } type FetchNextMessageResponse struct { Message string `json:"message"` Data []scheduling.ScheduledMessage `json:"data"` } // FetchNextMessage godoc // @Summary Fetch scheduled message // @Description Fetches a scheduled message that is available to be processed (requires JWT) // @Tags scheduled messages // @Accept json // @Produce json // @Security BearerAuth // @Success 200 {object} FetchNextMessageResponse // @Failure 400 {object} FetchNextMessageResponse // @Failure 500 {object} FetchNextMessageResponse // @Router /schedule/message/fetch [get] func (s *ScheduledMessageHandler) FetchNextMessage(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var resp FetchNextMessageResponse var statusCode int if schMsg, err := s.ScheduledMessageStore.FetchNextScheduledMessage(ctx); err != nil { statusCode = http.StatusInternalServerError resp.Message = err.Error() } else { if schMsg != nil { statusCode = http.StatusOK resp.Message = "Successful" resp.Data = append(resp.Data, *schMsg) } else { statusCode = http.StatusNotFound resp.Message = "No item" } } RespondWithJSON(w, statusCode, &resp) } func isScheduledTimeValid(schMsg *scheduling.ScheduledMessage) (bool, error) { now := time.Now() timeCutOff := now.Add(-5 * time.Minute) futureCutOff := now.Add((24 * 7) * time.Hour) if schMsg.Scheduled.After(timeCutOff) { if schMsg.Scheduled.Before(futureCutOff) { return true, nil } else { return false, fmt.Errorf("Scheduled Time is too far in the future") } } else { return false, nil } } func IsStatusValid(schMsg *scheduling.ScheduledMessage) (bool, error) { schMsg.Status = strings.ToUpper(schMsg.Status) if schMsg.Status == scheduling.Pending { return true, nil } else { return false, fmt.Errorf("Status is not valid for creating scheduled message %s", schMsg.Status) } }