diff --git a/backends/rapidpro/msg.go b/backends/rapidpro/msg.go index 0310809a2..0e28d8532 100644 --- a/backends/rapidpro/msg.go +++ b/backends/rapidpro/msg.go @@ -47,19 +47,20 @@ const ( // Msg is our base struct to represent msgs both in our JSON and db representations type Msg struct { - OrgID_ OrgID `json:"org_id" db:"org_id"` - ID_ courier.MsgID `json:"id" db:"id"` - UUID_ courier.MsgUUID `json:"uuid" db:"uuid"` - Direction_ MsgDirection ` db:"direction"` - Status_ courier.MsgStatus ` db:"status"` - Visibility_ MsgVisibility ` db:"visibility"` - HighPriority_ bool `json:"high_priority" db:"high_priority"` - Text_ string `json:"text" db:"text"` - Attachments_ pq.StringArray `json:"attachments" db:"attachments"` - QuickReplies_ pq.StringArray `json:"quick_replies" db:"quick_replies"` - Locale_ null.String `json:"locale" db:"locale"` - ExternalID_ null.String ` db:"external_id"` - Metadata_ json.RawMessage `json:"metadata" db:"metadata"` + OrgID_ OrgID `json:"org_id" db:"org_id"` + ID_ courier.MsgID `json:"id" db:"id"` + UUID_ courier.MsgUUID `json:"uuid" db:"uuid"` + Direction_ MsgDirection ` db:"direction"` + Status_ courier.MsgStatus ` db:"status"` + Visibility_ MsgVisibility ` db:"visibility"` + HighPriority_ bool `json:"high_priority" db:"high_priority"` + Text_ string `json:"text" db:"text"` + Attachments_ pq.StringArray `json:"attachments" db:"attachments"` + QuickReplies_ pq.StringArray `json:"quick_replies" db:"quick_replies"` + Locale_ null.String `json:"locale" db:"locale"` + Templating_ *courier.Templating `json:"templating" db:"templating"` + ExternalID_ null.String ` db:"external_id"` + Metadata_ json.RawMessage `json:"metadata" db:"metadata"` ChannelID_ courier.ChannelID ` db:"channel_id"` ContactID_ ContactID `json:"contact_id" db:"contact_id"` @@ -142,11 +143,12 @@ func (m *Msg) URN() urns.URN { return m.URN_ } func (m *Msg) Channel() courier.Channel { return m.channel } // outgoing specific -func (m *Msg) QuickReplies() []string { return m.QuickReplies_ } -func (m *Msg) Locale() i18n.Locale { return i18n.Locale(string(m.Locale_)) } -func (m *Msg) URNAuth() string { return m.URNAuth_ } -func (m *Msg) Origin() courier.MsgOrigin { return m.Origin_ } -func (m *Msg) ContactLastSeenOn() *time.Time { return m.ContactLastSeenOn_ } +func (m *Msg) QuickReplies() []string { return m.QuickReplies_ } +func (m *Msg) Locale() i18n.Locale { return i18n.Locale(string(m.Locale_)) } +func (m *Msg) Templating() *courier.Templating { return m.Templating_ } +func (m *Msg) URNAuth() string { return m.URNAuth_ } +func (m *Msg) Origin() courier.MsgOrigin { return m.Origin_ } +func (m *Msg) ContactLastSeenOn() *time.Time { return m.ContactLastSeenOn_ } func (m *Msg) Topic() string { if m.Metadata_ == nil { return "" diff --git a/handlers/dialog360/handler.go b/handlers/dialog360/handler.go index 0db5ec2e1..4001e917b 100644 --- a/handlers/dialog360/handler.go +++ b/handlers/dialog360/handler.go @@ -18,7 +18,6 @@ import ( "github.com/nyaruka/courier/utils" "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/gocommon/urns" - "github.com/pkg/errors" ) const ( @@ -317,13 +316,9 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen if len(msg.Attachments()) == 0 { // do we have a template? - templating, err := whatsapp.GetTemplating(msg) - if err != nil { - return errors.Wrapf(err, "unable to decode template: %s for channel: %s", string(msg.Metadata()), msg.Channel().UUID()) - } - if templating != nil { + if msg.Templating() != nil { payload.Type = "template" - payload.Template = whatsapp.GetTemplatePayload(templating) + payload.Template = whatsapp.GetTemplatePayload(msg.Templating()) } else { if i < (len(msgParts) + len(msg.Attachments()) - 1) { // this is still a msg part diff --git a/handlers/dialog360/handler_test.go b/handlers/dialog360/handler_test.go index 60e7bfa55..61c22df38 100644 --- a/handlers/dialog360/handler_test.go +++ b/handlers/dialog360/handler_test.go @@ -2,7 +2,6 @@ package dialog360 import ( "context" - "encoding/json" "fmt" "net/http" "net/http/httptest" @@ -421,11 +420,11 @@ var SendTestCasesD3C = []OutgoingTestCase{ ExpectedExtIDs: []string{"157b5e14568e8"}, }, { - Label: "Template Send", - MsgText: "templated message", - MsgURN: "whatsapp:250788123123", - MsgLocale: "eng", - MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [{"type": "body", "params": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}], "language": "en_US"}}`), + Label: "Template Send", + MsgText: "templated message", + MsgURN: "whatsapp:250788123123", + MsgLocale: "eng", + MsgTemplating: `{"template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [{"type": "body", "params": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}], "language": "en_US"}`, MockResponses: map[string][]*httpx.MockResponse{ "https://waba-v2.360dialog.io/messages": { httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), diff --git a/handlers/meta/handlers.go b/handlers/meta/handlers.go index 7609f8dd9..4005b6010 100644 --- a/handlers/meta/handlers.go +++ b/handlers/meta/handlers.go @@ -805,13 +805,9 @@ func (h *handler) sendWhatsAppMsg(ctx context.Context, msg courier.MsgOut, res * if len(msg.Attachments()) == 0 { // do we have a template? - templating, err := whatsapp.GetTemplating(msg) - if err != nil { - return errors.Wrapf(err, "unable to decode template: %s for channel: %s", string(msg.Metadata()), msg.Channel().UUID()) - } - if templating != nil { + if msg.Templating() != nil { payload.Type = "template" - payload.Template = whatsapp.GetTemplatePayload(templating) + payload.Template = whatsapp.GetTemplatePayload(msg.Templating()) } else { if i < (len(msgParts) + len(msg.Attachments()) - 1) { diff --git a/handlers/meta/whataspp_test.go b/handlers/meta/whataspp_test.go index 69f67e418..44e243047 100644 --- a/handlers/meta/whataspp_test.go +++ b/handlers/meta/whataspp_test.go @@ -2,7 +2,6 @@ package meta import ( "context" - "encoding/json" "testing" "time" @@ -390,11 +389,11 @@ var whatsappOutgoingTests = []OutgoingTestCase{ ExpectedExtIDs: []string{"157b5e14568e8"}, }, { - Label: "Template Send", - MsgText: "templated message", - MsgURN: "whatsapp:250788123123", - MsgLocale: "eng", - MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [{"type":"body", "params": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}], "language": "en_US"}}`), + Label: "Template Send", + MsgText: "templated message", + MsgURN: "whatsapp:250788123123", + MsgLocale: "eng", + MsgTemplating: `{"template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [{"type":"body", "params": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}], "language": "en_US"}`, MockResponses: map[string][]*httpx.MockResponse{ "*/12345_ID/messages": { httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), @@ -406,11 +405,11 @@ var whatsappOutgoingTests = []OutgoingTestCase{ ExpectedExtIDs: []string{"157b5e14568e8"}, }, { - Label: "Template Send, no variables", - MsgText: "templated message", - MsgURN: "whatsapp:250788123123", - MsgLocale: "eng", - MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [], "variables": [], "language": "en_US"}}`), + Label: "Template Send, no variables", + MsgText: "templated message", + MsgURN: "whatsapp:250788123123", + MsgLocale: "eng", + MsgTemplating: `{"template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [], "variables": [], "language": "en_US"}`, MockResponses: map[string][]*httpx.MockResponse{ "*/12345_ID/messages": { httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), @@ -426,7 +425,8 @@ var whatsappOutgoingTests = []OutgoingTestCase{ MsgText: "templated message", MsgURN: "whatsapp:250788123123", MsgLocale: "eng", - MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" },"components": [ + MsgTemplating: `{"template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, + "components": [ { "type": "body", "name": "body", @@ -461,7 +461,7 @@ var whatsappOutgoingTests = []OutgoingTestCase{ } ] } - ], "language": "en_US"}}`), + ], "language": "en_US"}`, MockResponses: map[string][]*httpx.MockResponse{ "*/12345_ID/messages": { httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), diff --git a/handlers/meta/whatsapp/templates.go b/handlers/meta/whatsapp/templates.go index 241339392..ab55e8895 100644 --- a/handlers/meta/whatsapp/templates.go +++ b/handlers/meta/whatsapp/templates.go @@ -1,55 +1,12 @@ package whatsapp import ( - "encoding/json" "strings" "github.com/nyaruka/courier" - "github.com/nyaruka/courier/utils" - "github.com/pkg/errors" ) -type MsgTemplating struct { - Template struct { - Name string `json:"name" validate:"required"` - UUID string `json:"uuid" validate:"required"` - } `json:"template" validate:"required,dive"` - Namespace string `json:"namespace"` - Components []struct { - Type string `json:"type"` - Name string `json:"name"` - Params []struct { - Type string `json:"type"` - Value string `json:"value"` - } `json:"params"` - } `json:"components"` - Language string `json:"language"` -} - -func GetTemplating(msg courier.MsgOut) (*MsgTemplating, error) { - if len(msg.Metadata()) == 0 { - return nil, nil - } - - metadata := &struct { - Templating *MsgTemplating `json:"templating"` - }{} - if err := json.Unmarshal(msg.Metadata(), metadata); err != nil { - return nil, err - } - - if metadata.Templating == nil { - return nil, nil - } - - if err := utils.Validate(metadata.Templating); err != nil { - return nil, errors.Wrapf(err, "invalid templating definition") - } - - return metadata.Templating, nil -} - -func GetTemplatePayload(templating *MsgTemplating) *Template { +func GetTemplatePayload(templating *courier.Templating) *Template { template := &Template{ Name: templating.Template.Name, Language: &Language{Policy: "deterministic", Code: templating.Language}, diff --git a/handlers/meta/whatsapp/templates_test.go b/handlers/meta/whatsapp/templates_test.go index e7b7b5ae1..89a1a835a 100644 --- a/handlers/meta/whatsapp/templates_test.go +++ b/handlers/meta/whatsapp/templates_test.go @@ -1,47 +1,15 @@ package whatsapp_test import ( - "encoding/json" - "fmt" "testing" + "github.com/nyaruka/courier" "github.com/nyaruka/courier/handlers/meta/whatsapp" "github.com/nyaruka/courier/test" + "github.com/nyaruka/gocommon/jsonx" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) -func TestGetTemplating(t *testing.T) { - msg := test.NewMockMsg(1, "87995844-2017-4ba0-bc73-f3da75b32f9b", nil, "tel:+1234567890", "hi", nil) - - // no metadata, no templating - tpl, err := whatsapp.GetTemplating(msg) - assert.NoError(t, err) - assert.Nil(t, tpl) - - msg.WithMetadata(json.RawMessage(`{}`)) - - // no templating in metadata, no templating - tpl, err = whatsapp.GetTemplating(msg) - assert.NoError(t, err) - assert.Nil(t, tpl) - - msg.WithMetadata(json.RawMessage(`{"templating": {"foo": "bar"}}`)) - - // invalid templating in metadata, error - tpl, err = whatsapp.GetTemplating(msg) - assert.Error(t, err, "x") - assert.Nil(t, tpl) - - msg.WithMetadata(json.RawMessage(`{"templating": {"template": {"uuid": "4ed5000f-5c94-4143-9697-b7cbd230a381", "name": "Update"}}}`)) - - // valid templating, no error - tpl, err = whatsapp.GetTemplating(msg) - assert.NoError(t, err) - assert.Equal(t, "4ed5000f-5c94-4143-9697-b7cbd230a381", tpl.Template.UUID) - assert.Equal(t, "Update", tpl.Template.Name) -} - func TestGetTemplatePayload(t *testing.T) { tcs := []struct { templating string @@ -121,12 +89,11 @@ func TestGetTemplatePayload(t *testing.T) { } for _, tc := range tcs { - metadata := json.RawMessage(fmt.Sprintf(`{"templating": %s}`, tc.templating)) - msg := test.NewMockMsg(1, "87995844-2017-4ba0-bc73-f3da75b32f9b", nil, "tel:+1234567890", "hi", nil).WithMetadata(metadata) - templating, err := whatsapp.GetTemplating(msg) - require.NoError(t, err) + templating := &courier.Templating{} + jsonx.MustUnmarshal([]byte(tc.templating), templating) - actual := whatsapp.GetTemplatePayload(templating) + msg := test.NewMockMsg(1, "87995844-2017-4ba0-bc73-f3da75b32f9b", nil, "tel:+1234567890", "hi", nil).WithTemplating(templating) + actual := whatsapp.GetTemplatePayload(msg.Templating()) assert.Equal(t, tc.expected, actual) } diff --git a/handlers/test.go b/handlers/test.go index 381a668dc..81578e111 100644 --- a/handlers/test.go +++ b/handlers/test.go @@ -3,7 +3,6 @@ package handlers import ( "bytes" "context" - "encoding/json" "fmt" "io" "log" @@ -21,6 +20,7 @@ import ( "github.com/nyaruka/courier/test" "github.com/nyaruka/gocommon/httpx" "github.com/nyaruka/gocommon/i18n" + "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/gocommon/urns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -301,9 +301,9 @@ type OutgoingTestCase struct { MsgQuickReplies []string MsgLocale i18n.Locale MsgTopic string + MsgTemplating string MsgHighPriority bool MsgResponseToExternalID string - MsgMetadata json.RawMessage MsgFlow *courier.FlowReference MsgOptIn *courier.OptInReference MsgUserID courier.UserID @@ -337,8 +337,10 @@ func (tc *OutgoingTestCase) Msg(mb *test.MockBackend, ch courier.Channel) courie if tc.MsgURNAuth != "" { m.WithURNAuth(tc.MsgURNAuth) } - if len(tc.MsgMetadata) > 0 { - m.WithMetadata(tc.MsgMetadata) + if tc.MsgTemplating != "" { + templating := &courier.Templating{} + jsonx.MustUnmarshal([]byte(tc.MsgTemplating), templating) + m.WithTemplating(templating) } if tc.MsgFlow != nil { m.WithFlow(tc.MsgFlow) diff --git a/handlers/whatsapp_legacy/handler.go b/handlers/whatsapp_legacy/handler.go index f7b147b65..e1bbcc657 100644 --- a/handlers/whatsapp_legacy/handler.go +++ b/handlers/whatsapp_legacy/handler.go @@ -697,12 +697,8 @@ func buildPayloads(msg courier.MsgOut, h *handler, clog *courier.ChannelLog) ([] } else { // do we have a template? - templating, err := h.getTemplating(msg) - if err != nil { - return nil, errors.Wrapf(err, "unable to decode template: %s for channel: %s", string(msg.Metadata()), msg.Channel().UUID()) - } - if templating != nil { - namespace := templating.Namespace + if msg.Templating() != nil { + namespace := msg.Templating().Namespace if namespace == "" { namespace = msg.Channel().StringConfigForKey(configNamespace, "") } @@ -715,11 +711,11 @@ func buildPayloads(msg courier.MsgOut, h *handler, clog *courier.ChannelLog) ([] Type: "template", } payload.Template.Namespace = namespace - payload.Template.Name = templating.Template.Name + payload.Template.Name = msg.Templating().Template.Name payload.Template.Language.Policy = "deterministic" payload.Template.Language.Code = langCode - for _, comp := range templating.Components { + for _, comp := range msg.Templating().Components { if comp.Type == "body" { component := &Component{Type: "body"} for _, p := range comp.Params { @@ -1080,45 +1076,6 @@ func (h *handler) checkWhatsAppContact(channel courier.Channel, baseURL string, } } -func (h *handler) getTemplating(msg courier.MsgOut) (*MsgTemplating, error) { - if len(msg.Metadata()) == 0 { - return nil, nil - } - - metadata := &struct { - Templating *MsgTemplating `json:"templating"` - }{} - if err := json.Unmarshal(msg.Metadata(), metadata); err != nil { - return nil, err - } - - if metadata.Templating == nil { - return nil, nil - } - - if err := utils.Validate(metadata.Templating); err != nil { - return nil, errors.Wrapf(err, "invalid templating definition") - } - - return metadata.Templating, nil -} - -type MsgTemplating struct { - Template struct { - Name string `json:"name" validate:"required"` - UUID string `json:"uuid" validate:"required"` - } `json:"template" validate:"required,dive"` - Namespace string `json:"namespace"` - Components []struct { - Type string `json:"type"` - Name string `json:"name"` - Params []struct { - Type string `json:"type"` - Value string `json:"value"` - } `json:"params"` - } `json:"components"` -} - func getSupportedLanguage(lc i18n.Locale) string { // look for exact match if lang := supportedLanguages[lc]; lang != "" { diff --git a/handlers/whatsapp_legacy/handler_test.go b/handlers/whatsapp_legacy/handler_test.go index dc5eea935..9775ecf4d 100644 --- a/handlers/whatsapp_legacy/handler_test.go +++ b/handlers/whatsapp_legacy/handler_test.go @@ -2,7 +2,6 @@ package whatsapp_legacy import ( "context" - "encoding/json" "testing" "time" @@ -762,11 +761,11 @@ var defaultSendTestCases = []OutgoingTestCase{ ExpectedExtIDs: []string{"157b5e14568e8"}, }, { - Label: "Template Send", - MsgText: "templated message", - MsgURN: "whatsapp:250788123123", - MsgLocale: "eng", - MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [{"type":"body", "params": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}]}}`), + Label: "Template Send", + MsgText: "templated message", + MsgURN: "whatsapp:250788123123", + MsgLocale: "eng", + MsgTemplating: `{"template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [{"type":"body", "params": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}]}`, MockResponses: map[string][]*httpx.MockResponse{ "*/v1/messages": { httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), @@ -780,11 +779,11 @@ var defaultSendTestCases = []OutgoingTestCase{ ExpectedExtIDs: []string{"157b5e14568e8"}, }, { - Label: "Template Send no variables", - MsgText: "templated message", - MsgURN: "whatsapp:250788123123", - MsgLocale: "eng", - MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": []}}`), + Label: "Template Send no variables", + MsgText: "templated message", + MsgURN: "whatsapp:250788123123", + MsgLocale: "eng", + MsgTemplating: `{"template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": []}`, MockResponses: map[string][]*httpx.MockResponse{ "*/v1/messages": { httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), @@ -798,11 +797,11 @@ var defaultSendTestCases = []OutgoingTestCase{ ExpectedExtIDs: []string{"157b5e14568e8"}, }, { - Label: "Template Country Language", - MsgText: "templated message", - MsgURN: "whatsapp:250788123123", - MsgLocale: "eng-US", - MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [{"type":"body", "params": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}]}}`), + Label: "Template Country Language", + MsgText: "templated message", + MsgURN: "whatsapp:250788123123", + MsgLocale: "eng-US", + MsgTemplating: `{"template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [{"type":"body", "params": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}]}`, MockResponses: map[string][]*httpx.MockResponse{ "*/v1/messages": { httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), @@ -816,11 +815,11 @@ var defaultSendTestCases = []OutgoingTestCase{ ExpectedExtIDs: []string{"157b5e14568e8"}, }, { - Label: "Template Namespace", - MsgText: "templated message", - MsgURN: "whatsapp:250788123123", - MsgLocale: "eng-US", - MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "namespace": "wa_template_namespace", "components": [{"type":"body", "params": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}]}}`), + Label: "Template Namespace", + MsgText: "templated message", + MsgURN: "whatsapp:250788123123", + MsgLocale: "eng-US", + MsgTemplating: `{"template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "namespace": "wa_template_namespace", "components": [{"type":"body", "params": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}]}`, MockResponses: map[string][]*httpx.MockResponse{ "*/v1/messages": { httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), @@ -834,11 +833,11 @@ var defaultSendTestCases = []OutgoingTestCase{ ExpectedExtIDs: []string{"157b5e14568e8"}, }, { - Label: "Template Invalid Language", - MsgText: "templated message", - MsgURN: "whatsapp:250788123123", - MsgLocale: "bnt", - MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [{"type":"body", "params": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}]}}`), + Label: "Template Invalid Language", + MsgText: "templated message", + MsgURN: "whatsapp:250788123123", + MsgLocale: "bnt", + MsgTemplating: `{"template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [{"type":"body", "params": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}]}`, MockResponses: map[string][]*httpx.MockResponse{ "*/v1/messages": { httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), @@ -848,7 +847,6 @@ var defaultSendTestCases = []OutgoingTestCase{ Path: "/v1/messages", Body: `{"to":"250788123123","type":"template","template":{"namespace":"waba_namespace","name":"revive_issue","language":{"policy":"deterministic","code":"en"},"components":[{"type":"body","parameters":[{"type":"text","text":"Chef"},{"type":"text","text":"tomorrow"}]}]}}`, }}, - ExpectedExtIDs: []string{"157b5e14568e8"}, }, { diff --git a/msg.go b/msg.go index 746e62ad7..010793665 100644 --- a/msg.go +++ b/msg.go @@ -53,6 +53,24 @@ const ( MsgOriginChat MsgOrigin = "chat" ) +type Templating struct { + Template struct { + Name string `json:"name" validate:"required"` + UUID string `json:"uuid" validate:"required"` + } `json:"template" validate:"required,dive"` + Namespace string `json:"namespace"` + Components []struct { + Type string `json:"type"` + Name string `json:"name"` + Params []struct { + Type string `json:"type"` + Value string `json:"value"` + } `json:"params"` + } `json:"components"` + Language string `json:"language"` + ExternalID string `json:"external_id"` +} + //----------------------------------------------------------------------------- // Msg interface //----------------------------------------------------------------------------- @@ -77,6 +95,7 @@ type MsgOut interface { // outgoing specific QuickReplies() []string Locale() i18n.Locale + Templating() *Templating URNAuth() string Origin() MsgOrigin ContactLastSeenOn() *time.Time diff --git a/test/msg.go b/test/msg.go index 2cf26cfd6..ee59cb789 100644 --- a/test/msg.go +++ b/test/msg.go @@ -19,6 +19,7 @@ type MockMsg struct { text string attachments []string locale i18n.Locale + templating *courier.Templating externalID string contactName string highPriority bool @@ -60,21 +61,22 @@ func (m *MockMsg) URN() urns.URN { return m.urn } func (m *MockMsg) Channel() courier.Channel { return m.channel } // outgoing specific -func (m *MockMsg) QuickReplies() []string { return m.quickReplies } -func (m *MockMsg) Locale() i18n.Locale { return m.locale } -func (m *MockMsg) URNAuth() string { return m.urnAuth } -func (m *MockMsg) Origin() courier.MsgOrigin { return m.origin } -func (m *MockMsg) ContactLastSeenOn() *time.Time { return m.contactLastSeenOn } -func (m *MockMsg) Topic() string { return m.topic } -func (m *MockMsg) Metadata() json.RawMessage { return m.metadata } -func (m *MockMsg) ResponseToExternalID() string { return m.responseToExternalID } -func (m *MockMsg) SentOn() *time.Time { return m.sentOn } -func (m *MockMsg) IsResend() bool { return m.isResend } -func (m *MockMsg) Flow() *courier.FlowReference { return m.flow } -func (m *MockMsg) OptIn() *courier.OptInReference { return m.optIn } -func (m *MockMsg) UserID() courier.UserID { return m.userID } -func (m *MockMsg) SessionStatus() string { return "" } -func (m *MockMsg) HighPriority() bool { return m.highPriority } +func (m *MockMsg) QuickReplies() []string { return m.quickReplies } +func (m *MockMsg) Locale() i18n.Locale { return m.locale } +func (m *MockMsg) Templating() *courier.Templating { return m.templating } +func (m *MockMsg) URNAuth() string { return m.urnAuth } +func (m *MockMsg) Origin() courier.MsgOrigin { return m.origin } +func (m *MockMsg) ContactLastSeenOn() *time.Time { return m.contactLastSeenOn } +func (m *MockMsg) Topic() string { return m.topic } +func (m *MockMsg) Metadata() json.RawMessage { return m.metadata } +func (m *MockMsg) ResponseToExternalID() string { return m.responseToExternalID } +func (m *MockMsg) SentOn() *time.Time { return m.sentOn } +func (m *MockMsg) IsResend() bool { return m.isResend } +func (m *MockMsg) Flow() *courier.FlowReference { return m.flow } +func (m *MockMsg) OptIn() *courier.OptInReference { return m.optIn } +func (m *MockMsg) UserID() courier.UserID { return m.userID } +func (m *MockMsg) SessionStatus() string { return "" } +func (m *MockMsg) HighPriority() bool { return m.highPriority } // incoming specific func (m *MockMsg) ReceivedOn() *time.Time { return m.receivedOn } @@ -90,14 +92,11 @@ func (m *MockMsg) WithURNAuthTokens(tokens map[string]string) courier.MsgIn { func (m *MockMsg) WithReceivedOn(date time.Time) courier.MsgIn { m.receivedOn = &date; return m } // used to create outgoing messages for testing -func (m *MockMsg) WithID(id courier.MsgID) courier.MsgOut { m.id = id; return m } -func (m *MockMsg) WithUUID(uuid courier.MsgUUID) courier.MsgOut { m.uuid = uuid; return m } -func (m *MockMsg) WithMetadata(metadata json.RawMessage) courier.MsgOut { - m.metadata = metadata - return m -} -func (m *MockMsg) WithFlow(f *courier.FlowReference) courier.MsgOut { m.flow = f; return m } -func (m *MockMsg) WithOptIn(o *courier.OptInReference) courier.MsgOut { m.optIn = o; return m } -func (m *MockMsg) WithUserID(uid courier.UserID) courier.MsgOut { m.userID = uid; return m } -func (m *MockMsg) WithLocale(lc i18n.Locale) courier.MsgOut { m.locale = lc; return m } -func (m *MockMsg) WithURNAuth(token string) courier.MsgOut { m.urnAuth = token; return m } +func (m *MockMsg) WithID(id courier.MsgID) courier.MsgOut { m.id = id; return m } +func (m *MockMsg) WithUUID(uuid courier.MsgUUID) courier.MsgOut { m.uuid = uuid; return m } +func (m *MockMsg) WithTemplating(t *courier.Templating) courier.MsgOut { m.templating = t; return m } +func (m *MockMsg) WithFlow(f *courier.FlowReference) courier.MsgOut { m.flow = f; return m } +func (m *MockMsg) WithOptIn(o *courier.OptInReference) courier.MsgOut { m.optIn = o; return m } +func (m *MockMsg) WithUserID(uid courier.UserID) courier.MsgOut { m.userID = uid; return m } +func (m *MockMsg) WithLocale(lc i18n.Locale) courier.MsgOut { m.locale = lc; return m } +func (m *MockMsg) WithURNAuth(token string) courier.MsgOut { m.urnAuth = token; return m }