Skip to content

Commit

Permalink
Add send_optin action and optin_sent event
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanseymour committed Sep 14, 2023
1 parent caa8b8b commit a7f63f8
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 15 deletions.
24 changes: 13 additions & 11 deletions flows/actions/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,15 @@ import (
"github.com/stretchr/testify/require"
)

var contactJSON = `{
var defaultContactJSON = []byte(`{
"uuid": "5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f",
"name": "Ryan Lewis",
"language": "eng",
"timezone": "America/Guayaquil",
"urns": [],
"urns": [
"tel:+12065551212?channel=57f1078f-88aa-46f4-a59a-948a5739c03d&id=123",
"twitterid:54784326227#nyaruka"
],
"groups": [
{"uuid": "b7cf0d83-f1c9-411c-96fd-c511a4cfa86d", "name": "Testers"},
{"uuid": "0ec97956-c451-48a0-a180-1ce766623e31", "name": "Males"}
Expand All @@ -53,7 +56,7 @@ var contactJSON = `{
}
},
"created_on": "2018-06-20T11:40:30.123456789-00:00"
}`
}`)

func TestActionTypes(t *testing.T) {
assetsJSON, err := os.ReadFile("testdata/_assets.json")
Expand Down Expand Up @@ -81,7 +84,7 @@ func testActionType(t *testing.T, assetsJSON json.RawMessage, typeName string) {
HTTPMocks *httpx.MockRequestor `json:"http_mocks,omitempty"`
SMTPError string `json:"smtp_error,omitempty"`
NoContact bool `json:"no_contact,omitempty"`
NoURNs bool `json:"no_urns,omitempty"`
Contact json.RawMessage `json:"contact,omitempty"`
HasTicket bool `json:"has_ticket,omitempty"`
NoInput bool `json:"no_input,omitempty"`
RedactURNs bool `json:"redact_urns,omitempty"`
Expand Down Expand Up @@ -164,15 +167,14 @@ func testActionType(t *testing.T, assetsJSON json.RawMessage, typeName string) {
// optionally load our contact
var contact *flows.Contact
if !tc.NoContact {
contact, err = flows.ReadContact(sa, json.RawMessage(contactJSON), assets.PanicOnMissing)
contactJSON := defaultContactJSON
if tc.Contact != nil {
contactJSON = tc.Contact
}

contact, err = flows.ReadContact(sa, contactJSON, assets.PanicOnMissing)
require.NoError(t, err)

// optionally give our contact some URNs and a ticket
if !tc.NoURNs {
channel := sa.Channels().Get("57f1078f-88aa-46f4-a59a-948a5739c03d")
contact.AddURN(urns.URN("tel:+12065551212?channel=57f1078f-88aa-46f4-a59a-948a5739c03d&id=123"), channel)
contact.AddURN(urns.URN("twitterid:54784326227#nyaruka"), nil)
}
if tc.HasTicket {
ticketer := sa.Ticketers().Get("d605bb96-258d-4097-ad0a-080937db2212")
topic := sa.Topics().Get("0d9a2c56-6fc2-4f27-93c5-a6322e26b740")
Expand Down
55 changes: 55 additions & 0 deletions flows/actions/send_optin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package actions

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

func init() {
registerType(TypeSendOptIn, func() flows.Action { return &SendOptInAction{} })
}

// TypeSendOptIn is the type for the send optin action
const TypeSendOptIn string = "send_optin"

// SendOptInAction can be used to send an optin to the contact if the channel supports that.
//
// An [event:optin_sent] event will be created if the optin was sent.
//
// {
// "uuid": "8eebd020-1af5-431c-b943-aa670fc74da9",
// "type": "send_optin",
// "optin": {
// "uuid": "248be71d-78e9-4d71-a6c4-9981d369e5cb",
// "name": "Joke Of The Day"
// }
// }
//
// @action send_optin
type SendOptInAction struct {
baseAction
onlineAction

OptIn *assets.OptInReference `json:"optin" validate:"required,dive"`
}

// NewSendOptIn creates a new send optin action
func NewSendOptIn(uuid flows.ActionUUID, optIn *assets.OptInReference) *SendOptInAction {
return &SendOptInAction{
baseAction: newBaseAction(TypeSendOptIn, uuid),
OptIn: optIn,
}
}

// Execute creates the optin events
func (a *SendOptInAction) Execute(run flows.Run, step flows.Step, logModifier flows.ModifierCallback, logEvent flows.EventCallback) error {
optIn := run.Session().Assets().OptIns().Get(a.OptIn.UUID)
destinations := run.Contact().ResolveDestinations(false)

if len(destinations) > 0 && destinations[0].Channel.HasFeature(assets.ChannelFeatureOptIns) {
logEvent(events.NewOptInSent(optIn))
}

return nil
}
21 changes: 21 additions & 0 deletions flows/actions/testdata/_assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,21 @@
"receive"
]
},
{
"uuid": "4bb288a0-7fca-4da1-abe8-59a593aff648",
"name": "Facebook Channel",
"address": "235326346322111",
"schemes": [
"facebook"
],
"roles": [
"send",
"receive"
],
"features": [
"optins"
]
},
{
"uuid": "8e21f093-99aa-413b-b55b-758b54308fcb",
"name": "Twitter Channel",
Expand Down Expand Up @@ -205,6 +220,12 @@
"name": "Spam"
}
],
"optins": [
{
"uuid": "248be71d-78e9-4d71-a6c4-9981d369e5cb",
"name": "Joke Of The Day"
}
],
"resthooks": [
{
"slug": "new-registration",
Expand Down
11 changes: 10 additions & 1 deletion flows/actions/testdata/call_resthook.json
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,16 @@
}
]
},
"no_urns": true,
"contact": {
"uuid": "5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f",
"name": "Ryan Lewis",
"language": "eng",
"timezone": "America/Guayaquil",
"urns": [],
"groups": [],
"fields": {},
"created_on": "2018-06-20T11:40:30.123456789-00:00"
},
"no_input": true,
"action": {
"type": "call_resthook",
Expand Down
11 changes: 10 additions & 1 deletion flows/actions/testdata/send_msg.json
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,16 @@
},
{
"description": "Msg created event even if contact has no sendable URNs",
"no_urns": true,
"contact": {
"uuid": "5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f",
"name": "Ryan Lewis",
"language": "eng",
"timezone": "America/Guayaquil",
"urns": [],
"groups": [],
"fields": {},
"created_on": "2018-06-20T11:40:30.123456789-00:00"
},
"action": {
"type": "send_msg",
"uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
Expand Down
48 changes: 48 additions & 0 deletions flows/actions/testdata/send_optin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[
{
"description": "NOOP when channel doesn't support optins feature",
"action": {
"type": "send_optin",
"uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
"optin": {
"uuid": "248be71d-78e9-4d71-a6c4-9981d369e5cb",
"name": "Joke Of The Day"
}
},
"events": []
},
{
"description": "Event created when channel does support optins feature",
"contact": {
"uuid": "5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f",
"name": "Ryan Lewis",
"language": "eng",
"timezone": "America/Guayaquil",
"urns": [
"facebook:1234567890"
],
"groups": [],
"fields": {},
"created_on": "2018-06-20T11:40:30.123456789-00:00"
},
"action": {
"type": "send_optin",
"uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
"optin": {
"uuid": "248be71d-78e9-4d71-a6c4-9981d369e5cb",
"name": "Joke Of The Day"
}
},
"events": [
{
"type": "optin_sent",
"created_on": "2018-10-18T14:20:30.000123456Z",
"step_uuid": "59d74b86-3e2f-4a93-aece-b05d2fdcde0c",
"optin": {
"name": "Joke Of The Day",
"uuid": "248be71d-78e9-4d71-a6c4-9981d369e5cb"
}
}
]
}
]
11 changes: 10 additions & 1 deletion flows/actions/testdata/transfer_airtime.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,16 @@
},
{
"description": "Error and failed transfer if contact has no tel urn",
"no_urns": true,
"contact": {
"uuid": "5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f",
"name": "Ryan Lewis",
"language": "eng",
"timezone": "America/Guayaquil",
"urns": [],
"groups": [],
"fields": {},
"created_on": "2018-06-20T11:40:30.123456789-00:00"
},
"action": {
"type": "transfer_airtime",
"uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
Expand Down
39 changes: 39 additions & 0 deletions flows/events/optin_sent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package events

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

func init() {
registerType(TypeOptInSent, func() flows.Event { return &OptInSentEvent{} })
}

// TypeOptInSent is our type for the optin event
const TypeOptInSent string = "optin_sent"

// OptInSentEvent events are created when an action has sent an optin.
//
// {
// "type": "optin_sent",
// "created_on": "2006-01-02T15:04:05Z",
// "optin": {
// "uuid": "248be71d-78e9-4d71-a6c4-9981d369e5cb",
// "name": "Joke Of The Day"
// }
// }
//
// @event optin_sent
type OptInSentEvent struct {
BaseEvent

OptIn *assets.OptInReference `json:"optin" validate:"required,dive"`
}

// NewOptInSent returns a new optin sent event
func NewOptInSent(optIn *flows.OptIn) *OptInSentEvent {
return &OptInSentEvent{
BaseEvent: NewBaseEvent(TypeOptInSent),
OptIn: optIn.Reference(),
}
}
3 changes: 2 additions & 1 deletion test/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ var sessionAssets = `{
"name": "Facebook Channel",
"address": "235326346322111",
"schemes": ["facebook"],
"roles": ["send", "receive"]
"roles": ["send", "receive"],
"features": ["optins"]
}
],
"classifiers": [
Expand Down

0 comments on commit a7f63f8

Please sign in to comment.