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
12 changes: 11 additions & 1 deletion assets/static/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,20 @@ type TemplateTranslation struct {
Locale_ i18n.Locale `json:"locale" validate:"required"`
Namespace_ string `json:"namespace"`
VariableCount_ int `json:"variable_count"`
Components_ []map[string]any `json:"components"`
Copy link
Member

Choose a reason for hiding this comment

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

we only use Content for generating a preview of the message so I guess components would be the same... couldn't these just be strings?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we can keep using content for the preview here and make sure each template has that generate properly when syncing them

Copy link
Member

Choose a reason for hiding this comment

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

Tho wouldn't we want to convert button components into quick replies etc? I kinda hate putting whatsapp specific knowledge in the engine, right in the send_msg action but I don't see any other way. Let's just try to isolate it in a single function for now that takes a template + parameters and returns a MsgOut.

The other way to approach this would be to put the onus on the UI to render a preview of the message based on the templating data.. but then the message still looks empty in API fetches, archives etc, so I think I prefer having the engine continue to create a preview.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah ideally we need have the quick replies turned/matched to the buttons of the templates

I have not thought of quick replies so far, I want to make sure we support all components for templates and see how to make the QRs and buttons merged

Params_ map[string][]any `json:"params"`
Copy link
Member

Choose a reason for hiding this comment

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

no reason not to use a struct for param

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added TemplateParam

}

// 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, components []map[string]any, params map[string][]any) *TemplateTranslation {
return &TemplateTranslation{
Channel_: channel,
Content_: content,
Namespace_: namespace,
Locale_: locale,
VariableCount_: variableCount,
Components_: components,
Params_: params,
}
}

Expand All @@ -70,3 +74,9 @@ 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_ }

// Components returns the components for this template translation
func (t *TemplateTranslation) Components() []map[string]any { return t.Components_ }

// Params returns the params for this template translation
func (t *TemplateTranslation) Params() map[string][]any { return t.Params_ }
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]any{}, map[string][]any{})
assert.Equal(t, channel, translation.Channel())
assert.Equal(t, i18n.Locale("eng-US"), translation.Locale())
assert.Equal(t, "Hello {{1}}", translation.Content())
Expand Down
2 changes: 2 additions & 0 deletions assets/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ type TemplateTranslation interface {
Namespace() string
VariableCount() int
Channel() *ChannelReference
Components() []map[string]any
Params() map[string][]any
}

// TemplateReference is used to reference a Template
Expand Down
32 changes: 28 additions & 4 deletions flows/actions/send_msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,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][]map[string]string `json:"params"`
}

// LocalizationUUID gets the UUID which identifies this object for localization
Expand Down Expand Up @@ -121,8 +122,31 @@ func (a *SendMsgAction) Execute(run flows.Run, step flows.Step, logModifier flow
evaluatedVariables[i] = sub
}

evaluatedParams := make(map[string][]any)

for compKey, compParams := range a.Templating.Params {
compVariables := make([]any, len(compParams))
for _, variableMap := range compParams {
evaluatedMap := make(map[string]string)
for k, v := range variableMap {
if k == "value" {
sub, err := run.EvaluateTemplate(variableMap[k])
if err != nil {
logEvent(events.NewError(err))
}
evaluatedMap[k] = sub
} else {
evaluatedMap[k] = v
}
}
compVariables = append(compVariables, evaluatedMap)
}
evaluatedParams[compKey] = compVariables

}

evaluatedText = translation.Substitute(evaluatedVariables)
templating = flows.NewMsgTemplating(a.Templating.Template, evaluatedVariables, translation.Namespace())
templating = flows.NewMsgTemplating(a.Templating.Template, evaluatedVariables, translation.Namespace(), evaluatedParams)
locale = translation.Locale()
}
}
Expand Down
7 changes: 6 additions & 1 deletion flows/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ type MsgTemplating struct {
Template_ *assets.TemplateReference `json:"template"`
Variables_ []string `json:"variables,omitempty"`
Namespace_ string `json:"namespace"`
Params_ map[string][]any `json:"params"`
}

// Template returns the template this msg template is for
Expand All @@ -184,12 +185,16 @@ func (t MsgTemplating) Variables() []string { return t.Variables_ }
// Namespace returns the namespace that should be for the template
func (t MsgTemplating) Namespace() string { return t.Namespace_ }

// Params returns the params that should be used for the template
func (t MsgTemplating) Params() map[string][]any { return t.Params_ }

// NewMsgTemplating creates and returns a new msg template
func NewMsgTemplating(template *assets.TemplateReference, variables []string, namespace string) *MsgTemplating {
func NewMsgTemplating(template *assets.TemplateReference, variables []string, namespace string, params map[string][]any) *MsgTemplating {
return &MsgTemplating{
Template_: template,
Variables_: variables,
Namespace_: namespace,
Params_: params,
}
}

Expand Down
10 changes: 5 additions & 5 deletions flows/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestTemplateTranslation(t *testing.T) {
channel := assets.NewChannelReference("0bce5fd3-c215-45a0-bcb8-2386eb194175", "Test Channel")

for i, tc := range tcs {
tt := flows.NewTemplateTranslation(static.NewTemplateTranslation(channel, i18n.Locale("eng-US"), tc.Content, len(tc.Variables), "a6a8863e_7879_4487_ad24_5e2ea429027c"))
tt := flows.NewTemplateTranslation(static.NewTemplateTranslation(channel, i18n.Locale("eng-US"), tc.Content, len(tc.Variables), "a6a8863e_7879_4487_ad24_5e2ea429027c", []map[string]any{}, map[string][]any{}))
result := tt.Substitute(tc.Variables)
assert.Equal(t, tc.Expected, result, "%d: unexpected template substitution", i)
}
Expand All @@ -39,10 +39,10 @@ func TestTemplate(t *testing.T) {
channel1Ref := assets.NewChannelReference(channel1.UUID(), channel1.Name())
channel2Ref := assets.NewChannelReference(channel2.UUID(), channel2.Name())

tt1 := static.NewTemplateTranslation(channel1Ref, i18n.Locale("eng"), "Hello {{1}}", 1, "")
tt2 := static.NewTemplateTranslation(channel1Ref, i18n.Locale("spa-EC"), "Que tal {{1}}", 1, "")
tt3 := static.NewTemplateTranslation(channel1Ref, i18n.Locale("spa-ES"), "Hola {{1}}", 1, "")
tt4 := static.NewTemplateTranslation(channel2Ref, i18n.Locale("en"), "Hello {{1}}", 1, "")
tt1 := static.NewTemplateTranslation(channel1Ref, i18n.Locale("eng"), "Hello {{1}}", 1, "", []map[string]any{}, map[string][]any{})
tt2 := static.NewTemplateTranslation(channel1Ref, i18n.Locale("spa-EC"), "Que tal {{1}}", 1, "", []map[string]any{}, map[string][]any{})
tt3 := static.NewTemplateTranslation(channel1Ref, i18n.Locale("spa-ES"), "Hola {{1}}", 1, "", []map[string]any{}, map[string][]any{})
tt4 := static.NewTemplateTranslation(channel2Ref, i18n.Locale("en"), "Hello {{1}}", 1, "", []map[string]any{}, map[string][]any{})
template := flows.NewTemplate(static.NewTemplate("c520cbda-e118-440f-aaf6-c0485088384f", "greeting", []*static.TemplateTranslation{tt1, tt2, tt3, tt4}))

tas := flows.NewTemplateAssets([]assets.Template{template})
Expand Down