Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP support WA template params #1199

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 29 additions & 6 deletions assets/static/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,23 @@ func (t *Template) Translations() []assets.TemplateTranslation {

// TemplateTranslation represents a single template translation
type TemplateTranslation struct {
Channel_ *assets.ChannelReference `json:"channel" validate:"required"`
Content_ string `json:"content" validate:"required"`
Locale_ i18n.Locale `json:"locale" validate:"required"`
Namespace_ string `json:"namespace"`
VariableCount_ int `json:"variable_count"`
Channel_ *assets.ChannelReference `json:"channel" validate:"required"`
Content_ string `json:"content" validate:"required"`
Locale_ i18n.Locale `json:"locale" validate:"required"`
Namespace_ string `json:"namespace"`
VariableCount_ int `json:"variable_count"`
Params_ map[string][]TemplateParam `json:"params"`
}

// NewTemplateTranslation creates a new template translation
func NewTemplateTranslation(channel *assets.ChannelReference, locale i18n.Locale, content string, variableCount int, namespace string) *TemplateTranslation {
func NewTemplateTranslation(channel *assets.ChannelReference, locale i18n.Locale, content string, variableCount int, namespace string, params map[string][]TemplateParam) *TemplateTranslation {
return &TemplateTranslation{
Channel_: channel,
Content_: content,
Namespace_: namespace,
Locale_: locale,
VariableCount_: variableCount,
Params_: params,
}
}

Expand All @@ -70,3 +72,24 @@ func (t *TemplateTranslation) VariableCount() int { return t.VariableCount_ }

// Channel returns the channel this template translation is for
func (t *TemplateTranslation) Channel() *assets.ChannelReference { return t.Channel_ }

// Params returns the params for this template translation
func (t *TemplateTranslation) Params() map[string][]assets.TemplateParam {
prs := make(map[string][]assets.TemplateParam, len(t.Params_))
for k, v := range t.Params_ {
compParams := make([]assets.TemplateParam, len(v))
for i, pr := range v {
compParams[i] = (assets.TemplateParam)(&pr)
}
prs[k] = compParams
}
return prs
}

// TemplateParam represents a single parameter for a template translation
type TemplateParam struct {
Type_ string `json:"type"`
}

// Type returns the type for this parameter
func (t *TemplateParam) Type() string { return t.Type_ }
2 changes: 1 addition & 1 deletion assets/static/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
func TestTemplate(t *testing.T) {
channel := assets.NewChannelReference("Test Channel", "ffffffff-9b24-92e1-ffff-ffffb207cdb4")

translation := NewTemplateTranslation(channel, i18n.Locale("eng-US"), "Hello {{1}}", 1, "0162a7f4_dfe4_4c96_be07_854d5dba3b2b")
translation := NewTemplateTranslation(channel, i18n.Locale("eng-US"), "Hello {{1}}", 1, "0162a7f4_dfe4_4c96_be07_854d5dba3b2b", map[string][]TemplateParam{})
assert.Equal(t, channel, translation.Channel())
assert.Equal(t, i18n.Locale("eng-US"), translation.Locale())
assert.Equal(t, "Hello {{1}}", translation.Content())
Expand Down
6 changes: 6 additions & 0 deletions assets/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,19 @@ type Template interface {
Translations() []TemplateTranslation
}

// TemplateParam is a parameter for template translation
type TemplateParam interface {
Type() string
}

// TemplateTranslation represents a single translation for a specific template and channel
type TemplateTranslation interface {
Content() string
Locale() i18n.Locale
Namespace() string
VariableCount() int
Channel() *ChannelReference
Params() map[string][]TemplateParam
}

// TemplateReference is used to reference a Template
Expand Down
92 changes: 65 additions & 27 deletions flows/actions/send_msg.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package actions

import (
"strings"

"github.com/nyaruka/gocommon/i18n"
"github.com/nyaruka/gocommon/urns"
"github.com/nyaruka/gocommon/uuids"
"github.com/nyaruka/goflow/assets"
"github.com/nyaruka/goflow/flows"
"github.com/nyaruka/goflow/flows/events"
"github.com/nyaruka/goflow/utils"
)

func init() {
Expand Down Expand Up @@ -52,9 +55,10 @@ type SendMsgAction struct {

// Templating represents the templating that should be used if possible
type Templating struct {
UUID uuids.UUID `json:"uuid" validate:"required,uuid4"`
Template *assets.TemplateReference `json:"template" validate:"required"`
Variables []string `json:"variables" engine:"localized,evaluated"`
UUID uuids.UUID `json:"uuid" validate:"required,uuid4"`
Template *assets.TemplateReference `json:"template" validate:"required"`
Variables []string `json:"variables" engine:"localized,evaluated"`
Params map[string][]flows.TemplateParam `json:"params"`
}

// LocalizationUUID gets the UUID which identifies this object for localization
Expand Down Expand Up @@ -99,35 +103,19 @@ func (a *SendMsgAction) Execute(run flows.Run, step flows.Step, logModifier flow
channelRef := assets.NewChannelReference(dest.Channel.UUID(), dest.Channel.Name())

// do we have a template defined?
var templating *flows.MsgTemplating
var msg *flows.MsgOut
if template != nil {
// looks for a translation in the contact locale or environment default
locales := []i18n.Locale{
run.Session().MergedEnvironment().DefaultLocale(),
run.Session().Environment().DefaultLocale(),
locales := []i18n.Locale{run.Session().MergedEnvironment().DefaultLocale(), run.Session().Environment().DefaultLocale()}
templateTranslation := template.FindTranslation(dest.Channel, locales)
if templateTranslation != nil {
msg = getTemplatingMsg(a, run, urn, channelRef, templateTranslation, evaluatedAttachments, evaluatedQuickReplies, unsendableReason, logEvent)
}
}

translation := template.FindTranslation(dest.Channel, locales)
if translation != nil {
localizedVariables, _ := run.GetTextArray(uuids.UUID(a.Templating.UUID), "variables", a.Templating.Variables, nil)

// evaluate our variables
evaluatedVariables := make([]string, len(localizedVariables))
for i, variable := range localizedVariables {
sub, err := run.EvaluateTemplate(variable)
if err != nil {
logEvent(events.NewError(err))
}
evaluatedVariables[i] = sub
}

evaluatedText = translation.Substitute(evaluatedVariables)
templating = flows.NewMsgTemplating(a.Templating.Template, evaluatedVariables, translation.Namespace())
locale = translation.Locale()
}
if msg == nil {
msg = flows.NewMsgOut(urn, channelRef, evaluatedText, evaluatedAttachments, evaluatedQuickReplies, nil, a.Topic, locale, unsendableReason)
}

msg := flows.NewMsgOut(urn, channelRef, evaluatedText, evaluatedAttachments, evaluatedQuickReplies, templating, a.Topic, locale, unsendableReason)
logEvent(events.NewMsgCreated(msg))
}

Expand All @@ -140,3 +128,53 @@ func (a *SendMsgAction) Execute(run flows.Run, step flows.Step, logModifier flow

return nil
}

func getTemplatingMsg(action *SendMsgAction, run flows.Run, urn urns.URN, channelRef *assets.ChannelReference, templateTranslation *flows.TemplateTranslation, evaluatedAttachments []utils.Attachment, evaluatedQuickReplies []string, unsendableReason flows.UnsendableReason, logEvent flows.EventCallback) *flows.MsgOut {
qrIndex := 0

localizedVariables, _ := run.GetTextArray(uuids.UUID(action.Templating.UUID), "variables", action.Templating.Variables, nil)
evaluatedVariables := make([]string, len(localizedVariables))
for i, variable := range localizedVariables {
sub, err := run.EvaluateTemplate(variable)
if err != nil {
logEvent(events.NewError(err))
}
evaluatedVariables[i] = sub
}
evaluatedText := templateTranslation.Substitute(evaluatedVariables)

evaluatedParams := make(map[string][]flows.TemplateParam)
for compKey, compParams := range action.Templating.Params {
compVariables := make([]flows.TemplateParam, len(compParams))
for i, templateParam := range compParams {
var paramValue string
var err error
if strings.HasPrefix(compKey, "button.") {
paramValue = evaluatedQuickReplies[qrIndex]
qrIndex++
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for QRs we iterate through them and take the next available for the parameter button parameters

} else if templateParam.Type() != "text" {
paramValue = ""
for _, att := range evaluatedAttachments {
attType := strings.Split(att.ContentType(), "/")[0]
if templateParam.Type() == attType {
paramValue = att.URL()
break
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use first attachment matching the type we expect from the template params

}
} else {
localizedParamVariables, _ := run.GetTextArray(uuids.UUID(templateParam.UUID()), "value", []string{templateParam.Value()}, nil)
paramValue, err = run.EvaluateTemplate(localizedParamVariables[0])
if err != nil {
logEvent(events.NewError(err))
}
}
evaluatedParam := flows.NewTemplateParam(templateParam.Type(), templateParam.UUID(), paramValue)
compVariables[i] = evaluatedParam
}
evaluatedParams[compKey] = compVariables
}
templating := flows.NewMsgTemplating(action.Templating.Template, evaluatedVariables, templateTranslation.Namespace(), evaluatedParams)
locale := templateTranslation.Locale()

return flows.NewMsgOut(urn, channelRef, evaluatedText, evaluatedAttachments, evaluatedQuickReplies, templating, action.Topic, locale, unsendableReason)
}
67 changes: 67 additions & 0 deletions flows/actions/testdata/send_msg.json
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@
"text": "Hi @contact.name, who's a good boy?",
"templating": {
"uuid": "9c4bf5b5-3aa4-48ec-9bb9-424a9cbc6785",
"params": null,
"template": {
"uuid": "b620b463-8d15-427f-b2e3-4f44f9f071ec",
"name": "missing"
Expand Down Expand Up @@ -419,6 +420,20 @@
"text": "Hi Ryan Lewis, who's a good boy?",
"templating": {
"uuid": "9c4bf5b5-3aa4-48ec-9bb9-424a9cbc6785",
"params": {
"body": [
{
"type": "text",
"uuid": "74d59988-18c0-4919-9cb4-9f9e3847ed50",
"value": "@contact.name"
},
{
"type": "text",
"uuid": "f383e94d-0598-4fd9-81d7-f0aea397d63b",
"value": "boy"
}
]
},
"template": {
"uuid": "5722e1fd-fe32-4e74-ac78-3cf41a6adb7e",
"name": "affirmation"
Expand All @@ -444,6 +459,20 @@
},
"text": "Hi Ryan Lewis, who's an excellent boy?",
"templating": {
"params": {
"body": [
{
"type": "text",
"uuid": "74d59988-18c0-4919-9cb4-9f9e3847ed50",
"value": "Ryan Lewis"
},
{
"type": "text",
"uuid": "f383e94d-0598-4fd9-81d7-f0aea397d63b",
"value": "boy"
}
]
},
"template": {
"uuid": "5722e1fd-fe32-4e74-ac78-3cf41a6adb7e",
"name": "affirmation"
Expand Down Expand Up @@ -491,6 +520,20 @@
"text": "Hi Ryan Lewis, who's a good boy?",
"templating": {
"uuid": "9c4bf5b5-3aa4-48ec-9bb9-424a9cbc6785",
"params": {
"body": [
{
"type": "text",
"uuid": "74d59988-18c0-4919-9cb4-9f9e3847ed50",
"value": "@contact.name"
},
{
"type": "text",
"uuid": "f383e94d-0598-4fd9-81d7-f0aea397d63b",
"value": "boy"
}
]
},
"template": {
"uuid": "5722e1fd-fe32-4e74-ac78-3cf41a6adb7e",
"name": "affirmation"
Expand All @@ -508,6 +551,12 @@
"@contact.name",
"niño"
]
},
"f383e94d-0598-4fd9-81d7-f0aea397d63b": {
"value": ["niño"]
},
"74d59988-18c0-4919-9cb4-9f9e3847ed50": {
"value": ["@contact.name"]
}
}
},
Expand All @@ -525,6 +574,20 @@
},
"text": "Hola Ryan Lewis, quien es un niño excelente?",
"templating": {
"params": {
"body": [
{
"type": "text",
"uuid": "74d59988-18c0-4919-9cb4-9f9e3847ed50",
"value": "Ryan Lewis"
},
{
"type": "text",
"uuid": "f383e94d-0598-4fd9-81d7-f0aea397d63b",
"value": "niño"
}
]
},
"template": {
"uuid": "5722e1fd-fe32-4e74-ac78-3cf41a6adb7e",
"name": "affirmation"
Expand Down Expand Up @@ -573,6 +636,7 @@
"text": "Hi there, it's time to get up!",
"templating": {
"uuid": "2edc8dfd-aef0-41cf-a900-8a71bdb00900",
"params": null,
"template": {
"uuid": "2edc8dfd-aef0-41cf-a900-8a71bdb00900",
"name": "wakeup"
Expand All @@ -594,6 +658,7 @@
},
"text": "Hi there, it's time to get up!",
"templating": {
"params": {},
"template": {
"uuid": "2edc8dfd-aef0-41cf-a900-8a71bdb00900",
"name": "wakeup"
Expand Down Expand Up @@ -631,6 +696,7 @@
"uuid": "4f0510e9-7e41-4f83-aaa3-59b6e9a17e70",
"text": "Hi Ryan, wakeup!",
"templating": {
"params": null,
"uuid": "dce1ab9c-81d7-4e2f-b7d6-33e577c731ba",
"template": {
"uuid": "2edc8dfd-aef0-41cf-a900-8a71bdb00900",
Expand Down Expand Up @@ -662,6 +728,7 @@
},
"text": "Hi there, it's time to get up!",
"templating": {
"params": {},
"template": {
"uuid": "2edc8dfd-aef0-41cf-a900-8a71bdb00900",
"name": "wakeup"
Expand Down
Loading