// Copyright (c) 2025 Nikolai Papin // // This file is part of Easywish // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See // the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . package services import ( "easywish/internal/database" "easywish/internal/dto" errs "easywish/internal/errors" "easywish/internal/utils" "easywish/internal/utils/enums" mapspecial "easywish/internal/utils/mapSpecial" "errors" "github.com/go-redis/redis/v8" "github.com/jackc/pgx/v5" "go.uber.org/zap" ) type wishListServiceImpl struct { log *zap.Logger dbctx database.DbContext redis *redis.Client s3 S3Service } type WishListService interface { CreateWishList(cinfo dto.ClientInfo, object dto.NewWishListDto) (*dto.WishListDto, error) UpdateWishListByGuid(cinfo dto.ClientInfo, guid string, object dto.NewWishDto) (bool, error) DeleteWishListByGuid(cinfo dto.ClientInfo, guid string) (bool, error) GetWishListByGuid(cinfo dto.ClientInfo, guid string) (*dto.WishListDto, error) GetUserWishListsPaginated(cinfo dto.ClientInfo, amount int, page int, sorting enums.Sorting, sortOrder enums.SortOrder) (*[]dto.WishListDto, error) CreateWish(cinfo dto.ClientInfo, object dto.NewWishDto) (*dto.WishDto, error) UpdateWish(cinfo dto.ClientInfo, guid string, object dto.NewWishDto) (bool, error) MoveWishToWishList(cinfo dto.ClientInfo, wishGuid string, wishListGuid string) (bool, error) GetWishByGuid(cinfo dto.ClientInfo, guid string) (*dto.WishDto, error) GetWishesByWishListGuidPaginated(cinfo dto.ClientInfo, guid string, amount int, page int, sorting enums.Sorting, sortOrder enums.SortOrder) (*[]dto.WishDto, error) } func NewWishListService(_log *zap.Logger, _dbctx database.DbContext, _redis *redis.Client, _s3 S3Service) WishListService { return wishListServiceImpl{ log: _log, dbctx: _dbctx, redis: _redis, s3: _s3, } } func (w wishListServiceImpl) CreateWish(cinfo dto.ClientInfo, object dto.NewWishDto) (*dto.WishDto, error) { helper, db, err := database.NewDbHelperTransaction(w.dbctx); if err != nil { w.log.Error( "Failed to open transaction", zap.Error(err)) return nil, errs.ErrServerError } defer helper.Rollback() // Check if wish list exists wishList, err := db.TXQueries.GetWishlistByGuid(db.CTX, object.WishListGuid); if err != nil { if errors.Is(err, pgx.ErrNoRows) { w.log.Warn( "Attempted to create a wish for a wish list that does not exist", zap.String("username", cinfo.Username), zap.String("wish_list_guid", object.WishListGuid), zap.Error(err)) return nil, errs.ErrNotFound } w.log.Error( "Failed to get wishlist for the new wish", zap.String("username", cinfo.Username), zap.String("wish_list_guid", object.WishListGuid), zap.Error(err)) return nil, errs.ErrServerError } wishListOwnerUser, err := db.TXQueries.GetWishListOwnerByGuid(db.CTX, wishList.Guid.String()); if err != nil { w.log.Error( "Failed to get wish list owner", zap.String("username", cinfo.Username), zap.String("wish_list_guid", wishList.Guid.String()), zap.Error(err)) return nil, errs.ErrServerError } if wishListOwnerUser.Username != cinfo.Username { w.log.Warn( "Attempt to create wish in a wish list the user does not own", zap.String("owner_username", wishListOwnerUser.Username), zap.String("username", cinfo.Username), zap.String("wish_list_guid", object.WishListGuid)) // As usual, we will pretend that it does not exist return nil, errs.ErrNotFound } var avatarUrl *string if object.PictureUploadID != "" { key, err := w.s3.SaveUpload(object.PictureUploadID, "images"); if err != nil { if errors.Is(err, errs.ErrFileNotFound) { return nil, err } w.log.Error( "Failed to save image", zap.String("upload_id", object.PictureUploadID), zap.String("username", cinfo.Username), zap.Error(err)) return nil, errs.ErrServerError } urlObj := w.s3.GetLocalizedFileUrl(*key, "images") avatarUrl = utils.NewPointer(urlObj.String()) } else { avatarUrl = utils.NewPointer("") } newWish, err := db.TXQueries.CreateWish(db.CTX, database.CreateWishParams{ WishListGuid: object.WishListGuid, Name: object.Name, Description: object.Description, PictureUrl: *avatarUrl, Stars: int16(object.Stars), }); if err != nil { w.log.Error( "Failed to create a new wish", zap.String("username", cinfo.Username), zap.String("wish_list_guid", object.WishListGuid), zap.Error(err)) return nil, errs.ErrServerError } err = helper.Commit(); if err != nil { w.log.Error( "Failed to commit transaction", zap.Error(err)) return nil, errs.ErrServerError } wishDto := &dto.WishDto{} mapspecial.MapWishDto(newWish, wishDto) return wishDto, nil } func (w wishListServiceImpl) CreateWishList(cinfo dto.ClientInfo, object dto.NewWishListDto) (*dto.WishListDto, error) { helper, db, err := database.NewDbHelperTransaction(w.dbctx); if err != nil { w.log.Error( "Failed to open transaction", zap.Error(err)) return nil, errs.ErrServerError } defer helper.Rollback() createdWishList, err := db.TXQueries.CreateWishList(db.CTX, database.CreateWishListParams{ Username: cinfo.Username, Hidden: object.Hidden, Name: object.Name, IconName: object.IconName, Color: object.Color, ColorGrad: object.ColorGrad, }); if err != nil { w.log.Error( "Failed to create wish list", zap.String("username", cinfo.Username), zap.Error(err)) return nil, errs.ErrServerError } err = helper.Commit(); if err != nil { w.log.Error( "Failed to commit transaction", zap.Error(err)) return nil, errs.ErrServerError } wishListDto := &dto.WishListDto{} mapspecial.MapWishListDto(createdWishList, wishListDto) return wishListDto, nil } func (w wishListServiceImpl) DeleteWishListByGuid(cinfo dto.ClientInfo, guid string) (bool, error) { panic("unimplemented") } func (w wishListServiceImpl) GetUserWishListsPaginated(cinfo dto.ClientInfo, amount int, page int, sorting enums.Sorting, sortOrder enums.SortOrder) (*[]dto.WishListDto, error) { panic("unimplemented") } func (w wishListServiceImpl) GetWishByGuid(cinfo dto.ClientInfo, guid string) (*dto.WishDto, error) { panic("unimplemented") } func (w wishListServiceImpl) GetWishListByGuid(cinfo dto.ClientInfo, guid string) (*dto.WishListDto, error) { panic("unimplemented") } func (w wishListServiceImpl) GetWishesByWishListGuidPaginated(cinfo dto.ClientInfo, guid string, amount int, page int, sorting enums.Sorting, sortOrder enums.SortOrder) (*[]dto.WishDto, error) { panic("unimplemented") } func (w wishListServiceImpl) MoveWishToWishList(cinfo dto.ClientInfo, wishGuid string, wishListGuid string) (bool, error) { panic("unimplemented") } func (w wishListServiceImpl) UpdateWish(cinfo dto.ClientInfo, guid string, object dto.NewWishDto) (bool, error) { panic("unimplemented") } func (w wishListServiceImpl) UpdateWishListByGuid(cinfo dto.ClientInfo, guid string, object dto.NewWishDto) (bool, error) { panic("unimplemented") }