diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index d1219fc4d3d..f27d58a6f72 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1595,10 +1595,10 @@ func Routes() *web.Router { m.Get("/signing-key.pub", misc.SigningKeySSH) m.Group("/topics", func() { m.Combo("").Get(repo.ListTopics). - Put(reqToken(), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics) + Put(reqToken(), reqAdmin(), mustNotBeArchived, bind(api.RepoTopicOptions{}), repo.UpdateTopics) m.Group("/{topic}", func() { - m.Combo("").Put(reqToken(), repo.AddTopic). - Delete(reqToken(), repo.DeleteTopic) + m.Combo("").Put(reqToken(), mustNotBeArchived, repo.AddTopic). + Delete(reqToken(), mustNotBeArchived, repo.DeleteTopic) }, reqAdmin()) }, reqAnyRepoReader()) m.Get("/issue_templates", reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(), repo.GetIssueTemplates) diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index 7d2dcc39cff..4b825cddda4 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -100,6 +100,8 @@ func UpdateTopics(ctx *context.APIContext) { // "$ref": "#/responses/notFound" // "422": // "$ref": "#/responses/invalidTopicsError" + // "423": + // "$ref": "#/responses/repoArchivedError" form := web.GetForm[*api.RepoTopicOptions](ctx) topicNames := form.Topics @@ -161,6 +163,8 @@ func AddTopic(ctx *context.APIContext) { // "$ref": "#/responses/notFound" // "422": // "$ref": "#/responses/invalidTopicsError" + // "423": + // "$ref": "#/responses/repoArchivedError" topicName := strings.TrimSpace(strings.ToLower(ctx.PathParam("topic"))) @@ -228,6 +232,8 @@ func DeleteTopic(ctx *context.APIContext) { // "$ref": "#/responses/notFound" // "422": // "$ref": "#/responses/invalidTopicsError" + // "423": + // "$ref": "#/responses/repoArchivedError" topicName := strings.TrimSpace(strings.ToLower(ctx.PathParam("topic"))) diff --git a/templates/swagger/v1-openapi3.generated.json b/templates/swagger/v1-openapi3.generated.json index 5de99641213..bea1be6261c 100644 --- a/templates/swagger/v1-openapi3.generated.json +++ b/templates/swagger/v1-openapi3.generated.json @@ -32751,6 +32751,9 @@ }, "422": { "$ref": "#/components/responses/invalidTopicsError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" } }, "summary": "Replace list of topics for a repository", @@ -32800,6 +32803,9 @@ }, "422": { "$ref": "#/components/responses/invalidTopicsError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" } }, "summary": "Delete a topic from a repository", @@ -32847,6 +32853,9 @@ }, "422": { "$ref": "#/components/responses/invalidTopicsError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" } }, "summary": "Add a topic to a repository", diff --git a/templates/swagger/v1-swagger.generated.json b/templates/swagger/v1-swagger.generated.json index 9526e7860d3..41559dab8b5 100644 --- a/templates/swagger/v1-swagger.generated.json +++ b/templates/swagger/v1-swagger.generated.json @@ -20099,6 +20099,9 @@ }, "422": { "$ref": "#/responses/invalidTopicsError" + }, + "423": { + "$ref": "#/responses/repoArchivedError" } } } @@ -20145,6 +20148,9 @@ }, "422": { "$ref": "#/responses/invalidTopicsError" + }, + "423": { + "$ref": "#/responses/repoArchivedError" } } }, @@ -20189,6 +20195,9 @@ }, "422": { "$ref": "#/responses/invalidTopicsError" + }, + "423": { + "$ref": "#/responses/repoArchivedError" } } } diff --git a/tests/integration/api_repo_topic_test.go b/tests/integration/api_repo_topic_test.go index 650e9df4e9d..bd45f0d440d 100644 --- a/tests/integration/api_repo_topic_test.go +++ b/tests/integration/api_repo_topic_test.go @@ -186,3 +186,30 @@ func TestAPIRepoTopic(t *testing.T) { AddTokenAuth(token4) MakeRequest(t, req, http.StatusForbidden) } + +func TestAPIRepoTopicArchived(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 30}) // owner of the archived repo51 + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 51}) + assert.True(t, repo.IsArchived) + token := getUserToken(t, user.Name, auth_model.AccessTokenScopeWriteRepository) + + // writing topics on an archived repo must be rejected, matching the web UI + req := NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s", user.Name, repo.Name, "archivedtopic"). + AddTokenAuth(token) + MakeRequest(t, req, http.StatusLocked) + + req = NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/topics/%s", user.Name, repo.Name, "archivedtopic"). + AddTokenAuth(token) + MakeRequest(t, req, http.StatusLocked) + + req = NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/topics", user.Name, repo.Name), + &api.RepoTopicOptions{Topics: []string{"archivedtopic"}}).AddTokenAuth(token) + MakeRequest(t, req, http.StatusLocked) + + // reading topics stays allowed on an archived repo + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/topics", user.Name, repo.Name)). + AddTokenAuth(token) + MakeRequest(t, req, http.StatusOK) +}