Add filtering support to GET /api/v1/habits endpoint

Implemented comprehensive filtering capabilities for the habits list endpoint:
- Filter by type (BOOLEAN, COUNTER, VALUE)
- Filter by frequency (DAILY, WEEKLY, MONTHLY)
- Filter by archived status
- Text search in habit name and description
- All filters can be combined
- Filters work with pagination

Technical changes:
- Added FilterParams to GetUserHabitsQuery
- Created HabitFilter struct in repository interface
- Implemented dynamic SQL query building in SQLite repository
- Updated HTTP handler to parse filter query parameters
- Added comprehensive tests for repository and handler filtering
- Updated all test mocks with new filter methods
This commit is contained in:
2025-12-02 01:02:39 +01:00
parent 38e640c617
commit 935f742ac9
18 changed files with 662 additions and 2 deletions
@@ -1,6 +1,7 @@
package commands
import (
"apocapoc-api/internal/domain/repositories"
"apocapoc-api/internal/shared/pagination"
"context"
"testing"
@@ -238,3 +239,11 @@ func (m *mockHabitRepo) FindActiveByUserIDWithPagination(ctx context.Context, us
func (m *mockHabitRepo) CountActiveByUserID(ctx context.Context, userID string) (int, error) {
return 0, nil
}
func (m *mockHabitRepo) FindByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter, paginationParams *pagination.Params) ([]*entities.Habit, error) {
return nil, nil
}
func (m *mockHabitRepo) CountByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter) (int, error) {
return 0, nil
}
@@ -1,6 +1,7 @@
package commands
import (
"apocapoc-api/internal/domain/repositories"
"apocapoc-api/internal/shared/pagination"
"context"
"testing"
@@ -141,3 +142,11 @@ func (m *mockDeleteUserRepo) FindActiveByUserIDWithPagination(ctx context.Contex
func (m *mockDeleteUserRepo) CountActiveByUserID(ctx context.Context, userID string) (int, error) {
return 0, nil
}
func (m *mockDeleteUserRepo) FindByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter, paginationParams *pagination.Params) ([]*entities.Habit, error) {
return nil, nil
}
func (m *mockDeleteUserRepo) CountByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter) (int, error) {
return 0, nil
}
@@ -1,6 +1,7 @@
package commands
import (
"apocapoc-api/internal/domain/repositories"
"apocapoc-api/internal/shared/pagination"
"context"
"testing"
@@ -554,3 +555,19 @@ func (m *mockHabitRepoForMark) FindActiveByUserIDWithPagination(ctx context.Cont
func (m *mockHabitRepoForMark) CountActiveByUserID(ctx context.Context, userID string) (int, error) {
return 0, nil
}
func (m *mockEntryRepo) FindByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter, paginationParams *pagination.Params) ([]*entities.Habit, error) {
return nil, nil
}
func (m *mockEntryRepo) CountByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter) (int, error) {
return 0, nil
}
func (m *mockHabitRepoForMark) FindByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter, paginationParams *pagination.Params) ([]*entities.Habit, error) {
return nil, nil
}
func (m *mockHabitRepoForMark) CountByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter) (int, error) {
return 0, nil
}
@@ -1,6 +1,7 @@
package commands
import (
"apocapoc-api/internal/domain/repositories"
"apocapoc-api/internal/shared/pagination"
"context"
"errors"
@@ -304,3 +305,11 @@ func (m *mockUserRepo) FindActiveByUserIDWithPagination(ctx context.Context, use
func (m *mockUserRepo) CountActiveByUserID(ctx context.Context, userID string) (int, error) {
return 0, nil
}
func (m *mockUserRepo) FindByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter, paginationParams *pagination.Params) ([]*entities.Habit, error) {
return nil, nil
}
func (m *mockUserRepo) CountByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter) (int, error) {
return 0, nil
}
@@ -1,6 +1,7 @@
package commands
import (
"apocapoc-api/internal/domain/repositories"
"apocapoc-api/internal/shared/pagination"
"context"
"testing"
@@ -351,3 +352,19 @@ func (m *mockRequestResetUserRepo) FindActiveByUserIDWithPagination(ctx context.
func (m *mockRequestResetUserRepo) CountActiveByUserID(ctx context.Context, userID string) (int, error) {
return 0, nil
}
func (m *mockRequestResetTokenRepo) FindByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter, paginationParams *pagination.Params) ([]*entities.Habit, error) {
return nil, nil
}
func (m *mockRequestResetTokenRepo) CountByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter) (int, error) {
return 0, nil
}
func (m *mockRequestResetUserRepo) FindByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter, paginationParams *pagination.Params) ([]*entities.Habit, error) {
return nil, nil
}
func (m *mockRequestResetUserRepo) CountByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter) (int, error) {
return 0, nil
}
@@ -1,6 +1,7 @@
package commands
import (
"apocapoc-api/internal/domain/repositories"
"apocapoc-api/internal/shared/pagination"
"context"
"testing"
@@ -396,3 +397,19 @@ func (m *mockResetPasswordUserRepo) FindActiveByUserIDWithPagination(ctx context
func (m *mockResetPasswordUserRepo) CountActiveByUserID(ctx context.Context, userID string) (int, error) {
return 0, nil
}
func (m *mockPasswordResetTokenRepo) FindByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter, paginationParams *pagination.Params) ([]*entities.Habit, error) {
return nil, nil
}
func (m *mockPasswordResetTokenRepo) CountByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter) (int, error) {
return 0, nil
}
func (m *mockResetPasswordUserRepo) FindByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter, paginationParams *pagination.Params) ([]*entities.Habit, error) {
return nil, nil
}
func (m *mockResetPasswordUserRepo) CountByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter) (int, error) {
return 0, nil
}
@@ -1,6 +1,7 @@
package commands
import (
"apocapoc-api/internal/domain/repositories"
"apocapoc-api/internal/shared/pagination"
"context"
"testing"
@@ -195,3 +196,11 @@ func (m *mockRefreshTokenRepo) FindActiveByUserIDWithPagination(ctx context.Cont
func (m *mockRefreshTokenRepo) CountActiveByUserID(ctx context.Context, userID string) (int, error) {
return 0, nil
}
func (m *mockRefreshTokenRepo) FindByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter, paginationParams *pagination.Params) ([]*entities.Habit, error) {
return nil, nil
}
func (m *mockRefreshTokenRepo) CountByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter) (int, error) {
return 0, nil
}
@@ -1,6 +1,7 @@
package commands
import (
"apocapoc-api/internal/domain/repositories"
"apocapoc-api/internal/shared/pagination"
"context"
"testing"
@@ -146,3 +147,11 @@ func (m *mockEntryRepoForUnmark) FindActiveByUserIDWithPagination(ctx context.Co
func (m *mockEntryRepoForUnmark) CountActiveByUserID(ctx context.Context, userID string) (int, error) {
return 0, nil
}
func (m *mockEntryRepoForUnmark) FindByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter, paginationParams *pagination.Params) ([]*entities.Habit, error) {
return nil, nil
}
func (m *mockEntryRepoForUnmark) CountByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter) (int, error) {
return 0, nil
}
@@ -1,6 +1,7 @@
package commands
import (
"apocapoc-api/internal/domain/repositories"
"apocapoc-api/internal/shared/pagination"
"context"
"testing"
@@ -345,3 +346,11 @@ func (m *mockVerifyEmailUserRepo) FindActiveByUserIDWithPagination(ctx context.C
func (m *mockVerifyEmailUserRepo) CountActiveByUserID(ctx context.Context, userID string) (int, error) {
return 0, nil
}
func (m *mockVerifyEmailUserRepo) FindByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter, paginationParams *pagination.Params) ([]*entities.Habit, error) {
return nil, nil
}
func (m *mockVerifyEmailUserRepo) CountByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter) (int, error) {
return 0, nil
}