Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
6c21bd0
wishboardのrepositoryを追加
sana37 Sep 19, 2020
f5e3264
ドメイン層までの実装およびusers_wish_boards関連の追加
sana37 Sep 20, 2020
c1c1a37
create, get系までの実装およびwishBoardService.Createへのリレーション関連の処理追加
sana37 Sep 20, 2020
380d7f6
セミコロンは不要
sana37 Sep 21, 2020
63a937a
repository.Insertの引数を変更
sana37 Sep 21, 2020
bfea40e
time関連の修正
sana37 Sep 22, 2020
e77a907
wish_boardのupdate, deleteを追加
sana37 Sep 22, 2020
7ded141
update系を追加
sana37 Sep 22, 2020
e8ab473
ユーザのボード一覧について、所有一覧ではなく所属一覧へと修正
sana37 Sep 22, 2020
f1f6d6e
delete系を追加
sana37 Sep 22, 2020
a53b1e0
コメントを追加
sana37 Sep 23, 2020
97048bf
Url -> URL へと変更
sana37 Sep 23, 2020
af285f7
タイトルのバリデーションを関数として切り出し
sana37 Sep 26, 2020
4bcf2c3
命名を修正
sana37 Sep 26, 2020
651fca9
配列生成部分を修正
sana37 Sep 26, 2020
7c0a566
空リストを返したいときはnilでも同じ
sana37 Sep 26, 2020
3f420b1
時刻の取得をdomain層で行う
sana37 Sep 26, 2020
a7f6368
Merge branch 'develop' into feat/3/implWishListUsecase
TakumaKurosawa Sep 27, 2020
f040add
- datetimeにtime.Timeのポインタ型を使用
sana37 Sep 27, 2020
ea185d9
コメントの修正
sana37 Sep 27, 2020
72509ac
メソッド名を変更
sana37 Sep 27, 2020
1a419a6
GetByOwnerを削除、GetByMemberをGetMyBoardsに変更
sana37 Sep 27, 2020
27b7929
コメントは最低限必要なものに限る
sana37 Sep 27, 2020
cc13e1a
変数名をわかりやすく
sana37 Sep 27, 2020
f80c6d0
本プルリクの対象ではないメソッドを削除
sana37 Sep 27, 2020
f0abdec
変数名の修正
sana37 Sep 27, 2020
88962cd
コーディングスタイルの統一
sana37 Sep 27, 2020
471ac90
エラーログメッセージを修正
sana37 Sep 27, 2020
f9dd192
2点修正
sana37 Sep 27, 2020
d9759d2
空タイトル判定は別関数に切り出したりしない
sana37 Oct 4, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 219 additions & 0 deletions pkg/api/usecase/wishboard/interactor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
package wishboard

import (
"context"
"errors"
"wantum/pkg/domain/entity/wishboard"
"wantum/pkg/domain/repository"
fileservice "wantum/pkg/domain/service/file"
userservice "wantum/pkg/domain/service/user"
wishboardservice "wantum/pkg/domain/service/wishboard"
"wantum/pkg/tlog"
"wantum/pkg/werrors"
)

type Interactor interface {
CreateNewWishBoard(ctx context.Context, authID, title string, backgroundImage []byte) (*wishboard.Entity, error)
GetMyWishBoards(ctx context.Context, authID string) (wishboard.EntitySlice, error)
UpdateTitle(ctx context.Context, wishBoardID int, title, authID string) error
UpdateBackgroundImage(ctx context.Context, wishBoardID int, backgroundImage []byte, authID string) error
DeleteWishBoard(ctx context.Context, wishBoardID int, authID string) error
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ただの気づきです。。。
interactor内のメソッドの引数、確かにauthIDのがいいかもな...!わたしuserIDで実装してたけど、userIDってよく考えたら取れないのか...!!( ゚д゚)

}

type interactor struct {
masterTxManager repository.MasterTxManager
userService userservice.Service
wishBoardService wishboardservice.Service
fileService fileservice.Service
}

func New(masterTxManager repository.MasterTxManager, userService userservice.Service, wishBoardService wishboardservice.Service, fileService fileservice.Service) Interactor {
return &interactor{
masterTxManager: masterTxManager,
userService: userService,
wishBoardService: wishBoardService,
fileService: fileService,
}
}

func (i *interactor) CreateNewWishBoard(ctx context.Context, authID, title string, backgroundImage []byte) (*wishboard.Entity, error) {
if title == "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感想ですー!
この辺のバリデーションなのだけど、項目増えていくと膨れ上がっていくから、関数として出しちゃってもいいかも。
公開にする必要はないだろうから、validateTitle(title string) errorくらいな感じで。

err := errors.New("Error occurred when board title validation.")
tlog.PrintErrorLogWithCtx(ctx, err)
return nil, werrors.FromConstant(err, werrors.BadRequest)
}

var wishBoardEntity *wishboard.Entity
err := i.masterTxManager.Transaction(ctx, func(ctx context.Context, masterTx repository.MasterTx) error {
userEntity, err := i.userService.GetByAuthID(ctx, masterTx, authID)
if err != nil {
return werrors.Stack(err)
}

// 背景画像を保存し、URLを取得
backgroundImageURL, err := i.fileService.UploadImageToLocalFolder(backgroundImage)
if err != nil {
return werrors.Stack(err)
}

// TODO: 招待URLの自動生成
inviteURL := "hoge"
Comment on lines +59 to +60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uuidで自動生成するだけやと思うから、ここで実装しちゃっても良いかもね?w

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

あと、実装する場合は、 wishBoardService.CreateInviteURLみたいにService層の実装として欲しい!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

service層で実装する了解!
一応、仕様がはっきり決まるまでは実装しないでおく!
あと思ったんだけど、grpcで招待URLってどうやって使うんだろう…?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

あと思ったんだけど、grpcで招待URLってどうやって使うんだろう…?

招待を受け付けるエンドポイントをRESTで用意してあげる必要があるね!


wishBoardEntity, err = i.wishBoardService.Create(ctx, masterTx, title, backgroundImageURL, inviteURL, userEntity.ID)
if err != nil {
return werrors.Stack(err)
}

return nil
})
if err != nil {
return nil, werrors.Stack(err)
}

return wishBoardEntity, nil
}

func (i *interactor) GetMyWishBoards(ctx context.Context, authID string) (wishboard.EntitySlice, error) {
var wishBoardSlice wishboard.EntitySlice
err := i.masterTxManager.Transaction(ctx, func(ctx context.Context, masterTx repository.MasterTx) error {
userEntity, err := i.userService.GetByAuthID(ctx, masterTx, authID)
if err != nil {
return werrors.Stack(err)
}

// 自分が所属しているWishBoardのリストを取得
wishBoardSlice, err = i.wishBoardService.GetMyBoards(ctx, masterTx, userEntity.ID)
if err != nil {
return werrors.Stack(err)
}

return nil
})
if err != nil {
return nil, werrors.Stack(err)
}

return wishBoardSlice, nil
}

func (i *interactor) UpdateTitle(ctx context.Context, wishBoardID int, title, authID string) error {
if title == "" {
err := errors.New("Error occurred when board title validation.")
tlog.PrintErrorLogWithCtx(ctx, err)
return werrors.FromConstant(err, werrors.BadRequest)
}

err := i.masterTxManager.Transaction(ctx, func(ctx context.Context, masterTx repository.MasterTx) error {
userEntity, err := i.userService.GetByAuthID(ctx, masterTx, authID)
if err != nil {
return werrors.Stack(err)
}

// WishBoardが存在するか確認
wishBoardEntity, err := i.wishBoardService.GetByPK(ctx, masterTx, wishBoardID)
if err != nil {
return werrors.Stack(err)
}
Comment on lines +112 to +116
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WishBoardが存在するかのチェックが抜けてる気がする!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

いや、これはWishBoardを取得してエラーが出ないイコールWishBoardは存在しているって意味だから
これでok!


// ユーザがWishBoardのメンバーでなければPermissionDenied
isMember, err := i.wishBoardService.IsMember(ctx, masterTx, userEntity.ID, wishBoardEntity.ID)
if err != nil {
return werrors.Stack(err)
}
if !isMember {
err := errors.New("Error occurred when update board title. cause: permission denied")
tlog.PrintErrorLogWithCtx(ctx, err)
return werrors.FromConstant(err, werrors.WishBoardPermissionDenied)
}

if err := i.wishBoardService.UpdateTitle(ctx, masterTx, wishBoardEntity.ID, title); err != nil {
return werrors.Stack(err)
}
return nil
})
if err != nil {
return werrors.Stack(err)
}

return nil
}

func (i *interactor) UpdateBackgroundImage(ctx context.Context, wishBoardID int, backgroundImage []byte, authID string) error {
err := i.masterTxManager.Transaction(ctx, func(ctx context.Context, masterTx repository.MasterTx) error {
userEntity, err := i.userService.GetByAuthID(ctx, masterTx, authID)
if err != nil {
return werrors.Stack(err)
}

// WishBoardが存在するか確認
wishBoardEntity, err := i.wishBoardService.GetByPK(ctx, masterTx, wishBoardID)
if err != nil {
return werrors.Stack(err)
}

// ユーザがWishBoardのメンバーでなければPermissionDenied
isMember, err := i.wishBoardService.IsMember(ctx, masterTx, userEntity.ID, wishBoardEntity.ID)
if err != nil {
return werrors.Stack(err)
}
if !isMember {
err := errors.New("Error occurred when update board background image. cause: permission denied")
tlog.PrintErrorLogWithCtx(ctx, err)
return werrors.FromConstant(err, werrors.WishBoardPermissionDenied)
}

// 背景画像を保存し、URLを取得
backgroundImageURL, err := i.fileService.UploadImageToLocalFolder(backgroundImage)
if err != nil {
return werrors.Stack(err)
}

if err := i.wishBoardService.UpdateBackgroundImageURL(ctx, masterTx, wishBoardEntity.ID, backgroundImageURL); err != nil {
return werrors.Stack(err)
}

return nil
})
if err != nil {
return werrors.Stack(err)
}

return nil
}

func (i *interactor) DeleteWishBoard(ctx context.Context, wishBoardID int, authID string) error {
err := i.masterTxManager.Transaction(ctx, func(ctx context.Context, masterTx repository.MasterTx) error {
userEntity, err := i.userService.GetByAuthID(ctx, masterTx, authID)
if err != nil {
return werrors.Stack(err)
}

// WishBoardが存在するか確認
wishBoardEntity, err := i.wishBoardService.GetByPK(ctx, masterTx, wishBoardID)
if err != nil {
return werrors.Stack(err)
}

// ユーザがWishBoardのメンバーでなければPermissionDenied
isMember, err := i.wishBoardService.IsMember(ctx, masterTx, userEntity.ID, wishBoardEntity.ID)
if err != nil {
return werrors.Stack(err)
}
if !isMember {
err := errors.New("Error occurred when delete board. cause: permission denied")
tlog.PrintErrorLogWithCtx(ctx, err)
return werrors.FromConstant(err, werrors.WishBoardPermissionDenied)
}

if err := i.wishBoardService.Delete(ctx, masterTx, wishBoardEntity.ID); err != nil {
return werrors.Stack(err)
}

return nil
})
if err != nil {
return werrors.Stack(err)
}

return nil
}
17 changes: 17 additions & 0 deletions pkg/domain/entity/wishboard/entity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package wishboard

import "time"

type Entity struct {
ID int
Title string
BackgroundImageURL string
InviteURL string
UserID int
CreatedAt *time.Time
UpdatedAt *time.Time
DeletedAt *time.Time
// WishCategories
}

type EntitySlice []*Entity
13 changes: 13 additions & 0 deletions pkg/domain/repository/userwishboard/repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package userwishboard

import (
"context"
"wantum/pkg/domain/repository"
)

type Repository interface {
Insert(ctx context.Context, masterTx repository.MasterTx, userID, wishBoardID int) error
Exists(ctx context.Context, masterTx repository.MasterTx, userID, wishBoardID int) (bool, error)
SelectWishBoardIDsByUserID(ctx context.Context, masterTx repository.MasterTx, userID int) ([]int, error)
Delete(ctx context.Context, masterTx repository.MasterTx, userID, wishBoardID int) error
}
17 changes: 17 additions & 0 deletions pkg/domain/repository/wishboard/repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package wishboard

import (
"context"
"time"
"wantum/pkg/domain/entity/wishboard"
"wantum/pkg/domain/repository"
)

type Repository interface {
Insert(ctx context.Context, masterTx repository.MasterTx, wishBoardEntity *wishboard.Entity) (*wishboard.Entity, error)
SelectByPK(ctx context.Context, masterTx repository.MasterTx, wishBoardID int) (*wishboard.Entity, error)
SelectByPKs(ctx context.Context, masterTx repository.MasterTx, wishBoardIDs []int) (wishboard.EntitySlice, error)
UpdateTitle(ctx context.Context, masterTx repository.MasterTx, wishBoardID int, title string, updatedAt *time.Time) error
UpdateBackgroundImageURL(ctx context.Context, masterTx repository.MasterTx, wishBoardID int, backgroundImageURL string, updatedAt *time.Time) error
Delete(ctx context.Context, masterTx repository.MasterTx, wishBoardEntity *wishboard.Entity) error
}
124 changes: 124 additions & 0 deletions pkg/domain/service/wishboard/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package wishboard

import (
"context"
"time"
"wantum/pkg/domain/entity/wishboard"
"wantum/pkg/domain/repository"
userwishboardrepository "wantum/pkg/domain/repository/userwishboard"
wishboardrepository "wantum/pkg/domain/repository/wishboard"
"wantum/pkg/werrors"
)

type Service interface {
Create(ctx context.Context, masterTx repository.MasterTx, title, backgroundImageURL, inviteURL string, userID int) (*wishboard.Entity, error)
GetByPK(ctx context.Context, masterTx repository.MasterTx, wishBoardID int) (*wishboard.Entity, error)
GetMyBoards(ctx context.Context, masterTx repository.MasterTx, userID int) (wishboard.EntitySlice, error)
IsMember(ctx context.Context, masterTx repository.MasterTx, userID, wishBoardID int) (bool, error)
UpdateTitle(ctx context.Context, masterTx repository.MasterTx, wishBoardID int, title string) error
UpdateBackgroundImageURL(ctx context.Context, masterTx repository.MasterTx, wishBoardID int, backgroundImageURL string) error
Delete(ctx context.Context, masterTx repository.MasterTx, wishBoardID int) error
}

type service struct {
wishBoardRepository wishboardrepository.Repository
userWishBoardRepository userwishboardrepository.Repository
}

func New(wishBoardRepository wishboardrepository.Repository, userWishBoardRepository userwishboardrepository.Repository) Service {
return &service{
wishBoardRepository: wishBoardRepository,
userWishBoardRepository: userWishBoardRepository,
}
}

func (s *service) Create(ctx context.Context, masterTx repository.MasterTx, title, backgroundImageURL, inviteURL string, userID int) (*wishboard.Entity, error) {
now := time.Now()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sana37
@akubi0w1
現在時刻を取得するコードが至る所に散見されるようになると、Service層で作ったりRepositoryで作ったり、人によって実装がバラバラになってしまったりすると思うので、できればMiddlewareかどこかでContextにいれるみたいな処理にしたいね!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

たしかに!
新しくissue作って別のプルリクでその修正やる?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sana37
そうやね!それのIssueは立てようと思います!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらで立てました。
#32


newWishBoard := &wishboard.Entity{
Title: title,
BackgroundImageURL: backgroundImageURL,
InviteURL: inviteURL,
UserID: userID,
CreatedAt: &now,
UpdatedAt: &now,
}

createdWishBoard, err := s.wishBoardRepository.Insert(ctx, masterTx, newWishBoard)
if err != nil {
return nil, werrors.Stack(err)
}

if err := s.userWishBoardRepository.Insert(ctx, masterTx, userID, createdWishBoard.ID); err != nil {
return nil, werrors.Stack(err)
}

return createdWishBoard, nil
}

func (s *service) GetByPK(ctx context.Context, masterTx repository.MasterTx, wishBoardID int) (*wishboard.Entity, error) {
wishBoardEntity, err := s.wishBoardRepository.SelectByPK(ctx, masterTx, wishBoardID)
if err != nil {
return nil, werrors.Stack(err)
}
return wishBoardEntity, nil
}

func (s *service) GetMyBoards(ctx context.Context, masterTx repository.MasterTx, userID int) (wishboard.EntitySlice, error) {
wishBoardIDs, err := s.userWishBoardRepository.SelectWishBoardIDsByUserID(ctx, masterTx, userID)
if err != nil {
return nil, werrors.Stack(err)
}

if len(wishBoardIDs) == 0 {
return nil, nil
}

wishBoardSlice, err := s.wishBoardRepository.SelectByPKs(ctx, masterTx, wishBoardIDs)
if err != nil {
return nil, werrors.Stack(err)
}

return wishBoardSlice, nil
}

func (s *service) IsMember(ctx context.Context, masterTx repository.MasterTx, userID, wishBoardID int) (bool, error) {
exists, err := s.userWishBoardRepository.Exists(ctx, masterTx, userID, wishBoardID)
if err != nil {
return false, werrors.Stack(err)
}
return exists, nil
}

func (s *service) UpdateTitle(ctx context.Context, masterTx repository.MasterTx, wishBoardID int, title string) error {
now := time.Now()

if err := s.wishBoardRepository.UpdateTitle(ctx, masterTx, wishBoardID, title, &now); err != nil {
return werrors.Stack(err)
}
return nil
}

func (s *service) UpdateBackgroundImageURL(ctx context.Context, masterTx repository.MasterTx, wishBoardID int, backgroundImageURL string) error {
now := time.Now()

if err := s.wishBoardRepository.UpdateBackgroundImageURL(ctx, masterTx, wishBoardID, backgroundImageURL, &now); err != nil {
return werrors.Stack(err)
}
return nil
}

func (s *service) Delete(ctx context.Context, masterTx repository.MasterTx, wishBoardID int) error {
now := time.Now()

wishBoardEntity := &wishboard.Entity{
ID: wishBoardID,
UpdatedAt: &now,
DeletedAt: &now,
}

if err := s.wishBoardRepository.Delete(ctx, masterTx, wishBoardEntity); err != nil {
return werrors.Stack(err)
}
return nil
}
Loading