From 75cc26d5d19700a022bba36ddd8184a6cc687f1e Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sun, 8 Feb 2026 11:05:22 -0500 Subject: [PATCH] feat: update handlers to normalize tags and contributors Update CreateMediaItem handler: - Normalize tags for display using NormalizeTags() - Normalize contributors for display using NormalizeContributors() - Generate tags_search using NormalizeTagsSearch() - Generate contributors_search using NormalizeContributorsSearch() - Pass search fields to database Update UpdateMediaItem handler: - Same normalization logic as CreateMediaItem - Regenerate search fields on updates Update HandleBulkUpdate handler: - Add tag normalization with punctuation preference - Regenerate search fields when tags/contributors updated All handlers now populate both display and search fields, ensuring consistent normalization throughout the application. Relates to Tags & Contributors Migration Phase 6 --- internal/handlers/media.go | 129 ++++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 51 deletions(-) diff --git a/internal/handlers/media.go b/internal/handlers/media.go index 24d4234..9cbe148 100644 --- a/internal/handlers/media.go +++ b/internal/handlers/media.go @@ -467,26 +467,28 @@ func (h *MediaHandler) HandleBulkUpdate(c echo.Context) error { } updateParams := database.UpdateMediaItemParams{ - ID: bookUUID, - Title: existingBook.Title, - Author: existingBook.Author, - Genre: existingBook.Genre, - Language: existingBook.Language, - Tags: existingBook.Tags, - Description: existingBook.Description, - Publisher: existingBook.Publisher, - CopyrightYear: existingBook.CopyrightYear, - Isbn: existingBook.Isbn, - Series: existingBook.Series, - SeriesNumber: existingBook.SeriesNumber, - Asin: existingBook.Asin, - DatePublished: existingBook.DatePublished, - Contributors: existingBook.Contributors, - Edition: existingBook.Edition, - PageCount: existingBook.PageCount, - GoodreadsID: existingBook.GoodreadsID, - OpenlibraryID: existingBook.OpenlibraryID, - CoverImagePath: existingBook.CoverImagePath, + ID: bookUUID, + Title: existingBook.Title, + Author: existingBook.Author, + Genre: existingBook.Genre, + Language: existingBook.Language, + Tags: existingBook.Tags, + TagsSearch: existingBook.TagsSearch, + Description: existingBook.Description, + Publisher: existingBook.Publisher, + CopyrightYear: existingBook.CopyrightYear, + Isbn: existingBook.Isbn, + Series: existingBook.Series, + SeriesNumber: existingBook.SeriesNumber, + Asin: existingBook.Asin, + DatePublished: existingBook.DatePublished, + Contributors: existingBook.Contributors, + ContributorsSearch: existingBook.ContributorsSearch, + Edition: existingBook.Edition, + PageCount: existingBook.PageCount, + GoodreadsID: existingBook.GoodreadsID, + OpenlibraryID: existingBook.OpenlibraryID, + CoverImagePath: existingBook.CoverImagePath, } if update.Updates.Title != nil { @@ -502,7 +504,10 @@ func (h *MediaHandler) HandleBulkUpdate(c echo.Context) error { updateParams.Language = pgtype.Text{String: *update.Updates.Language, Valid: true} } if len(update.Updates.Tags) > 0 { - updateParams.Tags = update.Updates.Tags + normalizedTags := utils.NormalizeTags(update.Updates.Tags) + updateParams.Tags = normalizedTags + tagsSearch := utils.NormalizeTagsSearch(update.Updates.Tags) + updateParams.TagsSearch = tagsSearch } _, err = h.db.UpdateMediaItem(c.Request().Context(), updateParams) @@ -893,6 +898,13 @@ func (mh *MediaHandler) CreateMediaItem(c echo.Context) error { req.Tags = utils.NormalizeTags(req.Tags) } + if len(req.Contributors) > 0 { + req.Contributors = utils.NormalizeContributors(req.Contributors) + } + + tagsSearch := utils.NormalizeTagsSearch(req.Tags) + contributorsSearch := utils.NormalizeContributorsSearch(req.Contributors) + _, err := mh.db.GetLibrary(c.Request().Context(), pgtype.UUID{Bytes: req.LibraryID, Valid: true}) if err != nil { if err == pgx.ErrNoRows { @@ -902,23 +914,25 @@ func (mh *MediaHandler) CreateMediaItem(c echo.Context) error { } mediaItemID, err := mh.db.CreateMediaItem(c.Request().Context(), database.CreateMediaItemParams{ - LibraryID: pgtype.UUID{Bytes: req.LibraryID, Valid: true}, - Title: req.Title, - Author: pgtype.Text{String: req.Author, Valid: req.Author != ""}, - Isbn: pgtype.Text{String: req.ISBN, Valid: req.ISBN != ""}, - Description: pgtype.Text{String: req.Description, Valid: req.Description != ""}, - FilePath: req.FilePath, - FileSize: pgtype.Int8{Int64: req.FileSize, Valid: req.FileSize > 0}, - MimeType: pgtype.Text{String: req.MimeType, Valid: req.MimeType != ""}, - CoverImagePath: pgtype.Text{String: req.CoverImagePath, Valid: req.CoverImagePath != ""}, - Series: pgtype.Text{String: req.Series, Valid: req.Series != ""}, - SeriesNumber: pgtype.Int4{Int32: req.SeriesNumber, Valid: req.SeriesNumber > 0}, - Tags: req.Tags, - Asin: pgtype.Text{String: req.ASIN, Valid: req.ASIN != ""}, - DatePublished: pgtype.Date{Time: parseDate(req.DatePublished), Valid: req.DatePublished != ""}, - Publisher: pgtype.Text{String: req.Publisher, Valid: req.Publisher != ""}, - Contributors: req.Contributors, - AddedByAdminID: user.ID, + LibraryID: pgtype.UUID{Bytes: req.LibraryID, Valid: true}, + Title: req.Title, + Author: pgtype.Text{String: req.Author, Valid: req.Author != ""}, + Isbn: pgtype.Text{String: req.ISBN, Valid: req.ISBN != ""}, + Description: pgtype.Text{String: req.Description, Valid: req.Description != ""}, + FilePath: req.FilePath, + FileSize: pgtype.Int8{Int64: req.FileSize, Valid: req.FileSize > 0}, + MimeType: pgtype.Text{String: req.MimeType, Valid: req.MimeType != ""}, + CoverImagePath: pgtype.Text{String: req.CoverImagePath, Valid: req.CoverImagePath != ""}, + Series: pgtype.Text{String: req.Series, Valid: req.Series != ""}, + SeriesNumber: pgtype.Int4{Int32: req.SeriesNumber, Valid: req.SeriesNumber > 0}, + Tags: req.Tags, + TagsSearch: tagsSearch, + Asin: pgtype.Text{String: req.ASIN, Valid: req.ASIN != ""}, + DatePublished: pgtype.Date{Time: parseDate(req.DatePublished), Valid: req.DatePublished != ""}, + Publisher: pgtype.Text{String: req.Publisher, Valid: req.Publisher != ""}, + Contributors: req.Contributors, + ContributorsSearch: contributorsSearch, + AddedByAdminID: user.ID, }) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()}) @@ -954,20 +968,33 @@ func (mh *MediaHandler) UpdateMediaItem(c echo.Context) error { return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()}) } + if len(req.Tags) > 0 { + req.Tags = utils.NormalizeTags(req.Tags) + } + + if len(req.Contributors) > 0 { + req.Contributors = utils.NormalizeContributors(req.Contributors) + } + + tagsSearch := utils.NormalizeTagsSearch(req.Tags) + contributorsSearch := utils.NormalizeContributorsSearch(req.Contributors) + item, err := mh.db.UpdateMediaItem(c.Request().Context(), database.UpdateMediaItemParams{ - ID: pgtype.UUID{Bytes: mediaUUID, Valid: true}, - Title: req.Title, - Author: pgtype.Text{String: req.Author, Valid: req.Author != ""}, - Isbn: pgtype.Text{String: req.ISBN, Valid: req.ISBN != ""}, - Description: pgtype.Text{String: req.Description, Valid: req.Description != ""}, - CoverImagePath: pgtype.Text{String: req.CoverImagePath, Valid: req.CoverImagePath != ""}, - Series: pgtype.Text{String: req.Series, Valid: req.Series != ""}, - SeriesNumber: pgtype.Int4{Int32: req.SeriesNumber, Valid: req.SeriesNumber > 0}, - Tags: req.Tags, - Asin: pgtype.Text{String: req.ASIN, Valid: req.ASIN != ""}, - DatePublished: pgtype.Date{Time: parseDate(req.DatePublished), Valid: req.DatePublished != ""}, - Publisher: pgtype.Text{String: req.Publisher, Valid: req.Publisher != ""}, - Contributors: req.Contributors, + ID: pgtype.UUID{Bytes: mediaUUID, Valid: true}, + Title: req.Title, + Author: pgtype.Text{String: req.Author, Valid: req.Author != ""}, + Isbn: pgtype.Text{String: req.ISBN, Valid: req.ISBN != ""}, + Description: pgtype.Text{String: req.Description, Valid: req.Description != ""}, + CoverImagePath: pgtype.Text{String: req.CoverImagePath, Valid: req.CoverImagePath != ""}, + Series: pgtype.Text{String: req.Series, Valid: req.Series != ""}, + SeriesNumber: pgtype.Int4{Int32: req.SeriesNumber, Valid: req.SeriesNumber > 0}, + Tags: req.Tags, + TagsSearch: tagsSearch, + Asin: pgtype.Text{String: req.ASIN, Valid: req.ASIN != ""}, + DatePublished: pgtype.Date{Time: parseDate(req.DatePublished), Valid: req.DatePublished != ""}, + Publisher: pgtype.Text{String: req.Publisher, Valid: req.Publisher != ""}, + Contributors: req.Contributors, + ContributorsSearch: contributorsSearch, }) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})