From 1583f1b552b8b3946a6833523d5f467e365de0c4 Mon Sep 17 00:00:00 2001 From: "E.Muliukov" Date: Sat, 30 Mar 2024 15:16:31 +0300 Subject: [PATCH] Fixes #494. Display download count on file index --- e2e/download-history.spec.ts | 13 ++++++++++ e2e/upload.spec.ts | 2 +- handlers/templates/pages/file-index.html | 2 ++ picoshare/picoshare.go | 17 ++++++------ store/sqlite/entries.go | 33 +++++++++++++++++------- store/sqlite/entries_test.go | 4 +++ 6 files changed, 52 insertions(+), 19 deletions(-) diff --git a/e2e/download-history.spec.ts b/e2e/download-history.spec.ts index f04f51f7..0a10ab84 100644 --- a/e2e/download-history.spec.ts +++ b/e2e/download-history.spec.ts @@ -2,6 +2,7 @@ import { test, expect } from "@playwright/test"; import { login } from "./helpers/login.js"; const browserColumn = 3; +const downloadCountColumn = 4; test("upload a file and verify it has no download history", async ({ page, @@ -22,6 +23,12 @@ test("upload a file and verify it has no download history", async ({ await page.getByRole("menuitem", { name: "Files" }).click(); await expect(page).toHaveURL(/\/files$/); + const matchingRow = await page + .getByRole("row") + .filter({ hasText: "simple-upload.txt" }); + await expect(matchingRow.getByRole("cell").nth(downloadCountColumn)).toHaveText( + "0 times" + ); await page .getByRole("row") .filter({ hasText: "simple-upload.txt" }) @@ -69,6 +76,12 @@ test("upload a file, download it, and verify it has a download history", async ( await page.getByRole("menuitem", { name: "Files" }).click(); await expect(page).toHaveURL(/\/files$/); + const matchingRow = await page + .getByRole("row") + .filter({ hasText: "simple-upload.txt" }); + await expect(matchingRow.getByRole("cell").nth(downloadCountColumn)).toHaveText( + "1 times" + ); await page .getByRole("row") .filter({ hasText: "simple-upload.txt" }) diff --git a/e2e/upload.spec.ts b/e2e/upload.spec.ts index 21834d12..33a6e43b 100644 --- a/e2e/upload.spec.ts +++ b/e2e/upload.spec.ts @@ -2,7 +2,7 @@ import { test, expect } from "@playwright/test"; import { login } from "./helpers/login.js"; const noteColumn = 1; -const expirationColumn = 4; +const expirationColumn = 5; test("uploads a file without specifying any parameters", async ({ page, diff --git a/handlers/templates/pages/file-index.html b/handlers/templates/pages/file-index.html index 359bb9f2..4b4c36fa 100644 --- a/handlers/templates/pages/file-index.html +++ b/handlers/templates/pages/file-index.html @@ -77,6 +77,7 @@

Files

Note Size Uploaded + Downloaded Expires @@ -97,6 +98,7 @@

Files

{{ formatFileSize .Size }} {{ formatDate .Uploaded }} + {{ .DownloadCount }} times {{- formatExpiration .Expires -}} diff --git a/picoshare/picoshare.go b/picoshare/picoshare.go index 6f2d2dcd..be95add0 100644 --- a/picoshare/picoshare.go +++ b/picoshare/picoshare.go @@ -16,14 +16,15 @@ type ( } UploadMetadata struct { - ID EntryID - Filename Filename - Note FileNote - ContentType ContentType - Uploaded time.Time - Expires ExpirationTime - Size uint64 - GuestLink GuestLink + ID EntryID + Filename Filename + Note FileNote + ContentType ContentType + Uploaded time.Time + Expires ExpirationTime + Size uint64 + GuestLink GuestLink + DownloadCount uint64 } DownloadRecord struct { diff --git a/store/sqlite/entries.go b/store/sqlite/entries.go index 0b5f743c..83aa8baf 100644 --- a/store/sqlite/entries.go +++ b/store/sqlite/entries.go @@ -20,7 +20,8 @@ func (s Store) GetEntriesMetadata() ([]picoshare.UploadMetadata, error) { entries.content_type AS content_type, entries.upload_time AS upload_time, entries.expiration_time AS expiration_time, - sizes.file_size AS file_size + sizes.file_size AS file_size, + IFNULL(downloads.download_count, 0) AS download_count FROM entries INNER JOIN @@ -32,7 +33,17 @@ func (s Store) GetEntriesMetadata() ([]picoshare.UploadMetadata, error) { entries_data GROUP BY id - ) sizes ON entries.id = sizes.id`) + ) sizes ON entries.id = sizes.id + LEFT OUTER JOIN + ( + SELECT + entry_id, + COUNT (entry_id) as download_count + FROM + downloads + GROUP BY + entry_id + ) downloads ON entries.id = downloads.entry_id`) if err != nil { return []picoshare.UploadMetadata{}, err } @@ -46,7 +57,8 @@ func (s Store) GetEntriesMetadata() ([]picoshare.UploadMetadata, error) { var uploadTimeRaw string var expirationTimeRaw string var fileSize uint64 - if err = rows.Scan(&id, &filename, ¬e, &contentType, &uploadTimeRaw, &expirationTimeRaw, &fileSize); err != nil { + var downloadCount uint64 + if err = rows.Scan(&id, &filename, ¬e, &contentType, &uploadTimeRaw, &expirationTimeRaw, &fileSize, &downloadCount); err != nil { return []picoshare.UploadMetadata{}, err } @@ -61,13 +73,14 @@ func (s Store) GetEntriesMetadata() ([]picoshare.UploadMetadata, error) { } ee = append(ee, picoshare.UploadMetadata{ - ID: picoshare.EntryID(id), - Filename: picoshare.Filename(filename), - Note: picoshare.FileNote{Value: note}, - ContentType: picoshare.ContentType(contentType), - Uploaded: ut, - Expires: picoshare.ExpirationTime(et), - Size: fileSize, + ID: picoshare.EntryID(id), + Filename: picoshare.Filename(filename), + Note: picoshare.FileNote{Value: note}, + ContentType: picoshare.ContentType(contentType), + Uploaded: ut, + Expires: picoshare.ExpirationTime(et), + Size: fileSize, + DownloadCount: downloadCount, }) } diff --git a/store/sqlite/entries_test.go b/store/sqlite/entries_test.go index 84e6a461..3445f413 100644 --- a/store/sqlite/entries_test.go +++ b/store/sqlite/entries_test.go @@ -51,6 +51,10 @@ func TestInsertDeleteSingleEntry(t *testing.T) { t.Fatalf("unexpected file size in entry metadata: got %v, want %v", meta[0].Size, len(expected)) } + if meta[0].DownloadCount != 0 { + t.Fatalf("unexpected download count in entry metadata: got %v, want %v", meta[0].DownloadCount, 0) + } + expectedFilename := picoshare.Filename("dummy-file.txt") if meta[0].Filename != expectedFilename { t.Fatalf("unexpected filename: got %v, want %v", meta[0].Filename, expectedFilename)