Skip to content

Commit e5b2445

Browse files
authored
Merge pull request #35 from YASSERRMD/phase_2
phase 2: paginated GET /v1/receipts listing endpoint
2 parents 7b10e87 + a6bdb9b commit e5b2445

4 files changed

Lines changed: 54 additions & 0 deletions

File tree

internal/api/ingest.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"errors"
88
"log"
99
"net/http"
10+
"strconv"
1011
"time"
1112

1213
"github.com/google/uuid"
@@ -97,6 +98,47 @@ func (s *Server) handleIngestBatch(w http.ResponseWriter, r *http.Request) {
9798
writeJSON(w, http.StatusCreated, map[string]any{"count": len(receipts), "receipts": receipts})
9899
}
99100

101+
// ListReceiptsResponse is returned by GET /v1/receipts.
102+
type ListReceiptsResponse struct {
103+
Receipts []vur.Receipt `json:"receipts"`
104+
Offset int `json:"offset"`
105+
Limit int `json:"limit"`
106+
Count int `json:"count"`
107+
}
108+
109+
func (s *Server) handleListReceipts(w http.ResponseWriter, r *http.Request) {
110+
const defaultLimit = 50
111+
const maxLimit = 500
112+
113+
offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))
114+
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
115+
if limit <= 0 {
116+
limit = defaultLimit
117+
}
118+
if limit > maxLimit {
119+
limit = maxLimit
120+
}
121+
if offset < 0 {
122+
offset = 0
123+
}
124+
125+
stored, err := s.store.ListReceipts(offset, limit)
126+
if err != nil {
127+
writeError(w, http.StatusInternalServerError, "list receipts: "+err.Error())
128+
return
129+
}
130+
receipts := make([]vur.Receipt, len(stored))
131+
for i, sr := range stored {
132+
receipts[i] = sr.Receipt
133+
}
134+
writeJSON(w, http.StatusOK, ListReceiptsResponse{
135+
Receipts: receipts,
136+
Offset: offset,
137+
Limit: limit,
138+
Count: len(receipts),
139+
})
140+
}
141+
100142
func (s *Server) handleGetReceipt(w http.ResponseWriter, r *http.Request) {
101143
id := r.PathValue("id")
102144
sr, err := s.store.GetReceiptByID(id)

internal/api/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ func (s *Server) Routes() *http.ServeMux {
114114
// Receipt routes
115115
mux.HandleFunc("POST /v1/receipts", s.handleIngest)
116116
mux.HandleFunc("POST /v1/receipts/batch", s.handleIngestBatch)
117+
mux.HandleFunc("GET /v1/receipts", s.handleListReceipts)
117118
mux.HandleFunc("GET /v1/receipts/{id}", s.handleGetReceipt)
118119
mux.HandleFunc("GET /v1/receipts/{id}/proof", s.handleInclusionProof)
119120
mux.HandleFunc("GET /v1/receipts/{id}/attestation", s.handleAttestation)

internal/store/interface.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ type Store interface {
7272
// Receipt operations
7373
AppendReceipt(r vur.Receipt) (int64, error)
7474
GetReceiptByID(id string) (*StoredReceipt, error)
75+
ListReceipts(offset, limit int) ([]StoredReceipt, error)
7576
FetchUnbatched(limit int) ([]StoredReceipt, error)
7677
MarkBatched(ids []int64) error
7778
GetReceiptsByRange(startID, endID int64) ([]vur.Receipt, error)

internal/store/sqlite.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,16 @@ func (s *SQLiteStore) GetReceiptByID(id string) (*StoredReceipt, error) {
6262
return &StoredReceipt{RowID: rowID, Receipt: r}, nil
6363
}
6464

65+
func (s *SQLiteStore) ListReceipts(offset, limit int) ([]StoredReceipt, error) {
66+
rows, err := s.db.Query(
67+
`SELECT id, receipt_json FROM receipts ORDER BY id DESC LIMIT ? OFFSET ?`, limit, offset)
68+
if err != nil {
69+
return nil, err
70+
}
71+
defer rows.Close()
72+
return scanStoredReceipts(rows)
73+
}
74+
6575
func (s *SQLiteStore) FetchUnbatched(limit int) ([]StoredReceipt, error) {
6676
rows, err := s.db.Query(
6777
`SELECT id, receipt_json FROM receipts WHERE batched=0 ORDER BY id ASC LIMIT ?`, limit)

0 commit comments

Comments
 (0)