diff --git a/cmd/server/main.go b/cmd/server/main.go index 102ac7d..3110260 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -35,6 +35,7 @@ func main() { r.HandleFunc("/pagination/limitoffset/offset", pagination.HandleLimitOffsetOffset).Methods(http.MethodGet, http.MethodPut) r.HandleFunc("/pagination/cursor", pagination.HandleCursor).Methods(http.MethodGet, http.MethodPut) r.HandleFunc("/pagination/url", pagination.HandleURL).Methods(http.MethodGet) + r.HandleFunc("/pagination/cursor_non_numeric", pagination.HandleNonNumericCursor).Methods(http.MethodGet) r.HandleFunc("/retries", retries.HandleRetries).Methods(http.MethodGet, http.MethodPost) r.HandleFunc("/retries/after", retries.HandleRetries).Methods(http.MethodGet) r.HandleFunc("/errors/{status_code}", errors.HandleErrors).Methods(http.MethodGet) diff --git a/internal/pagination/service.go b/internal/pagination/service.go index d30d8e5..e12b6c9 100644 --- a/internal/pagination/service.go +++ b/internal/pagination/service.go @@ -18,10 +18,22 @@ type CursorRequest struct { Cursor int `json:"cursor"` } +type NonNumericCursorRequest struct { + Cursor string `json:"cursor"` +} + type PaginationResponse struct { - NumPages int `json:"numPages"` - ResultArray []int `json:"resultArray"` - Next *string `json:"next,omitempty"` + NumPages int `json:"numPages"` + ResultArray []interface{} `json:"resultArray"` + Next *string `json:"next,omitempty"` +} + +// Insecure reversable hashing for string cursors +func hash(s string) (int, error) { + return strconv.Atoi(s) +} +func unhash(h int) string { + return strconv.Itoa(h) } const total = 20 @@ -45,7 +57,7 @@ func HandleLimitOffsetPage(w http.ResponseWriter, r *http.Request) { res := PaginationResponse{ NumPages: int(math.Ceil(float64(total) / float64(limit))), - ResultArray: make([]int, 0), + ResultArray: make([]interface{}, 0), } for i := start; i < total && len(res.ResultArray) < limit; i++ { @@ -77,7 +89,7 @@ func HandleLimitOffsetOffset(w http.ResponseWriter, r *http.Request) { res := PaginationResponse{ NumPages: int(math.Ceil(float64(total) / float64(limit))), - ResultArray: make([]int, 0), + ResultArray: make([]interface{}, 0), } for i := offset; i < total && len(res.ResultArray) < limit; i++ { @@ -104,7 +116,7 @@ func HandleCursor(w http.ResponseWriter, r *http.Request) { res := PaginationResponse{ NumPages: 0, - ResultArray: make([]int, 0), + ResultArray: make([]interface{}, 0), } for i := cursor + 1; i < total && len(res.ResultArray) < 15; i++ { @@ -134,7 +146,7 @@ func HandleURL(w http.ResponseWriter, r *http.Request) { res := PaginationResponse{ NumPages: 0, - ResultArray: make([]int, 0), + ResultArray: make([]interface{}, 0), } if attempts > 1 { @@ -160,6 +172,31 @@ func HandleURL(w http.ResponseWriter, r *http.Request) { } } +func HandleNonNumericCursor(w http.ResponseWriter, r *http.Request) { + queryCursor := r.FormValue("cursor") + var pagination NonNumericCursorRequest + hasBody := true + if err := json.NewDecoder(r.Body).Decode(&pagination); err != nil { + hasBody = false + } + cursor := getNonNumericValue(queryCursor, hasBody, pagination.Cursor) + + res := PaginationResponse{ + NumPages: 0, + ResultArray: make([]interface{}, 0), + } + var cursorI, _ = hash(cursor) + for i := cursorI + 1; i < total && len(res.ResultArray) < 15; i++ { + res.ResultArray = append(res.ResultArray, unhash(i)) + } + + w.Header().Set("Content-Type", "application/json") + err := json.NewEncoder(w).Encode(res) + if err != nil { + w.WriteHeader(500) + } +} + func getValue(queryValue string, hasBody bool, paginationValue int) int { if hasBody { return paginationValue @@ -171,3 +208,15 @@ func getValue(queryValue string, hasBody bool, paginationValue int) int { return value } } + +func getNonNumericValue(queryValue string, hasBody bool, paginationValue string) string { + if hasBody { + return paginationValue + } else { + if queryValue == "" { + return "-1" + } else { + return queryValue + } + } +}