validate columnID match primaryField

This commit is contained in:
Linh Vo 2017-06-23 14:39:52 -05:00
parent 7a3baed3ac
commit 53c7e5946d
5 changed files with 106 additions and 23 deletions

View file

@ -1499,6 +1499,7 @@ func errorString(err error) string {
return err.Error()
}
// handlePOSTInputDefinition handles POST /input-definition request.
func (h *Handler) handlePostInputDefinition(w http.ResponseWriter, r *http.Request) {
indexName := mux.Vars(r)["index"]
inputDefName := mux.Vars(r)["input-definition"]
@ -1525,6 +1526,26 @@ func (h *Handler) handlePostInputDefinition(w http.ResponseWriter, r *http.Reque
}
def.Name = inputDefName
// Validate columnLabel & duplicate primaryKey
numPrimaryKey := 0
for _, field := range def.Fields {
if field.PrimaryKey {
numPrimaryKey += 1
if field.Name == index.columnLabel {
continue
} else {
err = fmt.Errorf("primary field's name not match columnLabel")
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
}
if numPrimaryKey > 1 {
err = errors.New("duplicate primaryKey with other field")
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Create InputDefinition.
_, err = index.CreateInputDefinition(def)
if err == ErrInputDefinitionExists {
@ -1550,6 +1571,7 @@ func (h *Handler) handlePostInputDefinition(w http.ResponseWriter, r *http.Reque
}
}
// handleGetInputDefinition handles GET /input-definition request.
func (h *Handler) handleGetInputDefinition(w http.ResponseWriter, r *http.Request) {
indexName := mux.Vars(r)["index"]
inputDefName := mux.Vars(r)["input-definition"]
@ -1563,7 +1585,6 @@ func (h *Handler) handleGetInputDefinition(w http.ResponseWriter, r *http.Reques
return
}
inputDef, _ := index.inputDefinitions[inputDefName]
//inputInfo := InputDefinitionInfo{Frames: inputDef.frames, Fields: inputDef.fields}
if err := json.NewEncoder(w).Encode(InputDefinitionInfo{
Frames: inputDef.frames,
Fields: inputDef.fields,
@ -1573,6 +1594,7 @@ func (h *Handler) handleGetInputDefinition(w http.ResponseWriter, r *http.Reques
}
// handleDeleteInputDefinition handles DELETE /input-definition request.
func (h *Handler) handleDeleteInputDefinition(w http.ResponseWriter, r *http.Request) {
indexName := mux.Vars(r)["index"]
inputDefName := mux.Vars(r)["input-definition"]

View file

@ -1084,7 +1084,7 @@ func TestHandler_CreateInputDefinition(t *testing.T) {
}],
"fields": [
{
"name": "id",
"name": "columnID",
"primaryKey": true
},
{
@ -1112,6 +1112,80 @@ func TestHandler_CreateInputDefinition(t *testing.T) {
} else if body := w.Body.String(); body != `{}`+"\n" {
t.Fatalf("unexpected body: %s", body)
}
}
//Ensure throwing error if there's duplicated primaryKey field
func TestHandler_DuplicatePrimaryKey(t *testing.T) {
hldr := MustOpenHolder()
defer hldr.Close()
hldr.MustCreateIndexIfNotExists("i0", pilosa.IndexOptions{})
inputBody1 := []byte(`
{
"frames":[{
"name":"event-time",
"options":{
"timeQuantum": "YMD",
"inverseEnabled": false,
"cacheType": "ranked"
}
}],
"fields": [
{
"name": "columnID",
"primaryKey": true
},
{
"name": "columnID",
"primaryKey": true
}
]
}`)
h := NewHandler()
h.Holder = hldr.Holder
h.Cluster = NewCluster(1)
w := httptest.NewRecorder()
h.ServeHTTP(w, MustNewHTTPRequest("POST", "/index/i0/input-definition/input2", bytes.NewBuffer(inputBody1)))
if w.Code != http.StatusBadRequest {
t.Fatalf("unexpected status code: %d", w.Code)
} else if body := w.Body.String(); body != `duplicate primaryKey with other field`+"\n" {
t.Fatalf("unexpected body: %s", body)
}
}
// Eusure throwing error if primary field's name doesn't match columnLabel
func TestHandler_UnmatchColumnID(t *testing.T) {
hldr := MustOpenHolder()
defer hldr.Close()
hldr.MustCreateIndexIfNotExists("i0", pilosa.IndexOptions{ColumnLabel: "id"})
inputBody := []byte(`
{
"frames":[{
"name":"event-time",
"options":{
"timeQuantum": "YMD",
"inverseEnabled": false,
"cacheType": "ranked"
}
}],
"fields": [
{
"name": "columnID",
"primaryKey": true
}
]
}`)
h := NewHandler()
h.Holder = hldr.Holder
h.Cluster = NewCluster(1)
w := httptest.NewRecorder()
h.ServeHTTP(w, MustNewHTTPRequest("POST", "/index/i0/input-definition/input1", bytes.NewBuffer(inputBody)))
if w.Code != http.StatusBadRequest {
t.Fatalf("unexpected status code: %d", w.Code)
} else if body := w.Body.String(); body != `primary field's name not match columnLabel`+"\n" {
t.Fatalf("unexpected body: %s", body)
}
}
// Ensure handler can delete a input definition.

View file

@ -99,7 +99,6 @@ func (i *InputDefinition) LoadDefinition(pb *internal.InputDefinition) error {
i.frames = append(i.frames, inputFrame)
}
numPrimaryKey := 0
countRowID := make(map[string]uint64)
for _, field := range pb.Fields {
var actions []Action
@ -122,13 +121,6 @@ func (i *InputDefinition) LoadDefinition(pb *internal.InputDefinition) error {
RowID: &action.RowID,
})
}
if field.PrimaryKey {
numPrimaryKey += 1
}
if numPrimaryKey > 1 {
return errors.New("duplicate primaryKey with other field")
}
inputField := InputDefinitionField{
Name: field.Name,
@ -252,6 +244,7 @@ func (o *Action) Encode() (*internal.InputDefinitionAction, error) {
}, nil
}
// convert pointer to uint64
func convert(x *uint64) uint64 {
if x != nil {
return *x
@ -267,8 +260,8 @@ type InputFrame struct {
// InputDefinitionInfo the json message format to create an InputDefinition.
type InputDefinitionInfo struct {
Frames []InputFrame `json:"frames"`
Fields []InputDefinitionField `json:"fields"`
Frames []InputFrame `json:"frames"`
Fields []InputDefinitionField `json:"fields"`
}
// Encode converts InputDefinitionInfo into its internal representation.
@ -288,6 +281,7 @@ func (i *InputDefinitionInfo) Encode() (*internal.InputDefinition, error) {
return &def, nil
}
// AddFrame adds frame to input definition
func (i *InputDefinition) AddFrame(frame InputFrame) error {
i.frames = append(i.frames, frame)
if err := i.saveMeta(); err != nil {
@ -296,6 +290,7 @@ func (i *InputDefinition) AddFrame(frame InputFrame) error {
return nil
}
// ValidateAction validate actions from input-definition
func (i *InputDefinition) ValidateAction(action *internal.InputDefinitionAction) error {
if action.Frame == "" {
return ErrFrameRequired

View file

@ -128,16 +128,8 @@ func TestInputDefinition_LoadDefinition(t *testing.T) {
}
action = internal.InputDefinitionAction{Frame: "f", ValueDestination: pilosa.SingleRowBool, RowID: 100}
action1 := internal.InputDefinitionAction{Frame: "f", ValueDestination: pilosa.SingleRowBool, RowID: 0}
field1 := internal.InputDefinitionField{Name: "newID", PrimaryKey: true, InputDefinitionActions: []*internal.InputDefinitionAction{&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.InputDefinitionAction{Frame: "f", ValueDestination: pilosa.SingleRowBool, RowID: 100}
field1 = internal.InputDefinitionField{Name: "id", PrimaryKey: true, InputDefinitionActions: []*internal.InputDefinitionAction{&action1}}
action1 := internal.InputDefinitionAction{Frame: "f", ValueDestination: pilosa.SingleRowBool, RowID: 100}
field1 := internal.InputDefinitionField{Name: "id", PrimaryKey: true, InputDefinitionActions: []*internal.InputDefinitionAction{&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") {

View file

@ -540,7 +540,7 @@ func TestMain_SendReceiveMessage(t *testing.T) {
"cacheType": "ranked",
"timeQuantum": "YMD"
}}],
"fields": [{"name": "id",
"fields": [{"name": "columnID",
"primaryKey": true
}]}
`); err != nil {