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

Rework template assets to use variables instead of component specific param lists #1241

Merged
merged 1 commit into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
61 changes: 33 additions & 28 deletions assets/static/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,40 @@

// TemplateTranslation represents a single template translation
type TemplateTranslation struct {
Channel_ *assets.ChannelReference `json:"channel" validate:"required"`
Locale_ i18n.Locale `json:"locale" validate:"required"`
Channel_ *assets.ChannelReference `json:"channel" validate:"required"`
Locale_ i18n.Locale `json:"locale" validate:"required"`
Namespace_ string `json:"namespace"`
Components_ []*TemplateComponent `json:"components"`
Variables_ []*TemplateVariable `json:"variables"`
}

// NewTemplateTranslation creates a new template translation
func NewTemplateTranslation(channel *assets.ChannelReference, locale i18n.Locale, namespace string, components []*TemplateComponent) *TemplateTranslation {
func NewTemplateTranslation(channel *assets.ChannelReference, locale i18n.Locale, namespace string, components []*TemplateComponent, variables []*TemplateVariable) *TemplateTranslation {
return &TemplateTranslation{
Channel_: channel,
Namespace_: namespace,
Locale_: locale,
Components_: components,
Variables_: variables,
}
}

// Components returns the components structure for this template
// Components returns the components for this template translation
func (t *TemplateTranslation) Components() []assets.TemplateComponent {
tcs := make([]assets.TemplateComponent, len(t.Components_))
cs := make([]assets.TemplateComponent, len(t.Components_))
for k, tc := range t.Components_ {
tcs[k] = tc
cs[k] = tc
}
return tcs
return cs
}

// Variables returns the variables for this template translation
func (t *TemplateTranslation) Variables() []assets.TemplateVariable {
vs := make([]assets.TemplateVariable, len(t.Variables_))

Check warning on line 70 in assets/static/template.go

View check run for this annotation

Codecov / codecov/patch

assets/static/template.go#L70

Added line #L70 was not covered by tests
for i := range t.Variables_ {
vs[i] = t.Variables_[i]

Check warning on line 72 in assets/static/template.go

View check run for this annotation

Codecov / codecov/patch

assets/static/template.go#L72

Added line #L72 was not covered by tests
}
return vs

Check warning on line 74 in assets/static/template.go

View check run for this annotation

Codecov / codecov/patch

assets/static/template.go#L74

Added line #L74 was not covered by tests
}

// Namespace returns the namespace for this template
Expand All @@ -73,11 +84,11 @@
func (t *TemplateTranslation) Channel() *assets.ChannelReference { return t.Channel_ }

type TemplateComponent struct {
Type_ string `json:"type"`
Name_ string `json:"name"`
Content_ string `json:"content"`
Display_ string `json:"display"`
Params_ []*TemplateParam `json:"params"`
Type_ string `json:"type"`
Name_ string `json:"name"`
Content_ string `json:"content"`
Display_ string `json:"display"`
Variables_ map[string]int `json:"variables"`
}

// Type returns the type for this template component
Expand All @@ -92,29 +103,23 @@
// Display returns the display for this template component
func (t *TemplateComponent) Display() string { return t.Display_ }

// Params returns the params for this template component
func (t *TemplateComponent) Params() []assets.TemplateParam {
tps := make([]assets.TemplateParam, len(t.Params_))
for i := range t.Params_ {
tps[i] = t.Params_[i]
}
return tps
}
// Variables returns the variable mapping for this template component
func (t *TemplateComponent) Variables() map[string]int { return t.Variables_ }

// NewTemplateComponent creates a new template param
func NewTemplateComponent(type_, name, content, display string, params []*TemplateParam) *TemplateComponent {
return &TemplateComponent{Type_: type_, Name_: name, Content_: content, Display_: display, Params_: params}
func NewTemplateComponent(type_, name, content, display string, variables map[string]int) *TemplateComponent {
return &TemplateComponent{Type_: type_, Name_: name, Content_: content, Display_: display, Variables_: variables}
}

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

// Type returns the type for this parameter
func (t *TemplateParam) Type() string { return t.Type_ }
func (t *TemplateVariable) Type() string { return t.Type_ }

// NewTemplateParam creates a new template param
func NewTemplateParam(paramType string) *TemplateParam {
return &TemplateParam{Type_: paramType}
// NewTemplateVariable creates a new template variable
func NewTemplateVariable(paramType string) *TemplateVariable {
return &TemplateVariable{Type_: paramType}
}
35 changes: 19 additions & 16 deletions assets/static/template_test.go
Original file line number Diff line number Diff line change
@@ -1,39 +1,42 @@
package static
package static_test

import (
"testing"

"github.com/nyaruka/gocommon/i18n"
"github.com/nyaruka/gocommon/jsonx"
"github.com/nyaruka/goflow/assets"
"github.com/nyaruka/goflow/assets/static"
"github.com/stretchr/testify/assert"
)

func TestTemplate(t *testing.T) {
channel := assets.NewChannelReference("Test Channel", "ffffffff-9b24-92e1-ffff-ffffb207cdb4")

tp1 := NewTemplateParam("text")
assert.Equal(t, "text", tp1.Type())
v1 := static.NewTemplateVariable("text")
v2 := static.NewTemplateVariable("text")
assert.Equal(t, "text", v1.Type())
assert.Equal(t, "text", v2.Type())

tc1 := NewTemplateComponent("body", "body", "Hello {{1}}", "", []*TemplateParam{tp1})
tc2 := NewTemplateComponent("button/url", "button.0", "http://google.com?q={{1}}", "Go {{1}", []*TemplateParam{NewTemplateParam("text"), NewTemplateParam("text")})
c1 := static.NewTemplateComponent("body", "body", "Hello {{1}}", "", map[string]int{"1": 0})
c2 := static.NewTemplateComponent("button/url", "button.0", "http://google.com?q={{1}}", "Go", map[string]int{"1": 1})

assert.Equal(t, "body", tc1.Type())
assert.Equal(t, "body", tc1.Name())
assert.Equal(t, "Hello {{1}}", tc1.Content())
assert.Equal(t, "", tc1.Display())
assert.Equal(t, []assets.TemplateParam{tp1}, tc1.Params())
assert.Equal(t, "body", c1.Type())
assert.Equal(t, "body", c1.Name())
assert.Equal(t, "Hello {{1}}", c1.Content())
assert.Equal(t, "", c1.Display())
assert.Equal(t, map[string]int{"1": 0}, c1.Variables())

translation := NewTemplateTranslation(channel, i18n.Locale("eng-US"), "0162a7f4_dfe4_4c96_be07_854d5dba3b2b", []*TemplateComponent{tc1, tc2})
translation := static.NewTemplateTranslation(channel, i18n.Locale("eng-US"), "0162a7f4_dfe4_4c96_be07_854d5dba3b2b", []*static.TemplateComponent{c1, c2}, []*static.TemplateVariable{v1, v2})
assert.Equal(t, channel, translation.Channel())
assert.Equal(t, i18n.Locale("eng-US"), translation.Locale())
assert.Equal(t, "0162a7f4_dfe4_4c96_be07_854d5dba3b2b", translation.Namespace())
assert.Equal(t, []assets.TemplateComponent{
(assets.TemplateComponent)(tc1),
(assets.TemplateComponent)(tc2),
(assets.TemplateComponent)(c1),
(assets.TemplateComponent)(c2),
}, translation.Components())

template := NewTemplate(assets.TemplateUUID("8a9c1f73-5059-46a0-ba4a-6390979c01d3"), "hello", []*TemplateTranslation{translation})
template := static.NewTemplate(assets.TemplateUUID("8a9c1f73-5059-46a0-ba4a-6390979c01d3"), "hello", []*static.TemplateTranslation{translation})
assert.Equal(t, assets.TemplateUUID("8a9c1f73-5059-46a0-ba4a-6390979c01d3"), template.UUID())
assert.Equal(t, "hello", template.Name())
assert.Equal(t, 1, len(template.Translations()))
Expand All @@ -42,8 +45,8 @@ func TestTemplate(t *testing.T) {
asJSON, err := jsonx.Marshal(template)
assert.NoError(t, err)

copy := Template{}
err = jsonx.Unmarshal(asJSON, &copy)
copy := &static.Template{}
err = jsonx.Unmarshal(asJSON, copy)
assert.NoError(t, err)

assert.Equal(t, copy.Name(), template.Name())
Expand Down
16 changes: 9 additions & 7 deletions assets/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ type TemplateUUID uuids.UUID
// "type": "body",
// "name": "body",
// "content": "Hello {{1}}",
// "params": [{"type": "text"}]
// "variables": {"1": 0}
// }
// ]
// ],
// "variables": [{"type": "text"}]
// },
// {
// "locale": "fra",
Expand All @@ -42,9 +43,10 @@ type TemplateUUID uuids.UUID
// "type": "body",
// "name": "body",
// "content": "Bonjour {{1}}",
// "params": [{"type": "text"}]
// "variables": {"1": 0}
// }
// ]
// ],
// "variables": [{"type": "text"}]
// }
// ]
// }
Expand All @@ -56,8 +58,7 @@ type Template interface {
Translations() []TemplateTranslation
}

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

Expand All @@ -66,7 +67,7 @@ type TemplateComponent interface {
Name() string
Content() string
Display() string
Params() []TemplateParam
Variables() map[string]int
}

// TemplateTranslation represents a single translation for a specific template and channel
Expand All @@ -75,6 +76,7 @@ type TemplateTranslation interface {
Namespace() string
Channel() *ChannelReference
Components() []TemplateComponent
Variables() []TemplateVariable
}

// TemplateReference is used to reference a Template
Expand Down
13 changes: 9 additions & 4 deletions flows/actions/send_msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"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 @@ -161,15 +162,19 @@
var previewParts []string
var previewQRs []string

variables := translation.Variables()

for _, comp := range translation.Components() {
varMap := comp.Variables()
paramValues := evaluatedParams[comp.Name()]
params := make([]flows.TemplatingParam, len(comp.Params()))
params := make([]flows.TemplatingParam, len(varMap))

for i, p := range comp.Params() {
for i, varName := range utils.SortedKeys(varMap) {
v := variables[varMap[varName]]
if i < len(paramValues) {
params[i] = flows.TemplatingParam{Type: p.Type(), Value: paramValues[i]}
params[i] = flows.TemplatingParam{Type: v.Type(), Value: paramValues[i]}
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason to not add varName in the TemplatingParam?
That is the new data we need to be able to support the Twilio templates which triggered this refactoring.
I guess that could be another PR too if this is just for backward compatibility

} else {
params[i] = flows.TemplatingParam{Type: p.Type(), Value: ""}
params[i] = flows.TemplatingParam{Type: v.Type(), Value: ""}

Check warning on line 177 in flows/actions/send_msg.go

View check run for this annotation

Codecov / codecov/patch

flows/actions/send_msg.go#L177

Added line #L177 was not covered by tests
}
}

Expand Down
Loading
Loading