diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 0238e9a06c0..a77ae9df892 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1514,10 +1514,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 00fef505954..c383c5a1101 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(ctx).(*api.RepoTopicOptions) 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_json.tmpl b/templates/swagger/v1_json.tmpl index 9b2af31f009..8537fd27d67 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -18058,6 +18058,9 @@ }, "422": { "$ref": "#/responses/invalidTopicsError" + }, + "423": { + "$ref": "#/responses/repoArchivedError" } } } @@ -18104,6 +18107,9 @@ }, "422": { "$ref": "#/responses/invalidTopicsError" + }, + "423": { + "$ref": "#/responses/repoArchivedError" } } }, @@ -18148,6 +18154,9 @@ }, "422": { "$ref": "#/responses/invalidTopicsError" + }, + "423": { + "$ref": "#/responses/repoArchivedError" } } } diff --git a/templates/swagger/v1_openapi3_json.tmpl b/templates/swagger/v1_openapi3_json.tmpl index df3cabbac5c..204d9cd845d 100644 --- a/templates/swagger/v1_openapi3_json.tmpl +++ b/templates/swagger/v1_openapi3_json.tmpl @@ -30293,6 +30293,9 @@ }, "422": { "$ref": "#/components/responses/invalidTopicsError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" } }, "summary": "Replace list of topics for a repository", @@ -30342,6 +30345,9 @@ }, "422": { "$ref": "#/components/responses/invalidTopicsError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" } }, "summary": "Delete a topic from a repository", @@ -30389,6 +30395,9 @@ }, "422": { "$ref": "#/components/responses/invalidTopicsError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" } }, "summary": "Add a topic to a repository", 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) +}