Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OptIn assets and triggers #1184

Merged
merged 5 commits into from
Sep 14, 2023
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
60 changes: 60 additions & 0 deletions assets/optin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package assets

import (
"fmt"

"github.com/nyaruka/gocommon/uuids"
)

// OptInUUID is the UUID of an opt in
type OptInUUID uuids.UUID

// OptIn are opt-ins for messaging campaign.
//
// {
// "uuid": "8925c76f-926b-4a63-a6eb-ab69e7a6b79b",
// "name": "Joke Of The Day"
// }
//
// @asset optin
type OptIn interface {
UUID() OptInUUID
Name() string
}

// OptInReference is used to reference an opt in
type OptInReference struct {
UUID OptInUUID `json:"uuid" validate:"required,uuid"`
Name string `json:"name"`
}

// NewOptInReference creates a new optin reference with the given UUID and name
func NewOptInReference(uuid OptInUUID, name string) *OptInReference {
return &OptInReference{UUID: uuid, Name: name}

Check warning on line 33 in assets/optin.go

View check run for this annotation

Codecov / codecov/patch

assets/optin.go#L33

Added line #L33 was not covered by tests
}

// Type returns the name of the asset type
func (r *OptInReference) Type() string {
return "optin"

Check warning on line 38 in assets/optin.go

View check run for this annotation

Codecov / codecov/patch

assets/optin.go#L38

Added line #L38 was not covered by tests
}

// GenericUUID returns the untyped UUID
func (r *OptInReference) GenericUUID() uuids.UUID {
return uuids.UUID(r.UUID)

Check warning on line 43 in assets/optin.go

View check run for this annotation

Codecov / codecov/patch

assets/optin.go#L43

Added line #L43 was not covered by tests
}

// Identity returns the unique identity of the asset
func (r *OptInReference) Identity() string {
return string(r.UUID)

Check warning on line 48 in assets/optin.go

View check run for this annotation

Codecov / codecov/patch

assets/optin.go#L48

Added line #L48 was not covered by tests
}

// Variable returns whether this a variable (vs concrete) reference
func (r *OptInReference) Variable() bool {
return false

Check warning on line 53 in assets/optin.go

View check run for this annotation

Codecov / codecov/patch

assets/optin.go#L53

Added line #L53 was not covered by tests
}

func (r *OptInReference) String() string {
return fmt.Sprintf("%s[uuid=%s,name=%s]", r.Type(), r.Identity(), r.Name)

Check warning on line 57 in assets/optin.go

View check run for this annotation

Codecov / codecov/patch

assets/optin.go#L57

Added line #L57 was not covered by tests
}

var _ UUIDReference = (*OptInReference)(nil)
1 change: 1 addition & 0 deletions assets/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Source interface {
Groups() ([]Group, error)
Labels() ([]Label, error)
Locations() ([]LocationHierarchy, error)
OptIns() ([]OptIn, error)
Resthooks() ([]Resthook, error)
Templates() ([]Template, error)
Ticketers() ([]Ticketer, error)
Expand Down
2 changes: 1 addition & 1 deletion assets/static/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type Channel struct {
}

// NewChannel creates a new channel
func NewChannel(uuid assets.ChannelUUID, name string, address string, schemes []string, roles []assets.ChannelRole, parent *assets.ChannelReference) assets.Channel {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated cleanup

func NewChannel(uuid assets.ChannelUUID, name string, address string, schemes []string, roles []assets.ChannelRole) assets.Channel {
return &Channel{
UUID_: uuid,
Name_: name,
Expand Down
1 change: 0 additions & 1 deletion assets/static/channel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ func TestChannel(t *testing.T) {
"+234151",
[]string{"tel"},
[]assets.ChannelRole{assets.ChannelRoleSend},
nil,
)
assert.Equal(t, assets.ChannelUUID("ffffffff-9b24-92e1-ffff-ffffb207cdb4"), channel.UUID())
assert.Equal(t, "Android", channel.Name())
Expand Down
25 changes: 25 additions & 0 deletions assets/static/optin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package static

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

// OptIn is a JSON serializable implementation of an optin asset
type OptIn struct {
UUID_ assets.OptInUUID `json:"uuid" validate:"required,uuid"`
Name_ string `json:"name" validate:"required"`
}

// NewOptIn creates a new topic
func NewOptIn(uuid assets.OptInUUID, name string, channel *assets.ChannelReference) assets.OptIn {
return &OptIn{
UUID_: uuid,
Name_: name,
}
}

// UUID returns the UUID of this ticketer
func (t *OptIn) UUID() assets.OptInUUID { return t.UUID_ }

// Name returns the name of this ticketer
func (t *OptIn) Name() string { return t.Name_ }
20 changes: 20 additions & 0 deletions assets/static/optin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package static_test

import (
"testing"

"github.com/nyaruka/goflow/assets"
"github.com/nyaruka/goflow/assets/static"

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

func TestOptIn(t *testing.T) {
optin := static.NewOptIn(
"37657cf7-5eab-4286-9cb0-bbf270587bad",
"Weather Updates",
assets.NewChannelReference("f4366920-cb05-47b9-a974-29be2d528984", "Facebook"),
)
assert.Equal(t, assets.OptInUUID("37657cf7-5eab-4286-9cb0-bbf270587bad"), optin.UUID())
assert.Equal(t, "Weather Updates", optin.Name())
}
11 changes: 10 additions & 1 deletion assets/static/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/nyaruka/goflow/assets"
"github.com/nyaruka/goflow/envs"
"github.com/nyaruka/goflow/utils"

"github.com/pkg/errors"
)

Expand All @@ -24,6 +23,7 @@ type StaticSource struct {
Groups []*Group `json:"groups" validate:"omitempty,dive"`
Labels []*Label `json:"labels" validate:"omitempty,dive"`
Locations []*envs.LocationHierarchy `json:"locations"`
OptIns []*OptIn `json:"optins" validate:"omitempty,dive"`
Resthooks []*Resthook `json:"resthooks" validate:"omitempty,dive"`
Templates []*Template `json:"templates" validate:"omitempty,dive"`
Ticketers []*Ticketer `json:"ticketers" validate:"omitempty,dive"`
Expand Down Expand Up @@ -138,6 +138,15 @@ func (s *StaticSource) Locations() ([]assets.LocationHierarchy, error) {
return set, nil
}

// OptIns returns all optin assets
func (s *StaticSource) OptIns() ([]assets.OptIn, error) {
set := make([]assets.OptIn, len(s.s.OptIns))
for i := range s.s.OptIns {
set[i] = s.s.OptIns[i]
}
return set, nil
}

// Resthooks returns all resthook assets
func (s *StaticSource) Resthooks() ([]assets.Resthook, error) {
set := make([]assets.Resthook, len(s.s.Resthooks))
Expand Down
26 changes: 25 additions & 1 deletion assets/static/source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ import (
)

var assetsJSON = `{
"channels": [
{
"uuid": "58e9b092-fe42-4173-876c-ff45a14a24fe",
"name": "Facebook",
"address": "457547478475",
"schemes": [
"facebook"
],
"roles": [
"send",
"receive"
]
}
],
"flows": [
{
"uuid": "76f0a02f-3b75-4b86-9064-e9195e1b3a02",
Expand All @@ -35,6 +49,12 @@ var assetsJSON = `{
"name": "Spam"
}
],
"optins": [
{
"uuid": "248be71d-78e9-4d71-a6c4-9981d369e5cb",
"name": "Joke Of The Day"
}
],
"resthooks": [
{
"slug": "new-registration",
Expand All @@ -59,7 +79,7 @@ func TestSource(t *testing.T) {

channels, err = src.Channels()
assert.NoError(t, err)
assert.Len(t, channels, 0)
assert.Len(t, channels, 1)

classifiers, err := src.Classifiers()
assert.NoError(t, err)
Expand Down Expand Up @@ -93,6 +113,10 @@ func TestSource(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, locations, 0)

optIns, err := src.OptIns()
assert.NoError(t, err)
assert.Len(t, optIns, 1)

resthooks, err := src.Resthooks()
assert.NoError(t, err)
assert.Len(t, resthooks, 1)
Expand Down
2 changes: 1 addition & 1 deletion cmd/docgen/docs/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func TestGenerateDocs(t *testing.T) {
assert.Equal(t, 88, len(functions))

types := context["types"].([]any)
assert.Equal(t, 18, len(types))
assert.Equal(t, 19, len(types))

root := context["root"].([]any)
assert.Equal(t, 14, len(root))
Expand Down
7 changes: 7 additions & 0 deletions flows/engine/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type sessionAssets struct {
groups *flows.GroupAssets
labels *flows.LabelAssets
locations *flows.LocationAssets
optIns *flows.OptInAssets
resthooks *flows.ResthookAssets
templates *flows.TemplateAssets
ticketers *flows.TicketerAssets
Expand Down Expand Up @@ -59,6 +60,10 @@ func NewSessionAssets(env envs.Environment, source assets.Source, migrationConfi
if err != nil {
return nil, err
}
optIns, err := source.OptIns()
if err != nil {
return nil, err
}
resthooks, err := source.Resthooks()
if err != nil {
return nil, err
Expand Down Expand Up @@ -93,6 +98,7 @@ func NewSessionAssets(env envs.Environment, source assets.Source, migrationConfi
groups: groupAssets,
labels: flows.NewLabelAssets(labels),
locations: flows.NewLocationAssets(locations),
optIns: flows.NewOptInAssets(optIns),
resthooks: flows.NewResthookAssets(resthooks),
templates: flows.NewTemplateAssets(templates),
ticketers: flows.NewTicketerAssets(ticketers),
Expand All @@ -110,6 +116,7 @@ func (s *sessionAssets) Globals() *flows.GlobalAssets { return s.globals
func (s *sessionAssets) Groups() *flows.GroupAssets { return s.groups }
func (s *sessionAssets) Labels() *flows.LabelAssets { return s.labels }
func (s *sessionAssets) Locations() *flows.LocationAssets { return s.locations }
func (s *sessionAssets) OptIns() *flows.OptInAssets { return s.optIns }
func (s *sessionAssets) Resthooks() *flows.ResthookAssets { return s.resthooks }
func (s *sessionAssets) Templates() *flows.TemplateAssets { return s.templates }
func (s *sessionAssets) Ticketers() *flows.TicketerAssets { return s.ticketers }
Expand Down
29 changes: 28 additions & 1 deletion flows/engine/assets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ import (
)

var assetsJSON = `{
"channels": [
{
"uuid": "58e9b092-fe42-4173-876c-ff45a14a24fe",
"name": "Facebook",
"address": "457547478475",
"schemes": [
"facebook"
],
"roles": [
"send",
"receive"
]
}
],
"flows": [
{
"uuid": "76f0a02f-3b75-4b86-9064-e9195e1b3a02",
Expand All @@ -41,6 +55,12 @@ var assetsJSON = `{
"name": "Spam"
}
],
"optins": [
{
"uuid": "248be71d-78e9-4d71-a6c4-9981d369e5cb",
"name": "Joke Of The Day"
}
],
"resthooks": [
{
"slug": "new-registration",
Expand Down Expand Up @@ -74,6 +94,9 @@ func TestSessionAssets(t *testing.T) {

assert.Nil(t, sa.Groups().Get("xyz"))

optIn := sa.OptIns().Get("248be71d-78e9-4d71-a6c4-9981d369e5cb")
assert.Equal(t, "Joke Of The Day", optIn.Name())

resthook := sa.Resthooks().FindBySlug("new-registration")
assert.Equal(t, "new-registration", resthook.Slug())
assert.Equal(t, []string{"http://temba.io/"}, resthook.Subscribers())
Expand Down Expand Up @@ -116,7 +139,7 @@ func TestSessionAssetsWithSourceErrors(t *testing.T) {
_, err = sa.Flows().FindByName("Catch All")
assert.EqualError(t, err, "unable to load flow assets")

for _, errType := range []string{"channels", "classifiers", "fields", "globals", "groups", "labels", "locations", "resthooks", "templates", "users"} {
for _, errType := range []string{"channels", "classifiers", "fields", "globals", "groups", "labels", "locations", "optins", "resthooks", "templates", "users"} {
source.currentErrType = errType
_, err = engine.NewSessionAssets(env, source, nil)
assert.EqualError(t, err, fmt.Sprintf("unable to load %s assets", errType), "error mismatch for type %s", errType)
Expand Down Expand Up @@ -175,6 +198,10 @@ func (s *testSource) Resthooks() ([]assets.Resthook, error) {
return nil, s.err("resthooks")
}

func (s *testSource) OptIns() ([]assets.OptIn, error) {
return nil, s.err("optins")
}

func (s *testSource) Templates() ([]assets.Template, error) {
return nil, s.err("templates")
}
Expand Down
1 change: 1 addition & 0 deletions flows/engine/testdata/templates.json
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,7 @@
"output_json": {
"campaign": null,
"keyword": "",
"optin": null,
"origin": "",
"params": {
"address": {
Expand Down
1 change: 1 addition & 0 deletions flows/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ type SessionAssets interface {
Groups() *GroupAssets
Labels() *LabelAssets
Locations() *LocationAssets
OptIns() *OptInAssets
Resthooks() *ResthookAssets
Templates() *TemplateAssets
Ticketers() *TicketerAssets
Expand Down
Loading
Loading