From e3758b15f4127b57737477c5643a59f557767098 Mon Sep 17 00:00:00 2001 From: Rowan Seymour Date: Tue, 15 Aug 2023 13:48:21 -0500 Subject: [PATCH] Merge both derived environments --- flows/engine/session.go | 2 +- flows/environment.go | 103 ++++++++++++++---------------- flows/environment_test.go | 81 ++++++----------------- flows/routers/cases/tests_test.go | 82 ++++++++++++++++-------- 4 files changed, 121 insertions(+), 147 deletions(-) diff --git a/flows/engine/session.go b/flows/engine/session.go index 341560bf7..82ff832cc 100644 --- a/flows/engine/session.go +++ b/flows/engine/session.go @@ -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 } diff --git a/flows/environment.go b/flows/environment.go index 8b3eedf5e..66e7c0ead 100644 --- a/flows/environment.go +++ b/flows/environment.go @@ -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 { @@ -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()) -} diff --git a/flows/environment_test.go b/flows/environment_test.go index 97853587c..b62c6ae2d 100644 --- a/flows/environment_test.go +++ b/flows/environment_test.go @@ -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", @@ -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, @@ -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") @@ -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) diff --git a/flows/routers/cases/tests_test.go b/flows/routers/cases/tests_test.go index 37d4dd4c8..6585cf0b4 100644 --- a/flows/routers/cases/tests_test.go +++ b/flows/routers/cases/tests_test.go @@ -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" @@ -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": [] } ] }` @@ -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)