mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-22 21:45:24 +02:00
Verified and tested the DeleteRepoOrgs
This commit is contained in:
parent
acf9dc0148
commit
e940dd27b1
@ -441,14 +441,23 @@ type UpdateRepoAvatarOption struct {
|
|||||||
Image string `json:"image" binding:"Required"`
|
Image string `json:"image" binding:"Required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteOrgReposResponse represents the response for deleting organization repositories
|
||||||
|
// swagger:model
|
||||||
type DeleteOrgReposResponse struct {
|
type DeleteOrgReposResponse struct {
|
||||||
SuccessCount int `json:"success_count"`
|
// Number of repositories successfully deleted
|
||||||
FailureCount int `json:"failure_count"`
|
SuccessCount int `json:"success_count"`
|
||||||
Deleted []string `json:"deleted"`
|
// Number of repositories that failed to delete
|
||||||
Failed []DeleteRepoFailure `json:"failed"`
|
FailureCount int `json:"failure_count"`
|
||||||
|
// List of repository names that were deleted
|
||||||
|
Deleted []string `json:"deleted"`
|
||||||
|
// Details about repositories that failed to delete
|
||||||
|
Failed []DeleteRepoFailure `json:"failed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteRepoFailure represents a repository that failed to delete
|
||||||
type DeleteRepoFailure struct {
|
type DeleteRepoFailure struct {
|
||||||
|
// Repository name
|
||||||
RepoName string `json:"repo_name"`
|
RepoName string `json:"repo_name"`
|
||||||
Reason string `json:"reason"`
|
// Reason for deletion failure
|
||||||
|
Reason string `json:"reason"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1622,7 +1622,7 @@ func Routes() *web.Router {
|
|||||||
m.Post("/rename", reqToken(), reqOrgOwnership(), bind(api.RenameOrgOption{}), org.Rename)
|
m.Post("/rename", reqToken(), reqOrgOwnership(), bind(api.RenameOrgOption{}), org.Rename)
|
||||||
m.Combo("/repos").Get(user.ListOrgRepos).
|
m.Combo("/repos").Get(user.ListOrgRepos).
|
||||||
Post(reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo).
|
Post(reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo).
|
||||||
Delete(reqToken(), reqOrgOwnership(), repo.DeleteOrgRepo)
|
Delete(reqToken(), reqOrgOwnership(), org.DeleteOrgRepos)
|
||||||
m.Group("/members", func() {
|
m.Group("/members", func() {
|
||||||
m.Get("", reqToken(), org.ListMembers)
|
m.Get("", reqToken(), org.ListMembers)
|
||||||
m.Combo("/{username}").Get(reqToken(), org.IsMember).
|
m.Combo("/{username}").Get(reqToken(), org.IsMember).
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import (
|
|||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/optional"
|
"code.gitea.io/gitea/modules/optional"
|
||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
@ -22,6 +23,7 @@ import (
|
|||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
feed_service "code.gitea.io/gitea/services/feed"
|
feed_service "code.gitea.io/gitea/services/feed"
|
||||||
"code.gitea.io/gitea/services/org"
|
"code.gitea.io/gitea/services/org"
|
||||||
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
user_service "code.gitea.io/gitea/services/user"
|
user_service "code.gitea.io/gitea/services/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -509,7 +511,7 @@ func DeleteOrgRepos(ctx *context.APIContext) {
|
|||||||
// required: true
|
// required: true
|
||||||
// responses:
|
// responses:
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/DeleteOrgReposResponse"
|
// "$ref": "#/responses/DeleteOrgReposList"
|
||||||
// "403":
|
// "403":
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
org := ctx.Org.Organization
|
org := ctx.Org.Organization
|
||||||
|
|||||||
@ -41,3 +41,11 @@ type swaggerResponseOrganizationPermissions struct {
|
|||||||
// in:body
|
// in:body
|
||||||
Body api.OrganizationPermissions `json:"body"`
|
Body api.OrganizationPermissions `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteOrgReposList
|
||||||
|
// swagger:response DeleteOrgReposList
|
||||||
|
type swaggerDeleteOrgReposList struct {
|
||||||
|
// List of successfully deleted repositories and failures
|
||||||
|
//in:body
|
||||||
|
Body []api.DeleteOrgReposResponse `json:"body"`
|
||||||
|
}
|
||||||
|
|||||||
88
templates/swagger/v1_json.tmpl
generated
88
templates/swagger/v1_json.tmpl
generated
@ -3638,6 +3638,33 @@
|
|||||||
"$ref": "#/responses/notFound"
|
"$ref": "#/responses/notFound"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"organization"
|
||||||
|
],
|
||||||
|
"summary": "Delete all repositories in an organization",
|
||||||
|
"operationId": "orgDeleteRepos",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "name of the organization",
|
||||||
|
"name": "org",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"$ref": "#/responses/DeleteOrgReposList"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"$ref": "#/responses/forbidden"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/orgs/{org}/teams": {
|
"/orgs/{org}/teams": {
|
||||||
@ -24180,6 +24207,58 @@
|
|||||||
},
|
},
|
||||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
},
|
},
|
||||||
|
"DeleteOrgReposResponse": {
|
||||||
|
"description": "DeleteOrgReposResponse represents the response for deleting organization repositories",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"deleted": {
|
||||||
|
"description": "List of repository names that were deleted",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"x-go-name": "Deleted"
|
||||||
|
},
|
||||||
|
"failed": {
|
||||||
|
"description": "Details about repositories that failed to delete",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/DeleteRepoFailure"
|
||||||
|
},
|
||||||
|
"x-go-name": "Failed"
|
||||||
|
},
|
||||||
|
"failure_count": {
|
||||||
|
"description": "Number of repositories that failed to delete",
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"x-go-name": "FailureCount"
|
||||||
|
},
|
||||||
|
"success_count": {
|
||||||
|
"description": "Number of repositories successfully deleted",
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"x-go-name": "SuccessCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
|
},
|
||||||
|
"DeleteRepoFailure": {
|
||||||
|
"description": "DeleteRepoFailure represents a repository that failed to delete ",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"reason": {
|
||||||
|
"description": "Reason for deletion failure",
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "Reason"
|
||||||
|
},
|
||||||
|
"repo_name": {
|
||||||
|
"description": "Repository name",
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "RepoName"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
|
},
|
||||||
"DeployKey": {
|
"DeployKey": {
|
||||||
"description": "DeployKey a deploy key",
|
"description": "DeployKey a deploy key",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@ -29728,6 +29807,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"DeleteOrgReposList": {
|
||||||
|
"description": "DeleteOrgReposList",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/DeleteOrgReposResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"DeployKey": {
|
"DeployKey": {
|
||||||
"description": "DeployKey",
|
"description": "DeployKey",
|
||||||
"schema": {
|
"schema": {
|
||||||
|
|||||||
@ -249,3 +249,100 @@ func TestAPIOrgGeneral(t *testing.T) {
|
|||||||
MakeRequest(t, req, http.StatusForbidden)
|
MakeRequest(t, req, http.StatusForbidden)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAPIDeleteOrgRepos(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
t.Run("Delete all repos successfully", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
// Create test org with owner
|
||||||
|
session := loginUser(t, "user1")
|
||||||
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization, auth_model.AccessTokenScopeWriteRepository)
|
||||||
|
|
||||||
|
orgName := "test_delete_org"
|
||||||
|
req := NewRequestWithJSON(t, "POST", "/api/v1/orgs", &api.CreateOrgOption{
|
||||||
|
UserName: orgName,
|
||||||
|
}).AddTokenAuth(token)
|
||||||
|
MakeRequest(t, req, http.StatusCreated)
|
||||||
|
|
||||||
|
// Create 60 repos to test efficiency
|
||||||
|
for i := range 60 {
|
||||||
|
repoName := fmt.Sprintf("test_repo_%d", i)
|
||||||
|
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/org/%s/repos", orgName), &api.CreateRepoOption{
|
||||||
|
Name: repoName,
|
||||||
|
}).AddTokenAuth(token)
|
||||||
|
MakeRequest(t, req, http.StatusCreated)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all repos
|
||||||
|
req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/orgs/%s/repos", orgName)).AddTokenAuth(token)
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
var result api.DeleteOrgReposResponse
|
||||||
|
DecodeJSON(t, resp, &result)
|
||||||
|
|
||||||
|
assert.Equal(t, 60, result.SuccessCount)
|
||||||
|
assert.Equal(t, 0, result.FailureCount)
|
||||||
|
assert.Len(t, result.Deleted, 60)
|
||||||
|
assert.Empty(t, result.Failed)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Verify response structure", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
session := loginUser(t, "user1")
|
||||||
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization, auth_model.AccessTokenScopeWriteRepository)
|
||||||
|
|
||||||
|
orgName := "test_response_org"
|
||||||
|
req := NewRequestWithJSON(t, "POST", "/api/v1/orgs", &api.CreateOrgOption{
|
||||||
|
UserName: orgName,
|
||||||
|
}).AddTokenAuth(token)
|
||||||
|
MakeRequest(t, req, http.StatusCreated)
|
||||||
|
|
||||||
|
// Create a few repos
|
||||||
|
for i := range 3 {
|
||||||
|
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/org/%s/repos", orgName), &api.CreateRepoOption{
|
||||||
|
Name: fmt.Sprintf("repo_%d", i),
|
||||||
|
}).AddTokenAuth(token)
|
||||||
|
MakeRequest(t, req, http.StatusCreated)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all repos
|
||||||
|
req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/orgs/%s/repos", orgName)).AddTokenAuth(token)
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
var result api.DeleteOrgReposResponse
|
||||||
|
DecodeJSON(t, resp, &result)
|
||||||
|
|
||||||
|
// Verify response structure
|
||||||
|
assert.Equal(t, 3, result.SuccessCount)
|
||||||
|
assert.Equal(t, 0, result.FailureCount)
|
||||||
|
assert.Len(t, result.Deleted, 3)
|
||||||
|
assert.Empty(t, result.Failed)
|
||||||
|
assert.NotNil(t, result.Deleted)
|
||||||
|
assert.NotNil(t, result.Failed)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Fail without permissions", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
// user2 is owner of org3
|
||||||
|
ownerSession := loginUser(t, "user2")
|
||||||
|
ownerToken := getTokenForLoggedInUser(t, ownerSession, auth_model.AccessTokenScopeWriteOrganization, auth_model.AccessTokenScopeWriteRepository)
|
||||||
|
|
||||||
|
// Create repo in org3
|
||||||
|
req := NewRequestWithJSON(t, "POST", "/api/v1/org/org3/repos", &api.CreateRepoOption{
|
||||||
|
Name: "test_perm_repo",
|
||||||
|
}).AddTokenAuth(ownerToken)
|
||||||
|
MakeRequest(t, req, http.StatusCreated)
|
||||||
|
|
||||||
|
// user4 is not owner of org3
|
||||||
|
nonOwnerSession := loginUser(t, "user4")
|
||||||
|
nonOwnerToken := getTokenForLoggedInUser(t, nonOwnerSession, auth_model.AccessTokenScopeWriteOrganization)
|
||||||
|
|
||||||
|
// Try to delete repos without owner permission
|
||||||
|
req = NewRequest(t, "DELETE", "/api/v1/orgs/org3/repos").AddTokenAuth(nonOwnerToken)
|
||||||
|
MakeRequest(t, req, http.StatusForbidden)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user