Skip to content

Commit

Permalink
Merge pull request #147 from nyaruka/legacy_exp_tweaks
Browse files Browse the repository at this point in the history
Legacy expression migration fixes
  • Loading branch information
rowanseymour authored Feb 5, 2018
2 parents 33f9c52 + 53cef17 commit cbcb254
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 89 deletions.
200 changes: 119 additions & 81 deletions excellent/legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ package excellent
import (
"bytes"
"fmt"
"log"
"strconv"
"strings"
"time"

"github.com/antlr/antlr4/runtime/Go/antlr"
"github.com/nyaruka/gocommon/urns"
Expand All @@ -15,41 +13,43 @@ import (
)

var topLevelScopes = []string{"contact", "child", "parent", "run"}
var times = map[string]time.Duration{}

func timeTrack(start time.Time, name string) {
elapsed := time.Since(start)
times[name] += elapsed
log.Printf("%s took %s (%s)", name, elapsed, times[name])
}
type varMapper struct {
// subitems that should be replaced completely with the given strings
substitutions map[string]string

type vars map[string]interface{}
// base for fixed subitems, e.g. "contact"
base string

func (v vars) Resolve(key string) interface{} {
value, ok := v[key]
if ok {
return value
}
return key
}
// recognized fixed subitems, e.g. "name" or "uuid"
baseVars map[string]interface{}

func (v vars) Default() interface{} {
return v["__default__"]
}
// nesting for arbitrary subitems, e.g. contact fields or run results
arbitraryNesting string

func (v vars) String() string {
return fmt.Sprintf("%s", v["__default__"])
// mapper for each arbitrary item
arbitraryVars map[string]interface{}
}

type varMapper struct {
substitutions map[string]string
base string
baseVars vars
arbitraryNesting string
arbitraryVars vars
// returns a copy of this mapper with a prefix applied to the previous base
func (v *varMapper) rebase(prefix string) *varMapper {
var newBase string
if prefix != "" {
newBase = fmt.Sprintf("%s.%s", prefix, v.base)
} else {
newBase = v.base
}
return &varMapper{
substitutions: v.substitutions,
base: newBase,
baseVars: v.baseVars,
arbitraryNesting: v.arbitraryNesting,
arbitraryVars: v.arbitraryVars,
}
}

func (v varMapper) Resolve(key string) interface{} {
func (v *varMapper) Resolve(key string) interface{} {

// is this a complete substitution?
if substitute, ok := v.substitutions[key]; ok {
return substitute
Expand All @@ -64,6 +64,16 @@ func (v varMapper) Resolve(key string) interface{} {
// is it a fixed base item?
value, ok := v.baseVars[key]
if ok {
// subitem may be a mapper itself
asVarMapper, isVarMapper := value.(*varMapper)
if isVarMapper {
if len(newPath) > 0 {
return asVarMapper.rebase(strings.Join(newPath, "."))
}
return asVarMapper
}

// or a simple string in which case we add to the end of the path and return that
newPath = append(newPath, value.(string))
return strings.Join(newPath, ".")
}
Expand All @@ -83,14 +93,17 @@ func (v varMapper) Resolve(key string) interface{} {
}

return strings.Join(newPath, ".")

}

func (v varMapper) Default() interface{} {
func (v *varMapper) Default() interface{} {
return v.base
}

func (v varMapper) String() string {
func (v *varMapper) String() string {
sub, exists := v.substitutions["__default__"]
if exists {
return sub
}
return v.base
}

Expand Down Expand Up @@ -142,16 +155,10 @@ var functionTemplates = map[string]functionTemplate{
"time": {name: "time", params: "(%s %s %s)"},
}

func newRootVarMapper() vars {
func newRootVarMapper() *varMapper {

urnSubstitutions := make(map[string]string)
for scheme := range urns.ValidSchemes {
urnSubstitutions[scheme] = fmt.Sprintf("format_urn(contact.urns.%s)", scheme)
}

contact := varMapper{
substitutions: urnSubstitutions,
base: "contact",
contact := &varMapper{
base: "contact",
baseVars: map[string]interface{}{
"uuid": "uuid",
"name": "name",
Expand All @@ -163,50 +170,81 @@ func newRootVarMapper() vars {
arbitraryNesting: "fields",
}

return vars{
"contact": contact,
"flow": varMapper{
base: "run.results",
arbitraryVars: map[string]interface{}{
"category": "category_localized",
for scheme := range urns.ValidSchemes {
contact.baseVars[scheme] = &varMapper{
substitutions: map[string]string{
"__default__": fmt.Sprintf("format_urn(contact.urns.%s)", scheme),
"display": fmt.Sprintf("format_urn(contact.urns.%s)", scheme),
"scheme": fmt.Sprintf("contact.urns.%s.0.scheme", scheme),
"path": fmt.Sprintf("contact.urns.%s.0.path", scheme),
"urn": fmt.Sprintf("contact.urns.%s.0", scheme),
},
},
"parent": varMapper{
base: "parent.results",
arbitraryVars: map[string]interface{}{
"category": "category_localized",
base: fmt.Sprintf("urns.%s", scheme),
}
}

return &varMapper{
baseVars: map[string]interface{}{
"contact": contact,
"flow": &varMapper{
base: "run.results",
arbitraryVars: map[string]interface{}{
"category": "category_localized",
},
},
},
"child": varMapper{
base: "child.results",
arbitraryVars: map[string]interface{}{
"category": "category_localized",
"parent": &varMapper{
base: "parent",
baseVars: map[string]interface{}{
"contact": contact,
},
arbitraryNesting: "results",
arbitraryVars: map[string]interface{}{
"category": "category_localized",
},
},
"child": &varMapper{
base: "child",
baseVars: map[string]interface{}{
"contact": contact,
},
arbitraryNesting: "results",
arbitraryVars: map[string]interface{}{
"category": "category_localized",
},
},
"step": &varMapper{
substitutions: map[string]string{
"__default__": "run.input",
"value": "run.input",
"text": "run.input.text",
"attachments": "run.input.attachments",
"time": "run.input.created_on",
},
baseVars: map[string]interface{}{
"contact": contact,
},
},
"channel": &varMapper{
substitutions: map[string]string{
"__default__": "contact.channel.address",
"name": "contact.channel.name",
"tel": "contact.channel.address",
"tel_e164": "contact.channel.address",
},
},
"date": &varMapper{
substitutions: map[string]string{
"__default__": "now()",
"now": "now()",
"today": "today()",
"tomorrow": "tomorrow()",
"yesterday": "yesterday()",
},
},
"extra": &varMapper{
base: "run.webhook",
arbitraryNesting: "json",
},
},
"step": vars{
"__default__": "run.input",
"value": "run.input",
"text": "run.input.text",
"attachments": "run.input.attachments",
"time": "run.input.created_on",
"contact": contact,
},
"channel": vars{
"__default__": "contact.channel.address",
"name": "contact.channel.name",
"tel": "contact.channel.address",
"tel_e164": "contact.channel.address",
},
"date": vars{
"now": "now()",
"today": "today()",
"tomorrow": "tomorrow()",
"yesterday": "yesterday()",
"__default__": "now()",
},
"extra": varMapper{
base: "run.webhook",
arbitraryNesting: "json",
},
}
}
Expand Down
32 changes: 24 additions & 8 deletions excellent/legacy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,40 @@ type testTemplate struct {
new string
}

// TestThis
func TestTranslate(t *testing.T) {
var tests = []testTemplate{

// variables
// contact variables
{old: "@contact", new: "@contact"},
{old: "@contact.first_name", new: "@contact.first_name"},
{old: "@contact.uuid", new: "@contact.uuid"},
{old: "@contact.name", new: "@contact.name"},
{old: "@contact.first_name", new: "@contact.first_name"},
{old: "@contact.blerg", new: "@contact.fields.blerg"},

// contact URN variables
{old: "@contact.tel", new: "@(format_urn(contact.urns.tel))"},
{old: "@contact.tel.display", new: "@(format_urn(contact.urns.tel))"},
{old: "@contact.tel.scheme", new: "@contact.urns.tel.0.scheme"},
{old: "@contact.tel.path", new: "@contact.urns.tel.0.path"},
{old: "@contact.tel.urn", new: "@contact.urns.tel.0"},
{old: "@contact.tel_e164", new: "@contact.urns.tel.0.path"},
{old: "@contact.telegram", new: "@(format_urn(contact.urns.telegram))"},
{old: "@contact.twitter", new: "@(format_urn(contact.urns.twitter))"},
{old: "@contact.facebook", new: "@(format_urn(contact.urns.facebook))"},
{old: "@contact.mailto", new: "@(format_urn(contact.urns.mailto))"},
{old: "@contact.uuid", new: "@contact.uuid"},
{old: "@contact.blerg", new: "@contact.fields.blerg"},
{old: "@child.blerg", new: "@child.results.blerg"},
{old: "@parent.blerg", new: "@parent.results.blerg"},

// run variables
{old: "@flow.blerg", new: "@run.results.blerg"},
{old: "@flow.blerg.category", new: "@run.results.blerg.category_localized"},
{old: "@child.blerg", new: "@child.results.blerg"},
{old: "@child.contact", new: "@child.contact"},
{old: "@child.contact.age", new: "@child.contact.fields.age"},
{old: "@parent.blerg", new: "@parent.results.blerg"},
{old: "@parent.blerg.category", new: "@parent.results.blerg.category_localized"},
{old: "@parent.contact", new: "@parent.contact"},
{old: "@parent.contact.name", new: "@parent.contact.name"},

// input
{old: "@step", new: "@run.input"},
{old: "@step.value", new: "@run.input"},
{old: "@step.text", new: "@run.input.text"},
Expand All @@ -37,11 +51,13 @@ func TestTranslate(t *testing.T) {
{old: "@step.contact", new: "@contact"},
{old: "@step.contact.name", new: "@contact.name"},
{old: "@step.contact.age", new: "@contact.fields.age"},

// dates
{old: "@date", new: "@(now())"},
{old: "@date.now", new: "@(now())"},
{old: "@date.today", new: "@(today())"},
{old: "@date.tomorrow", new: "@(tomorrow())"},
{old: "@date.yesterday", new: "@(yesterday())"},
{old: "@date", new: "@(now())"},

// variables in parens
{old: "@(contact.tel)", new: "@(format_urn(contact.urns.tel))"},
Expand Down

0 comments on commit cbcb254

Please sign in to comment.