From 2716bb50d33d987f3df3b8310e2a7a51204fc336 Mon Sep 17 00:00:00 2001 From: phoenix Date: Tue, 11 Nov 2025 16:43:51 +0000 Subject: [PATCH] tsk-29: Place restrictions on adding scheduled message event (#30) Closes #29 Reviewed-on: https://git.kundeng.us/phoenix/textsender-api/pulls/30 Co-authored-by: phoenix Co-committed-by: phoenix --- cmd/api/main.go | 2 +- cmd/api/main_test.go | 2 +- internal/handler/scheduled_message_event.go | 39 +++++++---- .../handler/scheduled_message_event_test.go | 6 +- internal/store/mock/mock_store.go | 38 ----------- .../store/mock/scheduled_message_store.go | 67 +++++++++++++++++++ internal/store/scheduled_message_store.go | 23 +++++++ 7 files changed, 121 insertions(+), 56 deletions(-) create mode 100644 internal/store/mock/scheduled_message_store.go diff --git a/cmd/api/main.go b/cmd/api/main.go index 2606ca4..bf14f3b 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -69,7 +69,7 @@ func main() { contactHandler := handler.NewContactHandler(contactStore) messageHandler := handler.NewMessageHandler(messageStore) scheduledMessageHandler := handler.NewScheduledMessageHandler(schStore) - scheduledMessageEventHandler := handler.NewScheduledMessageEventHandler(schMsgEventStore) + scheduledMessageEventHandler := handler.NewScheduledMessageEventHandler(schMsgEventStore, schStore) router := chi.NewRouter() diff --git a/cmd/api/main_test.go b/cmd/api/main_test.go index bf87e14..5150fb5 100644 --- a/cmd/api/main_test.go +++ b/cmd/api/main_test.go @@ -62,7 +62,7 @@ func TestMain(m *testing.M) { contactHandler := handler.NewContactHandler(contactStore) messageHandler := handler.NewMessageHandler(messageStore) scheduledMessageHandler := handler.NewScheduledMessageHandler(schStore) - scheduledMessageEventHandler := handler.NewScheduledMessageEventHandler(schMsgEventStore) + scheduledMessageEventHandler := handler.NewScheduledMessageEventHandler(schMsgEventStore, schStore) testRouter = chi.NewRouter() testRouter.Handle(endpoint.ADD_CONTACT_ENDPOINT, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(contactHandler.AddContact))) diff --git a/internal/handler/scheduled_message_event.go b/internal/handler/scheduled_message_event.go index 3c6f6ed..52746f3 100644 --- a/internal/handler/scheduled_message_event.go +++ b/internal/handler/scheduled_message_event.go @@ -29,10 +29,11 @@ type GetScheduledMessageEventResponse struct { type ScheduledMessageEventHandler struct { ScheduledMessageEventStore store.ScheduledMessageEventStore + ScheduledMessageStore store.ScheduledMessageStore } -func NewScheduledMessageEventHandler(str store.ScheduledMessageEventStore) *ScheduledMessageEventHandler { - return &ScheduledMessageEventHandler{ScheduledMessageEventStore: str} +func NewScheduledMessageEventHandler(str store.ScheduledMessageEventStore, schStore store.ScheduledMessageStore) *ScheduledMessageEventHandler { + return &ScheduledMessageEventHandler{ScheduledMessageEventStore: str, ScheduledMessageStore: schStore} } func (s *ScheduledMessageEventHandler) AddScheduledMessageEvent(w http.ResponseWriter, r *http.Request) { @@ -64,23 +65,33 @@ func (s *ScheduledMessageEventHandler) AddScheduledMessageEvent(w http.ResponseW } else { ctx := r.Context() - if exists, err := s.ScheduledMessageEventStore.Exists(ctx, &event); err == nil { - if exists { - statusCode = http.StatusBadRequest - resp.Message = "Event already present" - } else { - if err = s.ScheduledMessageEventStore.CreateScheduledMessageEvent(ctx, &event); err == nil { - statusCode = http.StatusCreated - resp.Message = "Successful" - resp.Data = append(resp.Data, event) + if schMsg, err := s.ScheduledMessageStore.Get(ctx, event.ScheduledMessageId); err != nil { + statusCode = http.StatusInternalServerError + resp.Message = err.Error() + } else { + if schMsg.Status == scheduling.Pending { + if exists, err := s.ScheduledMessageEventStore.Exists(ctx, &event); err == nil { + if exists { + statusCode = http.StatusBadRequest + resp.Message = "Event already present" + } else { + if err = s.ScheduledMessageEventStore.CreateScheduledMessageEvent(ctx, &event); err == nil { + statusCode = http.StatusCreated + resp.Message = "Successful" + resp.Data = append(resp.Data, event) + } else { + statusCode = http.StatusInternalServerError + resp.Message = err.Error() + } + } } else { statusCode = http.StatusInternalServerError resp.Message = err.Error() } + } else { + statusCode = http.StatusBadRequest + resp.Message = "Scheduled message cannot be modified due to the status" } - } else { - statusCode = http.StatusInternalServerError - resp.Message = err.Error() } } diff --git a/internal/handler/scheduled_message_event_test.go b/internal/handler/scheduled_message_event_test.go index 9067fcf..cdee1d9 100644 --- a/internal/handler/scheduled_message_event_test.go +++ b/internal/handler/scheduled_message_event_test.go @@ -32,7 +32,7 @@ func TestCreateScheduledMessageEventWithMock(t *testing.T) { messageStore := mock.NewMockMessageStore() schMsgStore := mock.NewMockScheduledMessageStore() - handler := NewScheduledMessageEventHandler(mockStore) + handler := NewScheduledMessageEventHandler(mockStore, schMsgStore) recipientId := uuid.New() messageId := uuid.New() @@ -53,6 +53,7 @@ func TestCreateScheduledMessageEventWithMock(t *testing.T) { schMsg.Id = scheduledMessageId schMsg.UserId = testUserId schMsg.Scheduled = now.Add(20 * time.Minute) + schMsg.Status = scheduling.Pending ctx := t.Context() @@ -93,7 +94,7 @@ func TestGetScheduledMessageEventWithMock(t *testing.T) { messageStore := mock.NewMockMessageStore() schMsgStore := mock.NewMockScheduledMessageStore() schMsgEventStore := mock.NewMockScheduledMessageEventStore() - handler := NewScheduledMessageEventHandler(schMsgEventStore) + handler := NewScheduledMessageEventHandler(schMsgEventStore, schMsgStore) recipientId := uuid.New() messageId := uuid.New() @@ -114,6 +115,7 @@ func TestGetScheduledMessageEventWithMock(t *testing.T) { schMsg.Id = scheduledMessageId schMsg.UserId = testUserId schMsg.Scheduled = now.Add(20 * time.Minute) + schMsg.Status = scheduling.Pending event := scheduling.ScheduledMessageEvent{} event.MessageId = msg.Id diff --git a/internal/store/mock/mock_store.go b/internal/store/mock/mock_store.go index d0c8323..0361965 100644 --- a/internal/store/mock/mock_store.go +++ b/internal/store/mock/mock_store.go @@ -9,7 +9,6 @@ import ( "git.kundeng.us/phoenix/textsender-models/pkg/contact" "git.kundeng.us/phoenix/textsender-models/pkg/message" - "git.kundeng.us/phoenix/textsender-models/pkg/message/scheduling" ) type Key struct { @@ -201,40 +200,3 @@ func (m *MockMessageStore) MessageExists(ctx context.Context, msg *message.Messa return exists, nil } - -type ScheduledMessageKey struct { - UserId uuid.UUID -} - -type MockScheduledMessageStore struct { - ScheduledMessages map[uuid.UUID]*scheduling.ScheduledMessage - ScheduledMessagesByKey map[ScheduledMessageKey]*scheduling.ScheduledMessage - mu sync.RWMutex - Error error // Optional: simulate errors -} - -func NewMockScheduledMessageStore() *MockScheduledMessageStore { - return &MockScheduledMessageStore{ - ScheduledMessages: make(map[uuid.UUID]*scheduling.ScheduledMessage), - ScheduledMessagesByKey: make(map[ScheduledMessageKey]*scheduling.ScheduledMessage), - } -} - -func (m *MockScheduledMessageStore) CreateScheduledMessage(ctx context.Context, schedMsg *scheduling.ScheduledMessage) error { - m.mu.Lock() - defer m.mu.Unlock() - - if m.Error != nil { - return m.Error - } - - if schedMsg.Id == uuid.Nil { - schedMsg.Id = uuid.New() - } - - key := ScheduledMessageKey{UserId: schedMsg.UserId} - - m.ScheduledMessages[schedMsg.Id] = schedMsg - m.ScheduledMessagesByKey[key] = schedMsg - return nil -} diff --git a/internal/store/mock/scheduled_message_store.go b/internal/store/mock/scheduled_message_store.go new file mode 100644 index 0000000..335f9d7 --- /dev/null +++ b/internal/store/mock/scheduled_message_store.go @@ -0,0 +1,67 @@ +package mock + +import ( + "context" + "fmt" + "sync" + + "github.com/google/uuid" + + "git.kundeng.us/phoenix/textsender-models/pkg/message/scheduling" +) + +type ScheduledMessageKey struct { + UserId uuid.UUID +} + +type MockScheduledMessageStore struct { + ScheduledMessages map[uuid.UUID]*scheduling.ScheduledMessage + ScheduledMessagesByKey map[ScheduledMessageKey]*scheduling.ScheduledMessage + mu sync.RWMutex + Error error // Optional: simulate errors +} + +func NewMockScheduledMessageStore() *MockScheduledMessageStore { + return &MockScheduledMessageStore{ + ScheduledMessages: make(map[uuid.UUID]*scheduling.ScheduledMessage), + ScheduledMessagesByKey: make(map[ScheduledMessageKey]*scheduling.ScheduledMessage), + } +} + +func (m *MockScheduledMessageStore) Get(ctx context.Context, id uuid.UUID) (*scheduling.ScheduledMessage, error) { + m.mu.Lock() + defer m.mu.Unlock() + + if m.Error != nil { + return nil, m.Error + } + + if id == uuid.Nil { + return nil, fmt.Errorf("Id is nil") + } else { + if schMsg := m.ScheduledMessages[id]; schMsg != nil { + return schMsg, nil + } else { + return nil, fmt.Errorf("Scheduled message does not exist") + } + } +} + +func (m *MockScheduledMessageStore) CreateScheduledMessage(ctx context.Context, schedMsg *scheduling.ScheduledMessage) error { + m.mu.Lock() + defer m.mu.Unlock() + + if m.Error != nil { + return m.Error + } + + if schedMsg.Id == uuid.Nil { + schedMsg.Id = uuid.New() + } + + key := ScheduledMessageKey{UserId: schedMsg.UserId} + + m.ScheduledMessages[schedMsg.Id] = schedMsg + m.ScheduledMessagesByKey[key] = schedMsg + return nil +} diff --git a/internal/store/scheduled_message_store.go b/internal/store/scheduled_message_store.go index ea71082..b1c3d9f 100644 --- a/internal/store/scheduled_message_store.go +++ b/internal/store/scheduled_message_store.go @@ -2,13 +2,17 @@ package store import ( "context" + "fmt" + "github.com/google/uuid" + "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" "git.kundeng.us/phoenix/textsender-models/pkg/message/scheduling" ) type ScheduledMessageStore interface { + Get(ctx context.Context, id uuid.UUID) (*scheduling.ScheduledMessage, error) CreateScheduledMessage(ctx context.Context, schedMsg *scheduling.ScheduledMessage) error } @@ -20,6 +24,25 @@ func NewScheduledMessageStore(db *pgxpool.Pool) *PGScheduledMessageStore { return &PGScheduledMessageStore{db: db} } +func (s *PGScheduledMessageStore) Get(ctx context.Context, id uuid.UUID) (*scheduling.ScheduledMessage, error) { + query := ` + SELECT id, scheduled, created, status, user_id FROM scheduled_messages WHERE id = $1 + ` + + var schMsg scheduling.ScheduledMessage + err := s.db.QueryRow(ctx, query, id).Scan( + &schMsg.Id, &schMsg.Scheduled, &schMsg.Created, &schMsg.Status, &schMsg.UserId, + ) + + if err == pgx.ErrNoRows { + return nil, fmt.Errorf("No rows") + } else if err != nil { + return nil, fmt.Errorf("Getting scheduled message: %w", err) + } + + return &schMsg, nil +} + func (s *PGScheduledMessageStore) CreateScheduledMessage(ctx context.Context, schedMsg *scheduling.ScheduledMessage) error { query := ` INSERT INTO scheduled_messages (scheduled, status, user_id)