diff --git a/request/filter.go b/request/filter.go index 12decb5dda040c9dee05a02d64dab91690762394..181bb88553521681f5fc168e32e435ec301982b2 100644 --- a/request/filter.go +++ b/request/filter.go @@ -14,11 +14,11 @@ type FilterOperation struct { } type FilterSet struct { - Filters map[string]FilterOperation + Filters map[string][]FilterOperation } func (fs *FilterSet) ParseFromQueries(values url.Values) error { - fs.Filters = make(map[string]FilterOperation) + fs.Filters = make(map[string][]FilterOperation) for key, valArr := range values { if !strings.HasPrefix(key, "filter[") || !strings.HasSuffix(key, "]") || len(valArr) == 0 { @@ -35,10 +35,10 @@ func (fs *FilterSet) ParseFromQueries(values url.Values) error { op := parts[1] val := valArr[0] - fs.Filters[field] = FilterOperation{ + fs.Filters[field] = append(fs.Filters[field], FilterOperation{ Operation: op, Value: val, - } + }) } return nil } @@ -46,9 +46,11 @@ func (fs *FilterSet) ParseFromQueries(values url.Values) error { func (fs *FilterSet) Validate() error { validate := validator.New() - for field, filter := range fs.Filters { - if err := validate.Struct(filter); err != nil { - return fmt.Errorf("validation failed for field '%s': %w", field, err) + for field, ops := range fs.Filters { + for i, filter := range ops { + if err := validate.Struct(filter); err != nil { + return fmt.Errorf("validation failed for field '%s' operation #%d: %w", field, i, err) + } } } diff --git a/request/filter_test.go b/request/filter_test.go index e3b01945978e431db210bb97e595fd9e57f66a40..3ef7a01c7410164cb45676cd0f34c3ef52d426d7 100644 --- a/request/filter_test.go +++ b/request/filter_test.go @@ -17,12 +17,13 @@ func TestFilterSet_ParseFromQueries_CreatedAtGte(t *testing.T) { assert.NoError(t, err) assert.Len(t, fs.Filters, 1) - op, ok := fs.Filters["createdAt"] + ops, ok := fs.Filters["createdAt"] assert.True(t, ok) + assert.Len(t, ops, 1) assert.Equal(t, FilterOperation{ Operation: "gte", Value: "2024-01-01", - }, op) + }, ops[0]) } func TestFilterSet_ParseFromQueries_CreatedAtLte(t *testing.T) { @@ -35,12 +36,13 @@ func TestFilterSet_ParseFromQueries_CreatedAtLte(t *testing.T) { assert.NoError(t, err) assert.Len(t, fs.Filters, 1) - op, ok := fs.Filters["createdAt"] + ops, ok := fs.Filters["createdAt"] assert.True(t, ok) + assert.Len(t, ops, 1) assert.Equal(t, FilterOperation{ Operation: "lte", Value: "2024-02-01", - }, op) + }, ops[0]) } func TestFilterSet_ParseFromQueries_ApproveEq(t *testing.T) { @@ -53,12 +55,13 @@ func TestFilterSet_ParseFromQueries_ApproveEq(t *testing.T) { assert.NoError(t, err) assert.Len(t, fs.Filters, 1) - op, ok := fs.Filters["approve"] + ops, ok := fs.Filters["approve"] assert.True(t, ok) + assert.Len(t, ops, 1) assert.Equal(t, FilterOperation{ Operation: "eq", Value: "true", - }, op) + }, ops[0]) } func TestFilterSet_ParseFromQueries_AmountLte(t *testing.T) { @@ -71,12 +74,13 @@ func TestFilterSet_ParseFromQueries_AmountLte(t *testing.T) { assert.NoError(t, err) assert.Len(t, fs.Filters, 1) - op, ok := fs.Filters["amount"] + ops, ok := fs.Filters["amount"] assert.True(t, ok) + assert.Len(t, ops, 1) assert.Equal(t, FilterOperation{ Operation: "lte", Value: "1000", - }, op) + }, ops[0]) } func TestFilterSet_ParseFromQueries_StatusEq(t *testing.T) { @@ -89,10 +93,36 @@ func TestFilterSet_ParseFromQueries_StatusEq(t *testing.T) { assert.NoError(t, err) assert.Len(t, fs.Filters, 1) - op, ok := fs.Filters["status"] + ops, ok := fs.Filters["status"] assert.True(t, ok) + assert.Len(t, ops, 1) assert.Equal(t, FilterOperation{ Operation: "eq", Value: "approved", - }, op) + }, ops[0]) +} + +func TestFilterSet_ParseFromQueries_MultipleOperationsSameField(t *testing.T) { + values := url.Values{} + values.Add("filter[viewedAt][gte]", "2025-05-01") + values.Add("filter[viewedAt][lte]", "2025-05-31") + + var fs FilterSet + err := fs.ParseFromQueries(values) + + assert.NoError(t, err) + assert.Len(t, fs.Filters, 1) + + ops, ok := fs.Filters["viewedAt"] + assert.True(t, ok) + assert.Len(t, ops, 2) + + assert.Contains(t, ops, FilterOperation{ + Operation: "gte", + Value: "2025-05-01", + }) + assert.Contains(t, ops, FilterOperation{ + Operation: "lte", + Value: "2025-05-31", + }) }