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

Refactor pipeline form parsing #109

Merged
merged 15 commits into from
Apr 17, 2024
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
25 changes: 25 additions & 0 deletions backend/database/mongo_seed/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,31 @@ func main() {
IsTerminalStep: true,
},
},
Form: models.Form{Fields: []models.FormField{
{
Name: "field1",
Title: "Field 1",
Type: models.InputField,
Required: true,
Placeholder: "Enter text...",
MinLength: 1,
},
{
Name: "field2",
Title: "Field 2",
Type: models.SelectField,
Required: true,
Placeholder: "Select an option",
Options: []string{"Option 1", "Option 2", "Option 3"},
Default: "Option 1",
},
{
Name: "field3",
Title: "Field 3",
Type: models.CheckboxField,
Options: []string{"Option 1", "Option 2", "Option 3"},
},
}},
}

res, err := database.NewPipeline(c).Create(&pipeline)
Expand Down
16 changes: 9 additions & 7 deletions backend/src/database/models/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,21 @@ import (
type FormFieldType string

const (
TextField FormFieldType = "TEXT"
DropdownField FormFieldType = "DROPDOWN"
OptionField FormFieldType = "OPTION"
CheckboxField FormFieldType = "CHECKBOX"
InputField FormFieldType = "input"
SelectField FormFieldType = "select"
CheckboxField FormFieldType = "checkboxes"
)

type FormField struct {
Name string `bson:"name" json:"name"`
Title string `bson:"title" json:"title"`
joshtyf marked this conversation as resolved.
Show resolved Hide resolved
Description string `bson:"description" json:"description"`
Type FormFieldType `bson:"type" json:"type"`
IsRequired bool `bson:"is_required" json:"is_required"`
Required bool `bson:"required" json:"required"`
Placeholder string `bson:"placeholder" json:"placeholder"`
Description string `bson:"description" json:"description"`
Values []string `bson:"values" json:"values"`
MinLength int `bson:"min_length" json:"min_length"`
Options []string `bson:"options" json:"options"`
Default string `bson:"default" json:"default"`
}

type Form struct {
Expand Down
15 changes: 15 additions & 0 deletions backend/src/server/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/joshtyf/flowforge/src/events"
"github.com/joshtyf/flowforge/src/logger"
"github.com/joshtyf/flowforge/src/util"
"github.com/joshtyf/flowforge/src/validation"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
Expand Down Expand Up @@ -360,6 +361,20 @@ func handleCreatePipeline(logger logger.ServerLogger, client *mongo.Client) http
encode(w, r, http.StatusBadRequest, newHandlerError(ErrJsonParseError, http.StatusBadRequest))
return
}
if pipeline.Form.Fields == nil {
logger.Error("missing form field")
encode(w, r, http.StatusUnprocessableEntity, newHandlerError(ErrJsonParseError, http.StatusUnprocessableEntity))
return
}

for _, element := range pipeline.Form.Fields {
err = validation.ValidateFormField(element)
if err != nil {
logger.Error(fmt.Sprintf("invalid form field: %s", err))
encode(w, r, http.StatusBadRequest, newHandlerError(err, http.StatusUnprocessableEntity))
return
}
}

pipeline.CreatedOn = time.Now()
pipeline.Version = 1
Expand Down
14 changes: 14 additions & 0 deletions backend/src/validation/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ func NewMissingRequiredFieldError(fieldName string) *MissingRequiredFieldError {
}
}

type InvalidPropertyError struct {
fieldName string
}

func (e *InvalidPropertyError) Error() string {
return fmt.Sprintf("invalid value found for field: '%s'", e.fieldName)
}

func NewInvalidPropertyValue(fieldName string) *InvalidPropertyError {
return &InvalidPropertyError{
fieldName: fieldName,
}
}

func (e *MissingRequiredFieldError) Error() string {
return fmt.Sprintf("missing required field: '%s'", e.fieldName)
}
Expand Down
50 changes: 22 additions & 28 deletions backend/src/validation/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,20 @@ func ValidateFormField(f models.FormField) error {
if f.Name == "" {
return NewMissingRequiredFieldError("name")
}
if f.Title == "" {
return NewMissingRequiredFieldError("title")
}
if f.Type == "" {
return NewMissingRequiredFieldError("type")
}
if f.Type == models.DropdownField || f.Type == models.OptionField || f.Type == models.CheckboxField {
if f.Values == nil || len(f.Values) == 0 {
return NewMissingRequiredFieldError("values")
if f.Type == models.SelectField || f.Type == models.CheckboxField {
if f.Options == nil || len(f.Options) == 0 {
return NewMissingRequiredFieldError("options")
}
for _, option := range f.Options {
if option == "" {
return NewInvalidPropertyValue("options")
}
}
}
return nil
Expand All @@ -85,36 +93,23 @@ func (f FormFieldDataValidator) validate(field models.FormField, data any) error
return f(field, data)
}

// Default text field validator
func defaultTextFieldDataValidator(field models.FormField, data any) error {
// Default input field validator
func defaultInputFieldDataValidator(field models.FormField, data any) error {
if _, ok := data.(string); !ok {
return NewInvalidFormDataTypeError(field.Name, "string")
}

return nil
}

// Default dropdown field validator
func defaultDropdownFieldDataValidator(field models.FormField, data any) error {
dataStr, ok := data.(string)
if !ok {
return NewInvalidFormDataTypeError(field.Name, "string")
}
if !helper.StringInSlice(dataStr, field.Values) {
return NewInvalidSelectedFormDataError(field.Values, dataStr)
}

return nil
}

// Default option field validator
func defaultOptionFieldDataValidator(field models.FormField, data any) error {
// Default select field validator
func defaultSelectFieldDataValidator(field models.FormField, data any) error {
dataStr, ok := data.(string)
if !ok {
return NewInvalidFormDataTypeError(field.Name, "string")
}
if !helper.StringInSlice(dataStr, field.Values) {
return NewInvalidSelectedFormDataError(field.Values, dataStr)
if !helper.StringInSlice(dataStr, field.Options) {
return NewInvalidSelectedFormDataError(field.Options, dataStr)
}

return nil
Expand All @@ -127,8 +122,8 @@ func defaultCheckboxFieldDataValidator(field models.FormField, data any) error {
return NewInvalidFormDataTypeError(field.Name, "[]string")
}
for _, s := range dataStrings {
if !helper.StringInSlice(s, field.Values) {
return NewInvalidSelectedFormDataError(field.Values, s)
if !helper.StringInSlice(s, field.Options) {
return NewInvalidSelectedFormDataError(field.Options, s)
}
}
return nil
Expand All @@ -142,9 +137,8 @@ func NewFormDataValidator(customValidators *map[models.FormFieldType]FormFieldDa
validator := &FormDataValidator{
// Initialize with default field validators
fieldDataValidators: map[models.FormFieldType]FormFieldDataValidator{
models.TextField: FormFieldDataValidator(defaultTextFieldDataValidator),
models.DropdownField: FormFieldDataValidator(defaultDropdownFieldDataValidator),
models.OptionField: FormFieldDataValidator(defaultOptionFieldDataValidator),
models.InputField: FormFieldDataValidator(defaultInputFieldDataValidator),
models.SelectField: FormFieldDataValidator(defaultSelectFieldDataValidator),
models.CheckboxField: FormFieldDataValidator(defaultCheckboxFieldDataValidator),
},
}
Expand All @@ -160,7 +154,7 @@ func (v *FormDataValidator) Validate(formData *models.FormData, form *models.For
for _, field := range form.Fields {
fieldData, ok := (*formData)[field.Name]
if !ok {
if field.IsRequired {
if field.Required {
return NewMissingRequiredFieldError(field.Name)
} else {
continue
Expand Down
Loading
Loading