Filter remote object listings by prefix #129

Merged
benvin merged 2 commits from benvin/objects-prefix-filter into master 2026-10-04 22:07:27 +11:00
3 changed files with 16 additions and 8 deletions
Showing only changes of commit f0c27d4e07 - Show all commits
+1 -1
View File
@@ -51,7 +51,7 @@ func (h *ObjectsHandler) list(w http.ResponseWriter, r *http.Request) {
remoteName := chi.URLParam(r, "name")
limit, offset := pageBounds(r)
artifacts, err := h.db.ListArtifacts(r.Context(), remoteName, limit, offset)
artifacts, err := h.db.ListArtifacts(r.Context(), remoteName, r.URL.Query().Get("prefix"), limit, offset)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
+5 -4
View File
@@ -65,7 +65,8 @@ func (db *DB) TouchArtifactAccess(ctx context.Context, remoteName, path string)
return err
}
func (db *DB) ListArtifacts(ctx context.Context, remoteName string, limit, offset int) ([]models.Artifact, error) {
// ListArtifacts pages a remote's artifacts whose path starts with prefix ("" lists all).
func (db *DB) ListArtifacts(ctx context.Context, remoteName, prefix string, limit, offset int) ([]models.Artifact, error) {
rows, err := db.Pool.Query(ctx, `
SELECT a.id, a.remote_name, a.path, a.content_hash, a.upstream_etag,
a.upstream_last_modified, a.first_seen_at, a.last_fetched_at,
@@ -73,10 +74,10 @@ func (db *DB) ListArtifacts(ctx context.Context, remoteName string, limit, offse
b.size_bytes, b.content_type
FROM artifacts a
JOIN blobs b ON a.content_hash = b.content_hash
WHERE a.remote_name = $1
WHERE a.remote_name = $1 AND left(a.path, length($2)) = $2
ORDER BY a.path
LIMIT $2 OFFSET $3
`, remoteName, limit, offset)
LIMIT $3 OFFSET $4
`, remoteName, prefix, limit, offset)
if err != nil {
return nil, err
}
+10 -3
View File
@@ -168,10 +168,17 @@ func TestArtifactsAndBlobs(t *testing.T) {
if err := testDB.TouchArtifactAccess(ctx(), "r-art", "path/a.txt"); err != nil {
t.Fatal(err)
}
arts, err := testDB.ListArtifacts(ctx(), "r-art", 10, 0)
if err != nil || len(arts) != 1 {
if err := testDB.UpsertArtifact(ctx(), "r-art", "other/b.txt", hash, ""); err != nil {
t.Fatal(err)
}
arts, err := testDB.ListArtifacts(ctx(), "r-art", "", 10, 0)
if err != nil || len(arts) != 2 {
t.Fatalf("list artifacts: %v %v", len(arts), err)
}
arts, err = testDB.ListArtifacts(ctx(), "r-art", "path/", 10, 0)
if err != nil || len(arts) != 1 || arts[0].Path != "path/a.txt" {
t.Fatalf("list artifacts with prefix: %+v %v", arts, err)
}
if err := testDB.InsertAccessLog(ctx(), "r-art", "path/a.txt", true, 10, 5, "1.2.3.4"); err != nil {
t.Fatal(err)
}
@@ -320,7 +327,7 @@ func TestDatabaseErrorPaths(t *testing.T) {
if _, err := bad.ListVirtuals(ctx); err == nil {
t.Error("ListVirtuals should error")
}
if _, err := bad.ListArtifacts(ctx, "r", 10, 0); err == nil {
if _, err := bad.ListArtifacts(ctx, "r", "", 10, 0); err == nil {
t.Error("ListArtifacts should error")
}
if _, err := bad.ListLocalFiles(ctx, "r", 10, 0); err == nil {