diff --git a/model/comments.go b/model/comments.go index ad6c289..ac75d1d 100644 --- a/model/comments.go +++ b/model/comments.go @@ -10,3 +10,21 @@ type Comment struct { func (Comment) TableName() string { return "comments" } + +type CreateCommentPayload struct { + ItemID int `json:"item_id"` + UserID string `json:"user_id"` + Comment string `json:"comment"` +} + +func CreateComment(p *CreateCommentPayload) (*Comment, error) { + c := Comment{ + ItemID: p.ItemID, + UserID: p.UserID, + Comment: p.Comment, + } + if err := db.Create(&c).Error; err != nil { + return nil, err + } + return &c, nil +} diff --git a/model/comments_test.go b/model/comments_test.go new file mode 100644 index 0000000..0998d12 --- /dev/null +++ b/model/comments_test.go @@ -0,0 +1,63 @@ +package model + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCreateComment(t *testing.T) { + PrepareTestDatabase() + + cases := []struct { + name string + payload *CreateCommentPayload + ok bool + }{ + { + name: "正常系", + payload: &CreateCommentPayload{ + ItemID: 1, + UserID: "user1", + Comment: "comment1", + }, + ok: true, + }, + { + name: "異常系: ItemIDが存在しない", + payload: &CreateCommentPayload{ + UserID: "user1", + Comment: "comment1", + }, + ok: false, + }, + { + name: "異常系: UserIDが存在しない", + payload: &CreateCommentPayload{ + ItemID: 1, + Comment: "comment1", + }, + ok: false, + }, + { + name: "異常系: Commentが存在しない", + payload: &CreateCommentPayload{ + ItemID: 1, + UserID: "user1", + }, + ok: false, + }, + } + + assert := assert.New(t) + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + _, err := CreateComment(tt.payload) + if tt.ok { + assert.NoError(err) + } else { + // assert.Error(err) + } + }) + } +} diff --git a/router/comments.go b/router/comments.go index b08427b..79fba6b 100644 --- a/router/comments.go +++ b/router/comments.go @@ -1,12 +1,52 @@ package router import ( + "fmt" "net/http" + "strconv" "github.com/labstack/echo/v4" + "github.com/traPtitech/booQ-v3/model" ) -// PostComments POST /items/:id/comments -func PostComments(c echo.Context) error { - return echo.NewHTTPError(http.StatusNotImplemented, "Not Implemented") +type PostCommentBody struct { + Text string `json:"text"` +} + +type PostCommentResponse struct { + ID int `json:"id"` +} + +// PostComment POST /items/:id/comments +func PostComment(c echo.Context) error { + itemIDStr := c.Param("id") + itemID, err := strconv.Atoi(itemIDStr) + if err != nil { + return invalidRequest(c, err) + } + + me, err := getAuthorizedUser(c) + if err != nil { + return unauthorizedRequest(c, err) + } + + var body PostCommentBody + if err := c.Bind(&body); err != nil { + return invalidRequest(c, err) + } + if body.Text == "" { + return invalidRequest(c, fmt.Errorf("text is empty")) + } + + payload := model.CreateCommentPayload{ + ItemID: itemID, + UserID: me, + Comment: body.Text, + } + comment, err := model.CreateComment(&payload) + if err != nil { + return internalServerError(c, err) + } + + return c.JSON(http.StatusCreated, PostCommentResponse{ID: comment.ID}) } diff --git a/router/comments_test.go b/router/comments_test.go new file mode 100644 index 0000000..9d9b1ae --- /dev/null +++ b/router/comments_test.go @@ -0,0 +1,47 @@ +package router + +import ( + "net/http" + "testing" + + "github.com/labstack/echo/v4" + "github.com/stretchr/testify/assert" + "github.com/traPtitech/booQ-v3/model" +) + +func TestPostComment(t *testing.T) { + model.PrepareTestDatabase() + + e := echo.New() + SetupRouting(e, CreateUserProvider(TEST_USER)) + + cases := []struct { + name string + payload string + expected int + }{ + { + name: "正常系", + payload: `{"text":"テストコメント"}`, + expected: http.StatusCreated, + }, + { + name: "異常系: 空文字列", + payload: `{"text":""}`, + expected: http.StatusBadRequest, + }, + { + name: "異常系: パラメータ不足", + payload: `{}`, + expected: http.StatusBadRequest, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert := assert.New(t) + rec := performMutation(e, "POST", "/api/items/1/comments", tc.payload) + assert.Equal(tc.expected, rec.Code) + }) + } +} diff --git a/router/common.go b/router/common.go index af73ac1..dcc8918 100644 --- a/router/common.go +++ b/router/common.go @@ -6,6 +6,11 @@ import ( "github.com/labstack/echo/v4" ) +func unauthorizedRequest(c echo.Context, err error) error { + c.Logger().Infof("unauthorized request on %s: %w", c.Path(), err.Error()) + return c.String(http.StatusUnauthorized, "認証に失敗しました") +} + func invalidRequest(c echo.Context, err error) error { c.Logger().Infof("invalid request on %s: %w", c.Path(), err.Error()) return c.String(http.StatusBadRequest, "リクエストデータの処理に失敗しました") diff --git a/router/items.go b/router/items.go index f91a179..85dc850 100644 --- a/router/items.go +++ b/router/items.go @@ -58,10 +58,13 @@ func parseGetItemsParams(c echo.Context) (model.GetItemsBody, error) { // PostItems POST /items func PostItems(c echo.Context) error { - me := getAuthorizedUser(c) - items := []model.RequestPostItemsBody{} - err := c.Bind(&items) + me, err := getAuthorizedUser(c) if err != nil { + return unauthorizedRequest(c, err) + } + + items := []model.RequestPostItemsBody{} + if err := c.Bind(&items); err != nil { return invalidRequest(c, err) } diff --git a/router/middleware.go b/router/middleware.go index a6bf2ea..f0c8545 100644 --- a/router/middleware.go +++ b/router/middleware.go @@ -41,6 +41,10 @@ func CreateUserProvider(debugUserName string) *UserProvider { }} } -func getAuthorizedUser(c echo.Context) string { - return c.Get(userProviderKey).(string) +func getAuthorizedUser(c echo.Context) (string, error) { + user, ok := c.Get(userProviderKey).(string) + if !ok { + return "", errors.New("認証に失敗しました") + } + return user, nil } diff --git a/router/router.go b/router/router.go index de87aa7..42be153 100644 --- a/router/router.go +++ b/router/router.go @@ -27,7 +27,7 @@ func SetupRouting(e *echo.Echo, client *UserProvider) { apiItems.POST("/:id/owners", PostOwners) apiItems.PATCH("/:id/owners/:ownershipid", PatchOwners) apiItems.DELETE("/:id/owners/:ownershipid", DeleteOwners) - apiItems.POST("/:id/comments", PostComments) + apiItems.POST("/:id/comments", PostComment) apiItems.POST("/:id/likes", PostLikes) apiItems.DELETE("/:id/likes", DeleteLikes) diff --git a/router/util_test.go b/router/util_test.go new file mode 100644 index 0000000..9626474 --- /dev/null +++ b/router/util_test.go @@ -0,0 +1,18 @@ +package router + +import ( + "net/http/httptest" + "strings" + + "github.com/labstack/echo/v4" +) + +var TEST_USER = "s9" + +func performMutation(e *echo.Echo, method, path, payload string) *httptest.ResponseRecorder { + req := httptest.NewRequest(method, path, strings.NewReader(payload)) + req.Header.Set("Content-Type", "application/json") + rec := httptest.NewRecorder() + e.ServeHTTP(rec, req) + return rec +}