From e88d67b774ae615208b25133c299f2d50b3a018b Mon Sep 17 00:00:00 2001
From: 6543 <6543@obermui.de>
Date: Wed, 8 Jan 2020 08:00:59 +0100
Subject: [PATCH] [API] add comments endpoint for single comment (#9494)

* add GET /repos/{owner}/{repo}/issues/comments/{id}
 and complete error list for swagger in other func

* add repo check
---
 integrations/api_comment_test.go     | 27 ++++++++++
 routers/api/v1/api.go                |  7 +--
 routers/api/v1/repo/issue_comment.go | 81 ++++++++++++++++++++++++++++
 templates/swagger/v1_json.tmpl       | 69 ++++++++++++++++++++++++
 4 files changed, 181 insertions(+), 3 deletions(-)

diff --git a/integrations/api_comment_test.go b/integrations/api_comment_test.go
index 0716c5d859..d21f56aaf8 100644
--- a/integrations/api_comment_test.go
+++ b/integrations/api_comment_test.go
@@ -83,6 +83,33 @@ func TestAPICreateComment(t *testing.T) {
 	models.AssertExistsAndLoadBean(t, &models.Comment{ID: updatedComment.ID, IssueID: issue.ID, Content: commentBody})
 }
 
+func TestAPIGetComment(t *testing.T) {
+	defer prepareTestEnv(t)()
+
+	comment := models.AssertExistsAndLoadBean(t, &models.Comment{ID: 2}).(*models.Comment)
+	assert.NoError(t, comment.LoadIssue())
+	repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: comment.Issue.RepoID}).(*models.Repository)
+	repoOwner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+
+	session := loginUser(t, repoOwner.Name)
+	token := getTokenForLoggedInUser(t, session)
+	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d", repoOwner.Name, repo.Name, comment.ID)
+	resp := session.MakeRequest(t, req, http.StatusOK)
+	req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d?token=%s", repoOwner.Name, repo.Name, comment.ID, token)
+	resp = session.MakeRequest(t, req, http.StatusOK)
+
+	var apiComment api.Comment
+	DecodeJSON(t, resp, &apiComment)
+
+	assert.NoError(t, comment.LoadPoster())
+	expect := comment.APIFormat()
+
+	assert.Equal(t, expect.ID, apiComment.ID)
+	assert.Equal(t, expect.Poster.FullName, apiComment.Poster.FullName)
+	assert.Equal(t, expect.Body, apiComment.Body)
+	assert.Equal(t, expect.Created.Unix(), apiComment.Created.Unix())
+}
+
 func TestAPIEditComment(t *testing.T) {
 	defer prepareTestEnv(t)()
 	const newCommentBody = "This is the new comment body"
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index e4288f40f6..3f766c7a74 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -661,9 +661,10 @@ func RegisterRoutes(m *macaron.Macaron) {
 					m.Group("/comments", func() {
 						m.Get("", repo.ListRepoIssueComments)
 						m.Group("/:id", func() {
-							m.Combo("", reqToken()).
-								Patch(mustNotBeArchived, bind(api.EditIssueCommentOption{}), repo.EditIssueComment).
-								Delete(repo.DeleteIssueComment)
+							m.Combo("").
+								Get(repo.GetIssueComment).
+								Patch(mustNotBeArchived, reqToken(), bind(api.EditIssueCommentOption{}), repo.EditIssueComment).
+								Delete(reqToken(), repo.DeleteIssueComment)
 							m.Combo("/reactions").
 								Get(repo.GetIssueCommentReactions).
 								Post(bind(api.EditReactionOption{}), reqToken(), repo.PostIssueCommentReaction).
diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go
index c13fc93cdf..3085c2e51f 100644
--- a/routers/api/v1/repo/issue_comment.go
+++ b/routers/api/v1/repo/issue_comment.go
@@ -204,6 +204,74 @@ func CreateIssueComment(ctx *context.APIContext, form api.CreateIssueCommentOpti
 	ctx.JSON(http.StatusCreated, comment.APIFormat())
 }
 
+// GetIssueComment Get a comment by ID
+func GetIssueComment(ctx *context.APIContext) {
+	// swagger:operation GET /repos/{owner}/{repo}/issues/comments/{id} issue issueGetComment
+	// ---
+	// summary: Get a comment
+	// consumes:
+	// - application/json
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo
+	//   type: string
+	//   required: true
+	// - name: id
+	//   in: path
+	//   description: id of the comment
+	//   type: integer
+	//   format: int64
+	//   required: true
+	// responses:
+	//   "200":
+	//     "$ref": "#/responses/Comment"
+	//   "204":
+	//     "$ref": "#/responses/empty"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
+
+	comment, err := models.GetCommentByID(ctx.ParamsInt64(":id"))
+	if err != nil {
+		if models.IsErrCommentNotExist(err) {
+			ctx.NotFound(err)
+		} else {
+			ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
+		}
+		return
+	}
+
+	if err = comment.LoadIssue(); err != nil {
+		ctx.InternalServerError(err)
+		return
+	}
+	if comment.Issue.RepoID != ctx.Repo.Repository.ID {
+		ctx.Status(http.StatusNotFound)
+		return
+	}
+
+	if comment.Type != models.CommentTypeComment {
+		ctx.Status(http.StatusNoContent)
+		return
+	}
+
+	if err := comment.LoadPoster(); err != nil {
+		ctx.Error(http.StatusInternalServerError, "comment.LoadPoster", err)
+		return
+	}
+
+	ctx.JSON(http.StatusOK, comment.APIFormat())
+}
+
 // EditIssueComment modify a comment of an issue
 func EditIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) {
 	// swagger:operation PATCH /repos/{owner}/{repo}/issues/comments/{id} issue issueEditComment
@@ -237,6 +305,13 @@ func EditIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/Comment"
+	//   "204":
+	//     "$ref": "#/responses/empty"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
+
 	editIssueComment(ctx, form)
 }
 
@@ -283,6 +358,8 @@ func EditIssueCommentDeprecated(ctx *context.APIContext, form api.EditIssueComme
 	//     "$ref": "#/responses/empty"
 	//   "403":
 	//     "$ref": "#/responses/forbidden"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
 
 	editIssueComment(ctx, form)
 }
@@ -343,6 +420,8 @@ func DeleteIssueComment(ctx *context.APIContext) {
 	//     "$ref": "#/responses/empty"
 	//   "403":
 	//     "$ref": "#/responses/forbidden"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
 
 	deleteIssueComment(ctx)
 }
@@ -380,6 +459,8 @@ func DeleteIssueCommentDeprecated(ctx *context.APIContext) {
 	//     "$ref": "#/responses/empty"
 	//   "403":
 	//     "$ref": "#/responses/forbidden"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
 
 	deleteIssueComment(ctx)
 }
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 45e84d3ed0..f37c900cca 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -3002,6 +3002,57 @@
       }
     },
     "/repos/{owner}/{repo}/issues/comments/{id}": {
+      "get": {
+        "consumes": [
+          "application/json"
+        ],
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "issue"
+        ],
+        "summary": "Get a comment",
+        "operationId": "issueGetComment",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "integer",
+            "format": "int64",
+            "description": "id of the comment",
+            "name": "id",
+            "in": "path",
+            "required": true
+          }
+        ],
+        "responses": {
+          "200": {
+            "$ref": "#/responses/Comment"
+          },
+          "204": {
+            "$ref": "#/responses/empty"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
+          "404": {
+            "$ref": "#/responses/notFound"
+          }
+        }
+      },
       "delete": {
         "tags": [
           "issue"
@@ -3038,6 +3089,9 @@
           },
           "403": {
             "$ref": "#/responses/forbidden"
+          },
+          "404": {
+            "$ref": "#/responses/notFound"
           }
         }
       },
@@ -3087,6 +3141,15 @@
         "responses": {
           "200": {
             "$ref": "#/responses/Comment"
+          },
+          "204": {
+            "$ref": "#/responses/empty"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
+          "404": {
+            "$ref": "#/responses/notFound"
           }
         }
       }
@@ -3491,6 +3554,9 @@
           },
           "403": {
             "$ref": "#/responses/forbidden"
+          },
+          "404": {
+            "$ref": "#/responses/notFound"
           }
         }
       },
@@ -3554,6 +3620,9 @@
           },
           "403": {
             "$ref": "#/responses/forbidden"
+          },
+          "404": {
+            "$ref": "#/responses/notFound"
           }
         }
       }