Skip to content

Commit

Permalink
Get rid of ticket references
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanseymour committed Jun 9, 2021
1 parent d69e984 commit 4069639
Show file tree
Hide file tree
Showing 12 changed files with 197 additions and 151 deletions.
2 changes: 1 addition & 1 deletion flows/actions/open_ticket.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (a *OpenTicketAction) Execute(run flows.FlowRun, step flows.Step, logModifi

ticket := a.open(run, step, ticketer, evaluatedSubject, evaluatedBody, logEvent)
if ticket != nil {
a.saveResult(run, step, a.ResultName, string(ticket.UUID), CategorySuccess, "", "", nil, logEvent)
a.saveResult(run, step, a.ResultName, string(ticket.UUID()), CategorySuccess, "", "", nil, logEvent)
} else {
a.saveResult(run, step, a.ResultName, "", CategoryFailure, "", "", nil, logEvent)
}
Expand Down
29 changes: 23 additions & 6 deletions flows/contact.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func NewContact(
urns []urns.URN,
groups []*assets.GroupReference,
fields map[string]*Value,
tickets []*TicketReference,
tickets []*Ticket,
missing assets.MissingCallback) (*Contact, error) {

urnList, err := ReadURNList(sa, urns, missing)
Expand All @@ -86,7 +86,7 @@ func NewContact(

groupList := NewGroupList(sa, groups, missing)
fieldValues := NewFieldValues(sa, fields, missing)
ticketList := NewTicketList(sa, tickets, missing)
ticketList := NewTicketList(tickets)

return &Contact{
uuid: uuid,
Expand Down Expand Up @@ -118,7 +118,7 @@ func NewEmptyContact(sa SessionAssets, name string, language envs.Language, time
urns: URNList{},
groups: NewGroupList(sa, nil, assets.IgnoreMissing),
fields: make(FieldValues),
tickets: NewTicketList(sa, nil, assets.IgnoreMissing),
tickets: NewTicketList([]*Ticket{}),
assets: sa,
}
}
Expand Down Expand Up @@ -582,7 +582,7 @@ type contactEnvelope struct {
URNs []urns.URN `json:"urns,omitempty" validate:"dive,urn"`
Groups []*assets.GroupReference `json:"groups,omitempty" validate:"dive"`
Fields map[string]*Value `json:"fields,omitempty"`
Tickets []*TicketReference `json:"tickets,omitempty" validate:"dive"`
Tickets []json.RawMessage `json:"tickets,omitempty"`
}

// ReadContact decodes a contact from the passed in JSON
Expand Down Expand Up @@ -626,13 +626,30 @@ func ReadContact(sa SessionAssets, data json.RawMessage, missing assets.MissingC

c.groups = NewGroupList(sa, envelope.Groups, missing)
c.fields = NewFieldValues(sa, envelope.Fields, missing)
c.tickets = NewTicketList(sa, envelope.Tickets, missing)

tickets := make([]*Ticket, len(envelope.Tickets))
for i := range envelope.Tickets {
tickets[i], err = ReadTicket(sa, envelope.Tickets[i], missing)
if err != nil {
return nil, errors.Wrap(err, "unable to read ticket")
}
}
c.tickets = NewTicketList(tickets)

return c, nil
}

// MarshalJSON marshals this contact into JSON
func (c *Contact) MarshalJSON() ([]byte, error) {
var err error
tickets := make([]json.RawMessage, len(c.tickets.tickets))
for i, ticket := range c.tickets.tickets {
tickets[i], err = jsonx.Marshal(ticket)
if err != nil {
return nil, err
}
}

ce := &contactEnvelope{
Name: c.name,
UUID: c.uuid,
Expand All @@ -643,7 +660,7 @@ func (c *Contact) MarshalJSON() ([]byte, error) {
LastSeenOn: c.lastSeenOn,
URNs: c.urns.RawURNs(),
Groups: c.groups.references(),
Tickets: c.tickets.references(),
Tickets: tickets,
}

if c.timezone != nil {
Expand Down
2 changes: 1 addition & 1 deletion flows/contact_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func TestContact(t *testing.T) {

assert.Equal(t, 0, contact.Tickets().Count())

ticket := flows.NewTicket(sa.Ticketers().Get("19dc6346-9623-4fe4-be80-538d493ecdf5"), "New ticket", "I have issues")
ticket := flows.OpenTicket(sa.Ticketers().Get("19dc6346-9623-4fe4-be80-538d493ecdf5"), "New ticket", "I have issues")
contact.Tickets().Add(ticket)

assert.Equal(t, 1, contact.Tickets().Count())
Expand Down
7 changes: 3 additions & 4 deletions flows/events/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ func TestEventMarshaling(t *testing.T) {
timeout := 500
gender := session.Assets().Fields().Get("gender")
mailgun := session.Assets().Ticketers().Get("19dc6346-9623-4fe4-be80-538d493ecdf5")
ticket := flows.NewTicket(mailgun, "Need help", "Where are my cookies?")
ticket.ExternalID = "1243252"
ticket := flows.NewTicket("7481888c-07dd-47dc-bf22-ef7448696ffe", mailgun, "Need help", "Where are my cookies?", "1243252")

eventTests := []struct {
event flows.Event
Expand Down Expand Up @@ -424,7 +423,7 @@ func TestEventMarshaling(t *testing.T) {
},
"text": "Hi there",
"urn": "tel:+12345678900",
"uuid": "04e910a5-d2e3-448b-958a-630e35c62431"
"uuid": "20cc4181-48cf-4344-9751-99419796decd"
},
"type": "ivr_created"
}`,
Expand Down Expand Up @@ -518,7 +517,7 @@ func TestEventMarshaling(t *testing.T) {
"type": "ticket_opened",
"created_on": "2018-10-18T14:20:30.000123456Z",
"ticket": {
"uuid": "20cc4181-48cf-4344-9751-99419796decd",
"uuid": "7481888c-07dd-47dc-bf22-ef7448696ffe",
"ticketer": {
"uuid": "19dc6346-9623-4fe4-be80-538d493ecdf5",
"name": "Support Tickets"
Expand Down
19 changes: 17 additions & 2 deletions flows/events/ticket_opened.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package events

import (
"github.com/nyaruka/goflow/assets"
"github.com/nyaruka/goflow/flows"
)

Expand All @@ -11,6 +12,14 @@ func init() {
// TypeTicketOpened is the type for our ticket opened events
const TypeTicketOpened string = "ticket_opened"

type Ticket struct {
UUID flows.TicketUUID `json:"uuid" validate:"required,uuid4"`
Ticketer *assets.TicketerReference `json:"ticketer" validate:"required,dive"`
Subject string `json:"subject"`
Body string `json:"body"`
ExternalID string `json:"external_id,omitempty"`
}

// TicketOpenedEvent events are created when a new ticket is opened.
//
// {
Expand All @@ -32,13 +41,19 @@ const TypeTicketOpened string = "ticket_opened"
type TicketOpenedEvent struct {
baseEvent

Ticket *flows.TicketReference `json:"ticket"`
Ticket *Ticket `json:"ticket"`
}

// NewTicketOpened returns a new ticket opened event
func NewTicketOpened(ticket *flows.Ticket) *TicketOpenedEvent {
return &TicketOpenedEvent{
baseEvent: newBaseEvent(TypeTicketOpened),
Ticket: ticket.Reference(),
Ticket: &Ticket{
UUID: ticket.UUID(),
Ticketer: ticket.Ticketer().Reference(),
Subject: ticket.Subject(),
Body: ticket.Body(),
ExternalID: ticket.ExternalID(),
},
}
}
157 changes: 80 additions & 77 deletions flows/tickets.go
Original file line number Diff line number Diff line change
@@ -1,73 +1,50 @@
package flows

import (
"github.com/nyaruka/gocommon/jsonx"
"github.com/nyaruka/gocommon/uuids"
"github.com/nyaruka/goflow/assets"
"github.com/nyaruka/goflow/envs"
"github.com/nyaruka/goflow/excellent/types"
"github.com/nyaruka/goflow/utils"
"github.com/pkg/errors"
)

// TicketUUID is the UUID of a ticket
type TicketUUID uuids.UUID

type baseTicket struct {
UUID TicketUUID `json:"uuid"`
Subject string `json:"subject"`
Body string `json:"body"`
ExternalID string `json:"external_id,omitempty"`
}

// TicketReference is a ticket with a reference to the ticketer
type TicketReference struct {
Ticketer *assets.TicketerReference `json:"ticketer"`
baseTicket
}

// NewTicketReference creates a new ticket with a reference to the ticketer
func NewTicketReference(uuid TicketUUID, ticketer *assets.TicketerReference, subject, body, externalID string) *TicketReference {
return &TicketReference{
baseTicket: baseTicket{
UUID: uuid,
Subject: subject,
Body: body,
ExternalID: externalID,
},
Ticketer: ticketer,
}
}

// Ticket is a ticket in a ticketing system
type Ticket struct {
Ticketer *Ticketer
baseTicket
uuid TicketUUID `json:"uuid"`
ticketer *Ticketer
subject string `json:"subject"`
body string `json:"body"`
externalID string `json:"external_id,omitempty"`
}

// NewTicket creates a new ticket. Used by ticketing services to open a new ticket.
func NewTicket(ticketer *Ticketer, subject, body string) *Ticket {
return newTicket(TicketUUID(uuids.New()), ticketer, subject, body, "")
}

// creates a new ticket
func newTicket(uuid TicketUUID, ticketer *Ticketer, subject, body, externalID string) *Ticket {
// NewTicketcreates a new ticket with a reference to the ticketer
func NewTicket(uuid TicketUUID, ticketer *Ticketer, subject, body, externalID string) *Ticket {
return &Ticket{
baseTicket: baseTicket{
UUID: uuid,
Subject: subject,
Body: body,
ExternalID: externalID,
},
Ticketer: ticketer,
uuid: uuid,
ticketer: ticketer,
subject: subject,
body: body,
externalID: externalID,
}
}

// Reference converts this ticket to a ticket reference suitable for marshaling
func (t *Ticket) Reference() *TicketReference {
return &TicketReference{
baseTicket: t.baseTicket,
Ticketer: t.Ticketer.Reference(),
}
// OpenTicket creates a new ticket. Used by ticketing services to open a new ticket.
func OpenTicket(ticketer *Ticketer, subject, body string) *Ticket {
return NewTicket(TicketUUID(uuids.New()), ticketer, subject, body, "")
}

func (t *Ticket) UUID() TicketUUID { return t.uuid }
func (t *Ticket) Ticketer() *Ticketer { return t.ticketer }
func (t *Ticket) Subject() string { return t.subject }
func (t *Ticket) Body() string { return t.body }
func (t *Ticket) ExternalID() string { return t.externalID }
func (t *Ticket) SetExternalID(id string) { t.externalID = id }

// Context returns the properties available in expressions
//
// uuid:text -> the UUID of the ticket
Expand All @@ -77,35 +54,70 @@ func (t *Ticket) Reference() *TicketReference {
// @context ticket
func (t *Ticket) Context(env envs.Environment) map[string]types.XValue {
return map[string]types.XValue{
"uuid": types.NewXText(string(t.UUID)),
"subject": types.NewXText(t.Subject),
"body": types.NewXText(t.Body),
"uuid": types.NewXText(string(t.uuid)),
"subject": types.NewXText(t.subject),
"body": types.NewXText(t.body),
}
}

//------------------------------------------------------------------------------------------
// JSON Encoding / Decoding
//------------------------------------------------------------------------------------------

type ticketEnvelope struct {
UUID TicketUUID `json:"uuid" validate:"required,uuid4"`
Ticketer *assets.TicketerReference `json:"ticketer" validate:"required,dive"`
Subject string `json:"subject"`
Body string `json:"body"`
ExternalID string `json:"external_id,omitempty"`
}

// ReadTicket ecodes a contact from the passed in JSON. If the ticketer can't be found in the assets,
// we return report the missing asset and return ticket with nil ticketer.
func ReadTicket(sa SessionAssets, data []byte, missing assets.MissingCallback) (*Ticket, error) {
e := &ticketEnvelope{}

if err := utils.UnmarshalAndValidate(data, e); err != nil {
return nil, errors.Wrap(err, "unable to read ticket")
}

ticketer := sa.Ticketers().Get(e.Ticketer.UUID)
if ticketer == nil {
missing(e.Ticketer, nil)
}

return &Ticket{
uuid: e.UUID,
ticketer: ticketer,
subject: e.Subject,
body: e.Body,
externalID: e.ExternalID,
}, nil
}

// MarshalJSON marshals this ticket into JSON
func (t *Ticket) MarshalJSON() ([]byte, error) {
var ticketerRef *assets.TicketerReference
if t.ticketer != nil {
ticketerRef = t.ticketer.Reference()
}

return jsonx.Marshal(&ticketEnvelope{
UUID: t.uuid,
Ticketer: ticketerRef,
Subject: t.subject,
Body: t.body,
ExternalID: t.externalID,
})
}

// TicketList defines a contact's list of tickets
type TicketList struct {
tickets []*Ticket
}

// NewTicketFromReference creates a new ticket from a ticket reference
func NewTicketFromReference(sa SessionAssets, ref *TicketReference) *Ticket {
ticketer := sa.Ticketers().Get(ref.Ticketer.UUID)
return newTicket(ref.UUID, ticketer, ref.Subject, ref.Body, ref.ExternalID)
}

// NewTicketList creates a new ticket list
func NewTicketList(sa SessionAssets, refs []*TicketReference, missing assets.MissingCallback) *TicketList {
tickets := make([]*Ticket, 0, len(refs))

for _, ref := range refs {
ticket := NewTicketFromReference(sa, ref)
if ticket.Ticketer != nil {
tickets = append(tickets, ticket)
} else {
missing(ref.Ticketer, nil)
}
}
func NewTicketList(tickets []*Ticket) *TicketList {
return &TicketList{tickets: tickets}
}

Expand All @@ -116,15 +128,6 @@ func (l *TicketList) clone() *TicketList {
return &TicketList{tickets: tickets}
}

// returns this ticket list as a slice of ticket references
func (l *TicketList) references() []*TicketReference {
refs := make([]*TicketReference, len(l.tickets))
for i, ticket := range l.tickets {
refs[i] = ticket.Reference()
}
return refs
}

// Adds adds the given ticket to this ticket list
func (l *TicketList) Add(ticket *Ticket) {
l.tickets = append(l.tickets, ticket)
Expand Down
Loading

0 comments on commit 4069639

Please sign in to comment.