Removes column/row labels for input definition. Resolves #810

This commit is contained in:
Yuce Tekol 2017-11-10 18:00:45 +03:00
parent 9e458aecd5
commit 83be24f4d2
No known key found for this signature in database
GPG key ID: CB59E46D2FB90573
7 changed files with 68 additions and 78 deletions

View file

@ -43,10 +43,6 @@ curl localhost:10101/index/repository/input-definition/stargazer \
}
],
"fields": [
{
"name": "repo_id",
"primaryKey": true
},
{
"actions": [
{
@ -105,8 +101,8 @@ curl localhost:10101/index/repository/input/stargazer \
-X POST \
-d '[
{
"columnID": 91720568,
"language_id": "Go",
"repo_id": 91720568,
"stargazer_id": 513114,
"time_value": "2017-05-18T20:40"
},

View file

@ -1760,8 +1760,7 @@ func (h *Handler) handlePostInputDefinition(w http.ResponseWriter, r *http.Reque
return
}
// Validation the input definition with the curent index's ColumnLabel.
if err := req.Validate(index.ColumnLabel()); err != nil {
if err := req.Validate(); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
@ -1908,10 +1907,13 @@ func (h *Handler) InputJSONDataParser(req map[string]interface{}, index *Index,
for _, field := range inputDef.Fields() {
validFields[field.Name] = true
if field.PrimaryKey {
columnLabel := field.Name
value, ok := req[columnLabel]
primaryKey := field.Name
if primaryKey != DefaultColumnLabel {
return nil, fmt.Errorf("Primary key field should have the name: %s", DefaultColumnLabel)
}
value, ok := req[primaryKey]
if !ok {
return nil, fmt.Errorf("columnLabel required")
return nil, fmt.Errorf("primary key does not exist")
}
rawValue, ok := value.(float64) // The default JSON marshalling will interpret this as a float
if !ok {

View file

@ -1309,34 +1309,6 @@ func TestHandler_DuplicatePrimaryKey(t *testing.T) {
t.Fatalf("unexpected body: %s", body)
}
// Eusure throwing error if primary field's name doesn't match columnLabel
hldr.MustCreateIndexIfNotExists("i1", pilosa.IndexOptions{ColumnLabel: "id"})
unmatchColumnBody := []byte(`
{
"frames":[{
"name":"event-time",
"options":{
"timeQuantum": "YMD",
"inverseEnabled": false,
"cacheType": "ranked"
}
}],
"fields": [
{
"name": "columnID",
"primaryKey": true
}
]
}`)
w = httptest.NewRecorder()
h.ServeHTTP(w, test.MustNewHTTPRequest("POST", "/index/i1/input-definition/input1", bytes.NewBuffer(unmatchColumnBody)))
if w.Code != http.StatusBadRequest {
t.Fatalf("unexpected status code: %d", w.Code)
} else if body := w.Body.String(); body != pilosa.ErrInputDefinitionColumnLabel.Error()+"\n" {
t.Fatalf("unexpected body: %s", body)
}
// Eusure throwing error if request body is invalid.
jsonErrorBody := []byte(`
{
@ -1578,7 +1550,7 @@ func TestHandler_CreateInput(t *testing.T) {
}
inputBody := []byte(`
[{
"id": 1,
"columnID": 1,
"cabType": "yellow",
"distanceMiles": 8,
"withPet": true,
@ -1659,14 +1631,14 @@ func TestInput_JSON(t *testing.T) {
err string
}{
{json: `[{
"id": 1,
"columnID": 1,
"cabType": "yellow",
"distanceMiles": 8,
"nofield": true
}]`,
err: "field not found: nofield"},
{json: `[{
"id": "abc",
"columnID": "abc",
"cabType": "yellow",
"distanceMiles": 8,
"withPet": true
@ -1677,23 +1649,23 @@ func TestInput_JSON(t *testing.T) {
"distanceMiles": 8,
"withPet": true
}]`,
err: "columnLabel required"},
err: "primary key does not exist"},
{json: `[{
"id": 1,
"columnID": 1,
"cabType": "yellow",
"distanceMiles": 8,
"withPet": true
}`,
err: "unexpected EOF"},
{json: `[{
"id": 1,
"columnID": 1,
"cabType": "yellow",
"distanceMiles": 8,
"noFrame": 1
}]`,
err: "Frame not found: foo"},
{json: `[{
"id": 1,
"columnID": 1,
"cabType": "yellow",
"distanceMiles": 8,
"time_value": 12345

View file

@ -687,7 +687,8 @@ func (i *Index) createInputDefinition(pb *internal.InputDefinition) (*InputDefin
for _, fr := range pb.Frames {
opt := FrameOptions{
RowLabel: fr.Meta.RowLabel,
// Deprecating row labels per #810. So, setting the default row label here.
RowLabel: DefaultRowLabel,
InverseEnabled: fr.Meta.InverseEnabled,
CacheType: fr.Meta.CacheType,
CacheSize: fr.Meta.CacheSize,

View file

@ -309,14 +309,14 @@ func TestIndex_CreateInputDefinition(t *testing.T) {
// Create Input Definition.
frames := internal.Frame{Name: "f", Meta: &internal.FrameMeta{RowLabel: "row"}}
action := internal.InputDefinitionAction{Frame: "f", ValueDestination: "mapping", ValueMap: map[string]uint64{"Green": 1}}
fields := internal.InputDefinitionField{Name: "id", PrimaryKey: true, InputDefinitionActions: []*internal.InputDefinitionAction{&action}}
fields := internal.InputDefinitionField{PrimaryKey: true, InputDefinitionActions: []*internal.InputDefinitionAction{&action}}
def := internal.InputDefinition{Name: "test", Frames: []*internal.Frame{&frames}, Fields: []*internal.InputDefinitionField{&fields}}
inputDef, err := index.CreateInputDefinition(&def)
if err != nil {
t.Fatal(err)
} else if inputDef.Frames()[0].Name != frames.Name {
t.Fatalf("unexpected input definition frames %v", inputDef.Frames())
} else if inputDef.Fields()[0].Name != fields.Name {
} else if inputDef.Fields()[0].Name != pilosa.DefaultColumnLabel {
t.Fatalf("unexpected input definition actions %v", inputDef.Fields())
}
}

View file

@ -90,7 +90,8 @@ func (i *InputDefinition) LoadDefinition(pb *internal.InputDefinition) error {
inputFrame := InputFrame{
Name: fr.Name,
Options: FrameOptions{
RowLabel: frameMeta.RowLabel,
// Deprecating row labels per #810. So, setting the default row label here.
RowLabel: DefaultRowLabel,
InverseEnabled: frameMeta.InverseEnabled,
CacheSize: frameMeta.CacheSize,
CacheType: frameMeta.CacheType,
@ -100,8 +101,11 @@ func (i *InputDefinition) LoadDefinition(pb *internal.InputDefinition) error {
i.frames = append(i.frames, inputFrame)
}
primaryKeyGiven := false
for _, field := range pb.Fields {
var actions []Action
fieldName := field.Name
for _, action := range field.InputDefinitionActions {
actions = append(actions, Action{
Frame: action.Frame,
@ -111,14 +115,27 @@ func (i *InputDefinition) LoadDefinition(pb *internal.InputDefinition) error {
})
}
if field.PrimaryKey {
// Deprecating column labels per #810.
// So, setting the default column label here.
fieldName = DefaultColumnLabel
primaryKeyGiven = true
}
inputField := InputDefinitionField{
Name: field.Name,
Name: fieldName,
PrimaryKey: field.PrimaryKey,
Actions: actions,
}
i.fields = append(i.fields, inputField)
}
if len(pb.Fields) > 0 && !primaryKeyGiven {
// primary field is required if there are other fields.
// add it if it doesn't exist.
i.fields = append(i.fields, InputDefDefaultPrimaryKeyField())
}
return nil
}
@ -265,7 +282,7 @@ type InputDefinitionInfo struct {
}
// Validate the InputDefinitionInfo data.
func (i *InputDefinitionInfo) Validate(columnLabel string) error {
func (i *InputDefinitionInfo) Validate() error {
numPrimaryKey := 0
accountRowID := make(map[string]uint64)
@ -298,9 +315,6 @@ func (i *InputDefinitionInfo) Validate(columnLabel string) error {
}
if field.PrimaryKey {
numPrimaryKey++
if field.Name != columnLabel {
return ErrInputDefinitionColumnLabel
}
} else if len(field.Actions) == 0 {
return ErrInputDefinitionActionRequired
}
@ -321,7 +335,16 @@ func (i *InputDefinitionInfo) Encode() *internal.InputDefinition {
for _, f := range i.Frames {
def.Frames = append(def.Frames, f.Encode())
}
primaryKeyGiven := false
for _, f := range i.Fields {
if f.PrimaryKey {
f.Name = DefaultColumnLabel
primaryKeyGiven = true
}
def.Fields = append(def.Fields, f.Encode())
}
if len(i.Fields) > 0 && !primaryKeyGiven {
f := InputDefDefaultPrimaryKeyField()
def.Fields = append(def.Fields, f.Encode())
}
return &def
@ -379,3 +402,11 @@ func HandleAction(a Action, value interface{}, colID uint64, timestamp int64) (*
}
return &bit, err
}
func InputDefDefaultPrimaryKeyField() InputDefinitionField {
return InputDefinitionField{
Name: DefaultColumnLabel,
PrimaryKey: true,
Actions: []Action{},
}
}

View file

@ -63,10 +63,6 @@ func TestInputDefinition_Encoding(t *testing.T) {
}
}],
"fields": [
{
"name": "id",
"primaryKey": true
},
{
"name": "cabType",
"actions": [
@ -96,9 +92,9 @@ func TestInputDefinition_Encoding(t *testing.T) {
t.Fatalf("unexpected frame meta data: %v", internalDef)
} else if len(internalDef.Fields) != 2 {
t.Fatalf("unexpected number of Fields: %d", len(internalDef.Fields))
} else if len(internalDef.Fields[1].InputDefinitionActions) != 1 {
} else if len(internalDef.Fields[0].InputDefinitionActions) != 1 {
t.Fatalf("unexpected number of Actions: %v", internalDef.Fields[1].InputDefinitionActions)
} else if internalDef.Fields[1].InputDefinitionActions[0].ValueDestination != "mapping" {
} else if internalDef.Fields[0].InputDefinitionActions[0].ValueDestination != "mapping" {
t.Fatalf("unexpected ValueDestination: %v", internalDef.Fields[1].InputDefinitionActions[0])
}
}
@ -110,14 +106,14 @@ func TestActionValidation(t *testing.T) {
action := pilosa.Action{Frame: "f", ValueDestination: pilosa.InputSingleRowBool, ValueMap: map[string]uint64{"Green": 1}}
field := pilosa.InputDefinitionField{Name: "id", PrimaryKey: false, Actions: []pilosa.Action{action}}
info := pilosa.InputDefinitionInfo{Fields: []pilosa.InputDefinitionField{field}}
err := info.Validate("id")
err := info.Validate()
if err != pilosa.ErrInputDefinitionAttrsRequired {
t.Fatalf("Expect error: %s, actual err: %s", pilosa.ErrInputDefinitionAttrsRequired, err)
}
frame := pilosa.InputFrame{Name: "f", Options: pilosa.FrameOptions{RowLabel: "row"}}
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field}}
err = info.Validate("id")
err = info.Validate()
if !strings.Contains(err.Error(), "rowID required for single-row-boolean") {
t.Fatalf("Expected rowID required for single-row-boolean error, actual error: %s", err)
}
@ -126,7 +122,7 @@ func TestActionValidation(t *testing.T) {
action = pilosa.Action{Frame: "f", ValueDestination: pilosa.InputSingleRowBool, RowID: &rowID}
field = pilosa.InputDefinitionField{Name: "id", PrimaryKey: true, Actions: []pilosa.Action{action}}
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field}}
err = info.Validate("id")
err = info.Validate()
if err != pilosa.ErrName {
t.Fatalf("Expect error: %s, actual err: %s", pilosa.ErrName, err)
}
@ -135,23 +131,15 @@ func TestActionValidation(t *testing.T) {
action = pilosa.Action{ValueDestination: pilosa.InputSingleRowBool, RowID: &rowID}
field = pilosa.InputDefinitionField{Name: "id", PrimaryKey: true, Actions: []pilosa.Action{action}}
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field}}
err = info.Validate("id")
err = info.Validate()
if err != pilosa.ErrFrameRequired {
t.Fatalf("Expect error: %s, actual err: %s", pilosa.ErrFrameRequired, err)
}
action = pilosa.Action{Frame: "f", ValueDestination: pilosa.InputSingleRowBool, RowID: &rowID}
field = pilosa.InputDefinitionField{Name: "id", PrimaryKey: true, Actions: []pilosa.Action{action}}
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field}}
err = info.Validate("test")
if err != pilosa.ErrInputDefinitionColumnLabel {
t.Fatalf("Expect error: %s, actual err: %s", pilosa.ErrInputDefinitionColumnLabel, err)
}
action = pilosa.Action{Frame: "f", ValueDestination: pilosa.InputSingleRowBool, RowID: &rowID}
field = pilosa.InputDefinitionField{Name: "x", PrimaryKey: false, Actions: []pilosa.Action{action}}
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field}}
err = info.Validate("id")
err = info.Validate()
if err != pilosa.ErrInputDefinitionHasPrimaryKey {
t.Fatalf("Expect error: %s, actual err: %s", pilosa.ErrInputDefinitionHasPrimaryKey, err)
}
@ -159,7 +147,7 @@ func TestActionValidation(t *testing.T) {
action = pilosa.Action{Frame: "f", ValueDestination: "value-to-ROW", ValueMap: map[string]uint64{"Green": 1}}
field = pilosa.InputDefinitionField{Name: "id", PrimaryKey: true, Actions: []pilosa.Action{action}}
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field}}
err = info.Validate("id")
err = info.Validate()
if !strings.Contains(err.Error(), "invalid ValueDestination") {
t.Fatalf("Expected invalid ValueDestination error, actual error: %s", err)
}
@ -167,7 +155,7 @@ func TestActionValidation(t *testing.T) {
action = pilosa.Action{Frame: "f", ValueDestination: pilosa.InputMapping, RowID: &rowID}
field = pilosa.InputDefinitionField{Name: "id", PrimaryKey: true, Actions: []pilosa.Action{action}}
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field}}
err = info.Validate("id")
err = info.Validate()
if err != pilosa.ErrInputDefinitionValueMap {
t.Fatalf("Expect error: %s, actual err: %s", pilosa.ErrInputDefinitionValueMap, err)
}
@ -177,15 +165,15 @@ func TestActionValidation(t *testing.T) {
action1 := pilosa.Action{Frame: "f", ValueDestination: pilosa.InputSingleRowBool, RowID: &rowID}
field1 := pilosa.InputDefinitionField{Name: "id", PrimaryKey: true, Actions: []pilosa.Action{action1}}
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field, field1}}
err = info.Validate("id")
err = info.Validate()
if !strings.Contains(err.Error(), "duplicate rowID with other field") {
t.Fatalf("Expected duplicate rowID with other field error, actual error: %s", err)
}
field = pilosa.InputDefinitionField{Name: "id", PrimaryKey: true}
field = pilosa.InputDefinitionField{PrimaryKey: true}
field1 = pilosa.InputDefinitionField{Name: "test", PrimaryKey: false}
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field, field1}}
err = info.Validate("id")
err = info.Validate()
if err != pilosa.ErrInputDefinitionActionRequired {
t.Fatalf("Expect error: %s, actual err: %s", pilosa.ErrInputDefinitionActionRequired, err)
}