Skip to content

Commit 645d870

Browse files
authored
Merge pull request #174 from nyaruka/event_origin
Event validation
2 parents e7fbb79 + 2297977 commit 645d870

24 files changed

+196
-6
lines changed

flows/engine/session.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ func (s *session) Start(trigger flows.Trigger, callerEvents []flows.Event) error
119119
return fmt.Errorf("validation failed for flow[uuid=%s]: %v", trigger.Flow().UUID(), err)
120120
}
121121

122+
// check caller events are valid
123+
if err := s.validateCallerEvents(callerEvents); err != nil {
124+
return err
125+
}
126+
122127
if trigger.Environment() != nil {
123128
s.env = trigger.Environment()
124129
}
@@ -149,6 +154,11 @@ func (s *session) Resume(callerEvents []flows.Event) error {
149154
return fmt.Errorf("validation failed for flow[uuid=%s]: %v", waitingRun.Flow().UUID(), err)
150155
}
151156

157+
// check caller events are valid
158+
if err := s.validateCallerEvents(callerEvents); err != nil {
159+
return err
160+
}
161+
152162
if err := s.tryToResume(waitingRun, callerEvents); err != nil {
153163
// if we got an error, add it to the log and shut everything down
154164
for _, run := range s.runs {
@@ -206,11 +216,7 @@ func (s *session) tryToResume(waitingRun flows.FlowRun, callerEvents []flows.Eve
206216
s.status = flows.SessionStatusActive
207217

208218
// off to the races again...
209-
if err = s.continueUntilWait(waitingRun, destination, step, []flows.Event{}); err != nil {
210-
return err
211-
}
212-
213-
return nil
219+
return s.continueUntilWait(waitingRun, destination, step, []flows.Event{})
214220
}
215221

216222
// finds the next destination in a run that may have been waiting or a parent paused for a child subflow
@@ -443,6 +449,18 @@ func (s *session) pickNodeExit(run flows.FlowRun, node flows.Node, step flows.St
443449

444450
const noDestination = flows.NodeUUID("")
445451

452+
func (s *session) validateCallerEvents(events []flows.Event) error {
453+
for _, event := range events {
454+
if event.AllowedOrigin()&flows.EventOriginCaller == 0 {
455+
return fmt.Errorf("event[type=%s] can't be sent by callers", event.Type())
456+
}
457+
if err := event.Validate(s.assets); err != nil {
458+
return fmt.Errorf("validation failed for event[type=%s]: %s", event.Type(), err)
459+
}
460+
}
461+
return nil
462+
}
463+
446464
//------------------------------------------------------------------------------------------
447465
// JSON Encoding / Decoding
448466
//------------------------------------------------------------------------------------------

flows/events/base.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package events
22

33
import (
4+
"github.com/nyaruka/goflow/flows"
45
"time"
56
)
67

@@ -18,3 +19,25 @@ func (e *BaseEvent) SetCreatedOn(time time.Time) { e.CreatedOn_ = time }
1819

1920
func (e *BaseEvent) FromCaller() bool { return e.FromCaller_ }
2021
func (e *BaseEvent) SetFromCaller(fromCaller bool) { e.FromCaller_ = fromCaller }
22+
23+
type callerOnlyEvent struct{}
24+
25+
// AllowedOrigin determines where this event type can originate
26+
func (e *callerOnlyEvent) AllowedOrigin() flows.EventOrigin { return flows.EventOriginCaller }
27+
28+
type engineOnlyEvent struct{}
29+
30+
// AllowedOrigin determines where this event type can originate
31+
func (e *engineOnlyEvent) AllowedOrigin() flows.EventOrigin { return flows.EventOriginEngine }
32+
33+
// Validate validates our event is valid and has all the assets it needs. We assume engine generated events are valid.
34+
func (e *engineOnlyEvent) Validate(assets flows.SessionAssets) error {
35+
return nil
36+
}
37+
38+
type callerOrEngineEvent struct{}
39+
40+
// AllowedOrigin determines where this event type can originate
41+
func (e *callerOrEngineEvent) AllowedOrigin() flows.EventOrigin {
42+
return flows.EventOriginCaller | flows.EventOriginEngine
43+
}

flows/events/broadcast_created.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ const TypeBroadcastCreated string = "broadcast_created"
2525
// @event broadcast_created
2626
type BroadcastCreatedEvent struct {
2727
BaseEvent
28+
engineOnlyEvent
29+
2830
Text string `json:"text"`
2931
Attachments []flows.Attachment `json:"attachments,omitempty"`
3032
QuickReplies []string `json:"quick_replies,omitempty"`

flows/events/contact_changed.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,19 @@ const TypeContactChanged string = "contact_changed"
2323
// @event contact_changed
2424
type ContactChangedEvent struct {
2525
BaseEvent
26+
callerOrEngineEvent
27+
2628
Contact json.RawMessage `json:"contact"`
2729
}
2830

2931
// Type returns the type of this event
3032
func (e *ContactChangedEvent) Type() string { return TypeContactChanged }
3133

34+
// Validate validates our event is valid and has all the assets it needs
35+
func (e *ContactChangedEvent) Validate(assets flows.SessionAssets) error {
36+
return nil
37+
}
38+
3239
// Apply applies this event to the given run
3340
func (e *ContactChangedEvent) Apply(run flows.FlowRun) error {
3441
contact, err := flows.ReadContact(run.Session(), e.Contact)

flows/events/contact_channel_changed.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ const TypeContactChannelChanged string = "contact_channel_changed"
2121
// @event contact_channel_changed
2222
type ContactChannelChangedEvent struct {
2323
BaseEvent
24+
callerOrEngineEvent
25+
2426
Channel *flows.ChannelReference `json:"channel" validate:"required"`
2527
}
2628

@@ -35,6 +37,12 @@ func NewContactChannelChangedEvent(channel *flows.ChannelReference) *ContactChan
3537
// Type returns the type of this event
3638
func (e *ContactChannelChangedEvent) Type() string { return TypeContactChannelChanged }
3739

40+
// Validate validates our event is valid and has all the assets it needs
41+
func (e *ContactChannelChangedEvent) Validate(assets flows.SessionAssets) error {
42+
_, err := assets.GetChannel(e.Channel.UUID)
43+
return err
44+
}
45+
3846
// Apply applies this event to the given run
3947
func (e *ContactChannelChangedEvent) Apply(run flows.FlowRun) error {
4048
if run.Contact() == nil {

flows/events/contact_field_changed.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ const TypeContactFieldChanged string = "contact_field_changed"
2323
// @event contact_field_changed
2424
type ContactFieldChangedEvent struct {
2525
BaseEvent
26+
callerOrEngineEvent
27+
2628
Field *flows.FieldReference `json:"field" validate:"required"`
2729
Value string `json:"value" validate:"required"`
2830
}
@@ -39,6 +41,12 @@ func NewContactFieldChangedEvent(field *flows.FieldReference, value string) *Con
3941
// Type returns the type of this event
4042
func (e *ContactFieldChangedEvent) Type() string { return TypeContactFieldChanged }
4143

44+
// Validate validates our event is valid and has all the assets it needs
45+
func (e *ContactFieldChangedEvent) Validate(assets flows.SessionAssets) error {
46+
_, err := assets.GetField(e.Field.Key)
47+
return err
48+
}
49+
4250
// Apply applies this event to the given run
4351
func (e *ContactFieldChangedEvent) Apply(run flows.FlowRun) error {
4452
if run.Contact() == nil {

flows/events/contact_groups_added.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ const TypeContactGroupsAdded string = "contact_groups_added"
2222
// @event contact_groups_added
2323
type ContactGroupsAddedEvent struct {
2424
BaseEvent
25+
callerOrEngineEvent
26+
2527
Groups []*flows.GroupReference `json:"groups" validate:"required,min=1,dive"`
2628
}
2729

@@ -36,6 +38,17 @@ func NewContactGroupsAddedEvent(groups []*flows.GroupReference) *ContactGroupsAd
3638
// Type returns the type of this event
3739
func (e *ContactGroupsAddedEvent) Type() string { return TypeContactGroupsAdded }
3840

41+
// Validate validates our event is valid and has all the assets it needs
42+
func (e *ContactGroupsAddedEvent) Validate(assets flows.SessionAssets) error {
43+
for _, group := range e.Groups {
44+
_, err := assets.GetGroup(group.UUID)
45+
if err != nil {
46+
return err
47+
}
48+
}
49+
return nil
50+
}
51+
3952
// Apply applies this event to the given run
4053
func (e *ContactGroupsAddedEvent) Apply(run flows.FlowRun) error {
4154
if run.Contact() == nil {

flows/events/contact_groups_removed.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ const TypeContactGroupsRemoved string = "contact_groups_removed"
2222
//
2323
// @event contact_groups_removed
2424
type ContactGroupsRemovedEvent struct {
25-
Groups []*flows.GroupReference `json:"groups" validate:"required,min=1,dive"`
2625
BaseEvent
26+
callerOrEngineEvent
27+
28+
Groups []*flows.GroupReference `json:"groups" validate:"required,min=1,dive"`
2729
}
2830

2931
// NewContactGroupsRemovedEvent returns a new remove from group event
@@ -37,6 +39,17 @@ func NewContactGroupsRemovedEvent(groups []*flows.GroupReference) *ContactGroups
3739
// Type returns the type of this event
3840
func (e *ContactGroupsRemovedEvent) Type() string { return TypeContactGroupsRemoved }
3941

42+
// Validate validates our event is valid and has all the assets it needs
43+
func (e *ContactGroupsRemovedEvent) Validate(assets flows.SessionAssets) error {
44+
for _, group := range e.Groups {
45+
_, err := assets.GetGroup(group.UUID)
46+
if err != nil {
47+
return err
48+
}
49+
}
50+
return nil
51+
}
52+
4053
// Apply applies this event to the given run
4154
func (e *ContactGroupsRemovedEvent) Apply(run flows.FlowRun) error {
4255
if run.Contact() == nil {

flows/events/contact_property_changed.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ const TypeContactPropertyChanged string = "contact_property_changed"
2424
// @event contact_property_changed
2525
type ContactPropertyChangedEvent struct {
2626
BaseEvent
27+
callerOrEngineEvent
28+
2729
Property string `json:"property" validate:"required,eq=name|eq=language"`
2830
Value string `json:"value"`
2931
}
@@ -40,6 +42,11 @@ func NewContactPropertyChangedEvent(property string, value string) *ContactPrope
4042
// Type returns the type of this event
4143
func (e *ContactPropertyChangedEvent) Type() string { return TypeContactPropertyChanged }
4244

45+
// Validate validates our event is valid and has all the assets it needs
46+
func (e *ContactPropertyChangedEvent) Validate(assets flows.SessionAssets) error {
47+
return nil
48+
}
49+
4350
// Apply applies this event to the given run
4451
func (e *ContactPropertyChangedEvent) Apply(run flows.FlowRun) error {
4552
if run.Contact() == nil {

flows/events/contact_urn_added.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ const TypeContactURNAdded string = "contact_urn_added"
2323
// @event contact_urn_added
2424
type ContactURNAddedEvent struct {
2525
BaseEvent
26+
callerOrEngineEvent
27+
2628
URN urns.URN `json:"urn" validate:"urn"`
2729
}
2830

@@ -34,6 +36,11 @@ func NewURNAddedEvent(urn urns.URN) *ContactURNAddedEvent {
3436
// Type returns the type of this event
3537
func (e *ContactURNAddedEvent) Type() string { return TypeContactURNAdded }
3638

39+
// Validate validates our event is valid and has all the assets it needs
40+
func (e *ContactURNAddedEvent) Validate(assets flows.SessionAssets) error {
41+
return nil
42+
}
43+
3744
// Apply applies this event to the given run
3845
func (e *ContactURNAddedEvent) Apply(run flows.FlowRun) error {
3946
if run.Contact() == nil {

flows/events/email_created.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ const TypeEmailCreated string = "email_created"
2020
// @event email_created
2121
type EmailCreatedEvent struct {
2222
BaseEvent
23+
engineOnlyEvent
24+
2325
Addresses []string `json:"addresses" validate:"required,min=1"`
2426
Subject string `json:"subject" validate:"required"`
2527
Body string `json:"body"`
@@ -38,6 +40,11 @@ func NewEmailCreatedEvent(addresses []string, subject string, body string) *Emai
3840
// Type returns the type of this event
3941
func (e *EmailCreatedEvent) Type() string { return TypeEmailCreated }
4042

43+
// Validate validates our event is valid and has all the assets it needs
44+
func (e *EmailCreatedEvent) Validate(assets flows.SessionAssets) error {
45+
return nil
46+
}
47+
4148
// Apply applies this event to the given run
4249
func (e *EmailCreatedEvent) Apply(run flows.FlowRun) error {
4350
return nil

flows/events/environment_changed.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,19 @@ const TypeEnvironmentChanged string = "environment_changed"
2828
// @event environment_changed
2929
type EnvironmentChangedEvent struct {
3030
BaseEvent
31+
callerOnlyEvent
32+
3133
Environment json.RawMessage `json:"environment"`
3234
}
3335

3436
// Type returns the type of this event
3537
func (e *EnvironmentChangedEvent) Type() string { return TypeEnvironmentChanged }
3638

39+
// Validate validates our event is valid and has all the assets it needs
40+
func (e *EnvironmentChangedEvent) Validate(assets flows.SessionAssets) error {
41+
return nil
42+
}
43+
3744
// Apply applies this event to the given run
3845
func (e *EnvironmentChangedEvent) Apply(run flows.FlowRun) error {
3946
env, err := utils.ReadEnvironment(e.Environment)

flows/events/error.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const TypeError string = "error"
1919
// @event error
2020
type ErrorEvent struct {
2121
BaseEvent
22+
callerOrEngineEvent
23+
2224
Text string `json:"text" validate:"required"`
2325
Fatal bool `json:"fatal"`
2426
}
@@ -42,6 +44,11 @@ func NewFatalErrorEvent(err error) *ErrorEvent {
4244
// Type returns the type of this event
4345
func (e *ErrorEvent) Type() string { return TypeError }
4446

47+
// Validate validates our event is valid and has all the assets it needs
48+
func (e *ErrorEvent) Validate(assets flows.SessionAssets) error {
49+
return nil
50+
}
51+
4552
// Apply applies this event to the given run
4653
func (e *ErrorEvent) Apply(run flows.FlowRun) error {
4754
if e.Fatal {

flows/events/flow_triggered.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ const TypeFlowTriggered string = "flow_triggered"
2121
// @event flow_triggered
2222
type FlowTriggeredEvent struct {
2323
BaseEvent
24+
engineOnlyEvent
25+
2426
Flow *flows.FlowReference `json:"flow" validate:"required"`
2527
ParentRunUUID flows.RunUUID `json:"parent_run_uuid" validate:"omitempty,uuid4"`
2628
}

flows/events/input_labels_added.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const TypeInputLabelsAdded string = "input_labels_added"
1919
// @event input_labels_added
2020
type InputLabelsAddedEvent struct {
2121
BaseEvent
22+
callerOrEngineEvent
23+
2224
InputUUID flows.InputUUID `json:"input_uuid" validate:"required,uuid4"`
2325
Labels []*flows.LabelReference `json:"labels" validate:"required,min=1,dive"`
2426
}
@@ -35,6 +37,17 @@ func NewInputLabelsAddedEvent(inputUUID flows.InputUUID, labels []*flows.LabelRe
3537
// Type returns the type of this event
3638
func (e *InputLabelsAddedEvent) Type() string { return TypeInputLabelsAdded }
3739

40+
// Validate validates our event is valid and has all the assets it needs
41+
func (e *InputLabelsAddedEvent) Validate(assets flows.SessionAssets) error {
42+
for _, label := range e.Labels {
43+
_, err := assets.GetLabel(label.UUID)
44+
if err != nil {
45+
return err
46+
}
47+
}
48+
return nil
49+
}
50+
3851
// Apply applies this event to the given run
3952
func (e *InputLabelsAddedEvent) Apply(run flows.FlowRun) error {
4053
return nil

flows/events/msg_created.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ const TypeMsgCreated string = "msg_created"
2626
// @event msg_created
2727
type MsgCreatedEvent struct {
2828
BaseEvent
29+
engineOnlyEvent
30+
2931
Msg flows.MsgOut `json:"msg" validate:"required,dive"`
3032
}
3133

flows/events/msg_received.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const TypeMsgReceived string = "msg_received"
2828
// @event msg_received
2929
type MsgReceivedEvent struct {
3030
BaseEvent
31+
callerOnlyEvent
32+
3133
Msg flows.MsgIn `json:"msg" validate:"required,dive"`
3234
}
3335

@@ -42,6 +44,15 @@ func NewMsgReceivedEvent(msg *flows.MsgIn) *MsgReceivedEvent {
4244
// Type returns the type of this event
4345
func (e *MsgReceivedEvent) Type() string { return TypeMsgReceived }
4446

47+
// Validate validates our event is valid and has all the assets it needs
48+
func (e *MsgReceivedEvent) Validate(assets flows.SessionAssets) error {
49+
if e.Msg.Channel() != nil {
50+
_, err := assets.GetChannel(e.Msg.Channel().UUID)
51+
return err
52+
}
53+
return nil
54+
}
55+
4556
// Apply applies this event to the given run
4657
func (e *MsgReceivedEvent) Apply(run flows.FlowRun) error {
4758
var channel flows.Channel

0 commit comments

Comments
 (0)