diff --git a/cmd/api/main.go b/cmd/api/main.go index 54da959..2606ca4 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -84,6 +84,7 @@ func main() { router.Handle(endpoint.GET_MESSAGE, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(messageHandler.GetMessage))) router.Handle(endpoint.ScheduleMessageEndpoint, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(scheduledMessageHandler.AddScheduledMessage))) router.Handle(endpoint.AddEventToScheduledMessageEndpoint, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(scheduledMessageEventHandler.AddScheduledMessageEvent))) + router.Handle(endpoint.GetScheduledMessageEventEndpoint, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(scheduledMessageEventHandler.GetScheduledMessageEvent))) // Start server server := &http.Server{ diff --git a/cmd/api/main_test.go b/cmd/api/main_test.go index 76b1601..bf87e14 100644 --- a/cmd/api/main_test.go +++ b/cmd/api/main_test.go @@ -9,7 +9,7 @@ import ( "path" "testing" - "github.com/gorilla/mux" + "github.com/go-chi/chi/v5" "github.com/joho/godotenv" "git.kundeng.us/phoenix/textsender-api/internal/config" @@ -21,7 +21,7 @@ import ( "git.kundeng.us/phoenix/textsender-api/internal/store" ) -var testRouter *mux.Router +var testRouter *chi.Mux func TestMain(m *testing.M) { cfg := load() @@ -64,13 +64,14 @@ func TestMain(m *testing.M) { scheduledMessageHandler := handler.NewScheduledMessageHandler(schStore) scheduledMessageEventHandler := handler.NewScheduledMessageEventHandler(schMsgEventStore) - testRouter = mux.NewRouter() - testRouter.HandleFunc(endpoint.ADD_CONTACT_ENDPOINT, contactHandler.AddContact).Methods("POST") + testRouter = chi.NewRouter() + testRouter.Handle(endpoint.ADD_CONTACT_ENDPOINT, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(contactHandler.AddContact))) testRouter.Handle(endpoint.GET_CONTACT, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(contactHandler.GetContact))) testRouter.Handle(endpoint.ADD_MESSAGE, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(messageHandler.AddMessage))) testRouter.Handle(endpoint.GET_MESSAGE, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(messageHandler.GetMessage))) testRouter.Handle(endpoint.ScheduleMessageEndpoint, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(scheduledMessageHandler.AddScheduledMessage))) testRouter.Handle(endpoint.AddEventToScheduledMessageEndpoint, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(scheduledMessageEventHandler.AddScheduledMessageEvent))) + testRouter.Handle(endpoint.GetScheduledMessageEventEndpoint, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(scheduledMessageEventHandler.GetScheduledMessageEvent))) code := m.Run() os.Exit(code) diff --git a/internal/handler/endpoint/endpoint.go b/internal/handler/endpoint/endpoint.go index acc7803..be1aac0 100644 --- a/internal/handler/endpoint/endpoint.go +++ b/internal/handler/endpoint/endpoint.go @@ -7,3 +7,4 @@ const GET_CONTACT = "/api/v1/contact" const ADD_CONTACT_ENDPOINT = "/api/v1/contact/new" const ScheduleMessageEndpoint = "/api/v1/schedule/message" const AddEventToScheduledMessageEndpoint = "/api/v1/schedule/message/event" +const GetScheduledMessageEventEndpoint = "/api/v1/schedule/message/event/{id}" diff --git a/internal/handler/scheduled_message_event.go b/internal/handler/scheduled_message_event.go index 3b1ed49..3c6f6ed 100644 --- a/internal/handler/scheduled_message_event.go +++ b/internal/handler/scheduled_message_event.go @@ -2,7 +2,9 @@ package handler import ( "net/http" + "strings" + "github.com/go-chi/chi/v5" "github.com/google/uuid" "git.kundeng.us/phoenix/textsender-api/internal/store" @@ -20,6 +22,11 @@ type AddScheduledMessageEventResponse struct { Data []scheduling.ScheduledMessageEvent `json:"data"` } +type GetScheduledMessageEventResponse struct { + Message string `json:"message"` + Data []scheduling.ScheduledMessageEvent `json:"data"` +} + type ScheduledMessageEventHandler struct { ScheduledMessageEventStore store.ScheduledMessageEventStore } @@ -79,3 +86,41 @@ func (s *ScheduledMessageEventHandler) AddScheduledMessageEvent(w http.ResponseW RespondWithJSON(w, statusCode, &resp) } + +func (s *ScheduledMessageEventHandler) GetScheduledMessageEvent(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "Metnot allowed", http.StatusMethodNotAllowed) + return + } + + id := chi.URLParam(r, "id") + if len(id) == 0 { + pathParts := strings.Split(r.URL.Path, "/") + if len(pathParts) < 7 { + http.Error(w, "Id not provided", http.StatusBadRequest) + return + } else { + id = pathParts[6] + } + } + + var statusCode int + var resp GetScheduledMessageEventResponse + + if parsedId, err := uuid.Parse(id); err != nil { + resp.Message = err.Error() + statusCode = http.StatusBadRequest + } else { + ctx := r.Context() + if event, err := s.ScheduledMessageEventStore.Get(ctx, parsedId); err != nil { + resp.Message = err.Error() + statusCode = http.StatusInternalServerError + } else { + resp.Message = "Successful" + statusCode = http.StatusOK + resp.Data = append(resp.Data, *event) + } + } + + RespondWithJSON(w, statusCode, &resp) +} diff --git a/internal/handler/scheduled_message_event_test.go b/internal/handler/scheduled_message_event_test.go index eb8d906..9067fcf 100644 --- a/internal/handler/scheduled_message_event_test.go +++ b/internal/handler/scheduled_message_event_test.go @@ -85,3 +85,68 @@ func TestCreateScheduledMessageEventWithMock(t *testing.T) { assert.NotEmpty(t, event.Created, "Created date should not be empty") } + +func TestGetScheduledMessageEventWithMock(t *testing.T) { + now := time.Now() + + contactStore := mock.NewMockContactStore() + messageStore := mock.NewMockMessageStore() + schMsgStore := mock.NewMockScheduledMessageStore() + schMsgEventStore := mock.NewMockScheduledMessageEventStore() + handler := NewScheduledMessageEventHandler(schMsgEventStore) + + recipientId := uuid.New() + messageId := uuid.New() + scheduledMessageId := uuid.New() + testUserId := uuid.New() + + con := contact.Contact{} + con.Id = recipientId + con.PhoneNumber = "+10123456789" + con.UserId = testUserId + + msg := message.Message{} + msg.Id = messageId + msg.Content = "Oh how the might have fallen" + msg.UserId = testUserId + + schMsg := scheduling.ScheduledMessage{} + schMsg.Id = scheduledMessageId + schMsg.UserId = testUserId + schMsg.Scheduled = now.Add(20 * time.Minute) + + event := scheduling.ScheduledMessageEvent{} + event.MessageId = msg.Id + event.RecipientId = con.Id + event.ScheduledMessageId = schMsg.Id + + ctx := t.Context() + + if err := contactStore.CreateContact(ctx, &con); err != nil { + assert.NoError(t, err, "Error creating contact: %v", err) + } else if err = messageStore.CreateMessage(ctx, &msg); err != nil { + assert.NoError(t, err, "Error creating message: %v", err) + } else if err = schMsgStore.CreateScheduledMessage(ctx, &schMsg); err != nil { + assert.NoError(t, err, "Error creating scheduled message: %v", err) + } else if err = schMsgEventStore.CreateScheduledMessageEvent(ctx, &event); err != nil { + assert.NoError(t, err, "Error creating scheduled message event: %v", err) + } + + endpointValue := strings.Replace(endpoint.GetScheduledMessageEventEndpoint, "{id}", event.Id.String(), 1) + req, _ := http.NewRequest("GET", endpointValue, nil) + rr := httptest.NewRecorder() + + handler.GetScheduledMessageEvent(rr, req) + + assert.Equal(t, http.StatusOK, rr.Code) + + var response GetScheduledMessageEventResponse + err := json.Unmarshal(rr.Body.Bytes(), &response) + assert.NoError(t, err, "Error creating event %v", err) + + assert.NotEmpty(t, response.Data, "No event created") + + msgEvent := response.Data[0] + + assert.NotEmpty(t, msgEvent.Created, "Created date should not be empty") +} diff --git a/internal/store/mock/mock_store.go b/internal/store/mock/mock_store.go index 93200d1..d0c8323 100644 --- a/internal/store/mock/mock_store.go +++ b/internal/store/mock/mock_store.go @@ -3,9 +3,7 @@ package mock import ( "context" "errors" - "fmt" "sync" - "time" "github.com/google/uuid" @@ -240,62 +238,3 @@ func (m *MockScheduledMessageStore) CreateScheduledMessage(ctx context.Context, m.ScheduledMessagesByKey[key] = schedMsg return nil } - -type ScheduledMessageEventKey struct { - RecipientId uuid.UUID - MessageId uuid.UUID - ScheduledMessageId uuid.UUID -} - -type MockScheduledMessageEventStore struct { - ScheduledMessageEvents map[uuid.UUID]*scheduling.ScheduledMessageEvent - ScheduledMessageEventsByKey map[ScheduledMessageEventKey]*scheduling.ScheduledMessageEvent - mu sync.RWMutex - Error error // Optional: simulate errors -} - -func NewMockScheduledMessageEventStore() *MockScheduledMessageEventStore { - return &MockScheduledMessageEventStore{ - ScheduledMessageEvents: make(map[uuid.UUID]*scheduling.ScheduledMessageEvent), - ScheduledMessageEventsByKey: make(map[ScheduledMessageEventKey]*scheduling.ScheduledMessageEvent), - } -} - -func (m *MockScheduledMessageEventStore) CreateScheduledMessageEvent(ctx context.Context, event *scheduling.ScheduledMessageEvent) error { - m.mu.Lock() - defer m.mu.Unlock() - - if m.Error != nil { - return m.Error - } - - if event.Id == uuid.Nil { - event.Id = uuid.New() - } - - key := ScheduledMessageEventKey{RecipientId: event.RecipientId, MessageId: event.MessageId, ScheduledMessageId: event.ScheduledMessageId} - - if _, exists := m.ScheduledMessageEventsByKey[key]; exists { - return fmt.Errorf("Already exists") - } else { - event.Created = time.Now() - m.ScheduledMessageEvents[event.Id] = event - m.ScheduledMessageEventsByKey[key] = event - return nil - } - -} - -func (m *MockScheduledMessageEventStore) Exists(ctx context.Context, event *scheduling.ScheduledMessageEvent) (bool, error) { - m.mu.Lock() - defer m.mu.Unlock() - - if m.Error != nil { - return false, m.Error - } - - key := ScheduledMessageEventKey{RecipientId: event.RecipientId, MessageId: event.MessageId, ScheduledMessageId: event.ScheduledMessageId} - _, exists := m.ScheduledMessageEventsByKey[key] - - return exists, nil -} diff --git a/internal/store/mock/scheduled_message_event_store.go b/internal/store/mock/scheduled_message_event_store.go new file mode 100644 index 0000000..60e287c --- /dev/null +++ b/internal/store/mock/scheduled_message_event_store.go @@ -0,0 +1,90 @@ +package mock + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/google/uuid" + + "git.kundeng.us/phoenix/textsender-models/pkg/message/scheduling" +) + +type ScheduledMessageEventKey struct { + RecipientId uuid.UUID + MessageId uuid.UUID + ScheduledMessageId uuid.UUID +} + +type MockScheduledMessageEventStore struct { + ScheduledMessageEvents map[uuid.UUID]*scheduling.ScheduledMessageEvent + ScheduledMessageEventsByKey map[ScheduledMessageEventKey]*scheduling.ScheduledMessageEvent + mu sync.RWMutex + Error error // Optional: simulate errors +} + +func NewMockScheduledMessageEventStore() *MockScheduledMessageEventStore { + return &MockScheduledMessageEventStore{ + ScheduledMessageEvents: make(map[uuid.UUID]*scheduling.ScheduledMessageEvent), + ScheduledMessageEventsByKey: make(map[ScheduledMessageEventKey]*scheduling.ScheduledMessageEvent), + } +} + +func (m *MockScheduledMessageEventStore) Get(ctx context.Context, id uuid.UUID) (*scheduling.ScheduledMessageEvent, 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") + } + + if _, exists := m.ScheduledMessageEvents[id]; exists { + return m.ScheduledMessageEvents[id], nil + } else { + fmt.Println("Not found") + return nil, fmt.Errorf("Not found") + } +} + +func (m *MockScheduledMessageEventStore) CreateScheduledMessageEvent(ctx context.Context, event *scheduling.ScheduledMessageEvent) error { + m.mu.Lock() + defer m.mu.Unlock() + + if m.Error != nil { + return m.Error + } + + if event.Id == uuid.Nil { + event.Id = uuid.New() + } + + key := ScheduledMessageEventKey{RecipientId: event.RecipientId, MessageId: event.MessageId, ScheduledMessageId: event.ScheduledMessageId} + + if _, exists := m.ScheduledMessageEventsByKey[key]; exists { + return fmt.Errorf("Already exists") + } else { + event.Created = time.Now() + m.ScheduledMessageEvents[event.Id] = event + m.ScheduledMessageEventsByKey[key] = event + return nil + } +} + +func (m *MockScheduledMessageEventStore) Exists(ctx context.Context, event *scheduling.ScheduledMessageEvent) (bool, error) { + m.mu.Lock() + defer m.mu.Unlock() + + if m.Error != nil { + return false, m.Error + } + + key := ScheduledMessageEventKey{RecipientId: event.RecipientId, MessageId: event.MessageId, ScheduledMessageId: event.ScheduledMessageId} + _, exists := m.ScheduledMessageEventsByKey[key] + + return exists, nil +} diff --git a/internal/store/scheduled_message_event_store.go b/internal/store/scheduled_message_event_store.go index 297b9d6..ce2ab26 100644 --- a/internal/store/scheduled_message_event_store.go +++ b/internal/store/scheduled_message_event_store.go @@ -4,12 +4,15 @@ 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 ScheduledMessageEventStore interface { + Get(ctx context.Context, id uuid.UUID) (*scheduling.ScheduledMessageEvent, error) CreateScheduledMessageEvent(ctx context.Context, event *scheduling.ScheduledMessageEvent) error Exists(ctx context.Context, event *scheduling.ScheduledMessageEvent) (bool, error) } @@ -22,6 +25,24 @@ func NewScheduledMessageEventStore(db *pgxpool.Pool) *PGScheduledMessageEventSto return &PGScheduledMessageEventStore{db: db} } +func (s *PGScheduledMessageEventStore) Get(ctx context.Context, id uuid.UUID) (*scheduling.ScheduledMessageEvent, error) { + query := `SELECT id, recipient_id, message_id, scheduled_message_id, created FROM scheduled_message_events WHERE id = $1` + + var event scheduling.ScheduledMessageEvent + err := s.db.QueryRow(ctx, query, id).Scan( + &event.Id, &event.RecipientId, &event.MessageId, &event.ScheduledMessageId, &event.Created, + ) + + if err == pgx.ErrNoRows { + return nil, nil + } + if err != nil { + return nil, fmt.Errorf("getting scheduled message event by ID: %w", err) + } + + return &event, nil +} + func (s *PGScheduledMessageEventStore) CreateScheduledMessageEvent(ctx context.Context, event *scheduling.ScheduledMessageEvent) error { query := ` INSERT INTO scheduled_message_events (recipient_id, message_id, scheduled_message_id)