Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/httpapi/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -6898,6 +6898,7 @@ func (s *Server) handleGetSessions(w http.ResponseWriter, r *http.Request) {
s.writeError(w, r, http.StatusBadRequest, err.Error())
return
}
sessionFilter.Offset = offset

// Get recent sessions from controller
sessions, total, err := s.controller.GetRecentSessions(sessionFilter)
Expand Down
12 changes: 12 additions & 0 deletions internal/httpapi/sessions_scope_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,15 @@ func TestSessionsScope_RowsCarryAttributionAndFiltersReachStorage(t *testing.T)
assert.Contains(t, w.Body.String(), "client_name is not supported on this endpoint; filter by client")
assert.Zero(t, ctrl.callCount, "the check runs before any storage read")
}

func TestSessions_OffsetReachesStorage(t *testing.T) {
ctrl := &scopedSessionController{mockSessionController{apiKey: "test-key", sessions: scopeTestSessions()}}
srv := NewServer(ctrl, zap.NewNop().Sugar(), nil)
req := httptest.NewRequest(http.MethodGet, "/api/v1/sessions?limit=1&offset=2", nil)
req.Header.Set("X-API-Key", "test-key")
w := httptest.NewRecorder()
srv.ServeHTTP(w, req)

require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, 2, ctrl.gotFilter.Offset, "offset query param must be passed to the session filter")
}
11 changes: 7 additions & 4 deletions internal/storage/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -1532,6 +1532,7 @@ type SessionRecord struct {
// TokenName are the credential at initialize.
type SessionFilter struct {
Limit int
Offset int // matching sessions to skip, applied after sort and filter
Status string
Profile string
ClientID string
Expand Down Expand Up @@ -1747,6 +1748,7 @@ func (m *Manager) GetRecentSessions(limit int, status string) ([]*SessionRecord,
// never truncated out by a page of newer non-matching ones.
func (m *Manager) GetRecentSessionsFiltered(f SessionFilter) ([]*SessionRecord, int, error) {
limit := f.Limit
offset := max(f.Offset, 0)
m.mu.RLock()
defer m.mu.RUnlock()

Expand Down Expand Up @@ -1792,17 +1794,18 @@ func (m *Manager) GetRecentSessionsFiltered(f SessionFilter) ([]*SessionRecord,
continue
}
total++
if len(sessions) < limit {
if total > offset && len(sessions) < limit {
sessions = append(sessions, session)
}
}
return sessions, total, nil
}

if limit < len(all) {
return all[:limit], len(all), nil
page := all[min(offset, len(all)):]
if limit < len(page) {
page = page[:limit]
}
return all, len(all), nil
return page, len(all), nil
}

// GetSessionByID retrieves a session by its ID
Expand Down
29 changes: 29 additions & 0 deletions internal/storage/sessions_offset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package storage

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestGetRecentSessionsFiltered_Offset(t *testing.T) {
m, cleanup := setupTestStorageForActivity(t)
defer cleanup()
seedScopeSessions(t, m) // newest first: s-cursor2, s-zed, s-api, s-old-cursor

got, total, err := m.GetRecentSessionsFiltered(SessionFilter{Limit: 2, Offset: 1})
require.NoError(t, err)
assert.Equal(t, 4, total)
assert.Equal(t, []string{"s-zed", "s-api"}, sessionIDs(got))

got, total, err = m.GetRecentSessionsFiltered(SessionFilter{Limit: 10, Offset: 1, ClientID: "cursor"})
require.NoError(t, err)
assert.Equal(t, 2, total, "total stays the filtered count")
assert.Equal(t, []string{"s-old-cursor"}, sessionIDs(got))

got, total, err = m.GetRecentSessionsFiltered(SessionFilter{Limit: 10, Offset: 9})
require.NoError(t, err)
assert.Equal(t, 4, total)
assert.Empty(t, got)
}
Loading