Skip to content

Commit 86baed2

Browse files
committed
add: pagination integrated with frontend
1 parent 9060774 commit 86baed2

File tree

9 files changed

+211
-188
lines changed

9 files changed

+211
-188
lines changed

backend/cmd/pagination.go

Lines changed: 0 additions & 163 deletions
This file was deleted.

backend/cmd/server.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,15 @@ func GetContextKeyAndKContext(w http.ResponseWriter,
150150
return ctx, span, contextKey, kContext, nil
151151
}
152152

153+
func shouldSkipCache(path string) bool {
154+
return strings.Contains(path, "version") ||
155+
strings.Contains(path, "selfsubjectrulesreview") ||
156+
strings.Contains(path, "selfsubjectaccessreviews")
157+
}
158+
153159
// CacheMiddleWare is Middleware for Caching purpose. It involves generating key for a request,
154160
// authorizing user , store resource data in cache and returns data if key is present.
155-
func CacheMiddleWare(c *HeadlampConfig) mux.MiddlewareFunc {
161+
func CacheMiddleWare(c *HeadlampConfig) mux.MiddlewareFunc { //no-lint:gocognit
156162
return func(next http.Handler) http.Handler {
157163
if !c.CacheEnabled {
158164
return next
@@ -163,6 +169,11 @@ func CacheMiddleWare(c *HeadlampConfig) mux.MiddlewareFunc {
163169
return
164170
}
165171

172+
if shouldSkipCache(r.URL.Path) {
173+
next.ServeHTTP(w, r)
174+
return
175+
}
176+
166177
ctx, span, contextKey, kContext, err := GetContextKeyAndKContext(w, r, c)
167178
if err != nil {
168179
return
@@ -198,7 +209,7 @@ func CacheMiddleWare(c *HeadlampConfig) mux.MiddlewareFunc {
198209
}
199210

200211
if served {
201-
c.telemetryHandler.RecordEvent(span, "Served from cache")
212+
c.telemetryHandler.RecordEvent(span, "served from cache")
202213
return
203214
}
204215

backend/pkg/k8cache/cacheStore.go

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ import (
2828
"io"
2929
"net/http"
3030
"net/url"
31+
"strconv"
3132
"strings"
33+
34+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
35+
3236
"time"
3337

3438
"github.com/kubernetes-sigs/headlamp/backend/pkg/cache"
@@ -38,9 +42,20 @@ import (
3842
// CachedResponseData stores information such as StatusCode, Headers, and Body.
3943
// It helps cache responses efficiently and serve them from the cache.
4044
type CachedResponseData struct {
41-
StatusCode int `json:"statusCode"`
42-
Headers http.Header `json:"headers"`
43-
Body string `json:"body"`
45+
StatusCode int `json:"statusCode"`
46+
Headers http.Header `json:"headers"`
47+
Body ResourceResponse `json:"body"`
48+
}
49+
50+
type Item struct {
51+
Metadata metav1.ObjectMeta `json:"metadata"`
52+
}
53+
54+
type ResourceResponse struct {
55+
Kind string `json:"kind"`
56+
Version string `json:"apiVersion"`
57+
Metadata metav1.ListMeta `json:"metadata"`
58+
Items []Item `json:"items"`
4459
}
4560

4661
// GetResponseBody decompresses a gzip-encoded response body and returns it as a string.
@@ -172,6 +187,32 @@ func FilterHeaderForCache(responseHeaders http.Header, encoding string) http.Hea
172187
return cacheHeader
173188
}
174189

190+
func sliceTheResponse(page int, pageSize int, sizeOfResponse int) (int, int) {
191+
start := (page - 1) * pageSize
192+
if start >= sizeOfResponse {
193+
return sizeOfResponse, sizeOfResponse // slice empty if page out of range
194+
}
195+
end := start + pageSize
196+
if end > sizeOfResponse {
197+
end = sizeOfResponse
198+
}
199+
return start, end
200+
}
201+
202+
func ReturnResponseToClient(rcw *ResponseCapture, v any, w http.ResponseWriter) error {
203+
var err error
204+
if rcw.Header().Get("Content-Encoding") == "gzip" {
205+
w.Header().Set("Content-Encoding", "gzip")
206+
gz := gzip.NewWriter(w)
207+
defer gz.Close()
208+
err = json.NewEncoder(gz).Encode(v)
209+
} else {
210+
err = json.NewEncoder(w).Encode(v)
211+
}
212+
213+
return err
214+
}
215+
175216
// LoadFromCache checks if a cached resource exists and the user has permission to view it.
176217
// If found, it writes the cached data to the ResponseWriter and returns (true, nil).
177218
// If not found or on error, it returns (false, error).
@@ -186,14 +227,30 @@ func LoadFromCache(k8scache cache.Cache[string], isAllowed bool,
186227
}
187228

188229
SetHeader(cachedData, w)
189-
_, writeErr := w.Write([]byte(cachedData.Body))
230+
w.Header().Set("Content-Type", "application/json")
231+
232+
// // 🔹 Always paginate, even for first request
233+
pageNo := r.URL.Query().Get("p")
234+
limit := 10
190235

191-
if writeErr != nil {
192-
return false, writeErr
236+
page, _ := strconv.Atoi(pageNo)
237+
238+
if page > 1 {
239+
start, end := sliceTheResponse(page, limit, len(cachedData.Body.Items))
240+
cachedData.Body.Items = cachedData.Body.Items[start:end]
241+
}
242+
243+
bodyBytes, err := json.Marshal(cachedData.Body)
244+
if err != nil {
245+
return false, err
193246
}
194247

195-
logger.Log(logger.LevelInfo, nil, nil, "serving from the cache with key "+key)
248+
if _, err := w.Write(bodyBytes); err != nil {
249+
// fmt.Println("since : ", time.Since(startNow))
250+
return false, err
251+
}
196252

253+
logger.Log(logger.LevelInfo, nil, nil, "serving from cache with key "+key)
197254
return true, nil
198255
}
199256

@@ -219,11 +276,18 @@ func StoreK8sResponseInCache(k8scache cache.Cache[string],
219276
}
220277

221278
headersToCache := FilterHeaderForCache(capturedHeaders, encoding)
279+
280+
var resourceBody ResourceResponse
281+
if err := json.Unmarshal([]byte(dcmpBody), &resourceBody); err != nil {
282+
logger.Log(logger.LevelError, nil, err, "failed to unmarshal resource response body")
283+
return err
284+
}
285+
222286
if !strings.Contains(url.Path, "selfsubjectrulesreviews") {
223287
cachedData := CachedResponseData{
224288
StatusCode: rcw.StatusCode,
225289
Headers: headersToCache,
226-
Body: dcmpBody,
290+
Body: resourceBody,
227291
}
228292

229293
jsonBytes, err := json.Marshal(cachedData)
@@ -239,6 +303,5 @@ func StoreK8sResponseInCache(k8scache cache.Cache[string],
239303
logger.Log(logger.LevelInfo, nil, nil, "k8s resource was stored with the key "+key)
240304
}
241305
}
242-
243306
return nil
244307
}

frontend/src/components/advancedSearch/ResourceSearch.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ export function ResourceSearch({
8181
const [isFocused, setIsFocused] = useState(false);
8282
const theme = useTheme();
8383
const monaco = useMonaco();
84+
85+
// console.log("resource: ", resources , "selectedClusters: ", selectedClusters , "maxItemsPerResource: ", maxItemsPerResource , "refreshIntervalMs: ", refetchIntervalMs)
8486
const {
8587
items: allItems,
8688
errors,
@@ -104,6 +106,8 @@ export function ResourceSearch({
104106

105107
searchWithQuery(allItems, deferredQuery, interruptRef).then(result => {
106108
if (interruptRef.current === false) {
109+
110+
console.log("results: ", result.results , "query : ", deferredQuery)
107111
setResults({ items: result.results, query: deferredQuery });
108112
}
109113
});

0 commit comments

Comments
 (0)