From 048fc88358bbd82421bfa0825a2f48a9640c0617 Mon Sep 17 00:00:00 2001 From: Rowan Seymour Date: Wed, 4 Oct 2023 10:31:49 -0500 Subject: [PATCH] Add optin_id to channels_channelevent --- backends/rapidpro/backend_test.go | 29 +++++++++++++++++++---------- backends/rapidpro/channel_event.go | 12 ++++++++++-- backends/rapidpro/schema.sql | 11 ++++++++++- backends/rapidpro/testdata.sql | 10 ++++++++-- handlers/meta/facebook_test.go | 4 ++-- handlers/meta/handlers.go | 7 ++----- 6 files changed, 51 insertions(+), 22 deletions(-) diff --git a/backends/rapidpro/backend_test.go b/backends/rapidpro/backend_test.go index 12727d383..34701bd27 100644 --- a/backends/rapidpro/backend_test.go +++ b/backends/rapidpro/backend_test.go @@ -1274,12 +1274,21 @@ func (ts *BackendTestSuite) TestChannelEvent() { ts.Equal(null.String("kermit frog"), contact.Name_) dbE := event.(*ChannelEvent) - dbE, err = readChannelEventFromDB(ts.b, dbE.ID_) - ts.NoError(err) + dbE = readChannelEventFromDB(ts.b, dbE.ID_) ts.Equal(dbE.EventType_, courier.EventTypeReferral) ts.Equal(map[string]any{"ref_id": "12345"}, dbE.Extra()) ts.Equal(contact.ID_, dbE.ContactID_) ts.Equal(contact.URNID_, dbE.ContactURNID_) + + event = ts.b.NewChannelEvent(channel, courier.EventTypeOptIn, urn, clog).WithExtra(map[string]any{"optin_id": "1", "optin_name": "Polls"}) + err = ts.b.WriteChannelEvent(ctx, event, clog) + ts.NoError(err) + + dbE = event.(*ChannelEvent) + dbE = readChannelEventFromDB(ts.b, dbE.ID_) + ts.Equal(dbE.EventType_, courier.EventTypeOptIn) + ts.Equal(map[string]any{"optin_id": "1", "optin_name": "Polls"}, dbE.Extra()) + ts.Equal(null.Int(1), dbE.OptInID_) } func (ts *BackendTestSuite) TestSessionTimeout() { @@ -1316,8 +1325,7 @@ func (ts *BackendTestSuite) TestMailroomEvents() { ts.Equal(null.String("kermit frog"), contact.Name_) dbE := event.(*ChannelEvent) - dbE, err = readChannelEventFromDB(ts.b, dbE.ID_) - ts.NoError(err) + dbE = readChannelEventFromDB(ts.b, dbE.ID_) ts.Equal(dbE.EventType_, courier.EventTypeReferral) ts.Equal(map[string]any{"ref_id": "12345"}, dbE.Extra()) ts.Equal(contact.ID_, dbE.ContactID_) @@ -1552,14 +1560,15 @@ WHERE ` const sqlSelectEvent = ` -SELECT org_id, channel_id, contact_id, contact_urn_id, event_type, extra, occurred_on, created_on, log_uuids +SELECT id, org_id, channel_id, contact_id, contact_urn_id, event_type, optin_id, extra, occurred_on, created_on, log_uuids FROM channels_channelevent WHERE id = $1` -func readChannelEventFromDB(b *backend, id ChannelEventID) (*ChannelEvent, error) { - e := &ChannelEvent{ - ID_: id, - } +func readChannelEventFromDB(b *backend, id ChannelEventID) *ChannelEvent { + e := &ChannelEvent{} err := b.db.Get(e, sqlSelectEvent, id) - return e, err + if err != nil { + panic(err) + } + return e } diff --git a/backends/rapidpro/channel_event.go b/backends/rapidpro/channel_event.go index 78fcdef7f..da0d2bead 100644 --- a/backends/rapidpro/channel_event.go +++ b/backends/rapidpro/channel_event.go @@ -43,6 +43,7 @@ type ChannelEvent struct { ChannelID_ courier.ChannelID `json:"channel_id" db:"channel_id"` URN_ urns.URN `json:"urn" db:"urn"` EventType_ courier.ChannelEventType `json:"event_type" db:"event_type"` + OptInID_ null.Int `json:"optin_id" db:"optin_id"` Extra_ null.Map[any] `json:"extra" db:"extra"` OccurredOn_ time.Time `json:"occurred_on" db:"occurred_on"` CreatedOn_ time.Time `json:"created_on" db:"created_on"` @@ -98,6 +99,13 @@ func (e *ChannelEvent) WithURNAuthTokens(tokens map[string]string) courier.Chann } func (e *ChannelEvent) WithExtra(extra map[string]any) courier.ChannelEvent { + optInID, ok := extra["optin_id"] + if ok { + asStr, _ := optInID.(string) + asInt, _ := strconv.Atoi(asStr) + e.OptInID_ = null.Int(asInt) + } + e.Extra_ = null.Map[any](extra) return e } @@ -127,8 +135,8 @@ func writeChannelEvent(ctx context.Context, b *backend, event courier.ChannelEve const sqlInsertChannelEvent = ` INSERT INTO - channels_channelevent( org_id, channel_id, contact_id, contact_urn_id, event_type, extra, occurred_on, created_on, log_uuids) - VALUES(:org_id, :channel_id, :contact_id, :contact_urn_id, :event_type, :extra, :occurred_on, :created_on, :log_uuids) + channels_channelevent( org_id, channel_id, contact_id, contact_urn_id, event_type, optin_id, extra, occurred_on, created_on, log_uuids) + VALUES(:org_id, :channel_id, :contact_id, :contact_urn_id, :event_type, :optin_id, :extra, :occurred_on, :created_on, :log_uuids) RETURNING id` // writeChannelEventToDB writes the passed in msg status to our db diff --git a/backends/rapidpro/schema.sql b/backends/rapidpro/schema.sql index 94bc18257..857260fe3 100644 --- a/backends/rapidpro/schema.sql +++ b/backends/rapidpro/schema.sql @@ -56,6 +56,14 @@ CREATE TABLE contacts_contacturn ( UNIQUE (org_id, identity) ); +DROP TABLE IF EXISTS msgs_optin CASCADE; +CREATE TABLE msgs_optin ( + id serial primary key, + uuid uuid NOT NULL, + org_id integer NOT NULL references orgs_org(id) on delete cascade, + name character varying(64) +); + DROP TABLE IF EXISTS msgs_msg CASCADE; CREATE TABLE msgs_msg ( id bigserial primary key, @@ -83,7 +91,7 @@ CREATE TABLE msgs_msg ( contact_urn_id integer NOT NULL references contacts_contacturn(id) on delete cascade, org_id integer NOT NULL references orgs_org(id) on delete cascade, metadata text, - topup_id integer, + optin_id integer references msgs_optin(id) on delete cascade, delete_from_counts boolean, log_uuids uuid[] ); @@ -111,6 +119,7 @@ CREATE TABLE channels_channelevent ( channel_id integer NOT NULL references channels_channel(id) on delete cascade, contact_id integer NOT NULL references contacts_contact(id) on delete cascade, contact_urn_id integer NOT NULL references contacts_contacturn(id) on delete cascade, + optin_id integer references msgs_optin(id) on delete cascade, org_id integer NOT NULL references orgs_org(id) on delete cascade, log_uuids uuid[] ); diff --git a/backends/rapidpro/testdata.sql b/backends/rapidpro/testdata.sql index ebee9b009..5487a2fc4 100644 --- a/backends/rapidpro/testdata.sql +++ b/backends/rapidpro/testdata.sql @@ -36,8 +36,14 @@ DELETE FROM contacts_contacturn; INSERT INTO contacts_contacturn("id", "identity", "path", "scheme", "priority", "channel_id", "contact_id", "org_id") VALUES(1000, 'tel:+12067799192', '+12067799192', 'tel', 50, 10, 100, 1); -/** Msg with id 10,000 */ -DELETE from msgs_msg; +/* Msg optins with ids 1, 2 */ +DELETE FROM msgs_optin; +INSERT INTO msgs_optin(id, uuid, org_id, name) VALUES + (1, 'fc1cef6e-b5b1-452d-9528-a4b24db28eb0', 1, 'Polls'), + (2, '2b1eba23-4a97-46ac-9022-11304412b32f', 1, 'Jokes'); + +/** Msg with id 10000 */ +DELETE FROM msgs_msg; INSERT INTO msgs_msg("id", "text", "high_priority", "created_on", "modified_on", "sent_on", "queued_on", "direction", "status", "visibility", "msg_type", "msg_count", "error_count", "next_attempt", "external_id", "channel_id", "contact_id", "contact_urn_id", "org_id") VALUES(10000, 'test message', True, now(), now(), now(), now(), 'O', 'W', 'V', 'T', diff --git a/handlers/meta/facebook_test.go b/handlers/meta/facebook_test.go index a0ede7c00..2ae6e239c 100644 --- a/handlers/meta/facebook_test.go +++ b/handlers/meta/facebook_test.go @@ -122,7 +122,7 @@ var facebookIncomingTests = []IncomingTestCase{ ExpectedRespStatus: 200, ExpectedBodyContains: "Handled", ExpectedEvents: []ExpectedEvent{ - {Type: courier.EventTypeOptIn, URN: "facebook:5678", Time: time.Date(2016, 4, 7, 1, 11, 27, 970000000, time.UTC), Extra: map[string]any{"optin_id": 3456, "optin_name": "Bird Facts"}}, + {Type: courier.EventTypeOptIn, URN: "facebook:5678", Time: time.Date(2016, 4, 7, 1, 11, 27, 970000000, time.UTC), Extra: map[string]any{"optin_id": "3456", "optin_name": "Bird Facts"}}, }, ExpectedURNAuthTokens: map[urns.URN]map[string]string{"facebook:5678": {"optin:3456": "12345678901234567890"}}, PrepRequest: addValidSignature, @@ -134,7 +134,7 @@ var facebookIncomingTests = []IncomingTestCase{ ExpectedRespStatus: 200, ExpectedBodyContains: "Handled", ExpectedEvents: []ExpectedEvent{ - {Type: courier.EventTypeOptOut, URN: "facebook:5678", Time: time.Date(2016, 4, 7, 1, 11, 27, 970000000, time.UTC), Extra: map[string]any{"optin_id": 3456, "optin_name": "Bird Facts"}}, + {Type: courier.EventTypeOptOut, URN: "facebook:5678", Time: time.Date(2016, 4, 7, 1, 11, 27, 970000000, time.UTC), Extra: map[string]any{"optin_id": "3456", "optin_name": "Bird Facts"}}, }, ExpectedURNAuthTokens: map[urns.URN]map[string]string{"facebook:5678": {}}, PrepRequest: addValidSignature, diff --git a/handlers/meta/handlers.go b/handlers/meta/handlers.go index b9fc25119..bd5358627 100644 --- a/handlers/meta/handlers.go +++ b/handlers/meta/handlers.go @@ -438,11 +438,8 @@ func (h *handler) processFacebookInstagramPayload(ctx context.Context, channel c if msg.OptIn.Type == "notification_messages" { eventType := courier.EventTypeOptIn authToken := msg.OptIn.NotificationMessagesToken + optInID := msg.OptIn.Payload optInName := msg.OptIn.Title - optInID, err := strconv.Atoi(msg.OptIn.Payload) - if err != nil { - return nil, nil, err - } if msg.OptIn.NotificationMessagesStatus == "STOP_NOTIFICATIONS" { eventType = courier.EventTypeOptOut @@ -452,7 +449,7 @@ func (h *handler) processFacebookInstagramPayload(ctx context.Context, channel c event = h.Backend().NewChannelEvent(channel, eventType, urn, clog). WithOccurredOn(date). WithExtra(map[string]any{"optin_id": optInID, "optin_name": optInName}). - WithURNAuthTokens(map[string]string{fmt.Sprintf("optin:%d", optInID): authToken}) + WithURNAuthTokens(map[string]string{fmt.Sprintf("optin:%s", optInID): authToken}) } else { // this is an opt in, if we have a user_ref, use that as our URN (this is a checkbox plugin)