Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -360,3 +360,5 @@ TWILIO_MESSAGING_SERVICE_SID=
TWILIO_PUBLIC_BASE_URL=
# Example only; replace with the externally reachable canonical HTTPS origin.
# TWILIO_PUBLIC_BASE_URL=https://api.example.com
# Country assumed for phone numbers entered without a country code (ISO 3166 alpha-2).
NOTIFICATION_DEFAULT_PHONE_REGION=US
41 changes: 41 additions & 0 deletions backend/internal/cmd/serve/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ import (
"github.com/trakrf/platform/backend/internal/logger"
"github.com/trakrf/platform/backend/internal/mustering"
"github.com/trakrf/platform/backend/internal/notification"
"github.com/trakrf/platform/backend/internal/notification/outbox"
"github.com/trakrf/platform/backend/internal/notification/resend"
"github.com/trakrf/platform/backend/internal/notification/routing"
"github.com/trakrf/platform/backend/internal/notification/twilio"
"github.com/trakrf/platform/backend/internal/readercontrol"
authservice "github.com/trakrf/platform/backend/internal/services/auth"
Expand Down Expand Up @@ -124,6 +126,45 @@ func Run(ctx context.Context, info buildinfo.Info, frontendFS fs.FS) error {
}
log.Info().Bool("enabled", emailConfig.Enabled).Msg("Notification email integration configured")

// The delivery planner turns a detected alarm or move into queued
// deliveries. Built at boot so a bad phone region or a broken template
// refuses to start; detection wires it in later, and the queue runtime
// that would run its trigger jobs is not started yet.
routingConfig, err := routing.ConfigFromEnv()
if err != nil {
return err
}
templates, err := routing.NewTemplateRenderer()
if err != nil {
return err
}
jobInserter, err := outbox.NewInserter(store.Pool().(*pgxpool.Pool))
if err != nil {
return err
}
planningMetrics, err := routing.NewMetrics(prometheus.DefaultRegisterer)
if err != nil {
return err
}
outboxMetrics, err := outbox.NewMetrics(prometheus.DefaultRegisterer)
if err != nil {
return err
}
deliveryPlanner, err := routing.NewDeliveryPlanner(routing.Deps{
Store: store,
Jobs: jobInserter,
Renderer: templates,
Metrics: planningMetrics,
OutboxMetrics: outboxMetrics,
PhoneRegion: routingConfig.PhoneRegion,
Log: *log,
})
if err != nil {
return err
}
_ = deliveryPlanner
log.Info().Str("phone_region", routingConfig.PhoneRegion).Msg("notification delivery planner constructed")

// Say it at boot as well as on /health (TRA-1190). The endpoint is what a
// test suite can check; this line is what a developer actually sees, and
// `just dev` completing against a schema two migrations behind is the root
Expand Down
30 changes: 30 additions & 0 deletions backend/internal/notification/routing/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package routing

import (
"fmt"
"os"
"strings"

"github.com/trakrf/platform/backend/internal/notification/phone"
)

// DefaultPhoneRegion is the country assumed for phone numbers entered without
// a country code, when NOTIFICATION_DEFAULT_PHONE_REGION is not set.
const DefaultPhoneRegion = "US"

// Config holds the delivery planner's environment settings.
type Config struct {
PhoneRegion string
}

// ConfigFromEnv reads the planner settings and rejects an unknown phone region.
func ConfigFromEnv() (Config, error) {
region := strings.ToUpper(strings.TrimSpace(os.Getenv("NOTIFICATION_DEFAULT_PHONE_REGION")))
if region == "" {
region = DefaultPhoneRegion
}
if err := phone.ValidateRegion(region); err != nil {
return Config{}, fmt.Errorf("NOTIFICATION_DEFAULT_PHONE_REGION %q: %w", region, err)
}
return Config{PhoneRegion: region}, nil
}
29 changes: 29 additions & 0 deletions backend/internal/notification/routing/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package routing

import (
"testing"

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

func TestConfigFromEnv(t *testing.T) {
t.Run("unset defaults to US", func(t *testing.T) {
t.Setenv("NOTIFICATION_DEFAULT_PHONE_REGION", "")
cfg, err := ConfigFromEnv()
require.NoError(t, err)
assert.Equal(t, "US", cfg.PhoneRegion)
})
t.Run("lower case is upper-cased", func(t *testing.T) {
t.Setenv("NOTIFICATION_DEFAULT_PHONE_REGION", "ug")
cfg, err := ConfigFromEnv()
require.NoError(t, err)
assert.Equal(t, "UG", cfg.PhoneRegion)
})
t.Run("unknown region is an error naming the variable", func(t *testing.T) {
t.Setenv("NOTIFICATION_DEFAULT_PHONE_REGION", "ZZ")
_, err := ConfigFromEnv()
require.Error(t, err)
assert.Contains(t, err.Error(), "NOTIFICATION_DEFAULT_PHONE_REGION")
})
}
Loading