Skip to content

Commit

Permalink
Simplify how we create a merged environment with properties from the …
Browse files Browse the repository at this point in the history
…contact
  • Loading branch information
rowanseymour committed Aug 15, 2023
1 parent 2fe2f47 commit 086fa95
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 210 deletions.
2 changes: 2 additions & 0 deletions flows/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/nyaruka/gocommon/uuids"
"github.com/nyaruka/goflow/assets"
"github.com/nyaruka/goflow/envs"
"github.com/nyaruka/goflow/flows"
)

Expand All @@ -20,6 +21,7 @@ type engine struct {
func (e *engine) NewSession(sa flows.SessionAssets, trigger flows.Trigger) (flows.Session, flows.Sprint, error) {
s := &session{
uuid: flows.SessionUUID(uuids.New()),
env: envs.NewBuilder().Build(),
engine: e,
assets: sa,
trigger: trigger,
Expand Down
1 change: 1 addition & 0 deletions flows/engine/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func (s *session) SetType(type_ flows.FlowType) { s.type_ = type_ }

func (s *session) Environment() envs.Environment { return s.env }
func (s *session) SetEnvironment(env envs.Environment) { s.env = env }
func (s *session) MergedEnvironment() envs.Environment { return flows.NewMergedEnvironment(s) }

func (s *session) Contact() *flows.Contact { return s.contact }
func (s *session) SetContact(contact *flows.Contact) { s.contact = contact }
Expand Down
54 changes: 54 additions & 0 deletions flows/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package flows
import (
"regexp"
"strings"
"time"

"github.com/nyaruka/goflow/assets"
"github.com/nyaruka/goflow/envs"
"golang.org/x/exp/slices"
)

type environment struct {
Expand Down Expand Up @@ -80,3 +82,55 @@ func (r *assetLocationResolver) FindLocationsFuzzy(text string, level envs.Locat
func (r *assetLocationResolver) LookupLocation(path envs.LocationPath) *envs.Location {
return r.locations.FindByPath(path)
}

// an extended environment which takes some values from a contact if there is one and if the have those values.
type mergedEnvironment struct {
envs.Environment

session Session
}

// NewMergedEnvironment creates a new merged environment from a session's base environment and its contact
func NewMergedEnvironment(s Session) envs.Environment {
return &mergedEnvironment{
NewEnvironment(s.Environment(), s.Assets().Locations()),
s,
}
}

func (e *mergedEnvironment) Timezone() *time.Location {
contact := e.session.Contact()

// if we have a contact and they have a timezone that overrides the base enviroment's timezone
if contact != nil && contact.Timezone() != nil {
return contact.Timezone()
}
return e.Environment.Timezone()
}

func (e *mergedEnvironment) DefaultLanguage() envs.Language {
contact := e.session.Contact()

// if we have a contact and they have a language and it's an allowed language that overrides the base environment's languuage
if contact != nil && contact.Language() != envs.NilLanguage && slices.Contains(e.AllowedLanguages(), contact.Language()) {
return contact.Language()
}
return e.Environment.DefaultLanguage()
}

func (e *mergedEnvironment) DefaultCountry() envs.Country {
contact := e.session.Contact()

// if we have a contact and they have a preferred channel with a country that overrides the base environment's country
if contact != nil {
cc := contact.Country()
if cc != envs.NilCountry {
return cc
}
}
return e.Environment.DefaultCountry()
}

func (e *mergedEnvironment) DefaultLocale() envs.Locale {
return envs.NewLocale(e.DefaultLanguage(), e.DefaultCountry())
}
128 changes: 126 additions & 2 deletions flows/environment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ package flows_test

import (
"testing"
"time"

"github.com/nyaruka/goflow/assets"
"github.com/nyaruka/goflow/assets/static"
"github.com/nyaruka/goflow/envs"
"github.com/nyaruka/goflow/flows"
"github.com/nyaruka/goflow/flows/engine"
"github.com/nyaruka/goflow/flows/triggers"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

var assetsJSON = `{
var assets1JSON = `{
"flows": [
{
"uuid": "76f0a02f-3b75-4b86-9064-e9195e1b3a02",
Expand Down Expand Up @@ -66,7 +69,7 @@ var assetsJSON = `{

func TestEnvironment(t *testing.T) {
env := envs.NewBuilder().WithDefaultCountry("RW").Build()
source, err := static.NewSource([]byte(assetsJSON))
source, err := static.NewSource([]byte(assets1JSON))
require.NoError(t, err)

sa, err := engine.NewSessionAssets(env, source, nil)
Expand All @@ -83,3 +86,124 @@ func TestEnvironment(t *testing.T) {
assert.Equal(t, 1, len(matches))
assert.Equal(t, "Gisozi", matches[0].Name())
}

var assets2JSON = `{
"flows": [
{
"uuid": "76f0a02f-3b75-4b86-9064-e9195e1b3a02",
"name": "Test",
"spec_version": "13.1.0",
"language": "eng",
"type": "messaging",
"nodes": []
}
],
"channels": [
{
"uuid": "57f1078f-88aa-46f4-a59a-948a5739c03d",
"name": "Android Channel",
"address": "+17036975131",
"schemes": ["tel"],
"roles": ["send", "receive"],
"country": "US"
}
],
"locations": [
{
"name": "Rwanda",
"aliases": ["Ruanda"],
"children": [
{
"name": "Kigali City",
"aliases": ["Kigali", "Kigari"],
"children": [
{
"name": "Gasabo",
"children": [
{
"name": "Gisozi"
},
{
"name": "Ndera"
}
]
},
{
"name": "Nyarugenge",
"children": []
}
]
}
]
}
]
}`

const contactJSON = `{
"uuid": "ba96bf7f-bc2a-4873-a7c7-254d1927c4e3",
"id": 1234567,
"name": "Ben Haggerty",
"created_on": "2018-01-01T12:00:00.000000000-00:00",
"fields": {},
"language": "fra",
"timezone": "America/Guayaquil",
"urns": [
"tel:+12065551212"
]
}`

func TestMergedEnvironment(t *testing.T) {
tzRW, _ := time.LoadLocation("Africa/Kigali")
tzEC, _ := time.LoadLocation("America/Guayaquil")
tzUK, _ := time.LoadLocation("Europe/London")

env := envs.NewBuilder().
WithAllowedLanguages([]envs.Language{"eng", "fra", "kin"}).
WithDefaultCountry("RW").
WithTimezone(tzRW).
Build()
source, err := static.NewSource([]byte(assets2JSON))
require.NoError(t, err)

sa, err := engine.NewSessionAssets(env, source, nil)
require.NoError(t, err)

contact, err := flows.ReadContact(sa, []byte(contactJSON), assets.IgnoreMissing)
require.NoError(t, err)

trigger := triggers.NewBuilder(env, assets.NewFlowReference("76f0a02f-3b75-4b86-9064-e9195e1b3a02", "Test"), contact).Manual().Build()
eng := engine.NewBuilder().Build()

session, _, err := eng.NewSession(sa, trigger)
require.NoError(t, err)

// main environment on the session has the values we started with
serializedEnv := session.Environment()
assert.Equal(t, envs.Language("eng"), serializedEnv.DefaultLanguage())
assert.Equal(t, []envs.Language{"eng", "fra", "kin"}, serializedEnv.AllowedLanguages())
assert.Equal(t, envs.Country("RW"), serializedEnv.DefaultCountry())
assert.Equal(t, "en-RW", serializedEnv.DefaultLocale().ToBCP47())
assert.Equal(t, tzRW, serializedEnv.Timezone())

// merged environment on the session has values from the contact
mergedEnv := session.MergedEnvironment()
assert.Equal(t, envs.Language("fra"), mergedEnv.DefaultLanguage())
assert.Equal(t, []envs.Language{"eng", "fra", "kin"}, mergedEnv.AllowedLanguages())
assert.Equal(t, envs.Country("US"), mergedEnv.DefaultCountry())
assert.Equal(t, "fr-US", mergedEnv.DefaultLocale().ToBCP47())
assert.Equal(t, tzEC, mergedEnv.Timezone())
assert.NotNil(t, mergedEnv.LocationResolver())

// can make changes to contact
session.Contact().SetLanguage(envs.Language("kin"))
session.Contact().SetTimezone(tzUK)

// and environment reflects those changes
assert.Equal(t, envs.Language("kin"), mergedEnv.DefaultLanguage())
assert.Equal(t, tzUK, mergedEnv.Timezone())

// if contact language is not an allowed language it won't be used
session.Contact().SetLanguage(envs.Language("spa"))
assert.Equal(t, envs.Language("eng"), mergedEnv.DefaultLanguage())
assert.Equal(t, "en-US", mergedEnv.DefaultLocale().ToBCP47())
}
1 change: 1 addition & 0 deletions flows/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ type Session interface {

Environment() envs.Environment
SetEnvironment(envs.Environment)
MergedEnvironment() envs.Environment

Contact() *Contact
SetContact(*Contact)
Expand Down
61 changes: 0 additions & 61 deletions flows/runs/environment.go

This file was deleted.

Loading

0 comments on commit 086fa95

Please sign in to comment.