diff --git a/internal/api/tickers.go b/internal/api/tickers.go index 15b939b7..e9dea32f 100644 --- a/internal/api/tickers.go +++ b/internal/api/tickers.go @@ -20,7 +20,8 @@ func (h *handler) GetTickers(c *gin.Context) { return } - tickers, err := h.storage.FindTickersByUser(me) + filter := storage.NewTickerFilter(c.Request) + tickers, err := h.storage.FindTickersByUser(me, filter) if err != nil { c.JSON(http.StatusNotFound, response.ErrorResponse(response.CodeDefault, response.TickerNotFound)) return diff --git a/internal/api/tickers_test.go b/internal/api/tickers_test.go index 49a9b9db..c231297f 100644 --- a/internal/api/tickers_test.go +++ b/internal/api/tickers_test.go @@ -55,7 +55,7 @@ func (s *TickerTestSuite) TestGetTickers() { s.Run("when storage returns an error", func() { s.ctx.Set("me", storage.User{IsSuperAdmin: true}) - s.store.On("FindTickersByUser", mock.Anything, mock.Anything).Return([]storage.Ticker{}, errors.New("storage error")).Once() + s.store.On("FindTickersByUser", mock.Anything, mock.Anything, mock.Anything).Return([]storage.Ticker{}, errors.New("storage error")).Once() h := s.handler() h.GetTickers(s.ctx) @@ -65,7 +65,7 @@ func (s *TickerTestSuite) TestGetTickers() { s.Run("when storage returns tickers", func() { s.ctx.Set("me", storage.User{IsSuperAdmin: true}) - s.store.On("FindTickersByUser", mock.Anything, mock.Anything).Return([]storage.Ticker{}, nil).Once() + s.store.On("FindTickersByUser", mock.Anything, mock.Anything, mock.Anything).Return([]storage.Ticker{}, nil).Once() h := s.handler() h.GetTickers(s.ctx) diff --git a/internal/storage/mock_Storage.go b/internal/storage/mock_Storage.go index 556613bb..fe6f0c9c 100644 --- a/internal/storage/mock_Storage.go +++ b/internal/storage/mock_Storage.go @@ -394,42 +394,6 @@ func (_m *MockStorage) FindTickerByUserAndID(user User, id int, opts ...func(*go return r0, r1 } -// FindTickers provides a mock function with given fields: opts -func (_m *MockStorage) FindTickers(opts ...func(*gorm.DB) *gorm.DB) ([]Ticker, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for FindTickers") - } - - var r0 []Ticker - var r1 error - if rf, ok := ret.Get(0).(func(...func(*gorm.DB) *gorm.DB) ([]Ticker, error)); ok { - return rf(opts...) - } - if rf, ok := ret.Get(0).(func(...func(*gorm.DB) *gorm.DB) []Ticker); ok { - r0 = rf(opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]Ticker) - } - } - - if rf, ok := ret.Get(1).(func(...func(*gorm.DB) *gorm.DB) error); ok { - r1 = rf(opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // FindTickersByIDs provides a mock function with given fields: ids, opts func (_m *MockStorage) FindTickersByIDs(ids []int, opts ...func(*gorm.DB) *gorm.DB) ([]Ticker, error) { _va := make([]interface{}, len(opts)) @@ -467,14 +431,14 @@ func (_m *MockStorage) FindTickersByIDs(ids []int, opts ...func(*gorm.DB) *gorm. return r0, r1 } -// FindTickersByUser provides a mock function with given fields: user, opts -func (_m *MockStorage) FindTickersByUser(user User, opts ...func(*gorm.DB) *gorm.DB) ([]Ticker, error) { +// FindTickersByUser provides a mock function with given fields: user, filter, opts +func (_m *MockStorage) FindTickersByUser(user User, filter TickerFilter, opts ...func(*gorm.DB) *gorm.DB) ([]Ticker, error) { _va := make([]interface{}, len(opts)) for _i := range opts { _va[_i] = opts[_i] } var _ca []interface{} - _ca = append(_ca, user) + _ca = append(_ca, user, filter) _ca = append(_ca, _va...) ret := _m.Called(_ca...) @@ -484,19 +448,19 @@ func (_m *MockStorage) FindTickersByUser(user User, opts ...func(*gorm.DB) *gorm var r0 []Ticker var r1 error - if rf, ok := ret.Get(0).(func(User, ...func(*gorm.DB) *gorm.DB) ([]Ticker, error)); ok { - return rf(user, opts...) + if rf, ok := ret.Get(0).(func(User, TickerFilter, ...func(*gorm.DB) *gorm.DB) ([]Ticker, error)); ok { + return rf(user, filter, opts...) } - if rf, ok := ret.Get(0).(func(User, ...func(*gorm.DB) *gorm.DB) []Ticker); ok { - r0 = rf(user, opts...) + if rf, ok := ret.Get(0).(func(User, TickerFilter, ...func(*gorm.DB) *gorm.DB) []Ticker); ok { + r0 = rf(user, filter, opts...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]Ticker) } } - if rf, ok := ret.Get(1).(func(User, ...func(*gorm.DB) *gorm.DB) error); ok { - r1 = rf(user, opts...) + if rf, ok := ret.Get(1).(func(User, TickerFilter, ...func(*gorm.DB) *gorm.DB) error); ok { + r1 = rf(user, filter, opts...) } else { r1 = ret.Error(1) } diff --git a/internal/storage/sql_storage.go b/internal/storage/sql_storage.go index b444bee5..c44e620e 100644 --- a/internal/storage/sql_storage.go +++ b/internal/storage/sql_storage.go @@ -107,17 +107,23 @@ func (s *SqlStorage) AddTickerUser(ticker *Ticker, user *User) error { return err } -func (s *SqlStorage) FindTickers(opts ...func(*gorm.DB) *gorm.DB) ([]Ticker, error) { +func (s *SqlStorage) FindTickersByUser(user User, filter TickerFilter, opts ...func(*gorm.DB) *gorm.DB) ([]Ticker, error) { tickers := make([]Ticker, 0) db := s.prepareDb(opts...) - err := db.Find(&tickers).Error - return tickers, err -} + if filter.Active != nil { + db = db.Where("active = ?", *filter.Active) + } -func (s *SqlStorage) FindTickersByUser(user User, opts ...func(*gorm.DB) *gorm.DB) ([]Ticker, error) { - tickers := make([]Ticker, 0) - db := s.prepareDb(opts...) + if filter.Domain != nil { + db = db.Where("domain LIKE ?", fmt.Sprintf("%%%s%%", *filter.Domain)) + } + + if filter.Title != nil { + db = db.Where("title LIKE ?", fmt.Sprintf("%%%s%%", *filter.Title)) + } + + db = db.Order(fmt.Sprintf("%s %s", filter.OrderBy, filter.Sort)) var err error if user.IsSuperAdmin { diff --git a/internal/storage/sql_storage_test.go b/internal/storage/sql_storage_test.go index 5d217914..9ee89439 100644 --- a/internal/storage/sql_storage_test.go +++ b/internal/storage/sql_storage_test.go @@ -355,23 +355,6 @@ func (s *SqlStorageTestSuite) TestAddTickerUser() { s.Equal(int64(1), count) } -func (s *SqlStorageTestSuite) TestFindTickers() { - s.Run("when no tickers exist", func() { - tickers, err := s.store.FindTickers() - s.NoError(err) - s.Empty(tickers) - }) - - s.Run("when tickers exist", func() { - err := s.db.Create(&Ticker{ID: 1}).Error - s.NoError(err) - - tickers, err := s.store.FindTickers() - s.NoError(err) - s.Len(tickers, 1) - }) -} - func (s *SqlStorageTestSuite) TestFindTickerByID() { s.Run("when ticker does not exist", func() { _, err := s.store.FindTickerByID(1) @@ -464,7 +447,8 @@ func (s *SqlStorageTestSuite) TestFindTickerByDomain() { func (s *SqlStorageTestSuite) TestFindTickersByUser() { s.Run("when no tickers exist", func() { - tickers, err := s.store.FindTickersByUser(User{ID: 1}) + filter := TickerFilter{OrderBy: "id", Sort: "desc"} + tickers, err := s.store.FindTickersByUser(User{ID: 1}, filter) s.NoError(err) s.Empty(tickers) }) @@ -473,27 +457,62 @@ func (s *SqlStorageTestSuite) TestFindTickersByUser() { err := s.db.Create(&user).Error s.NoError(err) - ticker := Ticker{Users: []User{user}} + ticker := Ticker{Users: []User{user}, Active: false, Domain: "localhost", Title: "title"} err = s.db.Create(&ticker).Error s.NoError(err) s.Run("when tickers exist", func() { - tickers, err := s.store.FindTickersByUser(user) + filter := TickerFilter{OrderBy: "id", Sort: "desc"} + tickers, err := s.store.FindTickersByUser(user, filter) s.NoError(err) s.Len(tickers, 1) }) s.Run("when tickers exist with preload", func() { - tickers, err := s.store.FindTickersByUser(user, WithPreload()) + filter := TickerFilter{OrderBy: "id", Sort: "desc"} + tickers, err := s.store.FindTickersByUser(user, filter, WithPreload()) s.NoError(err) s.Len(tickers, 1) s.Len(tickers[0].Users, 1) }) s.Run("when super admin", func() { - tickers, err := s.store.FindTickersByUser(User{IsSuperAdmin: true}) + filter := TickerFilter{OrderBy: "id", Sort: "desc"} + tickers, err := s.store.FindTickersByUser(User{IsSuperAdmin: true}, filter) + s.NoError(err) + s.Len(tickers, 1) + }) + + s.Run("when filter is set", func() { + active := true + filter := TickerFilter{OrderBy: "id", Sort: "desc", Active: &active} + tickers, err := s.store.FindTickersByUser(user, filter) + s.NoError(err) + s.Empty(tickers) + + active = false + filter = TickerFilter{OrderBy: "id", Sort: "desc", Active: &active} + tickers, err = s.store.FindTickersByUser(user, filter) s.NoError(err) s.Len(tickers, 1) + + title := "title" + filter = TickerFilter{OrderBy: "id", Sort: "desc", Title: &title} + tickers, err = s.store.FindTickersByUser(user, filter) + s.NoError(err) + s.Len(tickers, 1) + + domain := "localhost" + filter = TickerFilter{OrderBy: "id", Sort: "desc", Domain: &domain} + tickers, err = s.store.FindTickersByUser(user, filter) + s.NoError(err) + s.Len(tickers, 1) + + domain = "systemli.org" + filter = TickerFilter{OrderBy: "id", Sort: "desc", Domain: &domain} + tickers, err = s.store.FindTickersByUser(user, filter) + s.NoError(err) + s.Empty(tickers) }) } diff --git a/internal/storage/storage.go b/internal/storage/storage.go index e00da2cf..cf97e217 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -19,8 +19,7 @@ type Storage interface { DeleteTickerUsers(ticker *Ticker) error DeleteTickerUser(ticker *Ticker, user *User) error AddTickerUser(ticker *Ticker, user *User) error - FindTickers(opts ...func(*gorm.DB) *gorm.DB) ([]Ticker, error) - FindTickersByUser(user User, opts ...func(*gorm.DB) *gorm.DB) ([]Ticker, error) + FindTickersByUser(user User, filter TickerFilter, opts ...func(*gorm.DB) *gorm.DB) ([]Ticker, error) FindTickerByUserAndID(user User, id int, opts ...func(*gorm.DB) *gorm.DB) (Ticker, error) FindTickersByIDs(ids []int, opts ...func(*gorm.DB) *gorm.DB) ([]Ticker, error) FindTickerByDomain(domain string, opts ...func(*gorm.DB) *gorm.DB) (Ticker, error) diff --git a/internal/storage/ticker.go b/internal/storage/ticker.go index 680bf89a..b7a0229d 100644 --- a/internal/storage/ticker.go +++ b/internal/storage/ticker.go @@ -1,6 +1,8 @@ package storage import ( + "net/http" + "strconv" "time" ) @@ -118,3 +120,53 @@ type TickerLocation struct { Lat float64 Lon float64 } + +type TickerFilter struct { + Domain *string + Title *string + Active *bool + OrderBy string + Sort string +} + +func NewTickerFilter(req *http.Request) TickerFilter { + filter := TickerFilter{ + OrderBy: "id", + Sort: "asc", + } + + if req == nil { + return filter + } + + if req.URL.Query().Get("order_by") != "" { + opts := []string{"id", "created_at", "updated_at", "domain", "title", "active"} + for _, opt := range opts { + if req.URL.Query().Get("order_by") == opt { + filter.OrderBy = req.URL.Query().Get("order_by") + break + } + } + } + if req.URL.Query().Get("sort") != "" { + filter.Sort = req.URL.Query().Get("sort") + } + + domain := req.URL.Query().Get("domain") + if domain != "" { + filter.Domain = &domain + } + + title := req.URL.Query().Get("title") + if title != "" { + filter.Title = &title + } + + active := req.URL.Query().Get("active") + if active != "" { + activeBool, _ := strconv.ParseBool(active) + filter.Active = &activeBool + } + + return filter +} diff --git a/internal/storage/ticker_test.go b/internal/storage/ticker_test.go index d59e84b4..2308b591 100644 --- a/internal/storage/ticker_test.go +++ b/internal/storage/ticker_test.go @@ -1,6 +1,7 @@ package storage import ( + "net/http/httptest" "testing" "github.com/stretchr/testify/assert" @@ -42,3 +43,27 @@ func TestTickerReset(t *testing.T) { assert.Empty(t, ticker.Telegram.ChannelName) assert.Empty(t, ticker.Location) } + +func TestNewTickerFilter(t *testing.T) { + filter := NewTickerFilter(nil) + assert.Nil(t, filter.Active) + assert.Nil(t, filter.Domain) + assert.Nil(t, filter.Title) + + req := httptest.NewRequest("GET", "/", nil) + filter = NewTickerFilter(req) + assert.Nil(t, filter.Active) + assert.Nil(t, filter.Domain) + assert.Nil(t, filter.Title) + + req = httptest.NewRequest("GET", "/?active=true&domain=example.org&title=Title", nil) + filter = NewTickerFilter(req) + assert.True(t, *filter.Active) + assert.Equal(t, "example.org", *filter.Domain) + assert.Equal(t, "Title", *filter.Title) + + req = httptest.NewRequest("GET", "/?order_by=created_at&sort=asc", nil) + filter = NewTickerFilter(req) + assert.Equal(t, "created_at", filter.OrderBy) + assert.Equal(t, "asc", filter.Sort) +}