Skip to content

Commit

Permalink
Merge pull request #1294 from nyaruka/result_name_and_category_valida…
Browse files Browse the repository at this point in the history
…tion

Add custom validators for result name and category and make them match current floweditor validation
  • Loading branch information
rowanseymour authored Dec 5, 2024
2 parents e8da94f + ef2424d commit 4f72335
Show file tree
Hide file tree
Showing 12 changed files with 59 additions and 16 deletions.
2 changes: 1 addition & 1 deletion flows/actions/call_classifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type CallClassifierAction struct {

Classifier *assets.ClassifierReference `json:"classifier" validate:"required"`
Input string `json:"input" validate:"required" engine:"evaluated"`
ResultName string `json:"result_name" validate:"required,max=128"`
ResultName string `json:"result_name" validate:"required,result_name"`
}

// NewCallClassifier creates a new call classifier action
Expand Down
2 changes: 1 addition & 1 deletion flows/actions/call_resthook.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ type CallResthookAction struct {
onlineAction

Resthook string `json:"resthook" validate:"required"`
ResultName string `json:"result_name,omitempty" validate:"max=128"`
ResultName string `json:"result_name,omitempty" validate:"omitempty,result_name"`
}

// NewCallResthook creates a new call resthook action
Expand Down
2 changes: 1 addition & 1 deletion flows/actions/call_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ type CallWebhookAction struct {
URL string `json:"url" engine:"evaluated" validate:"required"`
Headers map[string]string `json:"headers,omitempty" engine:"evaluated"`
Body string `json:"body,omitempty" engine:"evaluated"`
ResultName string `json:"result_name,omitempty" validate:"max=128"`
ResultName string `json:"result_name,omitempty" validate:"omitempty,result_name"`
}

// NewCallWebhook creates a new call webhook action
Expand Down
2 changes: 1 addition & 1 deletion flows/actions/open_ticket.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type OpenTicketAction struct {
Topic *assets.TopicReference `json:"topic" validate:"omitempty"`
Body string `json:"body" engine:"evaluated"` // TODO will become "note" in future migration
Assignee *assets.UserReference `json:"assignee" validate:"omitempty"`
ResultName string `json:"result_name" validate:"required,max=128"`
ResultName string `json:"result_name" validate:"required,result_name"`
}

// NewOpenTicket creates a new open ticket action
Expand Down
4 changes: 2 additions & 2 deletions flows/actions/set_run_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ type SetRunResultAction struct {
baseAction
universalAction

Name string `json:"name" validate:"required,max=64"`
Name string `json:"name" validate:"required,result_name"`
Value string `json:"value" engine:"evaluated"`
Category string `json:"category,omitempty" engine:"localized" validate:"max=36"`
Category string `json:"category,omitempty" engine:"localized" validate:"omitempty,result_category"`
}

// NewSetRunResult creates a new set run result action
Expand Down
4 changes: 2 additions & 2 deletions flows/actions/testdata/call_resthook.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
"type": "call_resthook",
"uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
"resthook": "doesnt-exist",
"result_name": "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
"result_name": "12345678901234567890123456789012345678901234567890123456789012345"
},
"read_error": "field 'result_name' must be less than or equal to 128"
"read_error": "field 'result_name' is not a valid result name"
},
{
"description": "NOOP if resthook doesn't exist",
Expand Down
4 changes: 2 additions & 2 deletions flows/actions/testdata/call_webhook.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@
"headers": {
"Accept:": "something"
},
"result_name": "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
"result_name": "12345678901234567890123456789012345678901234567890123456789012345"
},
"read_error": "field 'result_name' must be less than or equal to 128"
"read_error": "field 'result_name' is not a valid result name"
},
{
"description": "Error events created if URL, header or body contain expression errors",
Expand Down
4 changes: 2 additions & 2 deletions flows/actions/testdata/open_ticket.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
},
"body": "Where are my cookies?",
"assignee": null,
"result_name": "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
"result_name": "12345678901234567890123456789012345678901234567890123456789012345"
},
"read_error": "field 'result_name' must be less than or equal to 128"
"read_error": "field 'result_name' is not a valid result name"
},
{
"description": "Error event for invalid topic reference",
Expand Down
2 changes: 1 addition & 1 deletion flows/actions/transfer_airtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type TransferAirtimeAction struct {
onlineAction

Amounts map[string]decimal.Decimal `json:"amounts" validate:"required"`
ResultName string `json:"result_name" validate:"required,max=128"`
ResultName string `json:"result_name" validate:"required,result_name"`
}

// NewTransferAirtime creates a new airtime transfer action
Expand Down
26 changes: 25 additions & 1 deletion flows/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,43 @@ package flows
import (
"encoding/json"
"fmt"
"regexp"
"sort"
"strings"
"time"

"github.com/go-playground/validator/v10"
"github.com/nyaruka/goflow/envs"
"github.com/nyaruka/goflow/excellent/types"
"github.com/nyaruka/goflow/utils"
)

func init() {
resultNameRegex := regexp.MustCompile(`^[a-zA-Z0-9\-_\s]{1,64}$`)
resultCategoryRegex := regexp.MustCompile(`^.{1,36}$`)

utils.RegisterValidatorTag("result_name",
func(fl validator.FieldLevel) bool {
return resultNameRegex.MatchString(fl.Field().String())
},
func(validator.FieldError) string {
return "is not a valid result name"
},
)
utils.RegisterValidatorTag("result_category",
func(fl validator.FieldLevel) bool {
return resultCategoryRegex.MatchString(fl.Field().String())
},
func(validator.FieldError) string {
return "is not a valid result category"
},
)
}

// Result describes a value captured during a run's execution. It might have been implicitly created by a router, or explicitly
// created by a [set_run_result](#action:set_run_result) action.
type Result struct {
Name string `json:"name" validate:"required"`
Name string `json:"name" validate:"required,result_name"`
Value string `json:"value"`
Category string `json:"category,omitempty"`
CategoryLocalized string `json:"category_localized,omitempty"`
Expand Down
19 changes: 19 additions & 0 deletions flows/results_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/nyaruka/goflow/excellent/types"
"github.com/nyaruka/goflow/flows"
"github.com/nyaruka/goflow/test"
"github.com/nyaruka/goflow/utils"

"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -60,3 +61,21 @@ func TestResults(t *testing.T) {
}),
}), resultsAsContext)
}

func TestResultNameAndCategoryValidation(t *testing.T) {
type testStruct struct {
ValidName string `json:"valid_name" validate:"result_name"`
InvalidName string `json:"invalid_name" validate:"result_name"`
ValidCategory string `json:"valid_category" validate:"result_category"`
InvalidCategory string `json:"invalid_category" validate:"result_category"`
}

obj := testStruct{
ValidName: "Color",
InvalidName: "#",
ValidCategory: "Blue",
InvalidCategory: "1234567890123456789012345678901234567",
}
err := utils.Validate(obj)
assert.EqualError(t, err, "field 'invalid_name' is not a valid result name, field 'invalid_category' is not a valid result category")
}
4 changes: 2 additions & 2 deletions flows/routers/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@ func (r *baseRouter) routeToCategory(run flows.Run, step flows.Step, categoryUUI
type baseRouterEnvelope struct {
Type string `json:"type" validate:"required"`
Wait json.RawMessage `json:"wait,omitempty"`
ResultName string `json:"result_name,omitempty"`
Categories []json.RawMessage `json:"categories,omitempty" validate:"required,min=1"`
ResultName string `json:"result_name,omitempty" validate:"omitempty,result_name"`
Categories []json.RawMessage `json:"categories,omitempty" validate:"required,min=1,dive,result_category"`
}

// ReadRouter reads a router from the given JSON
Expand Down

0 comments on commit 4f72335

Please sign in to comment.