From ccf0aee12e343ab57828e965fe3fb8fa198a08c6 Mon Sep 17 00:00:00 2001 From: litsynp Date: Sat, 27 Apr 2024 01:16:12 +0900 Subject: [PATCH] refactor: streamline and use sqlc for media and resource media --- cmd/server/handler/media_handler.go | 12 +- internal/common/null.go | 7 ++ internal/domain/media/media.go | 31 ----- internal/domain/media/model.go | 22 ++++ internal/domain/media/resource_media.go | 35 ------ internal/domain/media/view.go | 80 ++++++++++--- internal/domain/pet/request.go | 4 +- internal/domain/resourcemedia/model.go | 11 ++ internal/domain/sospost/sos_post.go | 34 +++--- internal/domain/sospost/view.go | 74 ++++++------ internal/domain/user/request.go | 6 +- internal/infra/database/gen/media.sql.go | 86 ++++++++++++++ .../infra/database/gen/resource_media.sql.go | 112 ++++++++++++++++++ internal/postgres/media_store.go | 64 ---------- internal/postgres/resource_media_store.go | 97 --------------- internal/postgres/sos_post_store.go | 9 +- internal/service/media_service.go | 49 ++++---- internal/service/sos_post_service.go | 30 +++-- .../service/tests/sos_post_service_test.go | 34 +++--- internal/service/user_service.go | 11 +- internal/tests/factories.go | 6 +- internal/tests/service.go | 11 +- queries/media.sql | 19 +++ queries/resource_media.sql | 25 ++++ 24 files changed, 485 insertions(+), 384 deletions(-) delete mode 100644 internal/domain/media/media.go create mode 100644 internal/domain/media/model.go delete mode 100644 internal/domain/media/resource_media.go create mode 100644 internal/domain/resourcemedia/model.go create mode 100644 internal/infra/database/gen/media.sql.go create mode 100644 internal/infra/database/gen/resource_media.sql.go delete mode 100644 internal/postgres/media_store.go delete mode 100644 internal/postgres/resource_media_store.go create mode 100644 queries/media.sql create mode 100644 queries/resource_media.sql diff --git a/cmd/server/handler/media_handler.go b/cmd/server/handler/media_handler.go index 3d63e8c6..16191314 100644 --- a/cmd/server/handler/media_handler.go +++ b/cmd/server/handler/media_handler.go @@ -28,7 +28,7 @@ func NewMediaHandler(mediaService service.MediaService) *MediaHandler { // @Tags media // @Produce json // @Param id path int true "미디어 ID" -// @Success 200 {object} media.MediaView +// @Success 200 {object} media.DetailView // @Router /media/{id} [get] func (h *MediaHandler) FindMediaByID(c echo.Context) error { id, err := pnd.ParseIDFromPath(c, "id") @@ -36,12 +36,12 @@ func (h *MediaHandler) FindMediaByID(c echo.Context) error { return c.JSON(err.StatusCode, err) } - found, err := h.mediaService.FindMediaByID(c.Request().Context(), *id) + found, err := h.mediaService.FindMediaByID(c.Request().Context(), int64(*id)) if err != nil { return c.JSON(err.StatusCode, err) } - return c.JSON(http.StatusOK, found.ToMediaView()) + return c.JSON(http.StatusOK, found) } // UploadImage godoc @@ -51,7 +51,7 @@ func (h *MediaHandler) FindMediaByID(c echo.Context) error { // @Accept multipart/form-data // @Produce json // @Param file formData file true "이미지 파일" -// @Success 201 {object} media.MediaView +// @Success 201 {object} media.DetailView // @Router /media/images [post] func (h *MediaHandler) UploadImage(c echo.Context) error { fileHeader, err := c.FormFile("file") @@ -79,12 +79,12 @@ func (h *MediaHandler) UploadImage(c echo.Context) error { return c.JSON(pndErr.StatusCode, pndErr) } - res, err2 := h.mediaService.UploadMedia(c.Request().Context(), file, media.MediaTypeImage, fileHeader.Filename) + res, err2 := h.mediaService.UploadMedia(c.Request().Context(), file, media.TypeImage, fileHeader.Filename) if err2 != nil { return c.JSON(err2.StatusCode, err2) } - return c.JSON(http.StatusCreated, res.ToMediaView()) + return c.JSON(http.StatusCreated, res) } var supportedMimeTypes = []string{ diff --git a/internal/common/null.go b/internal/common/null.go index 212f0fc6..c07ff670 100644 --- a/internal/common/null.go +++ b/internal/common/null.go @@ -70,6 +70,13 @@ func IntToNullInt32(val int) sql.NullInt32 { } } +func Int64ToNullInt32(val int64) sql.NullInt32 { + return sql.NullInt32{ + Int32: int32(val), + Valid: val != 0, + } +} + func IntPtrToNullInt32(val *int) sql.NullInt32 { return sql.NullInt32{ Int32: int32(DerefOrEmpty(val)), diff --git a/internal/domain/media/media.go b/internal/domain/media/media.go deleted file mode 100644 index 4f852a02..00000000 --- a/internal/domain/media/media.go +++ /dev/null @@ -1,31 +0,0 @@ -package media - -import ( - "context" - - "github.com/pet-sitter/pets-next-door-api/internal/infra/database" - - pnd "github.com/pet-sitter/pets-next-door-api/api" -) - -type MediaType string - -const ( - MediaTypeImage MediaType = "image" -) - -type Media struct { - ID int `field:"id" json:"id"` - MediaType MediaType `field:"media_type" json:"media_type"` - URL string `field:"url" json:"url"` - CreatedAt string `field:"created_at" json:"created_at"` - UpdatedAt string `field:"updated_at" json:"updated_at"` - DeletedAt string `field:"deleted_at" json:"deleted_at"` -} - -type MediaList []*Media - -type MediaStore interface { - CreateMedia(ctx context.Context, tx *database.Tx, media *Media) (*Media, *pnd.AppError) - FindMediaByID(ctx context.Context, tx *database.Tx, id int) (*Media, *pnd.AppError) -} diff --git a/internal/domain/media/model.go b/internal/domain/media/model.go new file mode 100644 index 00000000..f24d80a7 --- /dev/null +++ b/internal/domain/media/model.go @@ -0,0 +1,22 @@ +package media + +type Type string + +const ( + TypeImage Type = "image" +) + +func (mt Type) String() string { + return string(mt) +} + +type ViewForSOSPost struct { + ID int64 `field:"id" json:"id"` + MediaType Type `field:"media_type" json:"media_type"` + URL string `field:"url" json:"url"` + CreatedAt string `field:"created_at" json:"created_at"` + UpdatedAt string `field:"updated_at" json:"updated_at"` + DeletedAt string `field:"deleted_at" json:"deleted_at"` +} + +type ViewListForSOSPost []*ViewForSOSPost diff --git a/internal/domain/media/resource_media.go b/internal/domain/media/resource_media.go deleted file mode 100644 index 3177c6ba..00000000 --- a/internal/domain/media/resource_media.go +++ /dev/null @@ -1,35 +0,0 @@ -package media - -import ( - "context" - "time" - - "github.com/pet-sitter/pets-next-door-api/internal/infra/database" - - pnd "github.com/pet-sitter/pets-next-door-api/api" -) - -type ResourceType string - -const ( - SOSResourceType ResourceType = "sos_posts" -) - -type ResourceMedia struct { - ID int `field:"id"` - ResourceType ResourceType `field:"resource_type"` - ResourceID int `field:"resource_id"` - MediaID int `field:"media_id"` - CreatedAt time.Time `field:"created_at"` - UpdatedAt time.Time `field:"updated_at"` - DeletedAt time.Time `field:"deleted_at"` -} - -type ResourceMediaStore interface { - CreateResourceMedia( - ctx context.Context, tx *database.Tx, resourceID, mediaID int, resourceType string, - ) (*ResourceMedia, *pnd.AppError) - FindResourceMediaByResourceID( - ctx context.Context, tx *database.Tx, resourceID int, resourceType string, - ) (*MediaList, *pnd.AppError) -} diff --git a/internal/domain/media/view.go b/internal/domain/media/view.go index c0c46dee..8e72f6e5 100644 --- a/internal/domain/media/view.go +++ b/internal/domain/media/view.go @@ -1,34 +1,80 @@ package media -type MediaView struct { - ID int `json:"id"` - MediaType MediaType `json:"mediaType"` +import ( + "time" + + databasegen "github.com/pet-sitter/pets-next-door-api/internal/infra/database/gen" +) + +type DetailView struct { + ID int64 `json:"id"` + MediaType Type `json:"mediaType"` URL string `json:"url"` - CreatedAt string `json:"createdAt"` + CreatedAt time.Time `json:"createdAt"` +} + +type ListView []*DetailView + +func ToDetailView(media databasegen.FindSingleMediaRow) *DetailView { + return &DetailView{ + ID: int64(media.ID), + MediaType: Type(media.MediaType), + URL: media.Url, + CreatedAt: media.CreatedAt, + } +} + +func ToDetailViewFromCreated(media databasegen.CreateMediaRow) *DetailView { + return &DetailView{ + ID: int64(media.ID), + MediaType: Type(media.MediaType), + URL: media.Url, + CreatedAt: media.CreatedAt, + } } -type MediaViewList []*MediaView +func ToDetailViewFromResourceMediaRows(resourceMedia databasegen.FindResourceMediaRow) *DetailView { + return &DetailView{ + ID: int64(resourceMedia.MediaID), + MediaType: Type(resourceMedia.MediaType), + URL: resourceMedia.Url, + CreatedAt: resourceMedia.CreatedAt, + } +} -func (media *Media) ToMediaView() *MediaView { - return &MediaView{ +func ToDetailViewFromViewForSOSPost(media ViewForSOSPost) *DetailView { + createdAt, err := time.Parse(time.RFC3339, media.CreatedAt) + if err != nil { + createdAt = time.Time{} + } + + return &DetailView{ ID: media.ID, MediaType: media.MediaType, URL: media.URL, - CreatedAt: media.CreatedAt, + CreatedAt: createdAt, } } -func (mediaList *MediaList) ToMediaViewList() MediaViewList { - mediaViewList := make(MediaViewList, len(*mediaList)) - for i, media := range *mediaList { - mediaViewList[i] = media.ToMediaView() +func ToListViewFromResourceMediaRows(resourceMediaList []databasegen.FindResourceMediaRow) ListView { + mediaViewList := make(ListView, len(resourceMediaList)) + for i, resourceMedia := range resourceMediaList { + mediaViewList[i] = ToDetailViewFromResourceMediaRows(resourceMedia) } return mediaViewList } -type ResourceMediaView struct { - ID int `field:"id"` - ResourceType ResourceType `field:"resource_type"` - ResourceID int `field:"resource_id"` - MediaID int `field:"media_id"` +func ToListViewFromViewListForSOSPost(mediaList ViewListForSOSPost) ListView { + mediaViewList := make(ListView, len(mediaList)) + for i, media := range mediaList { + mediaViewList[i] = ToDetailViewFromViewForSOSPost( + ViewForSOSPost{ + ID: media.ID, + MediaType: media.MediaType, + URL: media.URL, + CreatedAt: media.CreatedAt, + }, + ) + } + return mediaViewList } diff --git a/internal/domain/pet/request.go b/internal/domain/pet/request.go index 3438c486..af970079 100644 --- a/internal/domain/pet/request.go +++ b/internal/domain/pet/request.go @@ -19,7 +19,7 @@ type AddPetRequest struct { BirthDate utils.Date `json:"birthDate" validate:"required"` WeightInKg decimal.Decimal `json:"weightInKg" validate:"required"` Remarks string `json:"remarks"` - ProfileImageID *int `json:"profileImageId"` + ProfileImageID *int64 `json:"profileImageId"` } type UpdatePetRequest struct { @@ -29,5 +29,5 @@ type UpdatePetRequest struct { BirthDate utils.Date `json:"birthDate" validate:"required"` WeightInKg decimal.Decimal `json:"weightInKg" validate:"required"` Remarks string `json:"remarks"` - ProfileImageID *int `json:"profileImageId"` + ProfileImageID *int64 `json:"profileImageId"` } diff --git a/internal/domain/resourcemedia/model.go b/internal/domain/resourcemedia/model.go new file mode 100644 index 00000000..bcbb53b7 --- /dev/null +++ b/internal/domain/resourcemedia/model.go @@ -0,0 +1,11 @@ +package resourcemedia + +type ResourceType string + +const ( + SOSResourceType ResourceType = "sos_posts" +) + +func (r ResourceType) String() string { + return string(r) +} diff --git a/internal/domain/sospost/sos_post.go b/internal/domain/sospost/sos_post.go index 38721a2e..de0b4ecf 100644 --- a/internal/domain/sospost/sos_post.go +++ b/internal/domain/sospost/sos_post.go @@ -42,7 +42,7 @@ type SOSPost struct { CareType CareType `field:"care_type"` CarerGender CarerGender `field:"carer_gender"` RewardType RewardType `field:"reward_type"` - ThumbnailID int `field:"thumbnail_id"` + ThumbnailID int64 `field:"thumbnail_id"` CreatedAt time.Time `field:"created_at"` UpdatedAt time.Time `field:"updated_at"` DeletedAt time.Time `field:"deleted_at"` @@ -53,22 +53,22 @@ type SOSPostList struct { } type SOSPostInfo struct { - ID int `field:"id" json:"id"` - AuthorID int `field:"author" json:"author"` - Title string `field:"title" json:"title"` - Content string `field:"content" json:"content"` - Media media.MediaList `field:"media" json:"media"` - Conditions ConditionList `field:"conditions" json:"conditions"` - Pets pet.ViewListForSOSPost `field:"pets" json:"pets"` - Reward string `field:"reward" json:"reward"` - Dates SOSDatesList `field:"dates" json:"dates"` - CareType CareType `field:"careType" json:"careType"` - CarerGender CarerGender `field:"carerGender" json:"carerGender"` - RewardType RewardType `field:"rewardType" json:"rewardType"` - ThumbnailID int `field:"thumbnailId" json:"thumbnailId"` - CreatedAt time.Time `field:"createdAt" json:"createdAt"` - UpdatedAt time.Time `field:"updatedAt" json:"updatedAt"` - DeletedAt time.Time `field:"deletedAt" json:"deletedAt"` + ID int `field:"id" json:"id"` + AuthorID int `field:"author" json:"author"` + Title string `field:"title" json:"title"` + Content string `field:"content" json:"content"` + Media media.ViewListForSOSPost `field:"media" json:"media"` + Conditions ConditionList `field:"conditions" json:"conditions"` + Pets pet.ViewListForSOSPost `field:"pets" json:"pets"` + Reward string `field:"reward" json:"reward"` + Dates SOSDatesList `field:"dates" json:"dates"` + CareType CareType `field:"careType" json:"careType"` + CarerGender CarerGender `field:"carerGender" json:"carerGender"` + RewardType RewardType `field:"rewardType" json:"rewardType"` + ThumbnailID int64 `field:"thumbnailId" json:"thumbnailId"` + CreatedAt time.Time `field:"createdAt" json:"createdAt"` + UpdatedAt time.Time `field:"updatedAt" json:"updatedAt"` + DeletedAt time.Time `field:"deletedAt" json:"deletedAt"` } type SOSPostInfoList struct { diff --git a/internal/domain/sospost/view.go b/internal/domain/sospost/view.go index 0a11cb0e..85ba30f3 100644 --- a/internal/domain/sospost/view.go +++ b/internal/domain/sospost/view.go @@ -44,25 +44,25 @@ type WriteSOSPostRequest struct { } type WriteSOSPostView struct { - ID int `json:"id"` - AuthorID int `json:"authorId"` - Title string `json:"title"` - Content string `json:"content"` - Media media.MediaViewList `json:"media"` - Conditions []ConditionView `json:"conditions"` - Pets []pet.DetailView `json:"pets"` - Reward string `json:"reward"` - Dates []SOSDateView `json:"dates"` - CareType CareType `json:"careType"` - CarerGender CarerGender `json:"carerGender"` - RewardType RewardType `json:"rewardType"` - ThumbnailID int `json:"thumbnailId"` - CreatedAt string `json:"createdAt"` - UpdatedAt string `json:"updatedAt"` + ID int `json:"id"` + AuthorID int `json:"authorId"` + Title string `json:"title"` + Content string `json:"content"` + Media media.ListView `json:"media"` + Conditions []ConditionView `json:"conditions"` + Pets []pet.DetailView `json:"pets"` + Reward string `json:"reward"` + Dates []SOSDateView `json:"dates"` + CareType CareType `json:"careType"` + CarerGender CarerGender `json:"carerGender"` + RewardType RewardType `json:"rewardType"` + ThumbnailID int64 `json:"thumbnailId"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` } func (p *SOSPost) ToWriteSOSPostView( - mediaList media.MediaViewList, + mediaList media.ListView, conditions []ConditionView, pets []pet.DetailView, sosDates []SOSDateView, @@ -91,7 +91,7 @@ type FindSOSPostView struct { Author *user.WithoutPrivateInfo `json:"author"` Title string `json:"title"` Content string `json:"content"` - Media media.MediaViewList `json:"media"` + Media media.ListView `json:"media"` Conditions []ConditionView `json:"conditions"` Pets []pet.DetailView `json:"pets"` Reward string `json:"reward"` @@ -99,14 +99,14 @@ type FindSOSPostView struct { CareType CareType `json:"careType"` CarerGender CarerGender `json:"carerGender"` RewardType RewardType `json:"rewardType"` - ThumbnailID int `json:"thumbnailId"` + ThumbnailID int64 `json:"thumbnailId"` CreatedAt string `json:"createdAt"` UpdatedAt string `json:"updatedAt"` } func (p *SOSPost) ToFindSOSPostView( author *user.WithoutPrivateInfo, - mediaList media.MediaViewList, + mediaList media.ListView, conditions []ConditionView, pets []pet.DetailView, sosDates []SOSDateView, @@ -152,7 +152,7 @@ func FromEmptySOSPostInfoList(sosPosts *SOSPostInfoList) *FindSOSPostListView { func (p *SOSPostInfo) ToFindSOSPostInfoView( author *user.WithoutPrivateInfo, - mediaList media.MediaViewList, + mediaList media.ListView, conditions []ConditionView, pets []pet.DetailView, sosDates []SOSDateView, @@ -180,7 +180,7 @@ type UpdateSOSPostRequest struct { ID int `json:"id" validate:"required"` Title string `json:"title" validate:"required"` Content string `json:"content" validate:"required"` - ImageIDs []int `json:"imageIds" validate:"required"` + ImageIDs []int64 `json:"imageIds" validate:"required"` Dates []SOSDateView `json:"dates" validate:"required"` Reward string `json:"reward"` CareType CareType `json:"careType" validate:"required,oneof=foster visiting"` @@ -191,25 +191,25 @@ type UpdateSOSPostRequest struct { } type UpdateSOSPostView struct { - ID int `json:"id"` - AuthorID int `json:"authorId"` - Title string `json:"title"` - Content string `json:"content"` - Media media.MediaViewList `json:"media"` - Conditions []ConditionView `json:"conditions"` - Pets []pet.DetailView `json:"pets"` - Reward string `json:"reward"` - Dates []SOSDateView `json:"dates"` - CareType CareType `json:"careType"` - CarerGender CarerGender `json:"carerGender"` - RewardType RewardType `json:"rewardType"` - ThumbnailID int `json:"thumbnailId"` - CreatedAt string `json:"createdAt"` - UpdatedAt string `json:"updatedAt"` + ID int `json:"id"` + AuthorID int `json:"authorId"` + Title string `json:"title"` + Content string `json:"content"` + Media media.ListView `json:"media"` + Conditions []ConditionView `json:"conditions"` + Pets []pet.DetailView `json:"pets"` + Reward string `json:"reward"` + Dates []SOSDateView `json:"dates"` + CareType CareType `json:"careType"` + CarerGender CarerGender `json:"carerGender"` + RewardType RewardType `json:"rewardType"` + ThumbnailID int64 `json:"thumbnailId"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` } func (p *SOSPost) ToUpdateSOSPostView( - mediaList media.MediaViewList, + mediaList media.ListView, conditions []ConditionView, pets []pet.DetailView, sosDates []SOSDateView, diff --git a/internal/domain/user/request.go b/internal/domain/user/request.go index f35e56b0..66497eea 100644 --- a/internal/domain/user/request.go +++ b/internal/domain/user/request.go @@ -9,7 +9,7 @@ type RegisterUserRequest struct { Email string `json:"email" validate:"required,email"` Nickname string `json:"nickname" validate:"required"` Fullname string `json:"fullname" validate:"required"` - ProfileImageID *int `json:"profileImageId"` + ProfileImageID *int64 `json:"profileImageId"` FirebaseProviderType FirebaseProviderType `json:"fbProviderType" validate:"required"` FirebaseUID string `json:"fbUid" validate:"required"` } @@ -20,7 +20,7 @@ func (r *RegisterUserRequest) ToDBParams() databasegen.CreateUserParams { Nickname: r.Nickname, Fullname: r.Fullname, Password: "", - ProfileImageID: utils.IntPtrToNullInt64(r.ProfileImageID), + ProfileImageID: utils.Int64PtrToNullInt64(r.ProfileImageID), FbProviderType: r.FirebaseProviderType.NullString(), FbUid: utils.StrToNullStr(r.FirebaseUID), } @@ -36,5 +36,5 @@ type UserStatusRequest struct { type UpdateUserRequest struct { Nickname string `json:"nickname" validate:"required"` - ProfileImageID *int `json:"profileImageId" validate:"omitempty"` + ProfileImageID *int64 `json:"profileImageId" validate:"omitempty"` } diff --git a/internal/infra/database/gen/media.sql.go b/internal/infra/database/gen/media.sql.go new file mode 100644 index 00000000..fb78fbe3 --- /dev/null +++ b/internal/infra/database/gen/media.sql.go @@ -0,0 +1,86 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 +// source: media.sql + +package databasegen + +import ( + "context" + "database/sql" + "time" +) + +const createMedia = `-- name: CreateMedia :one +INSERT INTO media +(media_type, + url, + created_at, + updated_at) +VALUES ($1, $2, NOW(), NOW()) +RETURNING id, media_type, url, created_at, updated_at +` + +type CreateMediaParams struct { + MediaType string + Url string +} + +type CreateMediaRow struct { + ID int32 + MediaType string + Url string + CreatedAt time.Time + UpdatedAt time.Time +} + +func (q *Queries) CreateMedia(ctx context.Context, arg CreateMediaParams) (CreateMediaRow, error) { + row := q.db.QueryRowContext(ctx, createMedia, arg.MediaType, arg.Url) + var i CreateMediaRow + err := row.Scan( + &i.ID, + &i.MediaType, + &i.Url, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const findSingleMedia = `-- name: FindSingleMedia :one +SELECT id, + media_type, + url, + created_at, + updated_at +FROM media +WHERE (id = $1 OR $1 IS NULL) + AND ($2::BOOLEAN = TRUE OR + ($2::BOOLEAN = FALSE AND deleted_at IS NULL)) +` + +type FindSingleMediaParams struct { + ID sql.NullInt32 + IncludeDeleted bool +} + +type FindSingleMediaRow struct { + ID int32 + MediaType string + Url string + CreatedAt time.Time + UpdatedAt time.Time +} + +func (q *Queries) FindSingleMedia(ctx context.Context, arg FindSingleMediaParams) (FindSingleMediaRow, error) { + row := q.db.QueryRowContext(ctx, findSingleMedia, arg.ID, arg.IncludeDeleted) + var i FindSingleMediaRow + err := row.Scan( + &i.ID, + &i.MediaType, + &i.Url, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} diff --git a/internal/infra/database/gen/resource_media.sql.go b/internal/infra/database/gen/resource_media.sql.go new file mode 100644 index 00000000..09a2b251 --- /dev/null +++ b/internal/infra/database/gen/resource_media.sql.go @@ -0,0 +1,112 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 +// source: resource_media.sql + +package databasegen + +import ( + "context" + "database/sql" + "time" +) + +const createResourceMedia = `-- name: CreateResourceMedia :one +INSERT INTO resource_media +(resource_id, + media_id, + resource_type, + created_at, + updated_at) +VALUES ($1, $2, $3, NOW(), NOW()) +RETURNING id, resource_id, media_id, resource_type, created_at, updated_at +` + +type CreateResourceMediaParams struct { + ResourceID sql.NullInt64 + MediaID sql.NullInt64 + ResourceType sql.NullString +} + +type CreateResourceMediaRow struct { + ID int32 + ResourceID sql.NullInt64 + MediaID sql.NullInt64 + ResourceType sql.NullString + CreatedAt time.Time + UpdatedAt time.Time +} + +func (q *Queries) CreateResourceMedia(ctx context.Context, arg CreateResourceMediaParams) (CreateResourceMediaRow, error) { + row := q.db.QueryRowContext(ctx, createResourceMedia, arg.ResourceID, arg.MediaID, arg.ResourceType) + var i CreateResourceMediaRow + err := row.Scan( + &i.ID, + &i.ResourceID, + &i.MediaID, + &i.ResourceType, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const findResourceMedia = `-- name: FindResourceMedia :many +SELECT m.id AS media_id, + m.media_type, + m.url, + m.created_at, + m.updated_at +FROM resource_media rm + INNER JOIN + media m + ON + rm.media_id = m.id +WHERE (rm.resource_id = $1 OR $1 IS NULL) + AND (rm.resource_type = $2 OR $2 IS NULL) + AND ($3::BOOLEAN = TRUE OR + ($3::BOOLEAN = FALSE AND rm.deleted_at IS NULL)) +` + +type FindResourceMediaParams struct { + ResourceID sql.NullInt64 + ResourceType sql.NullString + IncludeDeleted bool +} + +type FindResourceMediaRow struct { + MediaID int32 + MediaType string + Url string + CreatedAt time.Time + UpdatedAt time.Time +} + +func (q *Queries) FindResourceMedia(ctx context.Context, arg FindResourceMediaParams) ([]FindResourceMediaRow, error) { + rows, err := q.db.QueryContext(ctx, findResourceMedia, arg.ResourceID, arg.ResourceType, arg.IncludeDeleted) + if err != nil { + return nil, err + } + defer rows.Close() + var items []FindResourceMediaRow + for rows.Next() { + var i FindResourceMediaRow + if err := rows.Scan( + &i.MediaID, + &i.MediaType, + &i.Url, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/postgres/media_store.go b/internal/postgres/media_store.go deleted file mode 100644 index 4ee4b289..00000000 --- a/internal/postgres/media_store.go +++ /dev/null @@ -1,64 +0,0 @@ -package postgres - -import ( - "context" - - pnd "github.com/pet-sitter/pets-next-door-api/api" - "github.com/pet-sitter/pets-next-door-api/internal/domain/media" - "github.com/pet-sitter/pets-next-door-api/internal/infra/database" -) - -func CreateMedia(ctx context.Context, tx *database.Tx, mediaData *media.Media) (*media.Media, *pnd.AppError) { - const sql = ` - INSERT INTO - media - ( - media_type, - url, - created_at, - updated_at - ) - VALUES ($1, $2, NOW(), NOW()) - RETURNING id, created_at, updated_at - ` - - if err := tx.QueryRowContext(ctx, sql, - mediaData.MediaType, - mediaData.URL, - ).Scan(&mediaData.ID, &mediaData.CreatedAt, &mediaData.UpdatedAt); err != nil { - return nil, pnd.FromPostgresError(err) - } - - return mediaData, nil -} - -func FindMediaByID(ctx context.Context, tx *database.Tx, id int) (*media.Media, *pnd.AppError) { - const sql = ` - SELECT - id, - media_type, - url, - created_at, - updated_at - FROM - media - WHERE - id = $1 AND - deleted_at IS NULL - ` - - mediaData := &media.Media{} - if err := tx.QueryRowContext(ctx, sql, - id, - ).Scan( - &mediaData.ID, - &mediaData.MediaType, - &mediaData.URL, - &mediaData.CreatedAt, - &mediaData.UpdatedAt, - ); err != nil { - return nil, pnd.FromPostgresError(err) - } - - return mediaData, nil -} diff --git a/internal/postgres/resource_media_store.go b/internal/postgres/resource_media_store.go deleted file mode 100644 index 0e9ea271..00000000 --- a/internal/postgres/resource_media_store.go +++ /dev/null @@ -1,97 +0,0 @@ -package postgres - -import ( - "context" - - pnd "github.com/pet-sitter/pets-next-door-api/api" - "github.com/pet-sitter/pets-next-door-api/internal/domain/media" - "github.com/pet-sitter/pets-next-door-api/internal/infra/database" -) - -func CreateResourceMedia( - ctx context.Context, tx *database.Tx, resourceID, mediaID int, resourceType string, -) (*media.ResourceMedia, *pnd.AppError) { - const sql = ` - INSERT INTO - resource_media - ( - resource_id, - media_id, - resource_type, - created_at, - updated_at - ) - VALUES ($1, $2, $3, NOW(), NOW()) - RETURNING id, resource_id, media_id, created_at, updated_at - ` - - resourceMedia := &media.ResourceMedia{} - err := tx.QueryRowContext(ctx, sql, - resourceID, - mediaID, - resourceType, - ).Scan( - &resourceMedia.ID, - &resourceMedia.ResourceID, - &resourceMedia.MediaID, - &resourceMedia.CreatedAt, - &resourceMedia.UpdatedAt, - ) - if err != nil { - return nil, pnd.FromPostgresError(err) - } - - return resourceMedia, nil -} - -func FindResourceMediaByResourceID( - ctx context.Context, tx *database.Tx, resourceID int, resourceType string, -) (*media.MediaList, *pnd.AppError) { - const sql = ` - SELECT - m.id, - m.media_type, - m.url, - m.created_at, - m.updated_at - FROM - resource_media rm - INNER JOIN - media m - ON - rm.media_id = m.id - WHERE - rm.resource_id = $1 AND - rm.resource_type = $2 AND - rm.deleted_at IS NULL - ` - - var mediaList media.MediaList - rows, err := tx.QueryContext(ctx, sql, - resourceID, - resourceType, - ) - if err != nil { - return nil, pnd.FromPostgresError(err) - } - defer rows.Close() - - for rows.Next() { - mediaItem := media.Media{} - if err := rows.Scan( - &mediaItem.ID, - &mediaItem.MediaType, - &mediaItem.URL, - &mediaItem.CreatedAt, - &mediaItem.UpdatedAt, - ); err != nil { - return nil, pnd.FromPostgresError(err) - } - mediaList = append(mediaList, &mediaItem) - } - if err := rows.Err(); err != nil { - return nil, pnd.FromPostgresError(err) - } - - return &mediaList, nil -} diff --git a/internal/postgres/sos_post_store.go b/internal/postgres/sos_post_store.go index dc6530a3..91391a31 100644 --- a/internal/postgres/sos_post_store.go +++ b/internal/postgres/sos_post_store.go @@ -7,10 +7,11 @@ import ( "fmt" "time" + "github.com/pet-sitter/pets-next-door-api/internal/domain/resourcemedia" + utils "github.com/pet-sitter/pets-next-door-api/internal/common" pnd "github.com/pet-sitter/pets-next-door-api/api" - "github.com/pet-sitter/pets-next-door-api/internal/domain/media" "github.com/pet-sitter/pets-next-door-api/internal/domain/sospost" "github.com/pet-sitter/pets-next-door-api/internal/infra/database" ) @@ -134,7 +135,7 @@ func WriteSOSPost( VALUES ($1, $2, $3, NOW(), NOW())`, imageID, sosPost.ID, - media.SOSResourceType, + resourcemedia.SOSResourceType, ); err != nil { return nil, pnd.FromPostgresError(err) } @@ -590,7 +591,7 @@ func updateSOSPostsDates(ctx context.Context, tx *database.Tx, postID int, dates return nil } -func updateSOSPostsMedia(ctx context.Context, tx *database.Tx, postID int, mediaIDs []int) *pnd.AppError { +func updateSOSPostsMedia(ctx context.Context, tx *database.Tx, postID int, mediaIDs []int64) *pnd.AppError { if _, err := tx.ExecContext(ctx, ` UPDATE resource_media @@ -618,7 +619,7 @@ func updateSOSPostsMedia(ctx context.Context, tx *database.Tx, postID int, media `, mediaID, postID, - media.SOSResourceType, + resourcemedia.SOSResourceType, ); err != nil { return pnd.FromPostgresError(err) } diff --git a/internal/service/media_service.go b/internal/service/media_service.go index 2191ad03..6a0b8202 100644 --- a/internal/service/media_service.go +++ b/internal/service/media_service.go @@ -5,13 +5,15 @@ import ( "io" "path/filepath" + utils "github.com/pet-sitter/pets-next-door-api/internal/common" + databasegen "github.com/pet-sitter/pets-next-door-api/internal/infra/database/gen" + "github.com/aws/aws-sdk-go/private/protocol/rest" "github.com/google/uuid" pnd "github.com/pet-sitter/pets-next-door-api/api" "github.com/pet-sitter/pets-next-door-api/internal/domain/media" "github.com/pet-sitter/pets-next-door-api/internal/infra/database" s3infra "github.com/pet-sitter/pets-next-door-api/internal/infra/s3" - "github.com/pet-sitter/pets-next-door-api/internal/postgres" ) type MediaService struct { @@ -36,8 +38,8 @@ type UploadFileView struct { } func (s *MediaService) UploadMedia( - ctx context.Context, file io.ReadSeeker, mediaType media.MediaType, fileName string, -) (*media.Media, *pnd.AppError) { + ctx context.Context, file io.ReadSeeker, mediaType media.Type, fileName string, +) (*media.DetailView, *pnd.AppError) { randomFileName := generateRandomFileName(fileName) fullPath := "media/" + randomFileName @@ -51,10 +53,7 @@ func (s *MediaService) UploadMedia( return nil, pnd.ErrUnknown(err) } - created, err := s.CreateMedia(ctx, &media.Media{ - MediaType: mediaType, - URL: req.HTTPRequest.URL.String(), - }) + created, err := s.CreateMedia(ctx, mediaType, req.HTTPRequest.URL.String()) if err != nil { return nil, err } @@ -62,40 +61,36 @@ func (s *MediaService) UploadMedia( return created, nil } -func (s *MediaService) CreateMedia(ctx context.Context, mediaData *media.Media) (*media.Media, *pnd.AppError) { +func (s *MediaService) CreateMedia( + ctx context.Context, mediaType media.Type, url string, +) (*media.DetailView, *pnd.AppError) { tx, err := s.conn.BeginTx(ctx) defer tx.Rollback() if err != nil { return nil, err } - created, err := postgres.CreateMedia(ctx, tx, mediaData) - if err != nil { - return nil, err + created, err2 := databasegen.New(s.conn).CreateMedia(ctx, databasegen.CreateMediaParams{ + MediaType: mediaType.String(), + Url: url, + }) + if err2 != nil { + return nil, pnd.FromPostgresError(err2) } if err := tx.Commit(); err != nil { return nil, err } - - return created, nil + return media.ToDetailViewFromCreated(created), nil } -func (s *MediaService) FindMediaByID(ctx context.Context, id int) (*media.Media, *pnd.AppError) { - tx, err := s.conn.BeginTx(ctx) - defer tx.Rollback() - if err != nil { - return nil, err - } - - mediaData, err := postgres.FindMediaByID(ctx, tx, id) +func (s *MediaService) FindMediaByID(ctx context.Context, id int64) (*media.DetailView, *pnd.AppError) { + mediaData, err := databasegen.New(s.conn).FindSingleMedia(ctx, databasegen.FindSingleMediaParams{ + ID: utils.Int64ToNullInt32(id), + }) if err != nil { - return nil, err - } - - if err := tx.Commit(); err != nil { - return nil, err + return nil, pnd.FromPostgresError(err) } - return mediaData, nil + return media.ToDetailView(mediaData), nil } diff --git a/internal/service/sos_post_service.go b/internal/service/sos_post_service.go index 000781ce..f8953e64 100644 --- a/internal/service/sos_post_service.go +++ b/internal/service/sos_post_service.go @@ -3,6 +3,8 @@ package service import ( "context" + "github.com/pet-sitter/pets-next-door-api/internal/domain/resourcemedia" + "github.com/pet-sitter/pets-next-door-api/internal/domain/pet" utils "github.com/pet-sitter/pets-next-door-api/internal/common" @@ -48,9 +50,12 @@ func (service *SOSPostService) WriteSOSPost( return nil, err } - mediaData, err := postgres.FindResourceMediaByResourceID(ctx, tx, sosPost.ID, string(media.SOSResourceType)) - if err != nil { - return nil, err + mediaData, err2 := databasegen.New(tx).FindResourceMedia(ctx, databasegen.FindResourceMediaParams{ + ResourceID: utils.IntToNullInt64(sosPost.ID), + ResourceType: utils.StrToNullStr(resourcemedia.SOSResourceType.String()), + }) + if err2 != nil { + return nil, pnd.FromPostgresError(err2) } conditions, err := postgres.FindConditionByID(ctx, tx, sosPost.ID) @@ -73,7 +78,7 @@ func (service *SOSPostService) WriteSOSPost( } return sosPost.ToWriteSOSPostView( - mediaData.ToMediaViewList(), + media.ToListViewFromResourceMediaRows(mediaData), conditions.ToConditionViewList(), pet.ToDetailViewList(petRows), dates.ToSOSDateViewList(), @@ -111,7 +116,7 @@ func (service *SOSPostService) FindSOSPosts( Nickname: author.Nickname, ProfileImageURL: utils.NullStrToStrPtr(author.ProfileImageUrl), }, - sosPost.Media.ToMediaViewList(), + media.ToListViewFromViewListForSOSPost(sosPost.Media), sosPost.Conditions.ToConditionViewList(), sosPost.Pets.ToDetailViewList(), sosPost.Dates.ToSOSDateViewList(), @@ -153,7 +158,7 @@ func (service *SOSPostService) FindSOSPostsByAuthorID( Nickname: author.Nickname, ProfileImageURL: utils.NullStrToStrPtr(author.ProfileImageUrl), }, - sosPost.Media.ToMediaViewList(), + media.ToListViewFromViewListForSOSPost(sosPost.Media), sosPost.Conditions.ToConditionViewList(), sosPost.Pets.ToDetailViewList(), sosPost.Dates.ToSOSDateViewList(), @@ -194,7 +199,7 @@ func (service *SOSPostService) FindSOSPostByID(ctx context.Context, id int) (*so Nickname: author.Nickname, ProfileImageURL: utils.NullStrToStrPtr(author.ProfileImageUrl), }, - sosPost.Media.ToMediaViewList(), + media.ToListViewFromViewListForSOSPost(sosPost.Media), sosPost.Conditions.ToConditionViewList(), sosPost.Pets.ToDetailViewList(), sosPost.Dates.ToSOSDateViewList(), @@ -215,9 +220,12 @@ func (service *SOSPostService) UpdateSOSPost( return nil, err } - mediaData, err := postgres.FindResourceMediaByResourceID(ctx, tx, updateSOSPost.ID, string(media.SOSResourceType)) - if err != nil { - return nil, err + mediaData, err2 := databasegen.New(tx).FindResourceMedia(ctx, databasegen.FindResourceMediaParams{ + ResourceID: utils.IntToNullInt64(updateSOSPost.ID), + ResourceType: utils.StrToNullStr(resourcemedia.SOSResourceType.String()), + }) + if err2 != nil { + return nil, pnd.FromPostgresError(err2) } conditions, err := postgres.FindConditionByID(ctx, tx, updateSOSPost.ID) @@ -240,7 +248,7 @@ func (service *SOSPostService) UpdateSOSPost( } return updateSOSPost.ToUpdateSOSPostView( - mediaData.ToMediaViewList(), + media.ToListViewFromResourceMediaRows(mediaData), conditions.ToConditionViewList(), pet.ToDetailViewList(petRows), dates.ToSOSDateViewList(), diff --git a/internal/service/tests/sos_post_service_test.go b/internal/service/tests/sos_post_service_test.go index 6978a69d..447b64cf 100644 --- a/internal/service/tests/sos_post_service_test.go +++ b/internal/service/tests/sos_post_service_test.go @@ -55,7 +55,7 @@ func TestSOSPostService(t *testing.T) { // when sosPostService := service.NewSOSPostService(db) - imageIDs := []int64{int64(sosPostImage.ID), int64(sosPostImage2.ID)} + imageIDs := []int64{sosPostImage.ID, sosPostImage2.ID} petIDs := []int64{addPets.ID} sosPostData := tests.GenerateDummyWriteSOSPostRequest(imageIDs, petIDs, 0) @@ -67,7 +67,7 @@ func TestSOSPostService(t *testing.T) { // then assertConditionEquals(t, sosPost.Conditions, sosPostData.ConditionIDs) assertPetEquals(t, sosPost.Pets[0], *addPets) - assertMediaEquals(t, sosPost.Media, (&media.MediaList{sosPostImage, sosPostImage2}).ToMediaViewList()) + assertMediaEquals(t, sosPost.Media, media.ListView{sosPostImage, sosPostImage2}) assertDatesEquals(t, sosPost.Dates, sosPostData.Dates) if sosPost.Title != sosPostData.Title { @@ -88,7 +88,7 @@ func TestSOSPostService(t *testing.T) { if sosPost.RewardType != sosPostData.RewardType { t.Errorf("got %v want %v", sosPost.RewardType, sosPostData.RewardType) } - if int64(sosPost.ThumbnailID) != sosPostData.ImageIDs[0] { + if sosPost.ThumbnailID != sosPostData.ImageIDs[0] { t.Errorf("got %v want %v", sosPost.ThumbnailID, sosPostData.ImageIDs[0]) } if int64(sosPost.AuthorID) != owner.ID { @@ -120,7 +120,7 @@ func TestSOSPostService(t *testing.T) { addPets := tests.AddDummyPet(t, ctx, userService, uid, &profileImage.ID) sosPostService := service.NewSOSPostService(db) - imageIDs := []int64{int64(sosPostImage.ID), int64(sosPostImage2.ID)} + imageIDs := []int64{sosPostImage.ID, sosPostImage2.ID} petIDs := []int64{addPets.ID} conditionIDs := []int{1, 2} @@ -141,7 +141,7 @@ func TestSOSPostService(t *testing.T) { idx := len(sosPostList.Items) - i - 1 assertConditionEquals(t, sosPost.Conditions, conditionIDs) assertPetEquals(t, sosPost.Pets[0], *addPets) - assertMediaEquals(t, sosPost.Media, (&media.MediaList{sosPostImage, sosPostImage2}).ToMediaViewList()) + assertMediaEquals(t, sosPost.Media, media.ListView{sosPostImage, sosPostImage2}) assertAuthorEquals(t, sosPost.Author, author) assertDatesEquals(t, sosPost.Dates, sosPosts[idx].Dates) assertFindSOSPostEquals(t, sosPost, sosPosts[idx]) @@ -170,7 +170,7 @@ func TestSOSPostService(t *testing.T) { addPets := tests.AddDummyPets(t, ctx, userService, uid, &profileImage.ID) sosPostService := service.NewSOSPostService(db) - imageIDs := []int64{int64(sosPostImage.ID), int64(sosPostImage2.ID)} + imageIDs := []int64{sosPostImage.ID, sosPostImage2.ID} conditionIDs := []int{1, 2} var sosPosts []sospost.WriteSOSPostView @@ -190,7 +190,7 @@ func TestSOSPostService(t *testing.T) { idx := len(sosPostList.Items) - i - 1 assertConditionEquals(t, sosPost.Conditions, conditionIDs) assertPetEquals(t, sosPost.Pets[i-1], addPets.Pets[i-1]) - assertMediaEquals(t, sosPost.Media, (&media.MediaList{sosPostImage, sosPostImage2}).ToMediaViewList()) + assertMediaEquals(t, sosPost.Media, media.ListView{sosPostImage, sosPostImage2}) assertAuthorEquals(t, sosPost.Author, author) assertDatesEquals(t, sosPost.Dates, sosPosts[idx].Dates) assertFindSOSPostEquals(t, sosPost, sosPosts[idx]) @@ -219,7 +219,7 @@ func TestSOSPostService(t *testing.T) { addPets := tests.AddDummyPets(t, ctx, userService, uid, &profileImage.ID) sosPostService := service.NewSOSPostService(db) - imageIDs := []int64{int64(sosPostImage.ID), int64(sosPostImage2.ID)} + imageIDs := []int64{sosPostImage.ID, sosPostImage2.ID} conditionIDs := []int{1, 2} var sosPosts []sospost.WriteSOSPostView @@ -252,7 +252,7 @@ func TestSOSPostService(t *testing.T) { idx := len(sosPostList.Items) - i - 1 assertConditionEquals(t, sosPost.Conditions, conditionIDs) assertPetEquals(t, sosPost.Pets[i-1], addPets.Pets[i-1]) - assertMediaEquals(t, sosPost.Media, (&media.MediaList{sosPostImage, sosPostImage2}).ToMediaViewList()) + assertMediaEquals(t, sosPost.Media, media.ListView{sosPostImage, sosPostImage2}) assertAuthorEquals(t, sosPost.Author, author) assertDatesEquals(t, sosPost.Dates, sosPosts[idx].Dates) assertFindSOSPostEquals(t, sosPost, sosPosts[idx]) @@ -280,7 +280,7 @@ func TestSOSPostService(t *testing.T) { addPet := tests.AddDummyPet(t, ctx, userService, uid, &profileImage.ID) sosPostService := service.NewSOSPostService(db) - imageIDs := []int64{int64(sosPostImage.ID), int64(sosPostImage2.ID)} + imageIDs := []int64{sosPostImage.ID, sosPostImage2.ID} conditionIDs := []int{1, 2} sosPosts := make([]sospost.WriteSOSPostView, 0) @@ -300,7 +300,7 @@ func TestSOSPostService(t *testing.T) { idx := len(sosPostListByAuthorID.Items) - i - 1 assertConditionEquals(t, sosPost.Conditions, conditionIDs) assertPetEquals(t, sosPost.Pets[0], *addPet) - assertMediaEquals(t, sosPost.Media, (&media.MediaList{sosPostImage, sosPostImage2}).ToMediaViewList()) + assertMediaEquals(t, sosPost.Media, media.ListView{sosPostImage, sosPostImage2}) assertAuthorEquals(t, sosPost.Author, author) assertDatesEquals(t, sosPost.Dates, sosPosts[idx].Dates) assertFindSOSPostEquals(t, sosPost, sosPosts[idx]) @@ -330,7 +330,7 @@ func TestSOSPostService(t *testing.T) { addPet := tests.AddDummyPet(t, ctx, userService, uid, &profileImage.ID) sosPostService := service.NewSOSPostService(db) - imageIDs := []int64{int64(sosPostImage.ID), int64(sosPostImage2.ID)} + imageIDs := []int64{sosPostImage.ID, sosPostImage2.ID} conditionIDs := []int{1, 2} sosPosts := make([]sospost.WriteSOSPostView, 0) @@ -348,7 +348,7 @@ func TestSOSPostService(t *testing.T) { // then assertConditionEquals(t, sosPosts[0].Conditions, conditionIDs) assertPetEquals(t, sosPosts[0].Pets[0], *addPet) - assertMediaEquals(t, findSOSPostByID.Media, (&media.MediaList{sosPostImage, sosPostImage2}).ToMediaViewList()) + assertMediaEquals(t, findSOSPostByID.Media, media.ListView{sosPostImage, sosPostImage2}) assertAuthorEquals(t, findSOSPostByID.Author, author) assertDatesEquals(t, findSOSPostByID.Dates, sosPosts[0].Dates) assertFindSOSPostEquals(t, *findSOSPostByID, sosPosts[0]) @@ -375,7 +375,7 @@ func TestSOSPostService(t *testing.T) { sosPostService := service.NewSOSPostService(db) sosPost := tests.WriteDummySOSPosts(t, ctx, - sosPostService, uid, []int64{int64(sosPostImage.ID)}, []int64{addPet.ID}, + sosPostService, uid, []int64{sosPostImage.ID}, []int64{addPet.ID}, 1, ) @@ -383,7 +383,7 @@ func TestSOSPostService(t *testing.T) { ID: sosPost.ID, Title: "Title2", Content: "Content2", - ImageIDs: []int{sosPostImage.ID, sosPostImage2.ID}, + ImageIDs: []int64{sosPostImage.ID, sosPostImage2.ID}, Reward: "Reward2", Dates: []sospost.SOSDateView{ {"2024-04-10", "2024-04-20"}, @@ -404,7 +404,7 @@ func TestSOSPostService(t *testing.T) { assertConditionEquals(t, sosPost.Conditions, updateSOSPostData.ConditionIDs) assertPetEquals(t, sosPost.Pets[0], *addPet) - assertMediaEquals(t, updateSOSPost.Media, (&media.MediaList{sosPostImage, sosPostImage2}).ToMediaViewList()) + assertMediaEquals(t, updateSOSPost.Media, media.ListView{sosPostImage, sosPostImage2}) assertDatesEquals(t, updateSOSPost.Dates, updateSOSPostData.Dates) if updateSOSPost.Title != updateSOSPostData.Title { @@ -520,7 +520,7 @@ func assertPetEquals(t *testing.T, got, want pet.DetailView) { } } -func assertMediaEquals(t *testing.T, got, want media.MediaViewList) { +func assertMediaEquals(t *testing.T, got, want media.ListView) { t.Helper() for i, mediaData := range want { diff --git a/internal/service/user_service.go b/internal/service/user_service.go index 02e324c9..8359733d 100644 --- a/internal/service/user_service.go +++ b/internal/service/user_service.go @@ -13,7 +13,6 @@ import ( "github.com/pet-sitter/pets-next-door-api/internal/domain/pet" "github.com/pet-sitter/pets-next-door-api/internal/domain/user" "github.com/pet-sitter/pets-next-door-api/internal/infra/database" - "github.com/pet-sitter/pets-next-door-api/internal/postgres" ) type UserService struct { @@ -96,7 +95,7 @@ func (service *UserService) ExistsByNickname(ctx context.Context, nickname strin } func (service *UserService) UpdateUserByUID( - ctx context.Context, uid, nickname string, profileImageID *int, + ctx context.Context, uid, nickname string, profileImageID *int64, ) (*user.MyProfileView, *pnd.AppError) { tx, err := service.conn.BeginTx(ctx) defer tx.Rollback() @@ -106,7 +105,7 @@ func (service *UserService) UpdateUserByUID( _, err2 := databasegen.New(service.conn).WithTx(tx.Tx).UpdateUserByFbUID(ctx, databasegen.UpdateUserByFbUIDParams{ Nickname: nickname, - ProfileImageID: utils.IntPtrToNullInt64(profileImageID), + ProfileImageID: utils.Int64PtrToNullInt64(profileImageID), FbUid: utils.StrToNullStr(uid), }) if err2 != nil { @@ -181,7 +180,7 @@ func (service *UserService) AddPetsToOwner( // 프로필 이미지 ID가 DB에 존재하는지 확인 for _, item := range addPetsRequest.Pets { if item.ProfileImageID != nil { - if _, err := postgres.FindMediaByID(ctx, tx, *item.ProfileImageID); err != nil { + if _, err := service.mediaService.FindMediaByID(ctx, *item.ProfileImageID); err != nil { return nil, pnd.ErrInvalidBody(fmt.Errorf("존재하지 않는 프로필 이미지 ID입니다. ID: %d", *item.ProfileImageID)) } } @@ -205,7 +204,7 @@ func (service *UserService) AddPetsToOwner( BirthDate: birthDate, WeightInKg: item.WeightInKg.String(), Remarks: item.Remarks, - ProfileImageID: utils.IntPtrToNullInt64(item.ProfileImageID), + ProfileImageID: utils.Int64PtrToNullInt64(item.ProfileImageID), } row, err := databasegen.New(service.conn).WithTx(tx.Tx).CreatePet(ctx, petToCreate) if err != nil { @@ -270,7 +269,7 @@ func (service *UserService) UpdatePet( BirthDate: birthDate, WeightInKg: updatePetRequest.WeightInKg.String(), Remarks: updatePetRequest.Remarks, - ProfileImageID: utils.IntPtrToNullInt64(updatePetRequest.ProfileImageID), + ProfileImageID: utils.Int64PtrToNullInt64(updatePetRequest.ProfileImageID), }); err != nil { return nil, pnd.FromPostgresError(err) } diff --git a/internal/tests/factories.go b/internal/tests/factories.go index e8cbda45..e1bf1c43 100644 --- a/internal/tests/factories.go +++ b/internal/tests/factories.go @@ -10,7 +10,7 @@ import ( "github.com/shopspring/decimal" ) -func GenerateDummyRegisterUserRequest(profileImageID *int) *user.RegisterUserRequest { +func GenerateDummyRegisterUserRequest(profileImageID *int64) *user.RegisterUserRequest { return &user.RegisterUserRequest{ Email: "test@example.com", Nickname: "nickname", @@ -21,7 +21,7 @@ func GenerateDummyRegisterUserRequest(profileImageID *int) *user.RegisterUserReq } } -func GenerateDummyAddPetRequest(profileImageID *int) *pet.AddPetRequest { +func GenerateDummyAddPetRequest(profileImageID *int64) *pet.AddPetRequest { birthDate, _ := datatype.ParseDate("2020-01-01") return &pet.AddPetRequest{ Name: "name", @@ -35,7 +35,7 @@ func GenerateDummyAddPetRequest(profileImageID *int) *pet.AddPetRequest { } } -func GenerateDummyAddPetsRequest(profileImageID *int) []pet.AddPetRequest { +func GenerateDummyAddPetsRequest(profileImageID *int64) []pet.AddPetRequest { birthDate1, _ := datatype.ParseDate("2020-01-01") birthDate2, _ := datatype.ParseDate("2020-02-01") birthDate3, _ := datatype.ParseDate("2020-03-01") diff --git a/internal/tests/service.go b/internal/tests/service.go index 309f5093..6af6809e 100644 --- a/internal/tests/service.go +++ b/internal/tests/service.go @@ -11,12 +11,9 @@ import ( "github.com/pet-sitter/pets-next-door-api/internal/service" ) -func AddDummyMedia(t *testing.T, ctx context.Context, mediaService *service.MediaService) *media.Media { +func AddDummyMedia(t *testing.T, ctx context.Context, mediaService *service.MediaService) *media.DetailView { t.Helper() - mediaData, err := mediaService.CreateMedia(ctx, &media.Media{ - MediaType: media.MediaTypeImage, - URL: "http://example.com", - }) + mediaData, err := mediaService.CreateMedia(ctx, media.TypeImage, "http://example.com") if err != nil { t.Errorf("got %v want %v", err, nil) } @@ -46,7 +43,7 @@ func AddDummyPet( ctx context.Context, userService *service.UserService, ownerUID string, - profileImageID *int, + profileImageID *int64, ) *pet.DetailView { t.Helper() petList, err := userService.AddPetsToOwner(ctx, ownerUID, pet.AddPetsToOwnerRequest{ @@ -64,7 +61,7 @@ func AddDummyPets( ctx context.Context, userService *service.UserService, ownerUID string, - profileImageID *int, + profileImageID *int64, ) pet.ListView { t.Helper() petList, err := userService.AddPetsToOwner(ctx, ownerUID, pet.AddPetsToOwnerRequest{ diff --git a/queries/media.sql b/queries/media.sql new file mode 100644 index 00000000..27ca3b58 --- /dev/null +++ b/queries/media.sql @@ -0,0 +1,19 @@ +-- name: CreateMedia :one +INSERT INTO media +(media_type, + url, + created_at, + updated_at) +VALUES ($1, $2, NOW(), NOW()) +RETURNING id, media_type, url, created_at, updated_at; + +-- name: FindSingleMedia :one +SELECT id, + media_type, + url, + created_at, + updated_at +FROM media +WHERE (id = sqlc.narg('id') OR sqlc.narg('id') IS NULL) + AND (sqlc.arg('include_deleted')::BOOLEAN = TRUE OR + (sqlc.arg('include_deleted')::BOOLEAN = FALSE AND deleted_at IS NULL)); diff --git a/queries/resource_media.sql b/queries/resource_media.sql new file mode 100644 index 00000000..29a0733b --- /dev/null +++ b/queries/resource_media.sql @@ -0,0 +1,25 @@ +-- name: CreateResourceMedia :one +INSERT INTO resource_media +(resource_id, + media_id, + resource_type, + created_at, + updated_at) +VALUES ($1, $2, $3, NOW(), NOW()) +RETURNING id, resource_id, media_id, resource_type, created_at, updated_at; + +-- name: FindResourceMedia :many +SELECT m.id AS media_id, + m.media_type, + m.url, + m.created_at, + m.updated_at +FROM resource_media rm + INNER JOIN + media m + ON + rm.media_id = m.id +WHERE (rm.resource_id = sqlc.narg('resource_id') OR sqlc.narg('resource_id') IS NULL) + AND (rm.resource_type = sqlc.narg('resource_type') OR sqlc.narg('resource_type') IS NULL) + AND (sqlc.arg('include_deleted')::BOOLEAN = TRUE OR + (sqlc.arg('include_deleted')::BOOLEAN = FALSE AND rm.deleted_at IS NULL));