diff --git a/handler.go b/handler.go index 09f920920..ade37b0c3 100644 --- a/handler.go +++ b/handler.go @@ -1664,11 +1664,17 @@ func (h *Handler) InputJSONDataParser(req map[string]interface{}, index *Index, // if field in input data is not in defined definition, return error var columnLabel string validFields := make(map[string]bool) + timestampFrame := make(map[string]string) for _, field := range inputDef.Fields() { validFields[field.Name] = true if field.PrimaryKey { columnLabel = field.Name } + for _, action := range field.Actions { + if action.ValueDestination == InputSetTimestamp { + timestampFrame[action.Frame] = field.Name + } + } } for key := range req { _, ok := validFields[key] @@ -1678,6 +1684,7 @@ func (h *Handler) InputJSONDataParser(req map[string]interface{}, index *Index, } setBits := make(map[string][]*Bit) + for _, field := range inputDef.Fields() { // skip field that defined in definition but not in input data if _, ok := req[field.Name]; !ok { @@ -1692,9 +1699,17 @@ func (h *Handler) InputJSONDataParser(req map[string]interface{}, index *Index, return nil, fmt.Errorf("float64 require, got value:%s, type: %s", value, reflect.TypeOf(value)) } + var timestamp string for _, action := range field.Actions { frame := action.Frame - bit, err := HandleAction(action, req[field.Name], uint64(colValue)) + timeField, ok := timestampFrame[action.Frame] + if !ok { + timestamp = "" + } else { + timestamp = req[timeField].(string) + } + + bit, err := HandleAction(action, req[field.Name], uint64(colValue), timestamp) if err != nil { return nil, fmt.Errorf("error handling action: %s, err: %s", action.ValueDestination, err) } diff --git a/handler_test.go b/handler_test.go index d28221d7c..c1e752e32 100644 --- a/handler_test.go +++ b/handler_test.go @@ -1271,6 +1271,16 @@ var defaultBody = ` "frame":"foo", "valueDestination":"value-to-row" + } + ] + }, + { + "name":"time_value", + "actions":[ + { + "frame":"add-ons", + "valueDestination":"set_timestamp" + } ] } diff --git a/input_definition.go b/input_definition.go index a79b9c53e..ae8cc35c9 100644 --- a/input_definition.go +++ b/input_definition.go @@ -212,7 +212,10 @@ func (a *Action) Validate() error { if len(a.ValueMap) == 0 { return ErrInputDefinitionValueMap } + case InputSetTimestamp: + } + return nil } @@ -338,11 +341,17 @@ func (i *InputDefinition) AddFrame(frame InputFrame) error { // HandleAction Process the input data with its action and return a bit to be imported later // Note: if the Bit should not be set then nil is returned with no error // From the JSON marshalling the possible types are: float64, boolean, string -// TODO handle Timestamps -func HandleAction(a Action, value interface{}, colID uint64) (*Bit, error) { +func HandleAction(a Action, value interface{}, colID uint64, timestamp string) (*Bit, error) { var err error var bit Bit bit.ColumnID = colID + if timestamp != "" { + v, err := time.Parse(TimeFormat, timestamp) + if err != nil { + return nil, fmt.Errorf("set-timestamp value for :%v must in time format: YYYY-MM-DD", timestamp) + } + bit.Timestamp = v.Unix() + } switch a.ValueDestination { case InputMapping: @@ -370,12 +379,7 @@ func HandleAction(a Action, value interface{}, colID uint64) (*Bit, error) { } bit.RowID = uint64(v) case InputSetTimestamp: - v, err := time.Parse(TimeFormat, value.(string)) - if err != nil { - return nil, fmt.Errorf("set-timestamp value for :%v must in time format: YYYY-MM-DD", value) - } - bit.Timestamp = v.Unix() - + break default: return nil, fmt.Errorf("Unrecognized Value Destination: %s in Action", a.ValueDestination) } diff --git a/input_definition_test.go b/input_definition_test.go index e5138bf6b..d6bb7ba38 100644 --- a/input_definition_test.go +++ b/input_definition_test.go @@ -197,9 +197,10 @@ func TestHandleAction(t *testing.T) { colID := uint64(0) rowID := uint64(100) action := pilosa.Action{ValueDestination: pilosa.InputSingleRowBool, RowID: &rowID} + timestamp := "" value = 1 - b, err := pilosa.HandleAction(action, value, colID) + b, err := pilosa.HandleAction(action, value, colID, timestamp) if b != nil { t.Fatalf("Expected integer type is not handled by single-row-boolean") } else if !strings.Contains(err.Error(), "single-row-boolean value") { @@ -207,31 +208,31 @@ func TestHandleAction(t *testing.T) { } value = "1" - b, err = pilosa.HandleAction(action, value, colID) + b, err = pilosa.HandleAction(action, value, colID, timestamp) if b != nil { t.Fatalf("Expected Ignore strings, only accept boolean") } value = "t" - b, err = pilosa.HandleAction(action, value, colID) + b, err = pilosa.HandleAction(action, value, colID, timestamp) if !strings.Contains(err.Error(), "must equate to a Bool") { t.Fatalf("Expected Unrecognized Value Destination error, actual error: %s", err) } value = float64(1) - b, err = pilosa.HandleAction(action, value, colID) + b, err = pilosa.HandleAction(action, value, colID, timestamp) if !strings.Contains(err.Error(), "must equate to a Bool") { t.Fatalf("Expected Unrecognized Value Destination error, actual error: %s", err) } value = false - b, err = pilosa.HandleAction(action, value, colID) + b, err = pilosa.HandleAction(action, value, colID, timestamp) if b != nil { t.Fatalf("Expected Ignore values that do not equate to True") } value = true - b, err = pilosa.HandleAction(action, value, colID) + b, err = pilosa.HandleAction(action, value, colID, timestamp) if b != nil { if b.ColumnID != 0 { t.Fatalf("Unexpected ColumnID %v", b.ColumnID) @@ -244,35 +245,35 @@ func TestHandleAction(t *testing.T) { action.ValueDestination = pilosa.InputValueToRow rowID = 101 value = float64(25.0) - b, err = pilosa.HandleAction(action, value, colID) + b, err = pilosa.HandleAction(action, value, colID, timestamp) if b != nil { if b.RowID != 25 { t.Fatalf("Unexpected RowID %v", b.RowID) } } value = "25" - b, err = pilosa.HandleAction(action, value, colID) + b, err = pilosa.HandleAction(action, value, colID, timestamp) if b != nil { t.Fatalf("Expected Ignore values that are not type float64") } action.ValueDestination = pilosa.InputMapping value = "test" - b, err = pilosa.HandleAction(action, value, colID) + b, err = pilosa.HandleAction(action, value, colID, timestamp) if b != nil { t.Fatalf("Expected Ignore values that are not type string") } value = 25 - b, err = pilosa.HandleAction(action, value, colID) + b, err = pilosa.HandleAction(action, value, colID, timestamp) if b != nil { t.Fatalf("Expected Ignore values that are not type string") } action.ValueDestination = pilosa.InputSetTimestamp - value = "2017-03-20T19:35" + timestamp = "2017-03-20T19:35" parsedTime, _ := time.Parse(pilosa.TimeFormat, value.(string)) - b, err = pilosa.HandleAction(action, value, colID) + b, err = pilosa.HandleAction(action, value, colID, timestamp) if b == nil { t.Fatalf("Expected return bit") } else if b.Timestamp != parsedTime.Unix() { @@ -280,13 +281,14 @@ func TestHandleAction(t *testing.T) { } value = "12345677" - b, err = pilosa.HandleAction(action, value, colID) + b, err = pilosa.HandleAction(action, value, colID, timestamp) if !strings.Contains(err.Error(), "set-timestamp value for :12345677 must in time format: YYYY-MM-DD") { t.Fatal("Expect invalid timestamp format") } action.ValueDestination = "test" - b, err = pilosa.HandleAction(action, value, colID) + timestamp = "" + b, err = pilosa.HandleAction(action, value, colID, timestamp) if !strings.Contains(err.Error(), "Unrecognized Value Destination") { t.Fatalf("Expected Unrecognized Value Destination error, actual error: %s", err) }