From f23c3286937b1dc8fc98957d113bd0a7b8f3f7bb Mon Sep 17 00:00:00 2001
From: Johan Van de Wauw <johan@gisky.be>
Date: Thu, 24 Sep 2020 14:36:37 +0200
Subject: [PATCH 1/8] Add release by tags endpoint

Get a release based on a tag name (for which a release exists).
Based on:
https://developer.github.com/v3/repos/releases/#get-a-release-by-tag-name
---
 routers/api/v1/api.go               |  3 ++
 routers/api/v1/repo/release_tags.go | 54 +++++++++++++++++++++++++++++
 2 files changed, 57 insertions(+)
 create mode 100644 routers/api/v1/repo/release_tags.go

diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 8b3a7545c63ae..ca52bcfbcbd83 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -797,6 +797,9 @@ func RegisterRoutes(m *macaron.Macaron) {
 								Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteReleaseAttachment)
 						})
 					})
+					m.Group("/tags", func() {
+						m.Get("/:tag", repo.GetReleaseTag)
+					})
 				}, reqRepoReader(models.UnitTypeReleases))
 				m.Post("/mirror-sync", reqToken(), reqRepoWriter(models.UnitTypeCode), repo.MirrorSync)
 				m.Get("/editorconfig/:filename", context.RepoRef(), reqRepoReader(models.UnitTypeCode), repo.GetEditorconfig)
diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go
new file mode 100644
index 0000000000000..711a8b10264bb
--- /dev/null
+++ b/routers/api/v1/repo/release_tags.go
@@ -0,0 +1,54 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo
+
+import (
+	"net/http"
+
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/context"
+)
+
+// GetReleaseTag get a single release of a repository by its tagname
+func GetReleaseTag(ctx *context.APIContext) {
+	// swagger:operation GET /repos/{owner}/{repo}/releases/tags/{tag} repository repoGetReleaseTag
+	// ---
+	// summary: Get a release by tagname
+	// 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: tag
+	//   in: path
+	//   description: tagname of the release to get
+	//   type: string
+	//   required: true
+	// responses:
+	//   "200":
+	//     "$ref": "#/responses/Release"
+
+	tag := ctx.Params(":tag")
+
+	release, err := models.GetRelease(ctx.Repo.Repository.ID, tag)
+	if err != nil {
+		ctx.Error(http.StatusInternalServerError, "GetRelease", err)
+		return
+	}
+
+	if err := release.LoadAttributes(); err != nil {
+		ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+		return
+	}
+	ctx.JSON(http.StatusOK, release.APIFormat())
+}

From 212db639bcaa52098dbd2ab0da2fa6cc9ed08081 Mon Sep 17 00:00:00 2001
From: Johan Van de Wauw <johan@gisky.be>
Date: Thu, 24 Sep 2020 15:23:50 +0200
Subject: [PATCH 2/8] Update swagger template

---
 templates/swagger/v1_json.tmpl | 40 ++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 6792f7444b062..4b4f519e626a4 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -7569,6 +7569,46 @@
         }
       }
     },
+    "/repos/{owner}/{repo}/releases/tags/{tag}": {
+      "get": {
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "Get a release by tagname",
+        "operationId": "repoGetReleaseTag",
+        "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": "string",
+            "description": "tagname of the release to get",
+            "name": "tag",
+            "in": "path",
+            "required": true
+          }
+        ],
+        "responses": {
+          "200": {
+            "$ref": "#/responses/Release"
+          }
+        }
+      }
+    },
     "/repos/{owner}/{repo}/releases/{id}": {
       "get": {
         "produces": [

From 2b5aaa20ac6e7637697aff31c09af5ae3910e2d5 Mon Sep 17 00:00:00 2001
From: Johan Van de Wauw <johan@gisky.be>
Date: Thu, 24 Sep 2020 15:28:28 +0200
Subject: [PATCH 3/8] Return 404 if not found instead of 500

---
 routers/api/v1/repo/release_tags.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go
index 711a8b10264bb..e308dc85bb7c4 100644
--- a/routers/api/v1/repo/release_tags.go
+++ b/routers/api/v1/repo/release_tags.go
@@ -42,7 +42,7 @@ func GetReleaseTag(ctx *context.APIContext) {
 
 	release, err := models.GetRelease(ctx.Repo.Repository.ID, tag)
 	if err != nil {
-		ctx.Error(http.StatusInternalServerError, "GetRelease", err)
+		ctx.Error(http.StatusNotFound, "GetRelease", err)
 		return
 	}
 

From 2803b2fe49fda3af19b258511fa963f9f3a7bde6 Mon Sep 17 00:00:00 2001
From: Johan Van de Wauw <johan@gisky.be>
Date: Thu, 24 Sep 2020 16:22:45 +0200
Subject: [PATCH 4/8] Apply suggestions from code review a1012112796
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: 赵智超 <1012112796@qq.com>
---
 routers/api/v1/repo/release_tags.go | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go
index e308dc85bb7c4..b2caa5b086930 100644
--- a/routers/api/v1/repo/release_tags.go
+++ b/routers/api/v1/repo/release_tags.go
@@ -15,7 +15,7 @@ import (
 func GetReleaseTag(ctx *context.APIContext) {
 	// swagger:operation GET /repos/{owner}/{repo}/releases/tags/{tag} repository repoGetReleaseTag
 	// ---
-	// summary: Get a release by tagname
+	// summary: Get a release by tag name
 	// produces:
 	// - application/json
 	// parameters:
@@ -37,6 +37,8 @@ func GetReleaseTag(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/Release"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
 
 	tag := ctx.Params(":tag")
 

From 4ae584ad80e8ceaf0876e44971bc8c1d5dbcd3c3 Mon Sep 17 00:00:00 2001
From: Johan Van de Wauw <johan@gisky.be>
Date: Thu, 24 Sep 2020 16:28:32 +0200
Subject: [PATCH 5/8] Improve error handling

Co-authored-by: 6543 <6543@obermui.de>
---
 routers/api/v1/repo/release_tags.go | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go
index b2caa5b086930..95899264c7366 100644
--- a/routers/api/v1/repo/release_tags.go
+++ b/routers/api/v1/repo/release_tags.go
@@ -44,7 +44,11 @@ func GetReleaseTag(ctx *context.APIContext) {
 
 	release, err := models.GetRelease(ctx.Repo.Repository.ID, tag)
 	if err != nil {
-		ctx.Error(http.StatusNotFound, "GetRelease", err)
+		if models.IsErrReleaseNotExist(err) {
+				ctx.Error(http.StatusNotFound, "GetRelease", err)
+				return
+		}
+		ctx.Error(http.StatusInternalServerError, "GetRelease", err)
 		return
 	}
 

From 685016e26ed12e3ce75c2665f79218c093fba46b Mon Sep 17 00:00:00 2001
From: Johan Van de Wauw <johan@gisky.be>
Date: Thu, 24 Sep 2020 21:21:45 +0200
Subject: [PATCH 6/8] Add tests

---
 integrations/api_releases_test.go | 34 +++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/integrations/api_releases_test.go b/integrations/api_releases_test.go
index 9aef33d068979..58c2e35440f09 100644
--- a/integrations/api_releases_test.go
+++ b/integrations/api_releases_test.go
@@ -7,6 +7,7 @@ package integrations
 import (
 	"fmt"
 	"net/http"
+	"strings"
 	"testing"
 
 	"code.gitea.io/gitea/models"
@@ -120,3 +121,36 @@ func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) {
 
 	createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test")
 }
+
+func TestAPIGetReleaseByTag(t *testing.T) {
+	defer prepareTestEnv(t)()
+
+	repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
+	owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	session := loginUser(t, owner.LowerName)
+
+	tag := "v1.1"
+
+	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s/",
+		owner.Name, repo.Name, tag)
+
+	req := NewRequestf(t, "GET", urlStr)
+	resp := session.MakeRequest(t, req, http.StatusOK)
+
+	var release *api.Release
+	DecodeJSON(t, resp, &release)
+
+	assert.Equal(t, "testing-release", release.Title)
+
+	nonexistingtag := "nonexistingtag"
+
+	urlStr = fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s/",
+		owner.Name, repo.Name, nonexistingtag)
+
+	req = NewRequestf(t, "GET", urlStr)
+	resp = session.MakeRequest(t, req, http.StatusNotFound)
+
+	var err *api.APIError
+	DecodeJSON(t, resp, &err)
+	assert.True(t, strings.HasPrefix(err.Message, "release tag does not exist"))
+}

From 14872dd3a4c5b1156fadb4e39e1f7df19963b426 Mon Sep 17 00:00:00 2001
From: Johan Van de Wauw <johan@gisky.be>
Date: Thu, 24 Sep 2020 21:22:14 +0200
Subject: [PATCH 7/8] Update swagger template

---
 templates/swagger/v1_json.tmpl | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 4b4f519e626a4..bb05c604363a2 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -7577,7 +7577,7 @@
         "tags": [
           "repository"
         ],
-        "summary": "Get a release by tagname",
+        "summary": "Get a release by tag name",
         "operationId": "repoGetReleaseTag",
         "parameters": [
           {
@@ -7605,6 +7605,9 @@
         "responses": {
           "200": {
             "$ref": "#/responses/Release"
+          },
+          "404": {
+            "$ref": "#/responses/notFound"
           }
         }
       }

From ea32bf7ce80d48fed46918aa171b34416b723f6a Mon Sep 17 00:00:00 2001
From: Johan Van de Wauw <johan@gisky.be>
Date: Thu, 24 Sep 2020 21:48:23 +0200
Subject: [PATCH 8/8] gofmt

---
 routers/api/v1/repo/release_tags.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go
index 95899264c7366..bde3251ba28ca 100644
--- a/routers/api/v1/repo/release_tags.go
+++ b/routers/api/v1/repo/release_tags.go
@@ -45,8 +45,8 @@ func GetReleaseTag(ctx *context.APIContext) {
 	release, err := models.GetRelease(ctx.Repo.Repository.ID, tag)
 	if err != nil {
 		if models.IsErrReleaseNotExist(err) {
-				ctx.Error(http.StatusNotFound, "GetRelease", err)
-				return
+			ctx.Error(http.StatusNotFound, "GetRelease", err)
+			return
 		}
 		ctx.Error(http.StatusInternalServerError, "GetRelease", err)
 		return