From bb6005a7a3fbb1b3abd443d80975a8c75d5702fd Mon Sep 17 00:00:00 2001 From: Vishal Choudhary Date: Fri, 6 Jan 2023 22:18:13 +0530 Subject: [PATCH] Added a time_add() filter to add duration and absolute time (#5817) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * time_add Signed-off-by: Vishal Choudhary * output is RFC3339 only now Signed-off-by: Vishal Choudhary * added note to timeadd Signed-off-by: Vishal Choudhary * added time_convert Signed-off-by: Vishal Choudhary * removed blank string timezone Signed-off-by: Vishal Choudhary * renamed to time_parse Signed-off-by: Vishal Choudhary Signed-off-by: Vishal Choudhary Co-authored-by: shuting Co-authored-by: Charles-Edouard Brétéché --- pkg/engine/jmespath/functions.go | 84 +++++++++++++++++++++++++++ pkg/engine/jmespath/functions_test.go | 60 +++++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/pkg/engine/jmespath/functions.go b/pkg/engine/jmespath/functions.go index f513aaf768..91ae20508b 100644 --- a/pkg/engine/jmespath/functions.go +++ b/pkg/engine/jmespath/functions.go @@ -81,6 +81,8 @@ var ( objectFromLists = "object_from_lists" random = "random" x509_decode = "x509_decode" + timeAdd = "time_add" + timeParse = "time_parse" timeToCron = "time_to_cron" ) @@ -502,6 +504,31 @@ func GetFunctions() []*FunctionEntry { ReturnType: []JpType{JpString}, Note: "converts an absolute time (RFC 3339) to a cron expression (string).", }, + { + Entry: &gojmespath.FunctionEntry{ + Name: timeAdd, + Arguments: []ArgSpec{ + {Types: []JpType{JpString}}, + {Types: []JpType{JpString}}, + {Types: []JpType{JpString}}, + }, + Handler: jpTimeAdd, + }, + ReturnType: []JpType{JpString}, + Note: "adds duration (third string) to a time value (second string) of a specified layout (first string)", + }, + { + Entry: &gojmespath.FunctionEntry{ + Name: timeParse, + Arguments: []ArgSpec{ + {Types: []JpType{JpString}}, + {Types: []JpType{JpString}}, + }, + Handler: jpTimeParse, + }, + ReturnType: []JpType{JpString}, + Note: "changes a time value of a given layout to RFC 3339", + }, } } @@ -1131,3 +1158,60 @@ func jpTimeToCron(arguments []interface{}) (interface{}, error) { return cron, nil } + +func jpTimeAdd(arguments []interface{}) (interface{}, error) { + var err error + layout, err := validateArg("", arguments, 0, reflect.String) + if err != nil { + return nil, err + } + + ts, err := validateArg("", arguments, 1, reflect.String) + if err != nil { + return nil, err + } + + dr, err := validateArg("", arguments, 2, reflect.String) + if err != nil { + return nil, err + } + + var t time.Time + if layout.String() != "" { + t, err = time.Parse(layout.String(), ts.String()) + } else { + t, err = time.Parse(time.RFC3339, ts.String()) + } + if err != nil { + return nil, err + } + + var d time.Duration + d, err = time.ParseDuration(dr.String()) + if err != nil { + return nil, err + } + + return t.Add(d).Format(time.RFC3339), nil +} + +func jpTimeParse(arguments []interface{}) (interface{}, error) { + var err error + layout, err := validateArg("", arguments, 0, reflect.String) + if err != nil { + return nil, err + } + + ts, err := validateArg("", arguments, 1, reflect.String) + if err != nil { + return nil, err + } + + var t time.Time + t, err = time.Parse(layout.String(), ts.String()) + if err != nil { + return nil, err + } + + return t.Format(time.RFC3339), nil +} diff --git a/pkg/engine/jmespath/functions_test.go b/pkg/engine/jmespath/functions_test.go index cfbd736875..a20b3c5361 100644 --- a/pkg/engine/jmespath/functions_test.go +++ b/pkg/engine/jmespath/functions_test.go @@ -1579,3 +1579,63 @@ func Test_TimeToCron(t *testing.T) { }) } } + +func Test_TimeAdd(t *testing.T) { + testCases := []struct { + test string + expectedResult string + }{ + { + test: "time_add('', '2021-01-02T15:04:05-07:00', '3h')", + expectedResult: "2021-01-02T18:04:05-07:00", + }, + { + test: "time_add('Mon Jan 02 15:04:05 MST 2006', 'Sat Jan 02 15:04:05 MST 2021', '5h30m40s')", + expectedResult: "2021-01-02T20:34:45Z", + }, + } + for i, tc := range testCases { + t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + query, err := New(tc.test) + assert.NilError(t, err) + + res, err := query.Search("") + assert.NilError(t, err) + + result, ok := res.(string) + assert.Assert(t, ok) + + assert.Equal(t, result, tc.expectedResult) + }) + } +} + +func Test_TimeParse(t *testing.T) { + testCases := []struct { + test string + expectedResult string + }{ + { + test: "time_parse('2006-01-02T15:04:05Z07:00', '2021-01-02T15:04:05-07:00')", + expectedResult: "2021-01-02T15:04:05-07:00", + }, + { + test: "time_parse('Mon Jan 02 15:04:05 MST 2006', 'Sat Jan 02 15:04:05 MST 2021')", + expectedResult: "2021-01-02T15:04:05Z", + }, + } + for i, tc := range testCases { + t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) { + query, err := New(tc.test) + assert.NilError(t, err) + + res, err := query.Search("") + assert.NilError(t, err) + + result, ok := res.(string) + assert.Assert(t, ok) + + assert.Equal(t, result, tc.expectedResult) + }) + } +}