[FEAT] API support for repository flags
Expose the repository flags feature over the API, so the flags can be managed by a site administrator without using the web API. Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
This commit is contained in:
parent
78287806e2
commit
bac9f0225d
6 changed files with 686 additions and 0 deletions
9
modules/structs/repo_flags.go
Normal file
9
modules/structs/repo_flags.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package structs
|
||||
|
||||
// ReplaceFlagsOption options when replacing the flags of a repository
|
||||
type ReplaceFlagsOption struct {
|
||||
Flags []string `json:"flags"`
|
||||
}
|
|
@ -1085,6 +1085,18 @@ func Routes() *web.Route {
|
|||
m.Get("/permission", repo.GetRepoPermissions)
|
||||
})
|
||||
}, reqToken())
|
||||
if setting.Repository.EnableFlags {
|
||||
m.Group("/flags", func() {
|
||||
m.Combo("").Get(repo.ListFlags).
|
||||
Put(bind(api.ReplaceFlagsOption{}), repo.ReplaceAllFlags).
|
||||
Delete(repo.DeleteAllFlags)
|
||||
m.Group("/{flag}", func() {
|
||||
m.Combo("").Get(repo.HasFlag).
|
||||
Put(repo.AddFlag).
|
||||
Delete(repo.DeleteFlag)
|
||||
})
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryAdmin), reqToken(), reqSiteAdmin())
|
||||
}
|
||||
m.Get("/assignees", reqToken(), reqAnyRepoReader(), repo.GetAssignees)
|
||||
m.Get("/reviewers", reqToken(), reqAnyRepoReader(), repo.GetReviewers)
|
||||
m.Group("/teams", func() {
|
||||
|
|
245
routers/api/v1/repo/flags.go
Normal file
245
routers/api/v1/repo/flags.go
Normal file
|
@ -0,0 +1,245 @@
|
|||
// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
)
|
||||
|
||||
func ListFlags(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/flags repository repoListFlags
|
||||
// ---
|
||||
// summary: List a repository's flags
|
||||
// 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
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/StringSlice"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
repoFlags, err := ctx.Repo.Repository.ListFlags(ctx)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
flags := make([]string, len(repoFlags))
|
||||
for i := range repoFlags {
|
||||
flags[i] = repoFlags[i].Name
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(int64(len(repoFlags)))
|
||||
ctx.JSON(http.StatusOK, flags)
|
||||
}
|
||||
|
||||
func ReplaceAllFlags(ctx *context.APIContext) {
|
||||
// swagger:operation PUT /repos/{owner}/{repo}/flags repository repoReplaceAllFlags
|
||||
// ---
|
||||
// summary: Replace all flags of a repository
|
||||
// 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: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ReplaceFlagsOption"
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
flagsForm := web.GetForm(ctx).(*api.ReplaceFlagsOption)
|
||||
|
||||
if err := ctx.Repo.Repository.ReplaceAllFlags(ctx, flagsForm.Flags); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func DeleteAllFlags(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /repos/{owner}/{repo}/flags repository repoDeleteAllFlags
|
||||
// ---
|
||||
// summary: Remove all flags from a repository
|
||||
// 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
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
if err := ctx.Repo.Repository.ReplaceAllFlags(ctx, nil); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func HasFlag(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/flags/{flag} repository repoCheckFlag
|
||||
// ---
|
||||
// summary: Check if a repository has a given flag
|
||||
// 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: flag
|
||||
// in: path
|
||||
// description: name of the flag
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
hasFlag := ctx.Repo.Repository.HasFlag(ctx, ctx.Params(":flag"))
|
||||
if hasFlag {
|
||||
ctx.Status(http.StatusNoContent)
|
||||
} else {
|
||||
ctx.NotFound()
|
||||
}
|
||||
}
|
||||
|
||||
func AddFlag(ctx *context.APIContext) {
|
||||
// swagger:operation PUT /repos/{owner}/{repo}/flags/{flag} repository repoAddFlag
|
||||
// ---
|
||||
// summary: Add a flag to a repository
|
||||
// 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: flag
|
||||
// in: path
|
||||
// description: name of the flag
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
flag := ctx.Params(":flag")
|
||||
|
||||
if ctx.Repo.Repository.HasFlag(ctx, flag) {
|
||||
ctx.Status(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
if err := ctx.Repo.Repository.AddFlag(ctx, flag); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func DeleteFlag(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /repos/{owner}/{repo}/flags/{flag} repository repoDeleteFlag
|
||||
// ---
|
||||
// summary: Remove a flag from a repository
|
||||
// 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: flag
|
||||
// in: path
|
||||
// description: name of the flag
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
flag := ctx.Params(":flag")
|
||||
|
||||
if _, err := ctx.Repo.Repository.DeleteFlag(ctx, flag); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
|
@ -17,6 +17,9 @@ type swaggerParameterBodies struct {
|
|||
// in:body
|
||||
AddCollaboratorOption api.AddCollaboratorOption
|
||||
|
||||
// in:body
|
||||
ReplaceFlagsOption api.ReplaceFlagsOption
|
||||
|
||||
// in:body
|
||||
CreateEmailOption api.CreateEmailOption
|
||||
// in:body
|
||||
|
|
268
templates/swagger/v1_json.tmpl
generated
268
templates/swagger/v1_json.tmpl
generated
|
@ -4989,6 +4989,260 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/repos/{owner}/{repo}/flags": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"repository"
|
||||
],
|
||||
"summary": "List a repository's flags",
|
||||
"operationId": "repoListFlags",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/StringSlice"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"repository"
|
||||
],
|
||||
"summary": "Replace all flags of a repository",
|
||||
"operationId": "repoReplaceAllFlags",
|
||||
"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
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ReplaceFlagsOption"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"$ref": "#/responses/empty"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"repository"
|
||||
],
|
||||
"summary": "Remove all flags from a repository",
|
||||
"operationId": "repoDeleteAllFlags",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"$ref": "#/responses/empty"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/repos/{owner}/{repo}/flags/{flag}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"repository"
|
||||
],
|
||||
"summary": "Check if a repository has a given flag",
|
||||
"operationId": "repoCheckFlag",
|
||||
"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": "name of the flag",
|
||||
"name": "flag",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"$ref": "#/responses/empty"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"repository"
|
||||
],
|
||||
"summary": "Add a flag to a repository",
|
||||
"operationId": "repoAddFlag",
|
||||
"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": "name of the flag",
|
||||
"name": "flag",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"$ref": "#/responses/empty"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"repository"
|
||||
],
|
||||
"summary": "Remove a flag from a repository",
|
||||
"operationId": "repoDeleteFlag",
|
||||
"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": "name of the flag",
|
||||
"name": "flag",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"$ref": "#/responses/empty"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/repos/{owner}/{repo}/forks": {
|
||||
"get": {
|
||||
"produces": [
|
||||
|
@ -21984,6 +22238,20 @@
|
|||
},
|
||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||
},
|
||||
"ReplaceFlagsOption": {
|
||||
"description": "ReplaceFlagsOption options when replacing the flags of a repository",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"flags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-go-name": "Flags"
|
||||
}
|
||||
},
|
||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||
},
|
||||
"RepoCollaboratorPermission": {
|
||||
"description": "RepoCollaboratorPermission to get repository permission for a collaborator",
|
||||
"type": "object",
|
||||
|
|
|
@ -7,13 +7,16 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/routers"
|
||||
"code.gitea.io/gitea/tests"
|
||||
|
@ -42,6 +45,152 @@ func TestRepositoryFlagsUIDisabled(t *testing.T) {
|
|||
assert.Equal(t, 0, flagsLinkCount)
|
||||
}
|
||||
|
||||
func TestRepositoryFlagsAPI(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
defer test.MockVariableValue(&setting.Repository.EnableFlags, true)()
|
||||
defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
|
||||
|
||||
// *************
|
||||
// ** Helpers **
|
||||
// *************
|
||||
|
||||
adminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{IsAdmin: true}).Name
|
||||
normalUserBean := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
assert.False(t, normalUserBean.IsAdmin)
|
||||
normalUser := normalUserBean.Name
|
||||
|
||||
assertAccess := func(t *testing.T, user, method, uri string, expectedStatus int) {
|
||||
session := loginUser(t, user)
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeReadAdmin)
|
||||
|
||||
req := NewRequestf(t, method, "/api/v1/repos/user2/repo1/flags%s", uri).AddTokenAuth(token)
|
||||
MakeRequest(t, req, expectedStatus)
|
||||
}
|
||||
|
||||
// ***********
|
||||
// ** Tests **
|
||||
// ***********
|
||||
|
||||
t.Run("API access", func(t *testing.T) {
|
||||
t.Run("as admin", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
assertAccess(t, adminUser, "GET", "", http.StatusOK)
|
||||
})
|
||||
|
||||
t.Run("as normal user", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
assertAccess(t, normalUser, "GET", "", http.StatusForbidden)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("token scopes", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
// Trying to access the API with a token that lacks permissions, will
|
||||
// fail, even if the token owner is an instance admin.
|
||||
session := loginUser(t, adminUser)
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
||||
|
||||
req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1/flags").AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusForbidden)
|
||||
})
|
||||
|
||||
t.Run("setting.Repository.EnableFlags is respected", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
defer test.MockVariableValue(&setting.Repository.EnableFlags, false)()
|
||||
defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
|
||||
|
||||
t.Run("as admin", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
assertAccess(t, adminUser, "GET", "", http.StatusNotFound)
|
||||
})
|
||||
|
||||
t.Run("as normal user", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
assertAccess(t, normalUser, "GET", "", http.StatusNotFound)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("API functionality", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||
defer func() {
|
||||
repo.ReplaceAllFlags(db.DefaultContext, []string{})
|
||||
}()
|
||||
|
||||
baseURLFmtStr := "/api/v1/repos/user5/repo4/flags%s"
|
||||
|
||||
session := loginUser(t, adminUser)
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteAdmin)
|
||||
|
||||
// Listing flags
|
||||
req := NewRequestf(t, "GET", baseURLFmtStr, "").AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var flags []string
|
||||
DecodeJSON(t, resp, &flags)
|
||||
assert.Empty(t, flags)
|
||||
|
||||
// Replacing all tags works, twice in a row
|
||||
for i := 0; i < 2; i++ {
|
||||
req = NewRequestWithJSON(t, "PUT", fmt.Sprintf(baseURLFmtStr, ""), &api.ReplaceFlagsOption{
|
||||
Flags: []string{"flag-1", "flag-2", "flag-3"},
|
||||
}).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// The list now includes all three flags
|
||||
req = NewRequestf(t, "GET", baseURLFmtStr, "").AddTokenAuth(token)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &flags)
|
||||
assert.Len(t, flags, 3)
|
||||
for _, flag := range []string{"flag-1", "flag-2", "flag-3"} {
|
||||
assert.True(t, slices.Contains(flags, flag))
|
||||
}
|
||||
|
||||
// Check a flag that is on the repo
|
||||
req = NewRequestf(t, "GET", baseURLFmtStr, "/flag-1").AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
|
||||
// Check a flag that isn't on the repo
|
||||
req = NewRequestf(t, "GET", baseURLFmtStr, "/no-such-flag").AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// We can add the same flag twice
|
||||
for i := 0; i < 2; i++ {
|
||||
req = NewRequestf(t, "PUT", baseURLFmtStr, "/brand-new-flag").AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// The new flag is there
|
||||
req = NewRequestf(t, "GET", baseURLFmtStr, "/brand-new-flag").AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
|
||||
// We can delete a flag, twice
|
||||
for i := 0; i < 2; i++ {
|
||||
req = NewRequestf(t, "DELETE", baseURLFmtStr, "/flag-3").AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// We can delete a flag that wasn't there
|
||||
req = NewRequestf(t, "DELETE", baseURLFmtStr, "/no-such-flag").AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
|
||||
// We can delete all of the flags in one go, too
|
||||
req = NewRequestf(t, "DELETE", baseURLFmtStr, "").AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
|
||||
// ..once all flags are deleted, none are listed, either
|
||||
req = NewRequestf(t, "GET", baseURLFmtStr, "").AddTokenAuth(token)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &flags)
|
||||
assert.Empty(t, flags)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRepositoryFlagsUI(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
defer test.MockVariableValue(&setting.Repository.EnableFlags, true)()
|
||||
|
|
Loading…
Add table
Reference in a new issue