Skip to content

Commit

Permalink
Merge both derived environments
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanseymour committed Aug 15, 2023
1 parent 086fa95 commit e3758b1
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 147 deletions.
2 changes: 1 addition & 1 deletion flows/engine/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +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) MergedEnvironment() envs.Environment { return flows.NewEnvironment(s) }

func (s *session) Contact() *flows.Contact { return s.contact }
func (s *session) SetContact(contact *flows.Contact) { s.contact = contact }
Expand Down
103 changes: 47 additions & 56 deletions flows/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,62 @@ import (
type environment struct {
envs.Environment

session Session
locationResolver envs.LocationResolver
}

// NewEnvironment creates a new environment
func NewEnvironment(base envs.Environment, la *LocationAssets) envs.Environment {
// NewEnvironment creates a new environment from a session's base environment that merges some properties with
// those from the contact, and adds support for location resolving using the session's locations assets.
func NewEnvironment(s Session) envs.Environment {
var locationResolver envs.LocationResolver

hierarchies := la.Hierarchies()
hierarchies := s.Assets().Locations().Hierarchies()
if len(hierarchies) > 0 {
locationResolver = &assetLocationResolver{hierarchies[0]}
}

return &environment{base, locationResolver}
return &environment{
Environment: s.Environment(),
session: s,
locationResolver: locationResolver,
}
}

func (e *environment) 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 *environment) 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 *environment) 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 *environment) DefaultLocale() envs.Locale {
return envs.NewLocale(e.DefaultLanguage(), e.DefaultCountry())
}

func (e *environment) LocationResolver() envs.LocationResolver {
Expand Down Expand Up @@ -82,55 +125,3 @@ 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())
}
81 changes: 18 additions & 63 deletions flows/environment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ import (
"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 assets1JSON = `{
var assetsJSON = `{
"flows": [
{
"uuid": "76f0a02f-3b75-4b86-9064-e9195e1b3a02",
Expand Down Expand Up @@ -67,78 +66,34 @@ var assets1JSON = `{
]
}`

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

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

fenv := flows.NewEnvironment(env, sa.Locations())
assert.Equal(t, envs.Country("RW"), fenv.DefaultCountry())
require.NotNil(t, fenv.LocationResolver())
contact := flows.NewEmptyContact(sa, "", envs.NilLanguage, nil)

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)

senv := session.MergedEnvironment()
assert.Equal(t, envs.Country("RW"), senv.DefaultCountry())
require.NotNil(t, senv.LocationResolver())

kigali := fenv.LocationResolver().LookupLocation("Rwanda > Kigali City")
kigali := senv.LocationResolver().LookupLocation("Rwanda > Kigali City")
assert.Equal(t, "Kigali City", kigali.Name())

matches := fenv.LocationResolver().FindLocationsFuzzy("gisozi town", flows.LocationLevelWard, nil)
matches := senv.LocationResolver().FindLocationsFuzzy("gisozi town", flows.LocationLevelWard, nil)
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,
Expand All @@ -152,7 +107,7 @@ const contactJSON = `{
]
}`

func TestMergedEnvironment(t *testing.T) {
func TestEnvironmentMerging(t *testing.T) {
tzRW, _ := time.LoadLocation("Africa/Kigali")
tzEC, _ := time.LoadLocation("America/Guayaquil")
tzUK, _ := time.LoadLocation("Europe/London")
Expand All @@ -162,7 +117,7 @@ func TestMergedEnvironment(t *testing.T) {
WithDefaultCountry("RW").
WithTimezone(tzRW).
Build()
source, err := static.NewSource([]byte(assets2JSON))
source, err := static.NewSource([]byte(assetsJSON))
require.NoError(t, err)

sa, err := engine.NewSessionAssets(env, source, nil)
Expand Down
82 changes: 55 additions & 27 deletions flows/routers/cases/tests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import (

"github.com/nyaruka/gocommon/dates"
"github.com/nyaruka/goflow/assets"
"github.com/nyaruka/goflow/assets/static"
"github.com/nyaruka/goflow/envs"
"github.com/nyaruka/goflow/excellent"
"github.com/nyaruka/goflow/excellent/types"
"github.com/nyaruka/goflow/flows"
"github.com/nyaruka/goflow/flows/engine"
"github.com/nyaruka/goflow/flows/routers/cases"
"github.com/nyaruka/goflow/flows/triggers"
"github.com/nyaruka/goflow/test"

"github.com/stretchr/testify/assert"
Expand All @@ -31,45 +34,59 @@ var ERROR = types.NewXErrorf("any error")

var kgl, _ = time.LoadLocation("Africa/Kigali")

var locationHierarchyJSON = `{
"name": "Rwanda",
"aliases": ["Ruanda"],
"children": [
var assetsJSON = `{
"flows": [
{
"uuid": "76f0a02f-3b75-4b86-9064-e9195e1b3a02",
"name": "Test",
"spec_version": "13.1.0",
"language": "eng",
"type": "messaging",
"nodes": []
}
],
"locations": [
{
"name": "Kigali City",
"aliases": ["Kigali", "Kigari"],
"name": "Rwanda",
"aliases": ["Ruanda"],
"children": [
{
"name": "Gasabo",
"name": "Kigali City",
"aliases": ["Kigali", "Kigari"],
"children": [
{
"name": "Gisozi"
"name": "Gasabo",
"children": [
{
"name": "Gisozi"
},
{
"name": "Ndera"
}
]
},
{
"name": "Ndera"
"name": "Nyarugenge",
"children": []
}
]
},
{
"name": "Nyarugenge",
"name": "Québec",
"aliases": ["Q.C", "Le Québec", "Quebec", "Que,", "Que", "Qc", "Québec"],
"children": []
},
{
"name": "Île-de-France",
"aliases": [],
"children": []
},
{
"name": "Paktika",
"aliases": ["Janikhel", "Terwa", "Yahyakhel", "Yusufkhel", "\u067e\u06a9\u062a\u06cc\u06a9\u0627", "\u062a\u0631\u0648\u0648", "\u06cc\u062d\u06cc\u06cc \u062e\u06cc\u0644", "\u06cc\u0648\u0633\u0641 \u062e\u06cc\u0644"],
"children": []
}
]
},
{
"name": "Québec",
"aliases": ["Q.C", "Le Québec", "Quebec", "Que,", "Que", "Qc", "Québec"],
"children": []
},
{
"name": "Île-de-France",
"aliases": [],
"children": []
},
{
"name": "Paktika",
"aliases": ["Janikhel", "Terwa", "Yahyakhel", "Yusufkhel", "\u067e\u06a9\u062a\u06cc\u06a9\u0627", "\u062a\u0631\u0648\u0648", "\u06cc\u062d\u06cc\u06cc \u062e\u06cc\u0644", "\u06cc\u0648\u0633\u0641 \u062e\u06cc\u0644"],
"children": []
}
]
}`
Expand Down Expand Up @@ -490,10 +507,21 @@ func TestTests(t *testing.T) {
WithDefaultCountry(envs.Country("RW")).
Build()

locations, err := envs.ReadLocationHierarchy([]byte(locationHierarchyJSON))
source, err := static.NewSource([]byte(assetsJSON))
require.NoError(t, err)

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

contact := flows.NewEmptyContact(sa, "", envs.NilLanguage, nil)

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)

env = flows.NewEnvironment(env, flows.NewLocationAssets([]assets.LocationHierarchy{locations}))
env = session.MergedEnvironment()

for _, tc := range testTests {
testID := fmt.Sprintf("%s(%#v)", tc.name, tc.args)
Expand Down

0 comments on commit e3758b1

Please sign in to comment.