diff --git a/db_utils/db_utils.go b/db_utils/db_utils.go index 5e9202cac83d863003045b74c95b0b72d6498908..4abd2d655b0392ae29c1dceb635836c8ec1c49a0 100644 --- a/db_utils/db_utils.go +++ b/db_utils/db_utils.go @@ -3,7 +3,9 @@ package db_utils import ( "fmt" "gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/date_utils" + "gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors" "gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/number_utils" + "gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/struct_utils" "regexp" "strings" "time" @@ -79,3 +81,38 @@ func AbsoluteDateStringQuery(absolute string) string { // Not matched, so try to match it literally (It'll probably break and return nothing, but that's good) return fmt.Sprintf(" = '%v'", absolute) } + +type PageParams struct { + Limit int64 `json:"limit,omitempty"` // Limit number of items returned in list (default: 0 (no limit)) + Offset int64 `json:"offset,omitempty"` // Offset in list (default: 0 (no offset)) +} + +func (params *PageParams) Validate() error { + if params.Limit < 0 { + return errors.Errorf("limit=%d must be positive", params.Limit) + } + if params.Offset < 0 { + return errors.Errorf("offset=%d must be >=0", params.Offset) + } + return nil +} + +func (params *PageParams) EnforceLimit() { + if params.Limit <= 0 { + params.Limit = 20 + } +} + +func ValidateDateFilters(params any, dateFiltersToValidate ...string) error { + queryParams := struct_utils.MapParams(params) + for _, filter := range dateFiltersToValidate { + date, present := queryParams[filter] + if present { + _, err := date_utils.ParseTimeString(date) + if err != nil { + return errors.Errorf("invalid %s: %s", filter, date) + } + } + } + return nil +} diff --git a/db_utils/db_utils_test.go b/db_utils/db_utils_test.go new file mode 100644 index 0000000000000000000000000000000000000000..33c50fddef89a08e00b952f18136e998d4bc3f4f --- /dev/null +++ b/db_utils/db_utils_test.go @@ -0,0 +1,89 @@ +package db_utils + +import ( + "testing" +) + +func TestPageParams_Validate(t *testing.T) { + tests := []struct { + name string + params PageParams + wantErr bool + }{ + {"ValidParams", PageParams{Limit: 10, Offset: 5}, false}, + {"NegativeLimit", PageParams{Limit: -1, Offset: 5}, true}, + {"NegativeOffset", PageParams{Limit: 10, Offset: -1}, true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.params.Validate() + if (err != nil) != tt.wantErr { + t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestPageParams_EnforceLimit(t *testing.T) { + tests := []struct { + name string + params PageParams + want int64 + }{ + {"NoLimit", PageParams{Limit: 0}, 20}, + {"NegativeLimit", PageParams{Limit: -5}, 20}, + {"PositiveLimit", PageParams{Limit: 10}, 10}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.params.EnforceLimit() + if tt.params.Limit != tt.want { + t.Errorf("EnforceLimit() = %v, want %v", tt.params.Limit, tt.want) + } + }) + } +} + +func TestValidateDateFilters(t *testing.T) { + type args struct { + params any + dateFiltersToValidate []string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + "ValidDateFilter", + args{params: struct { + Date string `json:"date"` + }{Date: "2023-10-01T00:00:00Z"}, dateFiltersToValidate: []string{"date"}}, + false, + }, + { + "InvalidDateFilter", + args{params: struct { + Date string `json:"date"` + }{Date: "invalid-date"}, dateFiltersToValidate: []string{"date"}}, + true, + }, + { + "MissingDateFilter", + args{params: struct { + Date string `json:"date"` + }{Date: "2023-10-01T00:00:00Z"}, dateFiltersToValidate: []string{"non_existent_date"}}, + false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := ValidateDateFilters(tt.args.params, tt.args.dateFiltersToValidate...); (err != nil) != tt.wantErr { + t.Errorf("ValidateDateFilters() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +}