Skip to content

Commit

Permalink
Add ticket topics (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanseymour committed Aug 24, 2021
1 parent 20fb787 commit 4fcc521
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 1 deletion.
22 changes: 21 additions & 1 deletion core/models/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ type OrgAssets struct {
ticketers []assets.Ticketer
ticketersByID map[TicketerID]*Ticketer
ticketersByUUID map[assets.TicketerUUID]*Ticketer
topics []assets.Topic
topicsByUUID map[assets.TopicUUID]*Topic

resthooks []assets.Resthook
templates []assets.Template
Expand Down Expand Up @@ -320,6 +322,20 @@ func NewOrgAssets(ctx context.Context, db *sqlx.DB, orgID OrgID, prev *OrgAssets
oa.ticketersByUUID = prev.ticketersByUUID
}

if prev == nil || refresh&RefreshTopics > 0 {
oa.topics, err = loadTopics(ctx, db, orgID)
if err != nil {
return nil, errors.Wrapf(err, "error loading topic assets for org %d", orgID)
}
oa.topicsByUUID = make(map[assets.TopicUUID]*Topic)
for _, t := range oa.topics {
oa.topicsByUUID[t.UUID()] = t.(*Topic)
}
} else {
oa.topics = prev.topics
oa.topicsByUUID = prev.topicsByUUID
}

if prev == nil || refresh&RefreshUsers > 0 {
oa.users, err = loadUsers(ctx, db, orgID)
if err != nil {
Expand Down Expand Up @@ -631,7 +647,11 @@ func (a *OrgAssets) TicketerByUUID(uuid assets.TicketerUUID) *Ticketer {
}

func (a *OrgAssets) Topics() ([]assets.Topic, error) {
return nil, nil // TODO
return a.topics, nil
}

func (a *OrgAssets) TopicByUUID(uuid assets.TopicUUID) *Topic {
return a.topicsByUUID[uuid]
}

func (a *OrgAssets) Users() ([]assets.User, error) {
Expand Down
105 changes: 105 additions & 0 deletions core/models/topics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package models

import (
"context"
"database/sql"
"database/sql/driver"
"time"

"github.com/jmoiron/sqlx"
"github.com/nyaruka/gocommon/dates"
"github.com/nyaruka/goflow/assets"
"github.com/nyaruka/mailroom/utils/dbutil"
"github.com/nyaruka/null"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)

type TopicID null.Int

type Topic struct {
t struct {
ID TopicID `json:"id"`
UUID assets.TopicUUID `json:"uuid"`
OrgID OrgID `json:"org_id"`
Name string `json:"name"`
IsDefault bool `json:"is_default"`
}
}

// ID returns the ID
func (t *Topic) ID() TopicID { return t.t.ID }

// UUID returns the UUID
func (t *Topic) UUID() assets.TopicUUID { return t.t.UUID }

// OrgID returns the org ID
func (t *Topic) OrgID() OrgID { return t.t.OrgID }

// Name returns the name
func (t *Topic) Name() string { return t.t.Name }

// Type returns the type
func (t *Topic) IsDefault() bool { return t.t.IsDefault }

const selectOrgTopicsSQL = `
SELECT ROW_TO_JSON(r) FROM (SELECT
t.id as id,
t.uuid as uuid,
t.org_id as org_id,
t.name as name,
t.is_default as is_default
FROM
tickets_topic t
WHERE
t.org_id = $1 AND
t.is_active = TRUE
ORDER BY
t.is_default DESC, t.created_on ASC
) r;
`

// loadTopics loads all the topics for the passed in org
func loadTopics(ctx context.Context, db sqlx.Queryer, orgID OrgID) ([]assets.Topic, error) {
start := dates.Now()

rows, err := db.Queryx(selectOrgTopicsSQL, orgID)
if err != nil && err != sql.ErrNoRows {
return nil, errors.Wrapf(err, "error querying topics for org: %d", orgID)
}
defer rows.Close()

topics := make([]assets.Topic, 0, 2)
for rows.Next() {
topic := &Topic{}
err := dbutil.ReadJSONRow(rows, &topic.t)
if err != nil {
return nil, errors.Wrapf(err, "error unmarshalling topic")
}
topics = append(topics, topic)
}

logrus.WithField("elapsed", time.Since(start)).WithField("org_id", orgID).WithField("count", len(topics)).Debug("loaded topics")

return topics, nil
}

// MarshalJSON marshals into JSON. 0 values will become null
func (i TopicID) MarshalJSON() ([]byte, error) {
return null.Int(i).MarshalJSON()
}

// UnmarshalJSON unmarshals from JSON. null values become 0
func (i *TopicID) UnmarshalJSON(b []byte) error {
return null.UnmarshalInt(b, (*null.Int)(i))
}

// Value returns the db value, null is returned for 0
func (i TopicID) Value() (driver.Value, error) {
return null.Int(i).Value()
}

// Scan scans from the db value. null values become 0
func (i *TopicID) Scan(value interface{}) error {
return null.ScanInt(value, (*null.Int)(i))
}
24 changes: 24 additions & 0 deletions core/models/topics_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package models_test

import (
"testing"

"github.com/nyaruka/mailroom/core/models"
"github.com/nyaruka/mailroom/testsuite"
"github.com/nyaruka/mailroom/testsuite/testdata"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestTopics(t *testing.T) {
ctx, _, db, _ := testsuite.Get()

oa, err := models.GetOrgAssetsWithRefresh(ctx, db, testdata.Org1.ID, models.RefreshTopics)
require.NoError(t, err)

topics, err := oa.Topics()
require.NoError(t, err)

assert.Equal(t, 1, len(topics))
assert.Equal(t, "General", topics[0].Name())
}

0 comments on commit 4fcc521

Please sign in to comment.