Skip to content

Commit 8efc9d4

Browse files
committed
Change lock issue API endpoint to fit GitHub specifications
1 parent 940c8c0 commit 8efc9d4

File tree

6 files changed

+56
-58
lines changed

6 files changed

+56
-58
lines changed

models/issues/issue_test.go

-3
Original file line numberDiff line numberDiff line change
@@ -468,9 +468,6 @@ func TestMigrate_CreateIssuesIsPullTrue(t *testing.T) {
468468
}
469469

470470
func TestIssueLock_IsValidReason(t *testing.T) {
471-
// Init settings
472-
_ = setting.Repository
473-
474471
cases := []struct {
475472
reason string
476473
expected bool

modules/structs/issue.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -269,5 +269,5 @@ type IssueMeta struct {
269269

270270
// LockIssueOption options to lock an issue
271271
type LockIssueOption struct {
272-
Reason string `json:"reason"`
272+
Reason string `json:"lock_reason"`
273273
}

routers/api/v1/api.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -1523,7 +1523,8 @@ func Routes() *web.Router {
15231523
m.Patch("/{position}", reqToken(), reqAdmin(), repo.MoveIssuePin)
15241524
})
15251525
m.Group("/lock", func() {
1526-
m.Combo("").Post(bind(api.LockIssueOption{}), repo.LockIssue).
1526+
m.Combo("").
1527+
Put(bind(api.LockIssueOption{}), repo.LockIssue).
15271528
Delete(repo.UnlockIssue)
15281529
}, reqToken())
15291530
})

routers/api/v1/repo/issue_lock.go

+36-35
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
// Copyright 2019 The Gitea Authors. All rights reserved.
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
22
// SPDX-License-Identifier: MIT
33

44
package repo
55

66
import (
77
"errors"
88
"net/http"
9+
"strings"
10+
"unicode"
911

1012
issues_model "code.gitea.io/gitea/models/issues"
1113
api "code.gitea.io/gitea/modules/structs"
@@ -15,7 +17,7 @@ import (
1517

1618
// LockIssue lock an issue
1719
func LockIssue(ctx *context.APIContext) {
18-
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/lock issue issueLockIssue
20+
// swagger:operation PUT /repos/{owner}/{repo}/issues/{index}/lock issue issueLockIssue
1921
// ---
2022
// summary: Lock an issue
2123
// consumes:
@@ -46,8 +48,6 @@ func LockIssue(ctx *context.APIContext) {
4648
// responses:
4749
// "204":
4850
// "$ref": "#/responses/empty"
49-
// "208":
50-
// "$ref": "#/responses/empty"
5151
// "400":
5252
// "$ref": "#/responses/error"
5353
// "403":
@@ -56,6 +56,15 @@ func LockIssue(ctx *context.APIContext) {
5656
// "$ref": "#/responses/notFound"
5757

5858
reason := web.GetForm(ctx).(*api.LockIssueOption).Reason
59+
reason = strings.ToLower(reason)
60+
61+
if reason != "" {
62+
// make the first character uppercase
63+
runes := []rune(reason)
64+
runes[0] = unicode.ToUpper(runes[0])
65+
reason = string(runes)
66+
}
67+
5968
if !issues_model.IsValidReason(reason) {
6069
ctx.APIError(http.StatusBadRequest, errors.New("reason not valid"))
6170
return
@@ -76,22 +85,19 @@ func LockIssue(ctx *context.APIContext) {
7685
return
7786
}
7887

79-
if issue.IsLocked {
80-
ctx.Status(http.StatusAlreadyReported)
81-
return
82-
}
83-
84-
opt := &issues_model.IssueLockOptions{
85-
Doer: ctx.ContextUser,
86-
Issue: issue,
87-
Reason: reason,
88-
}
88+
if !issue.IsLocked {
89+
opt := &issues_model.IssueLockOptions{
90+
Doer: ctx.ContextUser,
91+
Issue: issue,
92+
Reason: reason,
93+
}
8994

90-
issue.Repo = ctx.Repo.Repository
91-
err = issues_model.LockIssue(ctx, opt)
92-
if err != nil {
93-
ctx.APIErrorInternal(err)
94-
return
95+
issue.Repo = ctx.Repo.Repository
96+
err = issues_model.LockIssue(ctx, opt)
97+
if err != nil {
98+
ctx.APIErrorInternal(err)
99+
return
100+
}
95101
}
96102

97103
ctx.Status(http.StatusNoContent)
@@ -126,8 +132,6 @@ func UnlockIssue(ctx *context.APIContext) {
126132
// responses:
127133
// "204":
128134
// "$ref": "#/responses/empty"
129-
// "208":
130-
// "$ref": "#/responses/empty"
131135
// "403":
132136
// "$ref": "#/responses/forbidden"
133137
// "404":
@@ -148,21 +152,18 @@ func UnlockIssue(ctx *context.APIContext) {
148152
return
149153
}
150154

151-
if !issue.IsLocked {
152-
ctx.Status(http.StatusAlreadyReported)
153-
return
154-
}
155-
156-
opt := &issues_model.IssueLockOptions{
157-
Doer: ctx.ContextUser,
158-
Issue: issue,
159-
}
155+
if issue.IsLocked {
156+
opt := &issues_model.IssueLockOptions{
157+
Doer: ctx.ContextUser,
158+
Issue: issue,
159+
}
160160

161-
issue.Repo = ctx.Repo.Repository
162-
err = issues_model.UnlockIssue(ctx, opt)
163-
if err != nil {
164-
ctx.APIErrorInternal(err)
165-
return
161+
issue.Repo = ctx.Repo.Repository
162+
err = issues_model.UnlockIssue(ctx, opt)
163+
if err != nil {
164+
ctx.APIErrorInternal(err)
165+
return
166+
}
166167
}
167168

168169
ctx.Status(http.StatusNoContent)

templates/swagger/v1_json.tmpl

+2-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/integration/api_issue_lock_test.go

+15-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2017 The Gitea Authors. All rights reserved.
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
22
// SPDX-License-Identifier: MIT
33

44
package integration
@@ -32,23 +32,31 @@ func TestAPILockIssue(t *testing.T) {
3232
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
3333

3434
// check invalid reason
35-
req := NewRequestWithJSON(t, "POST", urlStr, api.LockIssueOption{Reason: "Not valid"}).AddTokenAuth(token)
35+
req := NewRequestWithJSON(t, "PUT", urlStr, api.LockIssueOption{Reason: "Not valid"}).AddTokenAuth(token)
3636
MakeRequest(t, req, http.StatusBadRequest)
3737

3838
// check lock issue
39-
req = NewRequestWithJSON(t, "POST", urlStr, api.LockIssueOption{Reason: "Spam"}).AddTokenAuth(token)
39+
req = NewRequestWithJSON(t, "PUT", urlStr, api.LockIssueOption{Reason: "Spam"}).AddTokenAuth(token)
4040
MakeRequest(t, req, http.StatusNoContent)
4141
issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
4242
assert.True(t, issueAfter.IsLocked)
4343

44-
// check locking a second time
45-
MakeRequest(t, req, http.StatusAlreadyReported)
44+
// check reason is case insensitive
45+
unlockReq := NewRequest(t, "DELETE", urlStr).AddTokenAuth(token)
46+
MakeRequest(t, unlockReq, http.StatusNoContent)
47+
issueAfter = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
48+
assert.False(t, issueAfter.IsLocked)
49+
50+
req = NewRequestWithJSON(t, "PUT", urlStr, api.LockIssueOption{Reason: "too heated"}).AddTokenAuth(token)
51+
MakeRequest(t, req, http.StatusNoContent)
52+
issueAfter = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
53+
assert.True(t, issueAfter.IsLocked)
4654

4755
// check with other user
4856
user34 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 34})
4957
session34 := loginUser(t, user34.Name)
5058
token34 := getTokenForLoggedInUser(t, session34, auth_model.AccessTokenScopeAll)
51-
req = NewRequestWithJSON(t, "POST", urlStr, api.LockIssueOption{Reason: "Spam"}).AddTokenAuth(token34)
59+
req = NewRequestWithJSON(t, "PUT", urlStr, api.LockIssueOption{Reason: "Spam"}).AddTokenAuth(token34)
5260
MakeRequest(t, req, http.StatusForbidden)
5361
}
5462

@@ -63,7 +71,7 @@ func TestAPIUnlockIssue(t *testing.T) {
6371
session := loginUser(t, owner.Name)
6472
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
6573

66-
lockReq := NewRequestWithJSON(t, "POST", urlStr, api.LockIssueOption{Reason: "Spam"}).AddTokenAuth(token)
74+
lockReq := NewRequestWithJSON(t, "PUT", urlStr, api.LockIssueOption{Reason: "Spam"}).AddTokenAuth(token)
6775
MakeRequest(t, lockReq, http.StatusNoContent)
6876

6977
// check unlock issue
@@ -72,9 +80,6 @@ func TestAPIUnlockIssue(t *testing.T) {
7280
issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
7381
assert.False(t, issueAfter.IsLocked)
7482

75-
// check unlocking a second time
76-
MakeRequest(t, req, http.StatusAlreadyReported)
77-
7883
// check with other user
7984
user34 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 34})
8085
session34 := loginUser(t, user34.Name)

0 commit comments

Comments
 (0)