Skip to content

Commit

Permalink
Merge pull request rapidpro#448 from nyaruka/less_config_singleton
Browse files Browse the repository at this point in the history
Less using the config singleton, more passing down
  • Loading branch information
rowanseymour authored Jun 24, 2021
2 parents 4420e59 + f099b4e commit ef4ef87
Show file tree
Hide file tree
Showing 33 changed files with 161 additions and 105 deletions.
20 changes: 10 additions & 10 deletions core/goflow/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,45 +45,45 @@ func RegisterAirtimeServiceFactory(factory engine.AirtimeServiceFactory) {
}

// Engine returns the global engine instance for use with real sessions
func Engine() flows.Engine {
func Engine(cfg *config.Config) flows.Engine {
engInit.Do(func() {
webhookHeaders := map[string]string{
"User-Agent": "RapidProMailroom/" + config.Mailroom.Version,
"User-Agent": "RapidProMailroom/" + cfg.Version,
"X-Mailroom-Mode": "normal",
}

httpClient, httpRetries, httpAccess := HTTP()
httpClient, httpRetries, httpAccess := HTTP(cfg)

eng = engine.NewBuilder().
WithWebhookServiceFactory(webhooks.NewServiceFactory(httpClient, httpRetries, httpAccess, webhookHeaders, config.Mailroom.WebhooksMaxBodyBytes)).
WithWebhookServiceFactory(webhooks.NewServiceFactory(httpClient, httpRetries, httpAccess, webhookHeaders, cfg.WebhooksMaxBodyBytes)).
WithClassificationServiceFactory(classificationFactory).
WithEmailServiceFactory(emailFactory).
WithTicketServiceFactory(ticketFactory).
WithAirtimeServiceFactory(airtimeFactory).
WithMaxStepsPerSprint(config.Mailroom.MaxStepsPerSprint).
WithMaxStepsPerSprint(cfg.MaxStepsPerSprint).
Build()
})

return eng
}

// Simulator returns the global engine instance for use with simulated sessions
func Simulator() flows.Engine {
func Simulator(cfg *config.Config) flows.Engine {
simulatorInit.Do(func() {
webhookHeaders := map[string]string{
"User-Agent": "RapidProMailroom/" + config.Mailroom.Version,
"User-Agent": "RapidProMailroom/" + cfg.Version,
"X-Mailroom-Mode": "simulation",
}

httpClient, _, httpAccess := HTTP() // don't do retries in simulator
httpClient, _, httpAccess := HTTP(cfg) // don't do retries in simulator

simulator = engine.NewBuilder().
WithWebhookServiceFactory(webhooks.NewServiceFactory(httpClient, nil, httpAccess, webhookHeaders, config.Mailroom.WebhooksMaxBodyBytes)).
WithWebhookServiceFactory(webhooks.NewServiceFactory(httpClient, nil, httpAccess, webhookHeaders, cfg.WebhooksMaxBodyBytes)).
WithClassificationServiceFactory(classificationFactory). // simulated sessions do real classification
WithEmailServiceFactory(simulatorEmailServiceFactory). // but faked emails
WithTicketServiceFactory(simulatorTicketServiceFactory). // and faked tickets
WithAirtimeServiceFactory(simulatorAirtimeServiceFactory). // and faked airtime transfers
WithMaxStepsPerSprint(config.Mailroom.MaxStepsPerSprint).
WithMaxStepsPerSprint(cfg.MaxStepsPerSprint).
Build()
})

Expand Down
19 changes: 12 additions & 7 deletions core/goflow/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import (
)

func TestEngineWebhook(t *testing.T) {
svc, err := goflow.Engine().Services().Webhook(nil)
rt := testsuite.RT()

svc, err := goflow.Engine(rt.Config).Services().Webhook(nil)
assert.NoError(t, err)

defer httpx.SetRequestor(httpx.DefaultRequestor)
Expand All @@ -38,7 +40,9 @@ func TestEngineWebhook(t *testing.T) {
}

func TestSimulatorAirtime(t *testing.T) {
svc, err := goflow.Simulator().Services().Airtime(nil)
rt := testsuite.RT()

svc, err := goflow.Simulator(rt.Config).Services().Airtime(nil)
assert.NoError(t, err)

amounts := map[string]decimal.Decimal{"USD": decimal.RequireFromString(`1.50`)}
Expand All @@ -56,14 +60,13 @@ func TestSimulatorAirtime(t *testing.T) {
}

func TestSimulatorTicket(t *testing.T) {
ctx := testsuite.CTX()
db := testsuite.DB()
testsuite.ResetDB()
ctx, db, _ := testsuite.Reset()
rt := testsuite.RT()

ticketer, err := models.LookupTicketerByUUID(ctx, db, testdata.Mailgun.UUID)
require.NoError(t, err)

svc, err := goflow.Simulator().Services().Ticket(nil, flows.NewTicketer(ticketer))
svc, err := goflow.Simulator(rt.Config).Services().Ticket(nil, flows.NewTicketer(ticketer))
assert.NoError(t, err)

ticket, err := svc.Open(nil, "New ticket", "Where are my cookies?", nil)
Expand All @@ -74,7 +77,9 @@ func TestSimulatorTicket(t *testing.T) {
}

func TestSimulatorWebhook(t *testing.T) {
svc, err := goflow.Simulator().Services().Webhook(nil)
rt := testsuite.RT()

svc, err := goflow.Simulator(rt.Config).Services().Webhook(nil)
assert.NoError(t, err)

defer httpx.SetRequestor(httpx.DefaultRequestor)
Expand Down
12 changes: 6 additions & 6 deletions core/goflow/flows.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ func SpecVersion() *semver.Version {
}

// ReadFlow reads a flow from the given JSON definition, migrating it if necessary
func ReadFlow(data json.RawMessage) (flows.Flow, error) {
return definition.ReadFlow(data, MigrationConfig())
func ReadFlow(cfg *config.Config, data json.RawMessage) (flows.Flow, error) {
return definition.ReadFlow(data, MigrationConfig(cfg))
}

// CloneDefinition clones the given flow definition
Expand All @@ -32,14 +32,14 @@ func CloneDefinition(data json.RawMessage, depMapping map[uuids.UUID]uuids.UUID)
}

// MigrateDefinition migrates the given flow definition to the specified version
func MigrateDefinition(data json.RawMessage, toVersion *semver.Version) (json.RawMessage, error) {
return migrations.MigrateToVersion(data, toVersion, MigrationConfig())
func MigrateDefinition(cfg *config.Config, data json.RawMessage, toVersion *semver.Version) (json.RawMessage, error) {
return migrations.MigrateToVersion(data, toVersion, MigrationConfig(cfg))
}

// MigrationConfig returns the migration configuration for flows
func MigrationConfig() *migrations.Config {
func MigrationConfig(cfg *config.Config) *migrations.Config {
migConfInit.Do(func() {
migConf = &migrations.Config{BaseMediaURL: "https://" + config.Mailroom.AttachmentDomain}
migConf = &migrations.Config{BaseMediaURL: "https://" + cfg.AttachmentDomain}
})

return migConf
Expand Down
13 changes: 9 additions & 4 deletions core/goflow/flows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/nyaruka/goflow/flows"
"github.com/nyaruka/goflow/test"
"github.com/nyaruka/mailroom/core/goflow"
"github.com/nyaruka/mailroom/testsuite"

"github.com/Masterminds/semver"
"github.com/stretchr/testify/assert"
Expand All @@ -19,21 +20,23 @@ func TestSpecVersion(t *testing.T) {
}

func TestReadFlow(t *testing.T) {
rt := testsuite.RT()

// try to read empty definition
flow, err := goflow.ReadFlow([]byte(`{}`))
flow, err := goflow.ReadFlow(rt.Config, []byte(`{}`))
assert.Nil(t, flow)
assert.EqualError(t, err, "unable to read flow header: field 'uuid' is required, field 'spec_version' is required")

// read legacy definition
flow, err = goflow.ReadFlow([]byte(`{"flow_type": "M", "base_language": "eng", "action_sets": [], "metadata": {"uuid": "502c3ee4-3249-4dee-8e71-c62070667d52", "name": "Legacy"}}`))
flow, err = goflow.ReadFlow(rt.Config, []byte(`{"flow_type": "M", "base_language": "eng", "action_sets": [], "metadata": {"uuid": "502c3ee4-3249-4dee-8e71-c62070667d52", "name": "Legacy"}}`))
assert.Nil(t, err)
assert.Equal(t, assets.FlowUUID("502c3ee4-3249-4dee-8e71-c62070667d52"), flow.UUID())
assert.Equal(t, "Legacy", flow.Name())
assert.Equal(t, envs.Language("eng"), flow.Language())
assert.Equal(t, flows.FlowTypeMessaging, flow.Type())

// read new definition
flow, err = goflow.ReadFlow([]byte(`{"uuid": "502c3ee4-3249-4dee-8e71-c62070667d52", "name": "New", "spec_version": "13.0.0", "type": "messaging", "language": "eng", "nodes": []}`))
flow, err = goflow.ReadFlow(rt.Config, []byte(`{"uuid": "502c3ee4-3249-4dee-8e71-c62070667d52", "name": "New", "spec_version": "13.0.0", "type": "messaging", "language": "eng", "nodes": []}`))
assert.Nil(t, err)
assert.Equal(t, assets.FlowUUID("502c3ee4-3249-4dee-8e71-c62070667d52"), flow.UUID())
assert.Equal(t, "New", flow.Name())
Expand All @@ -50,8 +53,10 @@ func TestCloneDefinition(t *testing.T) {
}

func TestMigrateDefinition(t *testing.T) {
rt := testsuite.RT()

// 13.0 > 13.1
migrated, err := goflow.MigrateDefinition([]byte(`{"uuid": "502c3ee4-3249-4dee-8e71-c62070667d52", "name": "New", "spec_version": "13.0.0", "type": "messaging", "language": "eng", "nodes": []}`), semver.MustParse("13.1.0"))
migrated, err := goflow.MigrateDefinition(rt.Config, []byte(`{"uuid": "502c3ee4-3249-4dee-8e71-c62070667d52", "name": "New", "spec_version": "13.0.0", "type": "messaging", "language": "eng", "nodes": []}`), semver.MustParse("13.1.0"))
assert.NoError(t, err)
test.AssertEqualJSON(t, []byte(`{"uuid": "502c3ee4-3249-4dee-8e71-c62070667d52", "name": "New", "spec_version": "13.1.0", "type": "messaging", "language": "eng", "nodes": []}`), migrated)
}
12 changes: 6 additions & 6 deletions core/goflow/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var httpRetries *httpx.RetryConfig
var httpAccess *httpx.AccessConfig

// HTTP returns the configuration objects for HTTP calls from the engine and its services
func HTTP() (*http.Client, *httpx.RetryConfig, *httpx.AccessConfig) {
func HTTP(cfg *config.Config) (*http.Client, *httpx.RetryConfig, *httpx.AccessConfig) {
httpInit.Do(func() {
// customize the default golang transport
t := http.DefaultTransport.(*http.Transport).Clone()
Expand All @@ -30,16 +30,16 @@ func HTTP() (*http.Client, *httpx.RetryConfig, *httpx.AccessConfig) {

httpClient = &http.Client{
Transport: t,
Timeout: time.Duration(config.Mailroom.WebhooksTimeout) * time.Millisecond,
Timeout: time.Duration(cfg.WebhooksTimeout) * time.Millisecond,
}

httpRetries = httpx.NewExponentialRetries(
time.Duration(config.Mailroom.WebhooksInitialBackoff)*time.Millisecond,
config.Mailroom.WebhooksMaxRetries,
config.Mailroom.WebhooksBackoffJitter,
time.Duration(cfg.WebhooksInitialBackoff)*time.Millisecond,
cfg.WebhooksMaxRetries,
cfg.WebhooksBackoffJitter,
)

disallowedIPs, disallowedNets, _ := config.Mailroom.ParseDisallowedNetworks()
disallowedIPs, disallowedNets, _ := cfg.ParseDisallowedNetworks()
httpAccess = httpx.NewAccessConfig(10*time.Second, disallowedIPs, disallowedNets)
})
return httpClient, httpRetries, httpAccess
Expand Down
3 changes: 2 additions & 1 deletion core/ivr/twiml/twiml.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/nyaruka/goflow/flows/routers/waits"
"github.com/nyaruka/goflow/flows/routers/waits/hints"
"github.com/nyaruka/goflow/utils"
"github.com/nyaruka/mailroom/config"
"github.com/nyaruka/mailroom/core/ivr"
"github.com/nyaruka/mailroom/core/models"

Expand Down Expand Up @@ -502,7 +503,7 @@ func responseForSprint(number urns.URN, resumeURL string, w flows.ActivatedWait,
commands = append(commands, Say{Text: event.Msg.Text(), Language: languageCode})
} else {
for _, a := range event.Msg.Attachments() {
a = models.NormalizeAttachment(a)
a = models.NormalizeAttachment(config.Mailroom, a)
commands = append(commands, Play{URL: a.URL()})
}
}
Expand Down
7 changes: 4 additions & 3 deletions core/models/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/nyaruka/goflow/envs"
"github.com/nyaruka/goflow/flows"
"github.com/nyaruka/goflow/flows/engine"
"github.com/nyaruka/mailroom/config"
"github.com/nyaruka/mailroom/core/goflow"
cache "github.com/patrickmn/go-cache"
"github.com/pkg/errors"
Expand Down Expand Up @@ -128,7 +129,7 @@ func NewOrgAssets(ctx context.Context, db *sqlx.DB, orgID OrgID, prev *OrgAssets
var err error

if prev == nil || refresh&RefreshOrg > 0 {
oa.org, err = LoadOrg(ctx, db, orgID)
oa.org, err = LoadOrg(ctx, config.Mailroom, db, orgID)
if err != nil {
return nil, errors.Wrapf(err, "error loading environment for org %d", orgID)
}
Expand Down Expand Up @@ -333,7 +334,7 @@ func NewOrgAssets(ctx context.Context, db *sqlx.DB, orgID OrgID, prev *OrgAssets
}

// intialize our session assets
oa.sessionAssets, err = engine.NewSessionAssets(oa.Env(), oa, goflow.MigrationConfig())
oa.sessionAssets, err = engine.NewSessionAssets(oa.Env(), oa, goflow.MigrationConfig(config.Mailroom))
if err != nil {
return nil, errors.Wrapf(err, "error build session assets for org: %d", orgID)
}
Expand Down Expand Up @@ -479,7 +480,7 @@ func (a *OrgAssets) CloneForSimulation(ctx context.Context, db *sqlx.DB, newDefs
clone.channels = append(clone.channels, testChannels...)

// rebuild our session assets with our new items
clone.sessionAssets, err = engine.NewSessionAssets(a.Env(), clone, goflow.MigrationConfig())
clone.sessionAssets, err = engine.NewSessionAssets(a.Env(), clone, goflow.MigrationConfig(config.Mailroom))
if err != nil {
return nil, errors.Wrapf(err, "error build session assets for org: %d", clone.OrgID())
}
Expand Down
3 changes: 2 additions & 1 deletion core/models/classifiers.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/nyaruka/goflow/services/classification/bothub"
"github.com/nyaruka/goflow/services/classification/luis"
"github.com/nyaruka/goflow/services/classification/wit"
"github.com/nyaruka/mailroom/config"
"github.com/nyaruka/mailroom/core/goflow"
"github.com/nyaruka/mailroom/utils/dbutil"
"github.com/nyaruka/null"
Expand Down Expand Up @@ -90,7 +91,7 @@ func (c *Classifier) Type() string { return c.c.Type }

// AsService builds the corresponding ClassificationService for the passed in Classifier
func (c *Classifier) AsService(classifier *flows.Classifier) (flows.ClassificationService, error) {
httpClient, httpRetries, httpAccess := goflow.HTTP()
httpClient, httpRetries, httpAccess := goflow.HTTP(config.Mailroom)

switch c.Type() {
case ClassifierTypeWit:
Expand Down
3 changes: 2 additions & 1 deletion core/models/flows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
func TestFlows(t *testing.T) {
ctx := testsuite.CTX()
db := testsuite.DB()
rt := testsuite.RT()

db.MustExec(`UPDATE flows_flow SET metadata = '{"ivr_retry": 30}'::json WHERE id = $1`, testdata.IVRFlow.ID)

Expand All @@ -41,7 +42,7 @@ func TestFlows(t *testing.T) {
assert.Equal(t, tc.FlowUUID, flow.UUID())
assert.Equal(t, tc.IVRRetry, flow.IVRRetryWait())

_, err := goflow.ReadFlow(flow.Definition())
_, err := goflow.ReadFlow(rt.Config, flow.Definition())
assert.NoError(t, err)
} else {
assert.Nil(t, flow)
Expand Down
14 changes: 7 additions & 7 deletions core/models/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ func NewIncomingIVR(orgID OrgID, conn *ChannelConnection, in *flows.MsgIn, creat

// add any attachments
for _, a := range in.Attachments() {
m.Attachments = append(m.Attachments, string(NormalizeAttachment(a)))
m.Attachments = append(m.Attachments, string(NormalizeAttachment(config.Mailroom, a)))
}

return msg
Expand Down Expand Up @@ -279,7 +279,7 @@ func NewOutgoingIVR(orgID OrgID, conn *ChannelConnection, out *flows.MsgOut, cre

// if we have attachments, add them
for _, a := range out.Attachments() {
m.Attachments = append(m.Attachments, string(NormalizeAttachment(a)))
m.Attachments = append(m.Attachments, string(NormalizeAttachment(config.Mailroom, a)))
}

return msg, nil
Expand Down Expand Up @@ -324,7 +324,7 @@ func NewOutgoingMsg(org *Org, channel *Channel, contactID ContactID, out *flows.
// if we have attachments, add them
if len(out.Attachments()) > 0 {
for _, a := range out.Attachments() {
m.Attachments = append(m.Attachments, string(NormalizeAttachment(a)))
m.Attachments = append(m.Attachments, string(NormalizeAttachment(config.Mailroom, a)))
}
}

Expand Down Expand Up @@ -379,7 +379,7 @@ func NewIncomingMsg(orgID OrgID, channel *Channel, contactID ContactID, in *flow

// add any attachments
for _, a := range in.Attachments() {
m.Attachments = append(m.Attachments, string(NormalizeAttachment(a)))
m.Attachments = append(m.Attachments, string(NormalizeAttachment(config.Mailroom, a)))
}

return msg
Expand Down Expand Up @@ -441,7 +441,7 @@ func LoadMessages(ctx context.Context, db Queryer, orgID OrgID, direction MsgDir

// NormalizeAttachment will turn any relative URL in the passed in attachment and normalize it to
// include the full host for attachment domains
func NormalizeAttachment(attachment utils.Attachment) utils.Attachment {
func NormalizeAttachment(cfg *config.Config, attachment utils.Attachment) utils.Attachment {
// don't try to modify geo type attachments which are just coordinates
if attachment.ContentType() == "geo" {
return attachment
Expand All @@ -450,9 +450,9 @@ func NormalizeAttachment(attachment utils.Attachment) utils.Attachment {
url := attachment.URL()
if !strings.HasPrefix(url, "http") {
if strings.HasPrefix(url, "/") {
url = fmt.Sprintf("https://%s%s", config.Mailroom.AttachmentDomain, url)
url = fmt.Sprintf("https://%s%s", cfg.AttachmentDomain, url)
} else {
url = fmt.Sprintf("https://%s/%s", config.Mailroom.AttachmentDomain, url)
url = fmt.Sprintf("https://%s/%s", cfg.AttachmentDomain, url)
}
}
return utils.Attachment(fmt.Sprintf("%s:%s", attachment.ContentType(), url))
Expand Down
9 changes: 5 additions & 4 deletions core/models/msgs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/nyaruka/goflow/envs"
"github.com/nyaruka/goflow/flows"
"github.com/nyaruka/goflow/utils"
"github.com/nyaruka/mailroom/config"
"github.com/nyaruka/mailroom/core/models"
"github.com/nyaruka/mailroom/testsuite"
"github.com/nyaruka/mailroom/testsuite/testdata"
Expand Down Expand Up @@ -218,8 +217,10 @@ func TestResendMessages(t *testing.T) {
}

func TestNormalizeAttachment(t *testing.T) {
config.Mailroom.AttachmentDomain = "foo.bar.com"
defer func() { config.Mailroom.AttachmentDomain = "" }()
rt := testsuite.RT()

rt.Config.AttachmentDomain = "foo.bar.com"
defer func() { rt.Config.AttachmentDomain = "" }()

tcs := []struct {
raw string
Expand All @@ -233,7 +234,7 @@ func TestNormalizeAttachment(t *testing.T) {
}

for _, tc := range tcs {
assert.Equal(t, tc.normalized, string(models.NormalizeAttachment(utils.Attachment(tc.raw))))
assert.Equal(t, tc.normalized, string(models.NormalizeAttachment(rt.Config, utils.Attachment(tc.raw))))
}
}

Expand Down
Loading

0 comments on commit ef4ef87

Please sign in to comment.