diff --git a/cmd/api/main.go b/cmd/api/main.go index 6a7e0a6..01c0885 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -119,6 +119,7 @@ func main() { router.Method("GET", endpoint.GetScheduledMessageEndpoint, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(scheduledMessageHandler.GetScheduledMessage))) router.Method("GET", endpoint.FetchNextScheduledMessageEndpoint, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(scheduledMessageHandler.FetchNextMessage))) router.Method("POST", endpoint.RecordEventResponse, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(eventHandler.RecordResponse))) + router.Method("GET", endpoint.FetchMessageEventResponse, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(eventHandler.Fetch))) router.Method("POST", endpoint.SendInstantMessageEndpoint, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(instantMessageHandler.Send))) router.Method("GET", "/swagger/*", httpSwagger.Handler( diff --git a/cmd/api/main_test.go b/cmd/api/main_test.go index d7f734f..953fff1 100644 --- a/cmd/api/main_test.go +++ b/cmd/api/main_test.go @@ -3,7 +3,6 @@ package main import ( "context" "flag" - "fmt" "log" "net/http" "os" @@ -30,8 +29,8 @@ func TestMain(m *testing.M) { database, err := db.NewDatabase(cfg.GetDBConnString()) if err != nil { - fmt.Println(err.Error()) - panic("Failed to initialize database") + log.Println(err.Error()) + log.Fatal("Failed to initialize database") } defer database.Close() @@ -39,18 +38,18 @@ func TestMain(m *testing.M) { if cfg.ResetDB { if err = database.ResetDatabase(ctx); err != nil { - fmt.Println(err.Error()) - panic("Failed to initialize database") + log.Println(err.Error()) + log.Fatalln("Failed to initialize database") } } else { if exists, err := db.TableExists(ctx, database.Pool, "contacts "); err == nil && !exists { - fmt.Println("Resetting database") + log.Println("Resetting database") err = database.ResetDatabase(ctx) if err != nil { - fmt.Printf("Error:%v", err) + log.Println("Error:", err) } } else { - fmt.Printf("Error:%v", err) + log.Println("Error:", err) } } @@ -68,6 +67,7 @@ func TestMain(m *testing.M) { scheduledMessageEventHandler := handler.NewScheduledMessageEventHandler(apiApp, schMsgEventStore, schStore) scheduledMessageStatusHandler := handler.NewScheduledMessageStatusHandler(apiApp, schMsgEventStore, schStore) eventHandler := handler.NewEventResponseHandler(apiApp, merStore) + instantMessageHandler := handler.NewSendInstantMessageHandler(apiApp, merStore, contactStore, messageStore) testRouter = chi.NewRouter() testRouter.Handle(endpoint.ADD_CONTACT_ENDPOINT, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(contactHandler.AddContact))) @@ -81,6 +81,8 @@ func TestMain(m *testing.M) { testRouter.Method("GET", endpoint.GetScheduledMessageEndpoint, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(scheduledMessageHandler.GetScheduledMessage))) testRouter.Method("GET", endpoint.FetchNextScheduledMessageEndpoint, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(scheduledMessageHandler.FetchNextMessage))) testRouter.Method("POST", endpoint.RecordEventResponse, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(eventHandler.RecordResponse))) + testRouter.Method("GET", endpoint.FetchMessageEventResponse, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(eventHandler.Fetch))) + testRouter.Method("POST", endpoint.SendInstantMessageEndpoint, mdlware.AuthMiddleware(jwtService)(http.HandlerFunc(instantMessageHandler.Send))) code := m.Run() os.Exit(code) diff --git a/docs/docs.go b/docs/docs.go index 39cbb99..a5cbb4c 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -490,6 +490,106 @@ const docTemplate = `{ } } }, + "/schedule/message/event/response": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Fetches a MessageEventResponse given a user id (requires JWT)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "message" + ], + "summary": "Fetcht MessageEventResponse", + "parameters": [ + { + "type": "string", + "description": "User Id", + "name": "user_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.GetMessageEventResponseFetchedResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handler.GetMessageEventResponseFetchedResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handler.GetMessageEventResponseFetchedResponse" + } + } + } + } + }, + "/schedule/message/event/response/record": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Saves the result of sending a text message (requires JWT)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "message" + ], + "summary": "Record MessageEventResponse", + "parameters": [ + { + "description": "Data to add MessageEventResponse", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handler.RecordEventRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/handler.RecordEventResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handler.RecordEventResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handler.RecordEventResponse" + } + } + } + } + }, "/schedule/message/event/{id}": { "delete": { "security": [ @@ -642,9 +742,18 @@ const docTemplate = `{ "contact.Contact": { "type": "object", "properties": { + "first_name": { + "type": "string" + }, "id": { "type": "string" }, + "last_name": { + "type": "string" + }, + "nickname": { + "type": "string" + }, "phone_number": { "type": "string" }, @@ -653,6 +762,36 @@ const docTemplate = `{ } } }, + "event.MessageEventResponse": { + "type": "object", + "properties": { + "contact_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "message_id": { + "type": "string" + }, + "response": { + "type": "object", + "additionalProperties": {} + }, + "scheduled_message_event_id": { + "type": "string" + }, + "sent": { + "type": "string" + }, + "status": { + "type": "string" + }, + "user_id": { + "type": "string" + } + } + }, "handler.AddContactResponse": { "type": "object", "properties": { @@ -751,6 +890,20 @@ const docTemplate = `{ } } }, + "handler.GetMessageEventResponseFetchedResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/event.MessageEventResponse" + } + }, + "message": { + "type": "string" + } + } + }, "handler.GetMessageResponse": { "type": "object", "properties": { @@ -793,6 +946,47 @@ const docTemplate = `{ } } }, + "handler.RecordEventRequest": { + "type": "object", + "properties": { + "contact_id": { + "type": "string" + }, + "message_id": { + "type": "string" + }, + "response": { + "type": "object", + "additionalProperties": {} + }, + "scheduled_message_event_id": { + "type": "string" + }, + "sent": { + "type": "string" + }, + "status": { + "type": "string" + }, + "user_id": { + "type": "string" + } + } + }, + "handler.RecordEventResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/event.MessageEventResponse" + } + }, + "message": { + "type": "string" + } + } + }, "handler.RequestAddContact": { "type": "object", "properties": { @@ -832,10 +1026,10 @@ const docTemplate = `{ "handler.RequestAddScheduledMessageEvent": { "type": "object", "properties": { - "message_id": { + "contact_id": { "type": "string" }, - "recipient_id": { + "message_id": { "type": "string" }, "scheduled_message_id": { @@ -902,7 +1096,7 @@ const docTemplate = `{ "data": { "type": "array", "items": { - "$ref": "#/definitions/message.MessageEventResponse" + "$ref": "#/definitions/event.MessageEventResponse" } }, "message": { @@ -924,29 +1118,6 @@ const docTemplate = `{ } } }, - "message.MessageEventResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "response": { - "type": "array", - "items": { - "type": "integer" - } - }, - "scheduled_message_event_id": { - "type": "string" - }, - "sent": { - "type": "string" - }, - "user_id": { - "type": "string" - } - } - }, "scheduling.ScheduledMessage": { "type": "object", "properties": { @@ -970,6 +1141,9 @@ const docTemplate = `{ "scheduling.ScheduledMessageEvent": { "type": "object", "properties": { + "contact_id": { + "type": "string" + }, "created": { "type": "string" }, @@ -979,9 +1153,6 @@ const docTemplate = `{ "message_id": { "type": "string" }, - "recipient_id": { - "type": "string" - }, "scheduled_message_id": { "type": "string" } diff --git a/docs/swagger.json b/docs/swagger.json index 7dddc2b..52f8d48 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -484,6 +484,106 @@ } } }, + "/schedule/message/event/response": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Fetches a MessageEventResponse given a user id (requires JWT)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "message" + ], + "summary": "Fetcht MessageEventResponse", + "parameters": [ + { + "type": "string", + "description": "User Id", + "name": "user_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.GetMessageEventResponseFetchedResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handler.GetMessageEventResponseFetchedResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handler.GetMessageEventResponseFetchedResponse" + } + } + } + } + }, + "/schedule/message/event/response/record": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Saves the result of sending a text message (requires JWT)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "message" + ], + "summary": "Record MessageEventResponse", + "parameters": [ + { + "description": "Data to add MessageEventResponse", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handler.RecordEventRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/handler.RecordEventResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handler.RecordEventResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handler.RecordEventResponse" + } + } + } + } + }, "/schedule/message/event/{id}": { "delete": { "security": [ @@ -636,9 +736,18 @@ "contact.Contact": { "type": "object", "properties": { + "first_name": { + "type": "string" + }, "id": { "type": "string" }, + "last_name": { + "type": "string" + }, + "nickname": { + "type": "string" + }, "phone_number": { "type": "string" }, @@ -647,6 +756,36 @@ } } }, + "event.MessageEventResponse": { + "type": "object", + "properties": { + "contact_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "message_id": { + "type": "string" + }, + "response": { + "type": "object", + "additionalProperties": {} + }, + "scheduled_message_event_id": { + "type": "string" + }, + "sent": { + "type": "string" + }, + "status": { + "type": "string" + }, + "user_id": { + "type": "string" + } + } + }, "handler.AddContactResponse": { "type": "object", "properties": { @@ -745,6 +884,20 @@ } } }, + "handler.GetMessageEventResponseFetchedResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/event.MessageEventResponse" + } + }, + "message": { + "type": "string" + } + } + }, "handler.GetMessageResponse": { "type": "object", "properties": { @@ -787,6 +940,47 @@ } } }, + "handler.RecordEventRequest": { + "type": "object", + "properties": { + "contact_id": { + "type": "string" + }, + "message_id": { + "type": "string" + }, + "response": { + "type": "object", + "additionalProperties": {} + }, + "scheduled_message_event_id": { + "type": "string" + }, + "sent": { + "type": "string" + }, + "status": { + "type": "string" + }, + "user_id": { + "type": "string" + } + } + }, + "handler.RecordEventResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/event.MessageEventResponse" + } + }, + "message": { + "type": "string" + } + } + }, "handler.RequestAddContact": { "type": "object", "properties": { @@ -826,10 +1020,10 @@ "handler.RequestAddScheduledMessageEvent": { "type": "object", "properties": { - "message_id": { + "contact_id": { "type": "string" }, - "recipient_id": { + "message_id": { "type": "string" }, "scheduled_message_id": { @@ -896,7 +1090,7 @@ "data": { "type": "array", "items": { - "$ref": "#/definitions/message.MessageEventResponse" + "$ref": "#/definitions/event.MessageEventResponse" } }, "message": { @@ -918,29 +1112,6 @@ } } }, - "message.MessageEventResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "response": { - "type": "array", - "items": { - "type": "integer" - } - }, - "scheduled_message_event_id": { - "type": "string" - }, - "sent": { - "type": "string" - }, - "user_id": { - "type": "string" - } - } - }, "scheduling.ScheduledMessage": { "type": "object", "properties": { @@ -964,6 +1135,9 @@ "scheduling.ScheduledMessageEvent": { "type": "object", "properties": { + "contact_id": { + "type": "string" + }, "created": { "type": "string" }, @@ -973,9 +1147,6 @@ "message_id": { "type": "string" }, - "recipient_id": { - "type": "string" - }, "scheduled_message_id": { "type": "string" } diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 9c5f966..6ebfb30 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -2,13 +2,39 @@ basePath: /api/v1 definitions: contact.Contact: properties: + first_name: + type: string id: type: string + last_name: + type: string + nickname: + type: string phone_number: type: string user_id: type: string type: object + event.MessageEventResponse: + properties: + contact_id: + type: string + id: + type: string + message_id: + type: string + response: + additionalProperties: {} + type: object + scheduled_message_event_id: + type: string + sent: + type: string + status: + type: string + user_id: + type: string + type: object handler.AddContactResponse: properties: data: @@ -72,6 +98,15 @@ definitions: message: type: string type: object + handler.GetMessageEventResponseFetchedResponse: + properties: + data: + items: + $ref: '#/definitions/event.MessageEventResponse' + type: array + message: + type: string + type: object handler.GetMessageResponse: properties: data: @@ -99,6 +134,33 @@ definitions: message: type: string type: object + handler.RecordEventRequest: + properties: + contact_id: + type: string + message_id: + type: string + response: + additionalProperties: {} + type: object + scheduled_message_event_id: + type: string + sent: + type: string + status: + type: string + user_id: + type: string + type: object + handler.RecordEventResponse: + properties: + data: + items: + $ref: '#/definitions/event.MessageEventResponse' + type: array + message: + type: string + type: object handler.RequestAddContact: properties: phone_number: @@ -124,9 +186,9 @@ definitions: type: object handler.RequestAddScheduledMessageEvent: properties: - message_id: + contact_id: type: string - recipient_id: + message_id: type: string scheduled_message_id: type: string @@ -169,7 +231,7 @@ definitions: properties: data: items: - $ref: '#/definitions/message.MessageEventResponse' + $ref: '#/definitions/event.MessageEventResponse' type: array message: type: string @@ -183,21 +245,6 @@ definitions: user_id: type: string type: object - message.MessageEventResponse: - properties: - id: - type: string - response: - items: - type: integer - type: array - scheduled_message_event_id: - type: string - sent: - type: string - user_id: - type: string - type: object scheduling.ScheduledMessage: properties: created: @@ -213,14 +260,14 @@ definitions: type: object scheduling.ScheduledMessageEvent: properties: + contact_id: + type: string created: type: string id: type: string message_id: type: string - recipient_id: - type: string scheduled_message_id: type: string type: object @@ -569,6 +616,69 @@ paths: summary: Delete scheduled message event tags: - scheduled message events + /schedule/message/event/response: + get: + consumes: + - application/json + description: Fetches a MessageEventResponse given a user id (requires JWT) + parameters: + - description: User Id + in: path + name: user_id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/handler.GetMessageEventResponseFetchedResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/handler.GetMessageEventResponseFetchedResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/handler.GetMessageEventResponseFetchedResponse' + security: + - BearerAuth: [] + summary: Fetcht MessageEventResponse + tags: + - message + /schedule/message/event/response/record: + post: + consumes: + - application/json + description: Saves the result of sending a text message (requires JWT) + parameters: + - description: Data to add MessageEventResponse + in: body + name: request + required: true + schema: + $ref: '#/definitions/handler.RecordEventRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/handler.RecordEventResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/handler.RecordEventResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/handler.RecordEventResponse' + security: + - BearerAuth: [] + summary: Record MessageEventResponse + tags: + - message /schedule/message/fetch: get: consumes: diff --git a/go.mod b/go.mod index 51f4ae7..cda6632 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module git.kundeng.us/phoenix/textsender-api go 1.25.4 require ( - git.kundeng.us/phoenix/swoosh v0.0.7-8-523c7cf67c-556 - git.kundeng.us/phoenix/textsender-models v0.0.12 + git.kundeng.us/phoenix/swoosh v0.1.0 + git.kundeng.us/phoenix/textsender-models v0.1.6 github.com/go-chi/chi/v5 v5.2.3 github.com/go-chi/cors v1.2.2 github.com/golang-jwt/jwt/v5 v5.3.0 diff --git a/go.sum b/go.sum index 848543d..1091a14 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ -git.kundeng.us/phoenix/swoosh v0.0.7-8-523c7cf67c-556 h1:vzTrhx7auc8OlUGmfTDektHXCkoFOKzVhdsW+6oKXMI= -git.kundeng.us/phoenix/swoosh v0.0.7-8-523c7cf67c-556/go.mod h1:OAh9jVBQ3vRJ1EHTM6pFyWd9eXf1H+CevbDKkJuoDZU= -git.kundeng.us/phoenix/textsender-models v0.0.12 h1:ps9H3FS5LyCwQhAiIvg4vYyfLZZ64dex1y9ytb0o9C4= -git.kundeng.us/phoenix/textsender-models v0.0.12/go.mod h1:9iPDQJg1Tc6WMNoW5+f8YKmnosMwlWHJ++hmxNLDEe0= +git.kundeng.us/phoenix/swoosh v0.1.0 h1:9P+wLuJsIZmVcRoG+W22l06hwp54eNrYbsBtGjucnjI= +git.kundeng.us/phoenix/swoosh v0.1.0/go.mod h1:6/97/aS8KVdYqlczQ9ALvS3GVoce1UVl+D8VQK42Bd0= +git.kundeng.us/phoenix/textsender-models v0.1.6 h1:d/rhvqjFSt1Ty/3UfwS21be+lYb9EtWEaDtkzfbzEp0= +git.kundeng.us/phoenix/textsender-models v0.1.6/go.mod h1:9iPDQJg1Tc6WMNoW5+f8YKmnosMwlWHJ++hmxNLDEe0= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= diff --git a/internal/app/app.go b/internal/app/app.go index f2ef963..9948d51 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -1,9 +1,9 @@ package app import ( - "git.kundeng.us/phoenix/textsender-models/tx0/config" + auxcfg "git.kundeng.us/phoenix/textsender-models/tx0/config/auxiliary" ) type App struct { - TwilioConfig *config.TwilioConfig + TwilioConfig *auxcfg.TwilioConfig } diff --git a/internal/config/config.go b/internal/config/config.go index f330faa..d23f4dc 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -7,7 +7,7 @@ import ( "strconv" "strings" - "git.kundeng.us/phoenix/textsender-models/tx0/config" + auxcfg "git.kundeng.us/phoenix/textsender-models/tx0/config/auxiliary" "github.com/joho/godotenv" "git.kundeng.us/phoenix/textsender-api/internal/version" @@ -18,7 +18,7 @@ type Config struct { ServerPort string ResetDB bool JWTSecret string `env:"JWT_SECRET" required:"true"` - TwilioConfig *config.TwilioConfig + TwilioConfig *auxcfg.TwilioConfig AllowedOrigins []string } @@ -49,7 +49,7 @@ func PrintName() { fmt.Println(version.String()) } -func Load() (*Config, *config.TwilioConfig, error) { +func Load() (*Config, *auxcfg.TwilioConfig, error) { versionFlag := flag.Bool("version", false, "Print version information") resetDb := flag.Bool("reset-db", false, "Reset the database schema and exit") port := flag.String("port", PORT, "Server port") @@ -138,7 +138,7 @@ func unpackAllowedOrigins() []string { return allowedOriginsSplit } -func TwilioConfig() (*config.TwilioConfig, error) { +func TwilioConfig() (*auxcfg.TwilioConfig, error) { authSid := os.Getenv("TWILIO_AUTH_SID") serviceSid := os.Getenv("TWILIO_SERVICE_SID") authToken := os.Getenv("TWILIO_AUTH_TOKEN") @@ -153,7 +153,7 @@ func TwilioConfig() (*config.TwilioConfig, error) { } else if len(phoneNumber) == 0 { return nil, fmt.Errorf("Twilio config phone number not found") } else { - var cfg config.TwilioConfig + var cfg auxcfg.TwilioConfig cfg.AccountSID = authSid cfg.ServiceSID = serviceSid cfg.AuthToken = authToken diff --git a/internal/handler/contact_test.go b/internal/handler/contact_test.go index 1ee4388..a132d85 100644 --- a/internal/handler/contact_test.go +++ b/internal/handler/contact_test.go @@ -1,7 +1,6 @@ package handler import ( - "context" "encoding/json" "fmt" "net/http" @@ -13,7 +12,6 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" - "git.kundeng.us/phoenix/textsender-api/internal/db" "git.kundeng.us/phoenix/textsender-api/internal/handler/endpoint" "git.kundeng.us/phoenix/textsender-api/internal/store/mock" ) @@ -80,11 +78,3 @@ func TestGetContactWithMock(t *testing.T) { assert.NotNil(t, response.Data[0].Id, "Id should not be nil") } - -func resetTestDB(t *testing.T) { - t.Helper() - _, err := db.Pool.Exec(context.Background(), "DELETE FROM contacts") - if err != nil { - t.Fatalf("Failed to reset test database: %v", err) - } -} diff --git a/internal/handler/endpoint/endpoint.go b/internal/handler/endpoint/endpoint.go index 03ec1ff..1c3676d 100644 --- a/internal/handler/endpoint/endpoint.go +++ b/internal/handler/endpoint/endpoint.go @@ -1,16 +1,28 @@ package endpoint -const MESSAGE_DRAFT_ENDPOINT = "/api/v1/message/draft" -const ADD_MESSAGE = "/api/v1/message/new" -const GET_MESSAGE = "/api/v1/message" -const GET_CONTACT = "/api/v1/contact" -const ADD_CONTACT_ENDPOINT = "/api/v1/contact/new" -const ScheduleMessageEndpoint = "/api/v1/schedule/message" -const GetScheduledMessageEndpoint = "/api/v1/schedule/message" -const AddEventToScheduledMessageEndpoint = "/api/v1/schedule/message/event" -const GetScheduledMessageEventEndpoint = "/api/v1/schedule/message/event" -const DeleteScheduledMessageEventEndpoint = "/api/v1/schedule/message/event/{id}" -const UpdateScheduledMessageStatusEndpoint = "/api/v1/schedule/message/status/update" -const FetchNextScheduledMessageEndpoint = "/api/v1/schedule/message/fetch" -const RecordEventResponse = "/api/v1/schedule/message/event/response/record" -const SendInstantMessageEndpoint = "/api/v1/instant/message" +const ( + MESSAGE_DRAFT_ENDPOINT = "/api/v1/message/draft" + + ADD_MESSAGE = "/api/v1/message/new" + GET_MESSAGE = "/api/v1/message" + + GET_CONTACT = "/api/v1/contact" + ADD_CONTACT_ENDPOINT = "/api/v1/contact/new" + + ScheduleMessageEndpoint = "/api/v1/schedule/message" + GetScheduledMessageEndpoint = "/api/v1/schedule/message" + + AddEventToScheduledMessageEndpoint = "/api/v1/schedule/message/event" + GetScheduledMessageEventEndpoint = "/api/v1/schedule/message/event" + DeleteScheduledMessageEventEndpoint = "/api/v1/schedule/message/event/{id}" + UpdateScheduledMessageStatusEndpoint = "/api/v1/schedule/message/status/update" + + FetchNextScheduledMessageEndpoint = "/api/v1/schedule/message/fetch" + + // TODO: Tweak endpoint verbage for MessageEventResponse endpoint + // TODO: Make RecordEventResponse more consistent with MessageEventResponse + RecordEventResponse = "/api/v1/schedule/message/event/response/record" + FetchMessageEventResponse = "/api/v1/schedule/message/event/response" + + SendInstantMessageEndpoint = "/api/v1/instant/message" +) diff --git a/internal/handler/helper_test.go b/internal/handler/helper_test.go index a980d0c..81ca2ea 100644 --- a/internal/handler/helper_test.go +++ b/internal/handler/helper_test.go @@ -1,14 +1,23 @@ package handler import ( + "context" + "encoding/json" "fmt" "os" "path" + "testing" + "time" + "git.kundeng.us/phoenix/textsender-models/tx0/contact" + "git.kundeng.us/phoenix/textsender-models/tx0/message" + "git.kundeng.us/phoenix/textsender-models/tx0/message/scheduling" + "github.com/google/uuid" "github.com/joho/godotenv" "git.kundeng.us/phoenix/textsender-api/internal/app" "git.kundeng.us/phoenix/textsender-api/internal/config" + "git.kundeng.us/phoenix/textsender-api/internal/db" ) func GetApp() (*app.App, error) { @@ -29,3 +38,74 @@ func GetApp() (*app.App, error) { return &app.App{TwilioConfig: tCfg}, nil } } + +func ResetTestDB(t *testing.T, tableName string) { + t.Helper() + _, err := db.Pool.Exec(context.Background(), fmt.Sprintf("DELETE FROM %s", tableName)) + if err != nil { + t.Fatalf("Failed to reset test database: %v", err) + } +} + +func ContactTest(id uuid.UUID, userId uuid.UUID) contact.Contact { + if id == uuid.Nil { + id = uuid.New() + return contact.Contact{Id: &id, PhoneNumber: "+10123456789", UserId: &userId} + } else { + return contact.Contact{Id: &id, PhoneNumber: "+10123456789", UserId: &userId} + } +} + +func MessageTest(id uuid.UUID, userId uuid.UUID) message.Message { + if id == uuid.Nil { + id = uuid.New() + return message.Message{Id: &id, Content: "Oh how the mighty have fallen", UserId: &userId} + } else { + return message.Message{Id: &id, Content: "Oh how the mighty have fallen", UserId: &userId} + } +} + +func ScheduledMessageTest(id uuid.UUID, userId uuid.UUID, now time.Time) scheduling.ScheduledMessage { + if id == uuid.Nil { + return scheduling.ScheduledMessage{Id: uuid.New(), UserId: userId, Scheduled: now.Add(20 * time.Minute), Status: scheduling.Pending} + } else { + return scheduling.ScheduledMessage{Id: id, UserId: userId, Scheduled: now.Add(20 * time.Minute), Status: scheduling.Pending} + } +} + +func ScheduledMessageEventTest(messageId, contactId, scheduledMessageId uuid.UUID) scheduling.ScheduledMessageEvent { + return scheduling.ScheduledMessageEvent{MessageId: messageId, ContactId: contactId, ScheduledMessageId: scheduledMessageId} +} + +func MERResponseInString() string { + return `{ + "body": "Whoknows?", + "num_segments": "0", + "direction": "outbound-api", + "from": "+12243026041", + "to": "+16303831708", + "date_updated": "Sat,29Nov202519:06:59+0000", + "uri": "/2010-04-01/Accounts/ACefa1ef516314c9d1a68cbd657de49277/Messages/SM1193a529e7f7a840667cd1e0f13ea95a.json", + "account_sid": "ACefa1ef516314c9d1a68cbd657de49277", + "num_media": "0", + "status": "scheduled", + "messaging_service_sid": "MG803f3676706b92eb02e18dd820c447f2", + "sid": "SM1193a529e7f7a840667cd1e0f13ea95a", + "date_created": "Sat,29Nov202519:06:59+0000", + "api_version": "2010-04-01", + "subresource_uris": { + "media": "/2010-04-01/Accounts/ACefa1ef516314c9d1a68cbd657de49277/Messages/SM1193a529e7f7a840667cd1e0f13ea95a/Media.json" + } +}` +} + +func ConvertStringToMapOfAny(response string) (map[string]any, error) { + bytes := []byte(response) + var merResponse map[string]any + err := json.Unmarshal(bytes, &merResponse) + if err != nil { + return nil, err + } else { + return merResponse, nil + } +} diff --git a/internal/handler/instant_message.go b/internal/handler/instant_message.go index 3160d4b..46ad663 100644 --- a/internal/handler/instant_message.go +++ b/internal/handler/instant_message.go @@ -2,7 +2,6 @@ package handler import ( "context" - "encoding/json" "fmt" "log" "net/http" @@ -12,6 +11,7 @@ import ( "git.kundeng.us/phoenix/swoosh/swoop/send" "git.kundeng.us/phoenix/textsender-models/tx0/contact" "git.kundeng.us/phoenix/textsender-models/tx0/message" + "git.kundeng.us/phoenix/textsender-models/tx0/message/event" "github.com/google/uuid" "git.kundeng.us/phoenix/textsender-api/internal/app" @@ -38,8 +38,8 @@ type SendInstantMessageRequest struct { } type SendInstantMessageResponse struct { - Message string `json:"message"` - Data []*message.MessageEventResponse `json:"data"` + Message string `json:"message"` + Data []*event.MessageEventResponse `json:"data"` } // Sent godoc @@ -93,34 +93,29 @@ func (s *SendInstantMessageHandler) Send(w http.ResponseWriter, r *http.Request) if s.App != nil { msgSender := send.MessageSender{Config: s.App.TwilioConfig} for _, c := range contacts { - if twilioResp, err := msgSender.Send(*msg, *c, nil); err != nil { + if twilioResp, twilioRespObject, err := msgSender.Send(*msg, *c, nil); err != nil { statusCode = http.StatusInternalServerError resp.Message = err.Error() } else { - if data, err := json.Marshal(twilioResp); err != nil { + dateStr := strings.TrimSpace(*twilioResp.DateUpdated) + if parsedTime, err := time.Parse(time.RFC1123Z, dateStr); err != nil { statusCode = http.StatusInternalServerError resp.Message = err.Error() } else { - dateStr := strings.TrimSpace(*twilioResp.DateUpdated) - if parsedTime, err := time.Parse(time.RFC1123Z, dateStr); err != nil { + mer := event.MessageEventResponse{} + mer.Response = twilioRespObject + mer.UserId = req.UserId + mer.Sent = parsedTime + mer.Status = event.Message_Event_Response_Status_Instant + mer.ContactId = c.Id + mer.MessageId = msg.Id + if err := s.MERStore.Create(ctx, &mer); err != nil { statusCode = http.StatusInternalServerError resp.Message = err.Error() } else { - mer := message.MessageEventResponse{} - mer.Response = data - mer.UserId = req.UserId - mer.Sent = parsedTime - mer.Status = message.Message_Event_Response_Status_Instant - mer.ContactId = c.Id - mer.MessageId = &msg.Id - if err := s.MERStore.Create(ctx, &mer); err != nil { - statusCode = http.StatusInternalServerError - resp.Message = err.Error() - } else { - statusCode = http.StatusOK - resp.Message = "Successful" - resp.Data = append(resp.Data, &mer) - } + statusCode = http.StatusOK + resp.Message = "Successful" + resp.Data = append(resp.Data, &mer) } } } diff --git a/internal/handler/message.go b/internal/handler/message.go index 8211e86..0d2cd06 100644 --- a/internal/handler/message.go +++ b/internal/handler/message.go @@ -12,16 +12,6 @@ import ( "git.kundeng.us/phoenix/textsender-api/internal/store" ) -type RequestAddMessage struct { - Content string `json:"content"` - UserId uuid.UUID `json:"user_id"` -} - -type AddMessageResponse struct { - Message string `json:"message"` - Data []message.Message `json:"data"` -} - type MessageHandler struct { App *app.App MessageStore store.MessageStore @@ -33,6 +23,16 @@ func NewMessageHandler(apiApp *app.App, str store.MessageStore) *MessageHandler const Message_Limit = 200 +type RequestAddMessage struct { + Content string `json:"content"` + UserId uuid.UUID `json:"user_id"` +} + +type AddMessageResponse struct { + Message string `json:"message"` + Data []message.Message `json:"data"` +} + // AddMessage godoc // @Summary Add message // @Description Add a message record to send a text to (requires JWT) @@ -46,11 +46,6 @@ const Message_Limit = 200 // @Failure 500 {object} AddMessageResponse // @Router /message/new [post] func (m *MessageHandler) AddMessage(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - var req RequestAddMessage if err := ExtractFromRequest(r, &req); err != nil { http.Error(w, "Invalid JSON: "+err.Error(), http.StatusBadRequest) @@ -59,7 +54,7 @@ func (m *MessageHandler) AddMessage(w http.ResponseWriter, r *http.Request) { var statusCode int var resp AddMessageResponse - newMessage := message.Message{Content: req.Content, UserId: req.UserId} + newMessage := message.Message{Content: req.Content, UserId: &req.UserId} if isMessageValid(&newMessage) { ctx := r.Context() @@ -160,7 +155,7 @@ func (c *MessageHandler) GetMessage(w http.ResponseWriter, r *http.Request) { if messages, err := c.MessageStore.GetAllMessages(ctx); err == nil { log.Println("Amount of messages:", len(messages)) for _, msg := range messages { - if msg.UserId == userId { + if *msg.UserId == userId { resp.Data = append(resp.Data, *msg) } } diff --git a/internal/handler/message_event_response.go b/internal/handler/message_event_response.go index cd9646b..7dc00ac 100644 --- a/internal/handler/message_event_response.go +++ b/internal/handler/message_event_response.go @@ -1,11 +1,11 @@ package handler import ( + "log" "net/http" "time" - "git.kundeng.us/phoenix/textsender-models/tx0/message" - "git.kundeng.us/phoenix/textsender-models/tx0/types" + "git.kundeng.us/phoenix/textsender-models/tx0/message/event" "github.com/google/uuid" "git.kundeng.us/phoenix/textsender-api/internal/app" @@ -22,20 +22,32 @@ func NewEventResponseHandler(apiApp *app.App, str store.MessageEventResponseStor } type RecordEventRequest struct { - ScheduledMessageEventId uuid.UUID `json:"scheduled_message_event_id"` - Response types.JSONB `json:"response"` - UserId uuid.UUID `json:"user_id"` - Sent *time.Time `json:"sent,omitempty"` - Status string `json:"status"` - ContactId *uuid.UUID `json:"contact_id"` - MessageId *uuid.UUID `json:"message_id"` + ScheduledMessageEventId uuid.UUID `json:"scheduled_message_event_id"` + Response map[string]any `json:"response"` + UserId uuid.UUID `json:"user_id"` + Sent *time.Time `json:"sent,omitempty"` + Status string `json:"status"` + ContactId *uuid.UUID `json:"contact_id"` + MessageId *uuid.UUID `json:"message_id"` } type RecordEventResponse struct { - Message string `json:"message"` - Data []*message.MessageEventResponse `json:"data"` + Message string `json:"message"` + Data []*event.MessageEventResponse `json:"data"` } +// RecordMessageEventResponse godoc +// @Summary Record MessageEventResponse +// @Description Saves the result of sending a text message (requires JWT) +// @Tags message +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param request body RecordEventRequest true "Data to add MessageEventResponse" +// @Success 201 {object} RecordEventResponse +// @Failure 400 {object} RecordEventResponse +// @Failure 500 {object} RecordEventResponse +// @Router /schedule/message/event/response/record [post] func (e *EventResponseHandler) RecordResponse(w http.ResponseWriter, r *http.Request) { var req RecordEventRequest if err := ExtractFromRequest(r, &req); err != nil { @@ -44,7 +56,7 @@ func (e *EventResponseHandler) RecordResponse(w http.ResponseWriter, r *http.Req defer r.Body.Close() var statusCode int - var rp message.MessageEventResponse + var rp event.MessageEventResponse var resp RecordEventResponse if req.Sent == nil { @@ -60,24 +72,39 @@ func (e *EventResponseHandler) RecordResponse(w http.ResponseWriter, r *http.Req statusCode = http.StatusBadRequest resp.Message = "Scheduled message event Id is empty" } else { + log.Println("Starting process to record Message Event Response") rp.ScheduledMessageEventId = req.ScheduledMessageEventId rp.Response = req.Response rp.UserId = req.UserId rp.Sent = *req.Sent - if req.Status == message.Message_Event_Response_Status_Instant { - rp.Status = req.Status - rp.MessageId = req.MessageId - rp.ContactId = req.ContactId - } else if req.Status == message.Message_Event_Response_Status_Scheduled { - rp.Status = req.Status + switch req.Status { + case event.Message_Event_Response_Status_Instant: + { + log.Println("Status:", req.Status) + rp.Status = req.Status + rp.MessageId = req.MessageId + rp.ContactId = req.ContactId + } + case event.Message_Event_Response_Status_Scheduled: + { + log.Println("Status:", req.Status) + rp.Status = req.Status + } + default: + { + statusCode = http.StatusBadRequest + resp.Message = "Status is invalid" + } } ctx := r.Context() if err := e.MessageEventResponseStore.Create(ctx, &rp); err != nil { + log.Println("Error:", err) resp.Message = err.Error() statusCode = http.StatusInternalServerError } else { + log.Println("Created Message Event Response") statusCode = http.StatusCreated resp.Message = "Successful" resp.Data = append(resp.Data, &rp) @@ -86,3 +113,62 @@ func (e *EventResponseHandler) RecordResponse(w http.ResponseWriter, r *http.Req RespondWithJSON(w, statusCode, &resp) } + +type GetMessageEventResponseFetchedResponse struct { + Message string `json:"message"` + Data []*event.MessageEventResponse `json:"data"` +} + +// FetchtMessageEventResponse godoc +// @Summary Fetcht MessageEventResponse +// @Description Fetches a MessageEventResponse given a user id (requires JWT) +// @Tags message +// @Accept json +// @Produce json +// @Param user_id path string true "User Id" +// @Security BearerAuth +// @Success 200 {object} GetMessageEventResponseFetchedResponse +// @Failure 400 {object} GetMessageEventResponseFetchedResponse +// @Failure 500 {object} GetMessageEventResponseFetchedResponse +// @Router /schedule/message/event/response [get] +func (e *EventResponseHandler) Fetch(w http.ResponseWriter, r *http.Request) { + queryParams := r.URL.Query() + var userId uuid.UUID + + if _, exists := queryParams["user_id"]; !exists { + http.Error(w, "Query params", http.StatusBadRequest) + return + } else { + var err error + userId, err = uuid.Parse(queryParams.Get("user_id")) + if err != nil { + http.Error(w, "Query params", http.StatusBadRequest) + return + } + } + + var statusCode int + var resp GetMessageEventResponseFetchedResponse + + if userId == uuid.Nil { + statusCode = http.StatusBadRequest + resp.Message = "User Id is nil" + } else { + ctx := r.Context() + if sentMessages, err := e.MessageEventResponseStore.GetWithUserId(ctx, userId); err != nil { + statusCode = http.StatusInternalServerError + resp.Message = err.Error() + } else { + if len(sentMessages) == 0 { + statusCode = http.StatusNotFound + resp.Message = "Not MessageEventResponses found" + } else { + statusCode = http.StatusOK + resp.Message = "Successful" + resp.Data = sentMessages + } + } + } + + RespondWithJSON(w, statusCode, &resp) +} diff --git a/internal/handler/message_event_response_test.go b/internal/handler/message_event_response_test.go index 210099e..0792ffa 100644 --- a/internal/handler/message_event_response_test.go +++ b/internal/handler/message_event_response_test.go @@ -2,13 +2,14 @@ package handler import ( "encoding/json" + "fmt" "net/http" "net/http/httptest" "strings" "testing" "time" - "git.kundeng.us/phoenix/textsender-models/tx0/message" + evnt "git.kundeng.us/phoenix/textsender-models/tx0/message/event" "git.kundeng.us/phoenix/textsender-models/tx0/message/scheduling" "github.com/google/uuid" "github.com/stretchr/testify/assert" @@ -30,14 +31,14 @@ func TestRecordMessageEventResponseWithMock(t *testing.T) { assert.NoError(t, err, "Error getting app") handler := NewEventResponseHandler(apiApp, merStore) - recipientId := uuid.New() + contactId := uuid.New() messageId := uuid.New() scheduledMessageId := uuid.New() testUserId := uuid.New() - con := testContact(recipientId, testUserId) - msg := testMessage(messageId, testUserId) - schMsg := testScheduledMessage(scheduledMessageId, testUserId, now) + con := ContactTest(contactId, testUserId) + msg := MessageTest(messageId, testUserId) + schMsg := ScheduledMessageTest(scheduledMessageId, testUserId, now) event := scheduling.ScheduledMessageEvent{} ctx := t.Context() @@ -50,17 +51,19 @@ func TestRecordMessageEventResponseWithMock(t *testing.T) { assert.NoError(t, err, "Error creating scheduled message: %v", err) } - event.MessageId = msg.Id - event.RecipientId = *con.Id + event.MessageId = *msg.Id + event.ContactId = *con.Id event.ScheduledMessageId = schMsg.Id if err := mockStore.CreateScheduledMessageEvent(ctx, &event); err != nil { assert.NoError(t, err, "Error creating scheduled message event: %v", err) } sent := now.Add(30 * time.Minute) - bytes := []byte("{\"body\":\"Whoknows?\",\"num_segments\":\"0\",\"direction\":\"outbound-api\",\"from\":\"+12243026041\",\"to\":\"+16303831708\",\"date_updated\":\"Sat,29Nov202519:06:59+0000\",\"uri\":\"/2010-04-01/Accounts/ACefa1ef516314c9d1a68cbd657de49277/Messages/SM1193a529e7f7a840667cd1e0f13ea95a.json\",\"account_sid\":\"ACefa1ef516314c9d1a68cbd657de49277\",\"num_media\":\"0\",\"status\":\"scheduled\",\"messaging_service_sid\":\"MG803f3676706b92eb02e18dd820c447f2\",\"sid\":\"SM1193a529e7f7a840667cd1e0f13ea95a\",\"date_created\":\"Sat,29Nov202519:06:59+0000\",\"api_version\":\"2010-04-01\",\"subresource_uris\":{\"media\":\"/2010-04-01/Accounts/ACefa1ef516314c9d1a68cbd657de49277/Messages/SM1193a529e7f7a840667cd1e0f13ea95a/Media.json\"}}") + var merResponse map[string]any + merResponse, err = ConvertStringToMapOfAny(MERResponseInString()) + assert.NoError(t, err, "Error Converting to map[string]any") - testReq := RecordEventRequest{ScheduledMessageEventId: event.Id, Response: bytes, UserId: schMsg.UserId, Sent: &sent, Status: message.Message_Event_Response_Status_Scheduled} + testReq := RecordEventRequest{ScheduledMessageEventId: event.Id, Response: merResponse, UserId: schMsg.UserId, Sent: &sent, Status: evnt.Message_Event_Response_Status_Scheduled} jsonValue, _ := json.Marshal(testReq) req, _ := http.NewRequest("POST", endpoint.RecordEventResponse, strings.NewReader(string(jsonValue))) @@ -76,9 +79,84 @@ func TestRecordMessageEventResponseWithMock(t *testing.T) { assert.NotEmpty(t, response.Data, "No message event response created") - var msgEventResp message.MessageEventResponse + var msgEventResp evnt.MessageEventResponse msgEventResp = *response.Data[0] assert.NotEmpty(t, msgEventResp.Response, "The response of the message event response is empty") assert.Equal(t, msgEventResp.Response, testReq.Response, "Responses do not match") } + +func TestGetMessageEventResponse(t *testing.T) { + now := time.Now() + + mockStore := mock.NewMockScheduledMessageEventStore() + contactStore := mock.NewMockContactStore() + messageStore := mock.NewMockMessageStore() + schMsgStore := mock.NewMockScheduledMessageStore() + merStore := mock.NewMockMessageEventResponseStore() + + apiApp, err := GetApp() + assert.NoError(t, err, "Error getting app") + handler := NewEventResponseHandler(apiApp, merStore) + + contactId := uuid.New() + messageId := uuid.New() + scheduledMessageId := uuid.New() + testUserId := uuid.New() + + con := ContactTest(contactId, testUserId) + msg := MessageTest(messageId, testUserId) + schMsg := ScheduledMessageTest(scheduledMessageId, testUserId, now) + + 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) + } + + event := scheduling.ScheduledMessageEvent{} + event.MessageId = *msg.Id + event.ContactId = *con.Id + event.ScheduledMessageId = schMsg.Id + sent := now.Add(30 * time.Minute) + if err := mockStore.CreateScheduledMessageEvent(ctx, &event); err != nil { + assert.NoError(t, err, "Error creating scheduled message event: %v", err) + } else { + m := evnt.MessageEventResponse{} + m.ScheduledMessageEventId = event.Id + m.UserId = schMsg.UserId + m.Status = evnt.Message_Event_Response_Status_Scheduled + m.Sent = sent + var merResponse map[string]any + merResponse, err = ConvertStringToMapOfAny(MERResponseInString()) + assert.NoError(t, err, "Error Converting to map[string]any") + m.Response = merResponse + + if err := merStore.Create(ctx, &m); err != nil { + assert.NoError(t, err, "Error creating MessageEventResponse: %v", err) + } + } + + url := fmt.Sprintf("%s?user_id=%s", endpoint.FetchMessageEventResponse, testUserId) + req, _ := http.NewRequest("GET", url, nil) + rr := httptest.NewRecorder() + + handler.Fetch(rr, req) + + assert.Equal(t, http.StatusOK, rr.Code) + + var response GetMessageEventResponseFetchedResponse + err = json.Unmarshal(rr.Body.Bytes(), &response) + assert.NoError(t, err, "Error parsing message event response: %v", err) + + assert.NotEmpty(t, response.Data, "No message event response found") + + var msgEventResp evnt.MessageEventResponse + msgEventResp = *response.Data[0] + + assert.NotEmpty(t, msgEventResp.Response, "The response of the message event response is empty") +} diff --git a/internal/handler/message_test.go b/internal/handler/message_test.go index 0fbb03e..d162e78 100644 --- a/internal/handler/message_test.go +++ b/internal/handler/message_test.go @@ -51,7 +51,7 @@ func TestGetMessageWithMock(t *testing.T) { mockstore := mock.NewMockMessageStore() testUserId := uuid.New() - testCon := message.Message{Content: "Who is the one that benefits?", UserId: testUserId} + testCon := message.Message{Content: "Who is the one that benefits?", UserId: &testUserId} ctx := t.Context() if err := mockstore.CreateMessage(ctx, &testCon); err != nil { assert.NoError(t, err, "Error creating message") diff --git a/internal/handler/schedule_message_status.go b/internal/handler/schedule_message_status.go index 265f32d..f935d75 100644 --- a/internal/handler/schedule_message_status.go +++ b/internal/handler/schedule_message_status.go @@ -10,6 +10,16 @@ import ( "git.kundeng.us/phoenix/textsender-api/internal/store" ) +type ScheduledMessageStatusHandler struct { + App *app.App + ScheduledMessageEventStore store.ScheduledMessageEventStore + ScheduledMessageStore store.ScheduledMessageStore +} + +func NewScheduledMessageStatusHandler(apiApp *app.App, str store.ScheduledMessageEventStore, schStore store.ScheduledMessageStore) *ScheduledMessageStatusHandler { + return &ScheduledMessageStatusHandler{App: apiApp, ScheduledMessageEventStore: str, ScheduledMessageStore: schStore} +} + type RequestScheduledMessageStatus struct { ScheduledMessageId uuid.UUID `json:"scheduled_message_id"` Status string `json:"status"` @@ -25,16 +35,6 @@ type ScheduledMessageStatusResponse struct { Data []ScheduledMessageChange `json:"data"` } -type ScheduledMessageStatusHandler struct { - App *app.App - ScheduledMessageEventStore store.ScheduledMessageEventStore - ScheduledMessageStore store.ScheduledMessageStore -} - -func NewScheduledMessageStatusHandler(apiApp *app.App, str store.ScheduledMessageEventStore, schStore store.ScheduledMessageStore) *ScheduledMessageStatusHandler { - return &ScheduledMessageStatusHandler{App: apiApp, ScheduledMessageEventStore: str, ScheduledMessageStore: schStore} -} - // UpdateStatus godoc // @Summary Update status // @Description Update the status of a scheduled message (requires JWT) diff --git a/internal/handler/schedule_message_status_test.go b/internal/handler/schedule_message_status_test.go index 10dcbe4..d868620 100644 --- a/internal/handler/schedule_message_status_test.go +++ b/internal/handler/schedule_message_status_test.go @@ -24,15 +24,15 @@ func TestUpdateScheduledMessageStatusWithMock(t *testing.T) { messageStore := mock.NewMockMessageStore() schMsgStore := mock.NewMockScheduledMessageStore() - recipientId := uuid.New() + contactId := uuid.New() messageId := uuid.New() scheduledMessageId := uuid.New() testUserId := uuid.New() - con := testContact(recipientId, testUserId) - msg := testMessage(messageId, testUserId) - schMsg := testScheduledMessage(scheduledMessageId, testUserId, now) - event := testScheduledMessageEvent(msg.Id, *con.Id, schMsg.Id) + con := ContactTest(contactId, testUserId) + msg := MessageTest(messageId, testUserId) + schMsg := ScheduledMessageTest(scheduledMessageId, testUserId, now) + event := ScheduledMessageEventTest(*msg.Id, *con.Id, schMsg.Id) ctx := t.Context() diff --git a/internal/handler/scheduled_message.go b/internal/handler/scheduled_message.go index ba88bc1..8af8ee2 100644 --- a/internal/handler/scheduled_message.go +++ b/internal/handler/scheduled_message.go @@ -13,6 +13,15 @@ import ( "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"` @@ -24,25 +33,6 @@ type AddScheduledMessageResponse struct { Data []scheduling.ScheduledMessage `json:"data"` } -type GetScheduledMessageResponse struct { - Message string `json:"message"` - Data []scheduling.ScheduledMessage `json:"data"` -} - -type FetchNextMessageResponse struct { - Message string `json:"message"` - Data []scheduling.ScheduledMessage `json:"data"` -} - -type ScheduledMessageHandler struct { - App *app.App - ScheduledMessageStore store.ScheduledMessageStore -} - -func NewScheduledMessageHandler(apiApp *app.App, str store.ScheduledMessageStore) *ScheduledMessageHandler { - return &ScheduledMessageHandler{App: apiApp, ScheduledMessageStore: str} -} - // AddScheduledMessage godoc // @Summary Add scheduled message // @Description Adds a scheduled message (requires JWT) @@ -106,6 +96,11 @@ func (s *ScheduledMessageHandler) AddScheduledMessage(w http.ResponseWriter, r * 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) @@ -183,6 +178,11 @@ func (s *ScheduledMessageHandler) GetScheduledMessage(w http.ResponseWriter, r * 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) diff --git a/internal/handler/scheduled_message_event.go b/internal/handler/scheduled_message_event.go index f05de6e..9c4e17e 100644 --- a/internal/handler/scheduled_message_event.go +++ b/internal/handler/scheduled_message_event.go @@ -12,27 +12,6 @@ import ( "git.kundeng.us/phoenix/textsender-api/internal/store" ) -type RequestAddScheduledMessageEvent struct { - RecipientId uuid.UUID `json:"recipient_id"` - MessageId uuid.UUID `json:"message_id"` - ScheduledMessageId uuid.UUID `json:"scheduled_message_id"` -} - -type AddScheduledMessageEventResponse struct { - Message string `json:"message"` - Data []scheduling.ScheduledMessageEvent `json:"data"` -} - -type GetScheduledMessageEventResponse struct { - Message string `json:"message"` - Data []scheduling.ScheduledMessageEvent `json:"data"` -} - -type DeleteScheduledMessageEventResponse struct { - Message string `json:"message"` - Data []scheduling.ScheduledMessageEvent `json:"data"` -} - type ScheduledMessageEventHandler struct { App *app.App ScheduledMessageEventStore store.ScheduledMessageEventStore @@ -43,7 +22,18 @@ func NewScheduledMessageEventHandler(apiApp *app.App, str store.ScheduledMessage return &ScheduledMessageEventHandler{App: apiApp, ScheduledMessageEventStore: str, ScheduledMessageStore: schStore} } -// AddContact godoc +type RequestAddScheduledMessageEvent struct { + ContactId uuid.UUID `json:"contact_id"` + MessageId uuid.UUID `json:"message_id"` + ScheduledMessageId uuid.UUID `json:"scheduled_message_id"` +} + +type AddScheduledMessageEventResponse struct { + Message string `json:"message"` + Data []scheduling.ScheduledMessageEvent `json:"data"` +} + +// AddScheduledMessageEvent godoc // @Summary Add scheduled message event // @Description Add a scheduled message event (requires JWT) // @Tags scheduled message events @@ -67,14 +57,14 @@ func (s *ScheduledMessageEventHandler) AddScheduledMessageEvent(w http.ResponseW } defer r.Body.Close() - event := scheduling.ScheduledMessageEvent{RecipientId: req.RecipientId, MessageId: req.MessageId, ScheduledMessageId: req.ScheduledMessageId} + event := scheduling.ScheduledMessageEvent{ContactId: req.ContactId, MessageId: req.MessageId, ScheduledMessageId: req.ScheduledMessageId} var statusCode int var resp AddScheduledMessageEventResponse - if event.RecipientId == uuid.Nil { + if event.ContactId == uuid.Nil { statusCode = http.StatusBadRequest - resp.Message = "Recipient Id is nil" + resp.Message = "Contact Id is nil" } else if event.MessageId == uuid.Nil { statusCode = http.StatusBadRequest resp.Message = "Message Id is nil" @@ -117,6 +107,11 @@ func (s *ScheduledMessageEventHandler) AddScheduledMessageEvent(w http.ResponseW RespondWithJSON(w, statusCode, &resp) } +type GetScheduledMessageEventResponse struct { + Message string `json:"message"` + Data []scheduling.ScheduledMessageEvent `json:"data"` +} + // GetScheduledMessageEvent godoc // @Summary Get scheduled message event // @Description Gets a scheduled message event (requires JWT) @@ -181,6 +176,11 @@ func (s *ScheduledMessageEventHandler) GetScheduledMessageEvent(w http.ResponseW RespondWithJSON(w, statusCode, &resp) } +type DeleteScheduledMessageEventResponse struct { + Message string `json:"message"` + Data []scheduling.ScheduledMessageEvent `json:"data"` +} + // DeleteScheduledMessageEvent godoc // @Summary Delete scheduled message event // @Description Deletes a scheduled message event (requires JWT) diff --git a/internal/handler/scheduled_message_event_test.go b/internal/handler/scheduled_message_event_test.go index 56fed9f..2889cc8 100644 --- a/internal/handler/scheduled_message_event_test.go +++ b/internal/handler/scheduled_message_event_test.go @@ -9,8 +9,6 @@ import ( "testing" "time" - "git.kundeng.us/phoenix/textsender-models/tx0/contact" - "git.kundeng.us/phoenix/textsender-models/tx0/message" "git.kundeng.us/phoenix/textsender-models/tx0/message/scheduling" "github.com/google/uuid" "github.com/stretchr/testify/assert" @@ -20,14 +18,14 @@ import ( ) var ( - recipientId = uuid.New() + contactId = uuid.New() messageId = uuid.New() scheduledMessageId = uuid.New() testUserId = uuid.New() ) type CreateScheduledMessageEventRequest struct { - RecipientId uuid.UUID `json:"recipient_id"` + ContactId uuid.UUID `json:"contact_id"` MessageId uuid.UUID `json:"message_id"` ScheduledMessageId uuid.UUID `json:"scheduled_message_id"` } @@ -44,14 +42,14 @@ func TestCreateScheduledMessageEventWithMock(t *testing.T) { assert.NoError(t, err, "Error getting app") handler := NewScheduledMessageEventHandler(apiApp, mockStore, schMsgStore) - recipientId := uuid.New() + contactId := uuid.New() messageId := uuid.New() scheduledMessageId := uuid.New() testUserId := uuid.New() - con := testContact(recipientId, testUserId) - msg := testMessage(messageId, testUserId) - schMsg := testScheduledMessage(scheduledMessageId, testUserId, now) + con := ContactTest(contactId, testUserId) + msg := MessageTest(messageId, testUserId) + schMsg := ScheduledMessageTest(scheduledMessageId, testUserId, now) ctx := t.Context() @@ -63,7 +61,7 @@ func TestCreateScheduledMessageEventWithMock(t *testing.T) { assert.NoError(t, err, "Error creating scheduled message: %v", err) } - testReq := CreateScheduledMessageEventRequest{RecipientId: recipientId, MessageId: messageId, ScheduledMessageId: scheduledMessageId} + testReq := CreateScheduledMessageEventRequest{ContactId: contactId, MessageId: messageId, ScheduledMessageId: scheduledMessageId} jsonValue, _ := json.Marshal(testReq) req, _ := http.NewRequest("POST", endpoint.AddEventToScheduledMessageEndpoint, strings.NewReader(string(jsonValue))) @@ -96,15 +94,15 @@ func TestGetScheduledMessageEventWithMock(t *testing.T) { assert.NoError(t, err, "Error getting app") handler := NewScheduledMessageEventHandler(apiApp, schMsgEventStore, schMsgStore) - recipientId := uuid.New() + contactId := uuid.New() messageId := uuid.New() scheduledMessageId := uuid.New() testUserId := uuid.New() - con := testContact(recipientId, testUserId) - msg := testMessage(messageId, testUserId) - schMsg := testScheduledMessage(scheduledMessageId, testUserId, now) - event := testScheduledMessageEvent(msg.Id, *con.Id, schMsg.Id) + con := ContactTest(contactId, testUserId) + msg := MessageTest(messageId, testUserId) + schMsg := ScheduledMessageTest(scheduledMessageId, testUserId, now) + event := ScheduledMessageEventTest(*msg.Id, *con.Id, schMsg.Id) ctx := t.Context() @@ -147,10 +145,10 @@ func TestDeleteScheduledMessageEventWithMock(t *testing.T) { assert.NoError(t, err, "Error getting app") handler := NewScheduledMessageEventHandler(apiApp, schMsgEventStore, schMsgStore) - con := testContact(recipientId, testUserId) - msg := testMessage(messageId, testUserId) - schMsg := testScheduledMessage(scheduledMessageId, testUserId, now) - event := testScheduledMessageEvent(msg.Id, *con.Id, schMsg.Id) + con := ContactTest(contactId, testUserId) + msg := MessageTest(messageId, testUserId) + schMsg := ScheduledMessageTest(scheduledMessageId, testUserId, now) + event := ScheduledMessageEventTest(*msg.Id, *con.Id, schMsg.Id) ctx := t.Context() @@ -182,32 +180,3 @@ func TestDeleteScheduledMessageEventWithMock(t *testing.T) { assert.NotEmpty(t, msgEvent.Created, "Created date should not be empty") } - -func testContact(id uuid.UUID, userId uuid.UUID) contact.Contact { - if id == uuid.Nil { - id = uuid.New() - return contact.Contact{Id: &id, PhoneNumber: "+10123456789", UserId: &userId} - } else { - return contact.Contact{Id: &id, PhoneNumber: "+10123456789", UserId: &userId} - } -} - -func testMessage(id uuid.UUID, userId uuid.UUID) message.Message { - if id == uuid.Nil { - return message.Message{Id: uuid.New(), Content: "Oh how the mighty have fallen", UserId: userId} - } else { - return message.Message{Id: id, Content: "Oh how the mighty have fallen", UserId: userId} - } -} - -func testScheduledMessage(id uuid.UUID, userId uuid.UUID, now time.Time) scheduling.ScheduledMessage { - if id == uuid.Nil { - return scheduling.ScheduledMessage{Id: uuid.New(), UserId: userId, Scheduled: now.Add(20 * time.Minute), Status: scheduling.Pending} - } else { - return scheduling.ScheduledMessage{Id: id, UserId: userId, Scheduled: now.Add(20 * time.Minute), Status: scheduling.Pending} - } -} - -func testScheduledMessageEvent(messageId, recipientId, scheduledMessageId uuid.UUID) scheduling.ScheduledMessageEvent { - return scheduling.ScheduledMessageEvent{MessageId: messageId, RecipientId: recipientId, ScheduledMessageId: scheduledMessageId} -} diff --git a/internal/handler/scheduled_message_test.go b/internal/handler/scheduled_message_test.go index b3ca5c3..95a4936 100644 --- a/internal/handler/scheduled_message_test.go +++ b/internal/handler/scheduled_message_test.go @@ -31,7 +31,7 @@ func TestCreateScheduledMessageWithMock(t *testing.T) { handler := NewScheduledMessageHandler(apiApp, mockStore) testUserId := uuid.New() - testBody := testCreateScheduledMessageRequest(testUserId, now) + testBody := createScheduledMessageRequest(testUserId, now) jsonValue, _ := json.Marshal(testBody) req, _ := http.NewRequest("POST", endpoint.ScheduleMessageEndpoint, strings.NewReader(string(jsonValue))) @@ -55,7 +55,7 @@ func TestGetScheduledMessageWithMock(t *testing.T) { mockStore := mock.NewMockScheduledMessageStore() testUserId := uuid.New() - testBody := testCreateScheduledMessageRequest(testUserId, now) + testBody := createScheduledMessageRequest(testUserId, now) ctx := t.Context() schMsg := scheduling.ScheduledMessage{} @@ -89,15 +89,15 @@ func TestFetchScheduledMessageWithMock(t *testing.T) { messageStore := mock.NewMockMessageStore() schMsgEventStore := mock.NewMockScheduledMessageEventStore() - recipientId := uuid.New() + contactId := uuid.New() messageId := uuid.New() scheduledMessageId := uuid.New() testUserId := uuid.New() - con := testContact(recipientId, testUserId) - msg := testMessage(messageId, testUserId) - schMsg := testScheduledMessage(scheduledMessageId, testUserId, now) - event := testScheduledMessageEvent(msg.Id, *con.Id, schMsg.Id) + con := ContactTest(contactId, testUserId) + msg := MessageTest(messageId, testUserId) + schMsg := ScheduledMessageTest(scheduledMessageId, testUserId, now) + event := ScheduledMessageEventTest(*msg.Id, *con.Id, schMsg.Id) ctx := t.Context() @@ -135,7 +135,7 @@ func TestFetchScheduledMessageWithMock(t *testing.T) { assert.Equal(t, scheduling.Processing, fetchedSchMsg.Status, "The statuses do not match") } -func testCreateScheduledMessageRequest(userId uuid.UUID, now time.Time) CreateScheduledMessageRequest { +func createScheduledMessageRequest(userId uuid.UUID, now time.Time) CreateScheduledMessageRequest { scheduled := now.Add(5 * time.Minute) return CreateScheduledMessageRequest{Status: scheduling.Pending, UserId: userId, Scheduled: scheduled} } diff --git a/internal/store/message_event_response_store.go b/internal/store/message_event_response_store.go index 031d25e..4cf0a19 100644 --- a/internal/store/message_event_response_store.go +++ b/internal/store/message_event_response_store.go @@ -4,13 +4,14 @@ import ( "context" "fmt" - "git.kundeng.us/phoenix/textsender-models/tx0/message" + "git.kundeng.us/phoenix/textsender-models/tx0/message/event" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" ) type MessageEventResponseStore interface { - Create(ctx context.Context, mer *message.MessageEventResponse) error + Create(ctx context.Context, mer *event.MessageEventResponse) error + GetWithUserId(ctx context.Context, userId uuid.UUID) ([]*event.MessageEventResponse, error) } type PGMessageEventResponseStore struct { @@ -21,16 +22,40 @@ func NewMessageEventResponseStore(db *pgxpool.Pool) *PGMessageEventResponseStore return &PGMessageEventResponseStore{db: db} } -func (m *PGMessageEventResponseStore) Create(ctx context.Context, mer *message.MessageEventResponse) error { +func (m *PGMessageEventResponseStore) Create(ctx context.Context, mer *event.MessageEventResponse) error { var query string queryBase := "INSERT INTO message_event_responses" queryReturn := "RETURNING id" if mer.ScheduledMessageEventId == uuid.Nil { - query = fmt.Sprintf("%s (response, user_id, sent, contact_id, message_id) VALUES ($1, $2, $3, $4, $5) %s", queryBase, queryReturn) - return m.db.QueryRow(ctx, query, mer.Response, mer.UserId, mer.Sent, mer.ContactId, mer.MessageId).Scan(&mer.Id) + query = fmt.Sprintf("%s (response, user_id, sent, contact_id, message_id, status) VALUES ($1, $2, $3, $4, $5, $6) %s", queryBase, queryReturn) + return m.db.QueryRow(ctx, query, mer.Response, mer.UserId, mer.Sent, mer.ContactId, mer.MessageId, mer.Status).Scan(&mer.Id) } else { - query = fmt.Sprintf("%s (scheduled_message_event_id, response, user_id, sent, contact_id, message_id) VALUES ($1, $2, $3, $4, $5, $6) %s", queryBase, queryReturn) - return m.db.QueryRow(ctx, query, mer.ScheduledMessageEventId, mer.Response, mer.UserId, mer.Sent, mer.ContactId, mer.MessageId).Scan(&mer.Id) + query = fmt.Sprintf("%s (scheduled_message_event_id, response, user_id, sent, contact_id, message_id, status) VALUES ($1, $2, $3, $4, $5, $6, $7) %s", queryBase, queryReturn) + return m.db.QueryRow(ctx, query, mer.ScheduledMessageEventId, mer.Response, mer.UserId, mer.Sent, mer.ContactId, mer.MessageId, mer.Status).Scan(&mer.Id) } } + +func (m *PGMessageEventResponseStore) GetWithUserId(ctx context.Context, userId uuid.UUID) ([]*event.MessageEventResponse, error) { + query := "SELECT id, scheduled_message_event_id, response, user_id, sent, contact_id, message_id, status FROM message_event_responses WHERE user_id = $1" + rows, err := m.db.Query(ctx, query, userId) + if err != nil { + return nil, fmt.Errorf("Error querying: %w", err) + } + defer rows.Close() + + var sentMessages []*event.MessageEventResponse + for rows.Next() { + var msg event.MessageEventResponse + if err := rows.Scan(&msg.Id, &msg.ScheduledMessageEventId, &msg.Response, &msg.UserId, &msg.Sent, &msg.ContactId, &msg.MessageId, &msg.Status); err != nil { + return nil, fmt.Errorf("Scanning row: %w", err) + } + sentMessages = append(sentMessages, &msg) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("Iterating message_event_responses: %w", err) + } + + return sentMessages, nil +} diff --git a/internal/store/mock/message_event_response_store.go b/internal/store/mock/message_event_response_store.go index a06a7b0..5ffc0ff 100644 --- a/internal/store/mock/message_event_response_store.go +++ b/internal/store/mock/message_event_response_store.go @@ -6,7 +6,7 @@ import ( "sync" "time" - "git.kundeng.us/phoenix/textsender-models/tx0/message" + "git.kundeng.us/phoenix/textsender-models/tx0/message/event" "github.com/google/uuid" ) @@ -15,20 +15,20 @@ type MessageEventResponseKey struct { UserId uuid.UUID } type MockMessageEventResponseStore struct { - MessageEventResponses map[uuid.UUID]*message.MessageEventResponse - MessageEventResponsesByKey map[MessageEventResponseKey]*message.MessageEventResponse + MessageEventResponses map[uuid.UUID]*event.MessageEventResponse + MessageEventResponsesByKey map[MessageEventResponseKey]*event.MessageEventResponse mu sync.RWMutex Error error } func NewMockMessageEventResponseStore() *MockMessageEventResponseStore { return &MockMessageEventResponseStore{ - MessageEventResponses: make(map[uuid.UUID]*message.MessageEventResponse), - MessageEventResponsesByKey: make(map[MessageEventResponseKey]*message.MessageEventResponse), + MessageEventResponses: make(map[uuid.UUID]*event.MessageEventResponse), + MessageEventResponsesByKey: make(map[MessageEventResponseKey]*event.MessageEventResponse), } } -func (m *MockMessageEventResponseStore) Create(ctx context.Context, mer *message.MessageEventResponse) error { +func (m *MockMessageEventResponseStore) Create(ctx context.Context, mer *event.MessageEventResponse) error { m.mu.Lock() defer m.mu.Unlock() @@ -60,3 +60,22 @@ func (m *MockMessageEventResponseStore) Create(ctx context.Context, mer *message return nil } } + +// TODO: Add code to get mock MessageEventResponse +func (m *MockMessageEventResponseStore) GetWithUserId(ctx context.Context, userId uuid.UUID) ([]*event.MessageEventResponse, error) { + m.mu.Lock() + defer m.mu.Unlock() + + if m.Error != nil { + return nil, m.Error + } + + var messages []*event.MessageEventResponse + for _, msg := range m.MessageEventResponses { + if msg.UserId == userId { + messages = append(messages, msg) + } + } + + return messages, nil +} diff --git a/internal/store/mock/scheduled_message_event_store.go b/internal/store/mock/scheduled_message_event_store.go index b7b3cf8..f8631f8 100644 --- a/internal/store/mock/scheduled_message_event_store.go +++ b/internal/store/mock/scheduled_message_event_store.go @@ -11,7 +11,7 @@ import ( ) type ScheduledMessageEventKey struct { - RecipientId uuid.UUID + ContactId uuid.UUID MessageId uuid.UUID ScheduledMessageId uuid.UUID } @@ -94,7 +94,7 @@ func (m *MockScheduledMessageEventStore) Delete(ctx context.Context, id uuid.UUI copiedEventsKey := make(map[ScheduledMessageEventKey]*scheduling.ScheduledMessageEvent) for i, schMsgEvent := range m.ScheduledMessageEvents { - key := ScheduledMessageEventKey{RecipientId: schMsgEvent.RecipientId, MessageId: schMsgEvent.MessageId, ScheduledMessageId: schMsgEvent.ScheduledMessageId} + key := ScheduledMessageEventKey{ContactId: schMsgEvent.ContactId, MessageId: schMsgEvent.MessageId, ScheduledMessageId: schMsgEvent.ScheduledMessageId} if schMsgEvent.Id != id { copiedEvents[i] = schMsgEvent copiedEventsKey[key] = schMsgEvent @@ -132,7 +132,7 @@ func (m *MockScheduledMessageEventStore) CreateScheduledMessageEvent(ctx context event.Id = uuid.New() } - key := ScheduledMessageEventKey{RecipientId: event.RecipientId, MessageId: event.MessageId, ScheduledMessageId: event.ScheduledMessageId} + key := ScheduledMessageEventKey{ContactId: event.ContactId, MessageId: event.MessageId, ScheduledMessageId: event.ScheduledMessageId} if _, exists := m.ScheduledMessageEventsByKey[key]; exists { return fmt.Errorf("Already exists") @@ -152,7 +152,7 @@ func (m *MockScheduledMessageEventStore) Exists(ctx context.Context, event *sche return false, m.Error } - key := ScheduledMessageEventKey{RecipientId: event.RecipientId, MessageId: event.MessageId, ScheduledMessageId: event.ScheduledMessageId} + key := ScheduledMessageEventKey{ContactId: event.ContactId, MessageId: event.MessageId, ScheduledMessageId: event.ScheduledMessageId} _, exists := m.ScheduledMessageEventsByKey[key] return exists, nil diff --git a/internal/store/mock/store.go b/internal/store/mock/store.go index aa2b014..ec48680 100644 --- a/internal/store/mock/store.go +++ b/internal/store/mock/store.go @@ -3,6 +3,7 @@ package mock import ( "context" "errors" + "fmt" "sync" "git.kundeng.us/phoenix/textsender-models/tx0/message" @@ -36,16 +37,22 @@ func (m *MockMessageStore) CreateMessage(ctx context.Context, msg *message.Messa return m.Error } - if msg.Id == uuid.Nil { - msg.Id = uuid.New() + var id uuid.UUID + if msg != nil { + if msg.Id == nil || *msg.Id == uuid.Nil { + id = uuid.New() + msg.Id = &id + } + } else { + return fmt.Errorf("Message not populated") } - key := MessageKey{Content: msg.Content, UserId: msg.UserId} + key := MessageKey{Content: msg.Content, UserId: *msg.UserId} if _, exists := m.MessagesByKey[key]; exists { return errors.New("Message already exists") } - m.Messages[msg.Id] = msg + m.Messages[*msg.Id] = msg m.MessagesByKey[key] = msg return nil } @@ -88,7 +95,7 @@ func (m *MockMessageStore) MessageExists(ctx context.Context, msg *message.Messa return false, m.Error } - _, exists := m.MessagesByKey[MessageKey{Content: msg.Content, UserId: msg.UserId}] + _, exists := m.MessagesByKey[MessageKey{Content: msg.Content, UserId: *msg.UserId}] return exists, nil } diff --git a/internal/store/scheduled_message_event_store.go b/internal/store/scheduled_message_event_store.go index e17fc7e..ae27c50 100644 --- a/internal/store/scheduled_message_event_store.go +++ b/internal/store/scheduled_message_event_store.go @@ -27,11 +27,11 @@ func NewScheduledMessageEventStore(db *pgxpool.Pool) *PGScheduledMessageEventSto } 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` + query := `SELECT id, contact_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, + &event.Id, &event.ContactId, &event.MessageId, &event.ScheduledMessageId, &event.Created, ) if err == pgx.ErrNoRows { @@ -45,7 +45,7 @@ func (s *PGScheduledMessageEventStore) Get(ctx context.Context, id uuid.UUID) (* func (s *PGScheduledMessageEventStore) GetWithScheduleMessageId(ctx context.Context, schMsgId uuid.UUID) ([]*scheduling.ScheduledMessageEvent, error) { var events []*scheduling.ScheduledMessageEvent - query := `SELECT id, recipient_id, message_id, scheduled_message_id, created FROM scheduled_message_events WHERE scheduled_message_id = $1 + query := `SELECT id, contact_id, message_id, scheduled_message_id, created FROM scheduled_message_events WHERE scheduled_message_id = $1 ` rows, err := s.db.Query(ctx, query, schMsgId) @@ -57,7 +57,7 @@ func (s *PGScheduledMessageEventStore) GetWithScheduleMessageId(ctx context.Cont for rows.Next() { var event scheduling.ScheduledMessageEvent - if err := rows.Scan(&event.Id, &event.RecipientId, &event.MessageId, &event.ScheduledMessageId, &event.Created); err != nil { + if err := rows.Scan(&event.Id, &event.ContactId, &event.MessageId, &event.ScheduledMessageId, &event.Created); err != nil { return nil, fmt.Errorf("Error fetching data from row: %w", err) } else { events = append(events, &event) @@ -90,21 +90,21 @@ func (s *PGScheduledMessageEventStore) Delete(ctx context.Context, id uuid.UUID) func (s *PGScheduledMessageEventStore) CreateScheduledMessageEvent(ctx context.Context, event *scheduling.ScheduledMessageEvent) error { query := ` - INSERT INTO scheduled_message_events (recipient_id, message_id, scheduled_message_id) + INSERT INTO scheduled_message_events (contact_id, message_id, scheduled_message_id) VALUES ($1, $2, $3) RETURNING id, created ` - return s.db.QueryRow(ctx, query, event.RecipientId, event.MessageId, event.ScheduledMessageId).Scan( + return s.db.QueryRow(ctx, query, event.ContactId, event.MessageId, event.ScheduledMessageId).Scan( &event.Id, &event.Created, ) } func (s *PGScheduledMessageEventStore) Exists(ctx context.Context, event *scheduling.ScheduledMessageEvent) (bool, error) { - query := `SELECT EXISTS(SELECT 1 FROM scheduled_message_events WHERE scheduled_message_id = $1 AND recipient_id = $2)` + query := `SELECT EXISTS(SELECT 1 FROM scheduled_message_events WHERE scheduled_message_id = $1 AND contact_id = $2)` var exists bool - err := s.db.QueryRow(ctx, query, event.ScheduledMessageId, event.RecipientId).Scan(&exists) + err := s.db.QueryRow(ctx, query, event.ScheduledMessageId, event.ContactId).Scan(&exists) if err != nil { return false, fmt.Errorf("Scheduled message event does not exist: %w", err) } else { diff --git a/migrations/schema.sql b/migrations/schema.sql index f7a8a1a..b8d3401 100644 --- a/migrations/schema.sql +++ b/migrations/schema.sql @@ -28,7 +28,7 @@ CREATE TABLE IF NOT EXISTS scheduled_messages ( CREATE TABLE IF NOT EXISTS scheduled_message_events ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - recipient_id UUID NOT NULL, + contact_id UUID NOT NULL, message_id UUID NOT NULL, scheduled_message_id UUID NOT NULL, created timestamptz DEFAULT now()