Skip to content

Commit

Permalink
Merge pull request rapidpro#276 from nyaruka/translations_tweak
Browse files Browse the repository at this point in the history
Translations tweak
  • Loading branch information
rowanseymour authored Jun 26, 2024
2 parents 1408713 + b69de21 commit 991cabf
Show file tree
Hide file tree
Showing 8 changed files with 31 additions and 64 deletions.
24 changes: 12 additions & 12 deletions core/models/broadcasts.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,17 @@ type Broadcast struct {
}

type dbBroadcast struct {
ID BroadcastID `db:"id"`
OrgID OrgID `db:"org_id"`
Translations flows.BroadcastTranslations `db:"translations"`
BaseLanguage i18n.Language `db:"base_language"`
OptInID OptInID `db:"optin_id"`
URNs pq.StringArray `db:"urns"`
Query null.String `db:"query"`
Exclusions Exclusions `db:"exclusions"`
CreatedByID UserID `db:"created_by_id"`
ScheduleID ScheduleID `db:"schedule_id"`
ParentID BroadcastID `db:"parent_id"`
ID BroadcastID `db:"id"`
OrgID OrgID `db:"org_id"`
Translations JSONB[flows.BroadcastTranslations] `db:"translations"`
BaseLanguage i18n.Language `db:"base_language"`
OptInID OptInID `db:"optin_id"`
URNs pq.StringArray `db:"urns"`
Query null.String `db:"query"`
Exclusions Exclusions `db:"exclusions"`
CreatedByID UserID `db:"created_by_id"`
ScheduleID ScheduleID `db:"schedule_id"`
ParentID BroadcastID `db:"parent_id"`
}

var ErrNoRecipients = errors.New("can't create broadcast with no recipients")
Expand Down Expand Up @@ -144,7 +144,7 @@ func InsertBroadcast(ctx context.Context, db DBorTx, bcast *Broadcast) error {
dbb := &dbBroadcast{
ID: bcast.ID,
OrgID: bcast.OrgID,
Translations: bcast.Translations,
Translations: JSONB[flows.BroadcastTranslations]{bcast.Translations},
BaseLanguage: bcast.BaseLanguage,
OptInID: bcast.OptInID,
URNs: ua,
Expand Down
27 changes: 0 additions & 27 deletions core/models/broadcasts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,33 +74,6 @@ func TestNonPersistentBroadcasts(t *testing.T) {
assertdb.Query(t, rt.DB, `SELECT count(*) FROM msgs_msg WHERE direction = 'O' AND broadcast_id IS NULL AND text = 'Hi there'`).Returns(2)
}

func TestBroadcastTranslations(t *testing.T) {
_, rt := testsuite.Runtime()

defer func() {
rt.DB.MustExec(`DELETE FROM msgs_broadcast_contacts`)
rt.DB.MustExec(`DELETE FROM msgs_broadcast`)
}()

bcastID := testdata.InsertBroadcast(rt, testdata.Org1, `eng`, map[i18n.Language]string{`eng`: "Hello", `spa`: "Hola"}, nil, models.NilScheduleID, []*testdata.Contact{testdata.Cathy}, nil)

type TestStruct struct {
Translations flows.BroadcastTranslations `json:"translations"`
}

s := &TestStruct{}
err := rt.DB.Get(s, `SELECT translations FROM msgs_broadcast WHERE id = $1`, bcastID)
require.NoError(t, err)

assert.Equal(t, flows.BroadcastTranslations{"eng": {Text: "Hello"}, "spa": {Text: "Hola"}}, s.Translations)

s.Translations = flows.BroadcastTranslations{"fra": {Text: "Bonjour"}}

rt.DB.MustExec(`UPDATE msgs_broadcast SET translations = $1 WHERE id = $2`, s.Translations, bcastID)

assertdb.Query(t, rt.DB, `SELECT count(*) FROM msgs_broadcast WHERE translations -> 'fra' ->> 'text' = 'Bonjour'`, 1)
}

func TestBroadcastBatchCreateMessage(t *testing.T) {
ctx, rt := testsuite.Runtime()

Expand Down
2 changes: 1 addition & 1 deletion core/models/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ type Templating struct {
*flows.MsgTemplating
}

// Scan supports reading translation values from JSON in database
// Scan supports reading templating values from JSON in database
func (t *Templating) Scan(value any) error {
if value == nil {
return nil
Expand Down
32 changes: 13 additions & 19 deletions core/models/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"database/sql"
"database/sql/driver"
"encoding/json"
"errors"
"fmt"
"log/slog"
"time"
Expand Down Expand Up @@ -101,26 +102,19 @@ func ScanJSONRows[T any](rows *sql.Rows, f func() T) ([]T, error) {
return as, nil
}

// Map is a generic map which is written to the database as JSON. For nullable fields use null.Map.
type JSONMap map[string]any

// Scan implements the Scanner interface
func (m *JSONMap) Scan(value any) error {
var raw []byte
switch typed := value.(type) {
case string:
raw = []byte(typed)
case []byte:
raw = typed
default:
return fmt.Errorf("unable to scan %T as map", value)
}
// JSONB is a generic wrapper for a type which should be written to and read from the database as JSON.
type JSONB[T any] struct {
V T
}

if err := json.Unmarshal(raw, m); err != nil {
return err
func (t *JSONB[T]) Scan(value any) error {
b, ok := value.([]byte)
if !ok {
return errors.New("failed type assertion to []byte")
}
return nil
return json.Unmarshal(b, &t.V)
}

// Value implements the Valuer interface
func (m JSONMap) Value() (driver.Value, error) { return json.Marshal(m) }
func (t JSONB[T]) Value() (driver.Value, error) {
return json.Marshal(t.V)
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ require (
github.com/lib/pq v1.10.9
github.com/nyaruka/ezconf v0.3.0
github.com/nyaruka/gocommon v1.55.5
github.com/nyaruka/goflow v0.216.4
github.com/nyaruka/goflow v0.217.0
github.com/nyaruka/null/v3 v3.0.0
github.com/nyaruka/redisx v0.8.1
github.com/nyaruka/rp-indexer/v9 v9.1.3
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ github.com/nyaruka/ezconf v0.3.0 h1:kGvJqVN8AHowb4HdaHAviJ0Z3yI5Pyekp1WqibFEaGk=
github.com/nyaruka/ezconf v0.3.0/go.mod h1:89GUW6EPRNLIxT7lC4LWnjWTgZeQwRoX7lBmc8ralAU=
github.com/nyaruka/gocommon v1.55.5 h1:1HCDTwoegsmd1FJQH/cFPHIzX3YEtqjRTJ09N+4nlSY=
github.com/nyaruka/gocommon v1.55.5/go.mod h1:hHczEMFfODl6k527y3yxQv48WDX5BnBT0/WpCveSYwg=
github.com/nyaruka/goflow v0.216.4 h1:pu0BeQ4OHywIXPpYdb6psQY1M9u3l4bXcufXp9Wnf98=
github.com/nyaruka/goflow v0.216.4/go.mod h1:lcdYSHmtkYzdI6VsXKpxD7ZISEHlU+6f49JKtcMJlMU=
github.com/nyaruka/goflow v0.217.0 h1:2Me2vS7DhlguHjH2gBjyCSb0bsSW5JYfjVzuA/DcdpI=
github.com/nyaruka/goflow v0.217.0/go.mod h1:lcdYSHmtkYzdI6VsXKpxD7ZISEHlU+6f49JKtcMJlMU=
github.com/nyaruka/librato v1.1.1 h1:0nTYtJLl3Sn7lX3CuHsLf+nXy1k/tGV0OjVxLy3Et4s=
github.com/nyaruka/librato v1.1.1/go.mod h1:fme1Fu1PT2qvkaBZyw8WW+SrnFe2qeeCWpvqmAaKAKE=
github.com/nyaruka/null/v2 v2.0.3 h1:rdmMRQyVzrOF3Jff/gpU/7BDR9mQX0lcLl4yImsA3kw=
Expand Down
2 changes: 1 addition & 1 deletion testsuite/testdata/channels.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func InsertChannel(rt *runtime.Runtime, org *Org, channelType models.ChannelType
var id models.ChannelID
must(rt.DB.Get(&id,
`INSERT INTO channels_channel(uuid, org_id, channel_type, name, address, schemes, role, config, last_seen, is_system, log_policy, is_active, created_on, modified_on, created_by_id, modified_by_id)
VALUES($1, $2, $3, $4, $5, $6, $7, $8, NOW(), FALSE, 'A', TRUE, NOW(), NOW(), 1, 1) RETURNING id`, uuid, org.ID, channelType, name, address, pq.Array(schemes), role, models.JSONMap(config),
VALUES($1, $2, $3, $4, $5, $6, $7, $8, NOW(), FALSE, 'A', TRUE, NOW(), NOW(), 1, 1) RETURNING id`, uuid, org.ID, channelType, name, address, pq.Array(schemes), role, models.JSONB[map[string]any]{config},
))
return &Channel{ID: id, UUID: uuid, Type: channelType}
}
Expand Down
2 changes: 1 addition & 1 deletion testsuite/testdata/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func InsertBroadcast(rt *runtime.Runtime, org *Org, baseLanguage i18n.Language,
var id models.BroadcastID
must(rt.DB.Get(&id,
`INSERT INTO msgs_broadcast(org_id, base_language, translations, optin_id, schedule_id, status, created_on, modified_on, created_by_id, modified_by_id, is_active)
VALUES($1, $2, $3, $4, $5, 'P', NOW(), NOW(), 1, 1, TRUE) RETURNING id`, org.ID, baseLanguage, translations, optInID, schedID,
VALUES($1, $2, $3, $4, $5, 'P', NOW(), NOW(), 1, 1, TRUE) RETURNING id`, org.ID, baseLanguage, models.JSONB[flows.BroadcastTranslations]{translations}, optInID, schedID,
))

for _, contact := range contacts {
Expand Down

0 comments on commit 991cabf

Please sign in to comment.