Skip to content

Commit 39d080c

Browse files
committed
filter_sort: add time.Time comparison support for gt, gte, lt, lte operators
Signed-off-by: Jeeva Kandasamy <jkandasa@gmail.com>
1 parent c143b0b commit 39d080c

File tree

2 files changed

+145
-0
lines changed

2 files changed

+145
-0
lines changed

pkg/utils/filter_sort/utils_filter.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"reflect"
66
"regexp"
7+
"time"
78

89
"github.com/mycontroller-org/server/v2/pkg/types/cmap"
910
sfTY "github.com/mycontroller-org/server/v2/pkg/types/service_filter"
@@ -61,6 +62,14 @@ func IsMatching(entity interface{}, filters []storageTY.Filter) bool {
6162
case reflect.Bool:
6263
match = CompareBool(value, filter.Operator, filter.Value)
6364

65+
case reflect.Struct:
66+
timeValue, ok := value.(time.Time)
67+
if !ok {
68+
match = false
69+
break
70+
}
71+
match = CompareTime(timeValue, filter.Operator, filter.Value)
72+
6473
default:
6574
match = false
6675
}
@@ -251,6 +260,30 @@ func VerifyFloatSlice(value float64, operator string, expectedValue interface{})
251260
return false
252261
}
253262

263+
// CompareTime compares time.Time values
264+
func CompareTime(value time.Time, operator string, expectedValue interface{}) bool {
265+
expectedStr := converterUtils.ToString(expectedValue)
266+
expected, err := time.Parse(time.RFC3339, expectedStr)
267+
if err != nil {
268+
return false
269+
}
270+
switch operator {
271+
case storageTY.OperatorEqual, storageTY.OperatorNone:
272+
return value.Equal(expected)
273+
case storageTY.OperatorNotEqual:
274+
return !value.Equal(expected)
275+
case storageTY.OperatorGreaterThan:
276+
return value.After(expected)
277+
case storageTY.OperatorGreaterThanEqual:
278+
return value.After(expected) || value.Equal(expected)
279+
case storageTY.OperatorLessThan:
280+
return value.Before(expected)
281+
case storageTY.OperatorLessThanEqual:
282+
return value.Before(expected) || value.Equal(expected)
283+
}
284+
return false
285+
}
286+
254287
// IsMine verifies the supplied id and labels with valid list
255288
func IsMine(svcFilter *sfTY.ServiceFilter, targetType, targetID string, targetLabels cmap.CustomStringMap) bool {
256289
if !svcFilter.HasFilter() {

pkg/utils/filter_sort/utils_filter_test.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ package helper
22

33
import (
44
"testing"
5+
"time"
56

67
"github.com/mycontroller-org/server/v2/pkg/types/cmap"
78
sfTY "github.com/mycontroller-org/server/v2/pkg/types/service_filter"
9+
storageTY "github.com/mycontroller-org/server/v2/plugin/database/storage/types"
10+
"github.com/stretchr/testify/assert"
811
"github.com/stretchr/testify/require"
912
)
1013

@@ -170,3 +173,112 @@ func TestIsMine(t *testing.T) {
170173
}
171174

172175
}
176+
177+
func TestCompareTime(t *testing.T) {
178+
base := time.Date(2024, time.June, 15, 12, 0, 0, 0, time.UTC)
179+
earlier := time.Date(2024, time.June, 10, 12, 0, 0, 0, time.UTC)
180+
later := time.Date(2024, time.June, 20, 12, 0, 0, 0, time.UTC)
181+
182+
baseStr := base.Format(time.RFC3339)
183+
earlierStr := earlier.Format(time.RFC3339)
184+
185+
tests := []struct {
186+
name string
187+
value time.Time
188+
operator string
189+
filter string
190+
expected bool
191+
}{
192+
{name: "eq match", value: base, operator: storageTY.OperatorEqual, filter: baseStr, expected: true},
193+
{name: "eq no match", value: base, operator: storageTY.OperatorEqual, filter: earlierStr, expected: false},
194+
{name: "ne match", value: base, operator: storageTY.OperatorNotEqual, filter: earlierStr, expected: true},
195+
{name: "ne no match", value: base, operator: storageTY.OperatorNotEqual, filter: baseStr, expected: false},
196+
{name: "gt match", value: later, operator: storageTY.OperatorGreaterThan, filter: baseStr, expected: true},
197+
{name: "gt equal no match", value: base, operator: storageTY.OperatorGreaterThan, filter: baseStr, expected: false},
198+
{name: "gt earlier no match", value: earlier, operator: storageTY.OperatorGreaterThan, filter: baseStr, expected: false},
199+
{name: "gte match equal", value: base, operator: storageTY.OperatorGreaterThanEqual, filter: baseStr, expected: true},
200+
{name: "gte match greater", value: later, operator: storageTY.OperatorGreaterThanEqual, filter: baseStr, expected: true},
201+
{name: "gte no match", value: earlier, operator: storageTY.OperatorGreaterThanEqual, filter: baseStr, expected: false},
202+
{name: "lt match", value: earlier, operator: storageTY.OperatorLessThan, filter: baseStr, expected: true},
203+
{name: "lt equal no match", value: base, operator: storageTY.OperatorLessThan, filter: baseStr, expected: false},
204+
{name: "lt later no match", value: later, operator: storageTY.OperatorLessThan, filter: baseStr, expected: false},
205+
{name: "lte match equal", value: base, operator: storageTY.OperatorLessThanEqual, filter: baseStr, expected: true},
206+
{name: "lte match less", value: earlier, operator: storageTY.OperatorLessThanEqual, filter: baseStr, expected: true},
207+
{name: "lte no match", value: later, operator: storageTY.OperatorLessThanEqual, filter: baseStr, expected: false},
208+
{name: "invalid filter value", value: base, operator: storageTY.OperatorEqual, filter: "not-a-time", expected: false},
209+
{name: "unknown operator", value: base, operator: "unknown", filter: baseStr, expected: false},
210+
}
211+
212+
for _, tc := range tests {
213+
t.Run(tc.name, func(t *testing.T) {
214+
result := CompareTime(tc.value, tc.operator, tc.filter)
215+
assert.Equal(t, tc.expected, result)
216+
})
217+
}
218+
}
219+
220+
func TestIsMatchingTimeField(t *testing.T) {
221+
type event struct {
222+
ID string
223+
CreatedAt time.Time
224+
}
225+
226+
base := time.Date(2024, time.June, 15, 12, 0, 0, 0, time.UTC)
227+
earlier := time.Date(2024, time.June, 10, 12, 0, 0, 0, time.UTC)
228+
later := time.Date(2024, time.June, 20, 12, 0, 0, 0, time.UTC)
229+
230+
tests := []struct {
231+
name string
232+
entity interface{}
233+
filters []storageTY.Filter
234+
expected bool
235+
}{
236+
{
237+
name: "gt match",
238+
entity: &event{ID: "1", CreatedAt: later},
239+
filters: []storageTY.Filter{
240+
{Key: "createdAt", Operator: storageTY.OperatorGreaterThan, Value: base.Format(time.RFC3339)},
241+
},
242+
expected: true,
243+
},
244+
{
245+
name: "gt no match",
246+
entity: &event{ID: "2", CreatedAt: earlier},
247+
filters: []storageTY.Filter{
248+
{Key: "createdAt", Operator: storageTY.OperatorGreaterThan, Value: base.Format(time.RFC3339)},
249+
},
250+
expected: false,
251+
},
252+
{
253+
name: "lt match",
254+
entity: &event{ID: "3", CreatedAt: earlier},
255+
filters: []storageTY.Filter{
256+
{Key: "createdAt", Operator: storageTY.OperatorLessThan, Value: base.Format(time.RFC3339)},
257+
},
258+
expected: true,
259+
},
260+
{
261+
name: "lte match equal",
262+
entity: &event{ID: "4", CreatedAt: base},
263+
filters: []storageTY.Filter{
264+
{Key: "createdAt", Operator: storageTY.OperatorLessThanEqual, Value: base.Format(time.RFC3339)},
265+
},
266+
expected: true,
267+
},
268+
{
269+
name: "gte no match",
270+
entity: &event{ID: "5", CreatedAt: earlier},
271+
filters: []storageTY.Filter{
272+
{Key: "createdAt", Operator: storageTY.OperatorGreaterThanEqual, Value: base.Format(time.RFC3339)},
273+
},
274+
expected: false,
275+
},
276+
}
277+
278+
for _, tc := range tests {
279+
t.Run(tc.name, func(t *testing.T) {
280+
result := IsMatching(tc.entity, tc.filters)
281+
assert.Equal(t, tc.expected, result)
282+
})
283+
}
284+
}

0 commit comments

Comments
 (0)