add validation for input definition fields

This commit is contained in:
Linh Vo 2017-06-20 12:12:49 -05:00
parent 3cecd74e2e
commit 6d12478e6b
2 changed files with 98 additions and 0 deletions

View file

@ -19,10 +19,14 @@ import (
"os"
"path/filepath"
"errors"
"fmt"
"github.com/gogo/protobuf/proto"
"github.com/pilosa/pilosa/internal"
)
var ValidValueDestination = []string{"map", "valueToRow", "stringToBool"}
// InputDefinition represents a container for the data input definition.
type InputDefinition struct {
name string
@ -89,9 +93,22 @@ func (i *InputDefinition) LoadDefinition(pb *internal.InputDefinition) error {
i.frames = append(i.frames, inputFrame)
}
numPrimaryKey := 0
countRowID := make(map[uint64]bool)
for _, field := range pb.Fields {
var actions []Action
for _, action := range field.Actions {
if err := i.ValidateAction(action); err != nil {
return err
}
if action.RowID != 0 {
_, ok := countRowID[action.RowID]
if !ok {
countRowID[action.RowID] = true
} else {
return fmt.Errorf("duplicate rowID with other field: %s", action.RowID)
}
}
actions = append(actions, Action{
Frame: action.Frame,
ValueDestination: action.ValueDestination,
@ -100,6 +117,14 @@ func (i *InputDefinition) LoadDefinition(pb *internal.InputDefinition) error {
})
}
if field.PrimaryKey {
numPrimaryKey += 1
}
if numPrimaryKey > 1 {
return errors.New("duplicate primaryKey with other field")
}
inputField := Field{
Name: field.Name,
PrimaryKey: field.PrimaryKey,
@ -246,3 +271,28 @@ func (i *InputDefinition) AddFrame(frame InputFrame) error {
}
return nil
}
func (i *InputDefinition) ValidateAction(action *internal.Action) error {
validValues := make(map[string]bool)
for _, val := range ValidValueDestination {
validValues[val] = true
}
if _, ok := validValues[action.ValueDestination]; !ok {
return fmt.Errorf("invalid ValueDestination: %s", action.ValueDestination)
}
switch action.ValueDestination {
case "map":
if len(action.ValueMap) == 0 {
return errors.New("valueMap required for map")
}
case "stringToBool":
if action.RowID == 0 {
return errors.New("rowID required for stringToBool")
}
default:
return nil
}
return nil
}

View file

@ -20,6 +20,7 @@ import (
"github.com/pilosa/pilosa"
"github.com/pilosa/pilosa/internal"
"strings"
)
func TestInputDefinition_Open(t *testing.T) {
@ -93,3 +94,50 @@ func TestInputDefinition_Encoding(t *testing.T) {
t.Fatalf("unexpected ValueDestination: %v", internalDef.Fields[1].Actions[0])
}
}
func TestInputDefinition_LoadDefinition(t *testing.T) {
index := MustOpenIndex()
defer index.Close()
// Create Input Definition.
input := pilosa.InputDefinition{}
frames := internal.Frame{Name: "f", Meta: &internal.FrameMeta{RowLabel: "row"}}
action := internal.Action{Frame: "f", ValueDestination: "ValueToRow", ValueMap: map[string]uint64{"Green": 1}}
field := internal.InputDefinitionField{Name: "id", PrimaryKey: true, Actions: []*internal.Action{&action}}
def := &internal.InputDefinition{Name: "test", Frames: []*internal.Frame{&frames}, Fields: []*internal.InputDefinitionField{&field}}
err := input.LoadDefinition(def)
if !strings.Contains(err.Error(), "invalid ValueDestination") {
t.Fatalf("Expected invalid ValueDestination error, actual error: %s", err)
}
action = internal.Action{Frame: "f", ValueDestination: "stringToBool", ValueMap: map[string]uint64{"Green": 1}}
def = &internal.InputDefinition{Name: "test", Frames: []*internal.Frame{&frames}, Fields: []*internal.InputDefinitionField{&field}}
err = input.LoadDefinition(def)
if !strings.Contains(err.Error(), "rowID required for stringToBool") {
t.Fatalf("Expected rowID required for stringToBool error, actual error: %s", err)
}
action = internal.Action{Frame: "f", ValueDestination: "map", RowID: 100}
def = &internal.InputDefinition{Name: "test", Frames: []*internal.Frame{&frames}, Fields: []*internal.InputDefinitionField{&field}}
err = input.LoadDefinition(def)
if !strings.Contains(err.Error(), "valueMap required for map") {
t.Fatalf("Expected valueMap required for map error, actual error: %s", err)
}
action = internal.Action{Frame: "f", ValueDestination: "stringToBool", RowID: 100}
action1 := internal.Action{Frame: "f", ValueDestination: "stringToBool", RowID: 101}
field1 := internal.InputDefinitionField{Name: "newID", PrimaryKey: true, Actions: []*internal.Action{&action1}}
def = &internal.InputDefinition{Name: "test", Frames: []*internal.Frame{&frames}, Fields: []*internal.InputDefinitionField{&field, &field1}}
err = input.LoadDefinition(def)
if !strings.Contains(err.Error(), "duplicate primaryKey with other field") {
t.Fatalf("Expected duplicate primaryKey error, actual error: %s", err)
}
action1 = internal.Action{Frame: "f", ValueDestination: "stringToBool", RowID: 100}
field1 = internal.InputDefinitionField{Name: "id", PrimaryKey: true, Actions: []*internal.Action{&action1}}
def = &internal.InputDefinition{Name: "test", Frames: []*internal.Frame{&frames}, Fields: []*internal.InputDefinitionField{&field, &field1}}
err = input.LoadDefinition(def)
if !strings.Contains(err.Error(), "duplicate rowID with other field") {
t.Fatalf("Expected duplicate rowID with other field error, actual error: %s", err)
}
}