mirror of https://github.com/harness/drone.git
List all Repos at Account level or Space Level recursively (#999)
parent
6f270eb3ae
commit
c949308596
|
@ -27,7 +27,11 @@ import (
|
|||
)
|
||||
|
||||
// Delete deletes a space.
|
||||
func (c *Controller) Delete(ctx context.Context, session *auth.Session, spaceRef string) error {
|
||||
func (c *Controller) Delete(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
spaceRef string,
|
||||
) error {
|
||||
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -41,7 +45,11 @@ func (c *Controller) Delete(ctx context.Context, session *auth.Session, spaceRef
|
|||
|
||||
// DeleteNoAuth deletes the space - no authorization is verified.
|
||||
// WARNING this is meant for internal calls only.
|
||||
func (c *Controller) DeleteNoAuth(ctx context.Context, session *auth.Session, spaceID int64) error {
|
||||
func (c *Controller) DeleteNoAuth(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
spaceID int64,
|
||||
) error {
|
||||
filter := &types.SpaceFilter{
|
||||
Page: 1,
|
||||
Size: math.MaxInt,
|
||||
|
@ -72,7 +80,11 @@ func (c *Controller) DeleteNoAuth(ctx context.Context, session *auth.Session, sp
|
|||
|
||||
// deleteRepositoriesNoAuth deletes all repositories in a space - no authorization is verified.
|
||||
// WARNING this is meant for internal calls only.
|
||||
func (c *Controller) deleteRepositoriesNoAuth(ctx context.Context, session *auth.Session, spaceID int64) error {
|
||||
func (c *Controller) deleteRepositoriesNoAuth(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
spaceID int64,
|
||||
) error {
|
||||
filter := &types.RepoFilter{
|
||||
Page: 1,
|
||||
Size: int(math.MaxInt),
|
||||
|
@ -81,6 +93,7 @@ func (c *Controller) deleteRepositoriesNoAuth(ctx context.Context, session *auth
|
|||
Sort: enum.RepoAttrNone,
|
||||
DeletedBefore: nil,
|
||||
}
|
||||
|
||||
repos, _, err := c.ListRepositoriesNoAuth(ctx, spaceID, filter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list space repositories: %w", err)
|
||||
|
|
|
@ -25,8 +25,12 @@ import (
|
|||
)
|
||||
|
||||
// ListRepositories lists the repositories of a space.
|
||||
func (c *Controller) ListRepositories(ctx context.Context, session *auth.Session,
|
||||
spaceRef string, filter *types.RepoFilter) ([]*types.Repository, int64, error) {
|
||||
func (c *Controller) ListRepositories(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
spaceRef string,
|
||||
filter *types.RepoFilter,
|
||||
) ([]*types.Repository, int64, error) {
|
||||
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
|
@ -35,6 +39,7 @@ func (c *Controller) ListRepositories(ctx context.Context, session *auth.Session
|
|||
if err = apiauth.CheckSpace(ctx, c.authorizer, session, space, enum.PermissionRepoView, true); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return c.ListRepositoriesNoAuth(ctx, space.ID, filter)
|
||||
}
|
||||
|
||||
|
|
|
@ -39,13 +39,20 @@ func HandleListRepos(spaceCtrl *space.Controller) http.HandlerFunc {
|
|||
filter.Order = enum.OrderAsc
|
||||
}
|
||||
|
||||
repos, totalCount, err := spaceCtrl.ListRepositories(ctx, session, spaceRef, filter)
|
||||
filter.Recursive, err = request.ParseRecursiveFromQuery(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Pagination(r, w, filter.Page, filter.Size, int(totalCount))
|
||||
repos, count, err := spaceCtrl.ListRepositories(
|
||||
ctx, session, spaceRef, filter)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Pagination(r, w, filter.Page, filter.Size, int(count))
|
||||
render.JSON(w, http.StatusOK, repos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,6 +84,20 @@ var queryParameterQueryRepo = openapi3.ParameterOrRef{
|
|||
},
|
||||
}
|
||||
|
||||
var queryParameterRecursive = openapi3.ParameterOrRef{
|
||||
Parameter: &openapi3.Parameter{
|
||||
Name: request.QueryParamQuery,
|
||||
In: openapi3.ParameterInQuery,
|
||||
Description: ptr.String("The boolean used to do space recursive op on repos."),
|
||||
Required: ptr.Bool(false),
|
||||
Schema: &openapi3.SchemaOrRef{
|
||||
Schema: &openapi3.Schema{
|
||||
Type: ptrSchemaType(openapi3.SchemaTypeBoolean),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var queryParameterSortSpace = openapi3.ParameterOrRef{
|
||||
Parameter: &openapi3.Parameter{
|
||||
Name: request.QueryParamSort,
|
||||
|
@ -268,7 +282,7 @@ func spaceOperations(reflector *openapi3.Reflector) {
|
|||
opRepos.WithTags("space")
|
||||
opRepos.WithMapOfAnything(map[string]interface{}{"operationId": "listRepos"})
|
||||
opRepos.WithParameters(queryParameterQueryRepo, queryParameterSortRepo, queryParameterOrder,
|
||||
queryParameterPage, queryParameterLimit)
|
||||
queryParameterPage, queryParameterLimit, queryParameterRecursive)
|
||||
_ = reflector.SetRequest(&opRepos, new(spaceRequest), http.MethodGet)
|
||||
_ = reflector.SetJSONResponse(&opRepos, []types.Repository{}, http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opRepos, new(usererror.Error), http.StatusInternalServerError)
|
||||
|
|
|
@ -26,6 +26,7 @@ const (
|
|||
PathParamRepoRef = "repo_ref"
|
||||
QueryParamRepoID = "repo_id"
|
||||
QueryParamRepoDeletedAt = "repo_deleted_at"
|
||||
QueryParamRecursive = "recursive"
|
||||
)
|
||||
|
||||
func GetRepoRefFromPath(r *http.Request) (string, error) {
|
||||
|
@ -60,3 +61,8 @@ func ParseRepoFilter(r *http.Request) *types.RepoFilter {
|
|||
func GetRepoDeletedAtFromQuery(r *http.Request) (int64, error) {
|
||||
return QueryParamAsPositiveInt64(r, QueryParamRepoDeletedAt)
|
||||
}
|
||||
|
||||
// ParseRecursiveFromQuery extracts the recursive option from the URL query.
|
||||
func ParseRecursiveFromQuery(r *http.Request) (bool, error) {
|
||||
return QueryParamAsBoolOrDefault(r, QueryParamRecursive, false)
|
||||
}
|
||||
|
|
|
@ -219,11 +219,7 @@ type (
|
|||
// Count of active repos in a space. With "DeletedBefore" filter, counts only deleted repos by opts.DeletedBefore.
|
||||
Count(ctx context.Context, parentID int64, opts *types.RepoFilter) (int64, error)
|
||||
|
||||
// Count all active repos in a hierarchy of spaces.
|
||||
CountAll(ctx context.Context, spaceID int64) (int64, error)
|
||||
|
||||
// List returns a list of active repos in a space.
|
||||
// With "DeletedBefore" filter, shows deleted repos by opts.DeletedBefore.
|
||||
// List returns a list of repos in a space.
|
||||
List(ctx context.Context, parentID int64, opts *types.RepoFilter) ([]*types.Repository, error)
|
||||
|
||||
// ListSizeInfos returns a list of all active repo sizes.
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/guregu/null"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -496,7 +497,22 @@ func (s *RepoStore) Restore(
|
|||
|
||||
// Count of active repos in a space. if parentID (space) is zero then it will count all repositories in the system.
|
||||
// With "DeletedBefore" filter, counts only deleted repos by opts.DeletedBefore.
|
||||
func (s *RepoStore) Count(ctx context.Context, parentID int64, opts *types.RepoFilter) (int64, error) {
|
||||
func (s *RepoStore) Count(
|
||||
ctx context.Context,
|
||||
parentID int64,
|
||||
filter *types.RepoFilter,
|
||||
) (int64, error) {
|
||||
if filter.Recursive {
|
||||
return s.countAll(ctx, parentID, filter)
|
||||
}
|
||||
return s.count(ctx, parentID, filter)
|
||||
}
|
||||
|
||||
func (s *RepoStore) count(
|
||||
ctx context.Context,
|
||||
parentID int64,
|
||||
filter *types.RepoFilter,
|
||||
) (int64, error) {
|
||||
stmt := database.Builder.
|
||||
Select("count(*)").
|
||||
From("repositories")
|
||||
|
@ -505,15 +521,7 @@ func (s *RepoStore) Count(ctx context.Context, parentID int64, opts *types.RepoF
|
|||
stmt = stmt.Where("repo_parent_id = ?", parentID)
|
||||
}
|
||||
|
||||
if opts.Query != "" {
|
||||
stmt = stmt.Where("LOWER(repo_uid) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(opts.Query)))
|
||||
}
|
||||
|
||||
if opts.DeletedBefore != nil {
|
||||
stmt = stmt.Where("repo_deleted < ?", opts.DeletedBefore)
|
||||
} else {
|
||||
stmt = stmt.Where("repo_deleted IS NULL")
|
||||
}
|
||||
stmt = applyQueryFilter(stmt, filter)
|
||||
|
||||
sql, args, err := stmt.ToSql()
|
||||
if err != nil {
|
||||
|
@ -530,8 +538,11 @@ func (s *RepoStore) Count(ctx context.Context, parentID int64, opts *types.RepoF
|
|||
return count, nil
|
||||
}
|
||||
|
||||
// Count all active repos in a hierarchy of spaces.
|
||||
func (s *RepoStore) CountAll(ctx context.Context, spaceID int64) (int64, error) {
|
||||
func (s *RepoStore) countAll(
|
||||
ctx context.Context,
|
||||
parentID int64,
|
||||
filter *types.RepoFilter,
|
||||
) (int64, error) {
|
||||
query := `WITH RECURSIVE SpaceHierarchy AS (
|
||||
SELECT space_id, space_parent_id
|
||||
FROM spaces
|
||||
|
@ -549,17 +560,24 @@ FROM SpaceHierarchy h1;`
|
|||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var spaceIDs []int64
|
||||
if err := db.SelectContext(ctx, &spaceIDs, query, spaceID); err != nil {
|
||||
if err := db.SelectContext(ctx, &spaceIDs, query, parentID); err != nil {
|
||||
return 0, database.ProcessSQLErrorf(err, "failed to retrieve spaces")
|
||||
}
|
||||
|
||||
query = fmt.Sprintf(
|
||||
"SELECT COUNT(repo_id) FROM repositories WHERE repo_parent_id IN (%s) AND repo_deleted IS NULL;",
|
||||
intsToCSV(spaceIDs),
|
||||
)
|
||||
stmt := database.Builder.
|
||||
Select("COUNT(repo_id)").
|
||||
From("repositories").
|
||||
Where(squirrel.Eq{"repo_parent_id": spaceIDs})
|
||||
|
||||
stmt = applyQueryFilter(stmt, filter)
|
||||
|
||||
sql, args, err := stmt.ToSql()
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "Failed to convert query to sql")
|
||||
}
|
||||
|
||||
var numRepos int64
|
||||
if err := db.GetContext(ctx, &numRepos, query); err != nil {
|
||||
if err := db.GetContext(ctx, &numRepos, sql, args...); err != nil {
|
||||
return 0, database.ProcessSQLErrorf(err, "failed to count repositories")
|
||||
}
|
||||
|
||||
|
@ -568,39 +586,29 @@ FROM SpaceHierarchy h1;`
|
|||
|
||||
// List returns a list of active repos in a space.
|
||||
// With "DeletedBefore" filter, shows deleted repos by opts.DeletedBefore.
|
||||
func (s *RepoStore) List(ctx context.Context, parentID int64, opts *types.RepoFilter) ([]*types.Repository, error) {
|
||||
func (s *RepoStore) List(
|
||||
ctx context.Context,
|
||||
parentID int64,
|
||||
filter *types.RepoFilter,
|
||||
) ([]*types.Repository, error) {
|
||||
if filter.Recursive {
|
||||
return s.listAll(ctx, parentID, filter)
|
||||
}
|
||||
return s.list(ctx, parentID, filter)
|
||||
}
|
||||
|
||||
func (s *RepoStore) list(
|
||||
ctx context.Context,
|
||||
parentID int64,
|
||||
filter *types.RepoFilter,
|
||||
) ([]*types.Repository, error) {
|
||||
stmt := database.Builder.
|
||||
Select(repoColumnsForJoin).
|
||||
From("repositories").
|
||||
Where("repo_parent_id = ?", fmt.Sprint(parentID))
|
||||
|
||||
if opts.DeletedBefore != nil {
|
||||
stmt = stmt.Where("repo_deleted < ?", opts.DeletedBefore)
|
||||
} else {
|
||||
stmt = stmt.Where("repo_deleted IS NULL")
|
||||
}
|
||||
|
||||
if opts.Query != "" {
|
||||
stmt = stmt.Where("LOWER(repo_uid) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(opts.Query)))
|
||||
}
|
||||
|
||||
stmt = stmt.Limit(database.Limit(opts.Size))
|
||||
stmt = stmt.Offset(database.Offset(opts.Page, opts.Size))
|
||||
|
||||
switch opts.Sort {
|
||||
// TODO [CODE-1363]: remove after identifier migration.
|
||||
case enum.RepoAttrUID, enum.RepoAttrIdentifier, enum.RepoAttrNone:
|
||||
// NOTE: string concatenation is safe because the
|
||||
// order attribute is an enum and is not user-defined,
|
||||
// and is therefore not subject to injection attacks.
|
||||
stmt = stmt.OrderBy("repo_importing desc, repo_uid " + opts.Order.String())
|
||||
case enum.RepoAttrCreated:
|
||||
stmt = stmt.OrderBy("repo_created " + opts.Order.String())
|
||||
case enum.RepoAttrUpdated:
|
||||
stmt = stmt.OrderBy("repo_updated " + opts.Order.String())
|
||||
case enum.RepoAttrDeleted:
|
||||
stmt = stmt.OrderBy("repo_deleted " + opts.Order.String())
|
||||
}
|
||||
stmt = applyQueryFilter(stmt, filter)
|
||||
stmt = applySortFilter(stmt, filter)
|
||||
|
||||
sql, args, err := stmt.ToSql()
|
||||
if err != nil {
|
||||
|
@ -617,6 +625,52 @@ func (s *RepoStore) List(ctx context.Context, parentID int64, opts *types.RepoFi
|
|||
return s.mapToRepos(ctx, dst)
|
||||
}
|
||||
|
||||
func (s *RepoStore) listAll(
|
||||
ctx context.Context,
|
||||
parentID int64,
|
||||
filter *types.RepoFilter,
|
||||
) ([]*types.Repository, error) {
|
||||
where := `WITH RECURSIVE SpaceHierarchy AS (
|
||||
SELECT space_id, space_parent_id
|
||||
FROM spaces
|
||||
WHERE space_id = $1
|
||||
|
||||
UNION
|
||||
|
||||
SELECT s.space_id, s.space_parent_id
|
||||
FROM spaces s
|
||||
JOIN SpaceHierarchy h ON s.space_parent_id = h.space_id
|
||||
)
|
||||
SELECT space_id
|
||||
FROM SpaceHierarchy h1;`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var spaceIDs []int64
|
||||
if err := db.SelectContext(ctx, &spaceIDs, where, parentID); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(err, "failed to retrieve spaces")
|
||||
}
|
||||
|
||||
stmt := database.Builder.
|
||||
Select(repoColumnsForJoin).
|
||||
From("repositories").
|
||||
Where(squirrel.Eq{"repo_parent_id": spaceIDs})
|
||||
|
||||
stmt = applyQueryFilter(stmt, filter)
|
||||
stmt = applySortFilter(stmt, filter)
|
||||
|
||||
sql, args, err := stmt.ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to convert query to sql")
|
||||
}
|
||||
repos := []*repository{}
|
||||
if err := db.SelectContext(ctx, &repos, sql, args...); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(err, "failed to count repositories")
|
||||
}
|
||||
|
||||
return s.mapToRepos(ctx, repos)
|
||||
}
|
||||
|
||||
type repoSize struct {
|
||||
ID int64 `db:"repo_id"`
|
||||
GitUID string `db:"repo_git_uid"`
|
||||
|
@ -755,10 +809,36 @@ func mapToInternalRepo(in *types.Repository) *repository {
|
|||
}
|
||||
}
|
||||
|
||||
func intsToCSV(elems []int64) string {
|
||||
strSlice := make([]string, len(elems))
|
||||
for i, num := range elems {
|
||||
strSlice[i] = fmt.Sprint(num)
|
||||
func applyQueryFilter(stmt squirrel.SelectBuilder, filter *types.RepoFilter) squirrel.SelectBuilder {
|
||||
if filter.Query != "" {
|
||||
stmt = stmt.Where("LOWER(repo_uid) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(filter.Query)))
|
||||
}
|
||||
return strings.Join(strSlice, ",")
|
||||
if filter.DeletedBefore != nil {
|
||||
stmt = stmt.Where("repo_deleted < ?", filter.DeletedBefore)
|
||||
} else {
|
||||
stmt = stmt.Where("repo_deleted IS NULL")
|
||||
}
|
||||
return stmt
|
||||
}
|
||||
|
||||
func applySortFilter(stmt squirrel.SelectBuilder, filter *types.RepoFilter) squirrel.SelectBuilder {
|
||||
stmt = stmt.Limit(database.Limit(filter.Size))
|
||||
stmt = stmt.Offset(database.Offset(filter.Page, filter.Size))
|
||||
|
||||
switch filter.Sort {
|
||||
// TODO [CODE-1363]: remove after identifier migration.
|
||||
case enum.RepoAttrUID, enum.RepoAttrIdentifier, enum.RepoAttrNone:
|
||||
// NOTE: string concatenation is safe because the
|
||||
// order attribute is an enum and is not user-defined,
|
||||
// and is therefore not subject to injection attacks.
|
||||
stmt = stmt.OrderBy("repo_importing desc, repo_uid " + filter.Order.String())
|
||||
case enum.RepoAttrCreated:
|
||||
stmt = stmt.OrderBy("repo_created " + filter.Order.String())
|
||||
case enum.RepoAttrUpdated:
|
||||
stmt = stmt.OrderBy("repo_updated " + filter.Order.String())
|
||||
|
||||
case enum.RepoAttrDeleted:
|
||||
}
|
||||
|
||||
return stmt
|
||||
}
|
||||
|
|
|
@ -16,15 +16,18 @@ package database_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/app/store/database"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
const repoSize = int64(100)
|
||||
const (
|
||||
numTestRepos = 10
|
||||
repoSize = int64(100)
|
||||
)
|
||||
|
||||
func TestDatabase_GetSize(t *testing.T) {
|
||||
db, teardown := setupDB(t)
|
||||
|
@ -34,11 +37,11 @@ func TestDatabase_GetSize(t *testing.T) {
|
|||
|
||||
ctx := context.Background()
|
||||
|
||||
createUser(t, &ctx, principalStore, 1)
|
||||
createSpace(t, &ctx, spaceStore, spacePathStore, userID, 1, 0)
|
||||
createUser(ctx, t, principalStore)
|
||||
createSpace(ctx, t, spaceStore, spacePathStore, userID, 1, 0)
|
||||
|
||||
repoID := int64(1)
|
||||
createRepo(t, &ctx, repoStore, repoID, 1, repoSize)
|
||||
createRepo(ctx, t, repoStore, repoID, 1, repoSize)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -76,7 +79,7 @@ func TestDatabase_GetSize(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDatabase_CountAll(t *testing.T) {
|
||||
func TestDatabase_Count(t *testing.T) {
|
||||
db, teardown := setupDB(t)
|
||||
defer teardown()
|
||||
|
||||
|
@ -84,24 +87,12 @@ func TestDatabase_CountAll(t *testing.T) {
|
|||
|
||||
ctx := context.Background()
|
||||
|
||||
createUser(t, &ctx, principalStore, 1)
|
||||
createUser(ctx, t, principalStore)
|
||||
|
||||
var numRepos int64
|
||||
spaceTree, numSpaces := createSpaceTree()
|
||||
createSpace(t, &ctx, spaceStore, spacePathStore, userID, 1, 0)
|
||||
for i := 1; i < numSpaces; i++ {
|
||||
parentID := int64(i)
|
||||
for _, spaceID := range spaceTree[parentID] {
|
||||
createSpace(t, &ctx, spaceStore, spacePathStore, userID, spaceID, parentID)
|
||||
createSpace(ctx, t, spaceStore, spacePathStore, userID, 1, 0)
|
||||
numRepos := createRepos(ctx, t, repoStore, 0, numTestRepos, 1)
|
||||
|
||||
for j := 0; j < rand.Intn(4); j++ {
|
||||
numRepos++
|
||||
createRepo(t, &ctx, repoStore, numRepos, spaceID, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
count, err := repoStore.CountAll(ctx, 1)
|
||||
count, err := repoStore.Count(ctx, 1, &types.RepoFilter{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to count repos %v", err)
|
||||
}
|
||||
|
@ -110,9 +101,85 @@ func TestDatabase_CountAll(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDatabase_CountAll(t *testing.T) {
|
||||
db, teardown := setupDB(t)
|
||||
defer teardown()
|
||||
|
||||
principalStore, spaceStore, spacePathStore, repoStore := setupStores(t, db)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
createUser(ctx, t, principalStore)
|
||||
|
||||
numSpaces := createNestedSpaces(ctx, t, spaceStore, spacePathStore)
|
||||
var numRepos int64
|
||||
for i := 1; i <= numSpaces; i++ {
|
||||
numRepos += createRepos(ctx, t, repoStore, numRepos, numTestRepos/2, int64(i))
|
||||
}
|
||||
|
||||
count, err := repoStore.Count(ctx, 1, &types.RepoFilter{Recursive: true})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to count repos %v", err)
|
||||
}
|
||||
if count != numRepos {
|
||||
t.Errorf("count = %v, want %v", count, numRepos)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDatabase_List(t *testing.T) {
|
||||
db, teardown := setupDB(t)
|
||||
defer teardown()
|
||||
|
||||
principalStore, spaceStore, spacePathStore, repoStore := setupStores(t, db)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
createUser(ctx, t, principalStore)
|
||||
|
||||
createSpace(ctx, t, spaceStore, spacePathStore, userID, 1, 0)
|
||||
numRepos := createRepos(ctx, t, repoStore, 0, numTestRepos, 1)
|
||||
|
||||
repos, err := repoStore.List(ctx, 1, &types.RepoFilter{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to count repos %v", err)
|
||||
}
|
||||
|
||||
lenRepos := int64(len(repos))
|
||||
if lenRepos != numRepos {
|
||||
t.Errorf("count = %v, want %v", lenRepos, numRepos)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDatabase_ListAll(t *testing.T) {
|
||||
db, teardown := setupDB(t)
|
||||
defer teardown()
|
||||
|
||||
principalStore, spaceStore, spacePathStore, repoStore := setupStores(t, db)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
createUser(ctx, t, principalStore)
|
||||
|
||||
numSpaces := createNestedSpaces(ctx, t, spaceStore, spacePathStore)
|
||||
var numRepos int64
|
||||
for i := 1; i <= numSpaces; i++ {
|
||||
numRepos += createRepos(ctx, t, repoStore, numRepos, numTestRepos/2, int64(i))
|
||||
}
|
||||
|
||||
repos, err := repoStore.List(ctx, 1,
|
||||
&types.RepoFilter{Size: numSpaces * numTestRepos, Recursive: true})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to count repos %v", err)
|
||||
}
|
||||
lenRepos := int64(len(repos))
|
||||
if lenRepos != numRepos {
|
||||
t.Errorf("count = %v, want %v", lenRepos, numRepos)
|
||||
}
|
||||
}
|
||||
|
||||
func createRepo(
|
||||
ctx context.Context,
|
||||
t *testing.T,
|
||||
ctx *context.Context,
|
||||
repoStore *database.RepoStore,
|
||||
id int64,
|
||||
spaceID int64,
|
||||
|
@ -122,7 +189,45 @@ func createRepo(
|
|||
|
||||
identifier := "repo_" + strconv.FormatInt(id, 10)
|
||||
repo := types.Repository{Identifier: identifier, ID: id, ParentID: spaceID, GitUID: identifier, Size: size}
|
||||
if err := repoStore.Create(*ctx, &repo); err != nil {
|
||||
if err := repoStore.Create(ctx, &repo); err != nil {
|
||||
t.Fatalf("failed to create repo %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func createRepos(
|
||||
ctx context.Context,
|
||||
t *testing.T,
|
||||
repoStore *database.RepoStore,
|
||||
numCreatedRepos int64,
|
||||
numReposToCreate int64,
|
||||
spaceID int64,
|
||||
) int64 {
|
||||
t.Helper()
|
||||
|
||||
var numRepos int64
|
||||
for j := 0; j < int(numReposToCreate); j++ {
|
||||
// numCreatedRepos+numRepos ensures the uniqueness of the repo id
|
||||
createRepo(ctx, t, repoStore, numCreatedRepos+numRepos, spaceID, 0)
|
||||
numRepos++
|
||||
}
|
||||
return numRepos
|
||||
}
|
||||
|
||||
func createNestedSpaces(
|
||||
ctx context.Context,
|
||||
t *testing.T,
|
||||
spaceStore *database.SpaceStore,
|
||||
spacePathStore store.SpacePathStore,
|
||||
) int {
|
||||
t.Helper()
|
||||
|
||||
spaceTree, numSpaces := createSpaceTree()
|
||||
createSpace(ctx, t, spaceStore, spacePathStore, userID, 1, 0)
|
||||
for i := 1; i < numSpaces; i++ {
|
||||
parentID := int64(i)
|
||||
for _, spaceID := range spaceTree[parentID] {
|
||||
createSpace(ctx, t, spaceStore, spacePathStore, userID, spaceID, parentID)
|
||||
}
|
||||
}
|
||||
return numSpaces
|
||||
}
|
||||
|
|
|
@ -84,23 +84,22 @@ func setupStores(t *testing.T, db *sqlx.DB) (
|
|||
}
|
||||
|
||||
func createUser(
|
||||
ctx context.Context,
|
||||
t *testing.T,
|
||||
ctx *context.Context,
|
||||
principalStore *database.PrincipalStore,
|
||||
userID int64,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
uid := "user_" + strconv.FormatInt(userID, 10)
|
||||
if err := principalStore.CreateUser(*ctx,
|
||||
if err := principalStore.CreateUser(ctx,
|
||||
&types.User{ID: userID, UID: uid}); err != nil {
|
||||
t.Fatalf("failed to create user %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func createSpace(
|
||||
ctx context.Context,
|
||||
t *testing.T,
|
||||
ctx *context.Context,
|
||||
spaceStore *database.SpaceStore,
|
||||
spacePathStore store.SpacePathStore,
|
||||
userID int64,
|
||||
|
@ -112,11 +111,11 @@ func createSpace(
|
|||
identifier := "space_" + strconv.FormatInt(spaceID, 10)
|
||||
|
||||
space := types.Space{ID: spaceID, Identifier: identifier, CreatedBy: userID, ParentID: parentID}
|
||||
if err := spaceStore.Create(*ctx, &space); err != nil {
|
||||
if err := spaceStore.Create(ctx, &space); err != nil {
|
||||
t.Fatalf("failed to create space %v", err)
|
||||
}
|
||||
|
||||
if err := spacePathStore.InsertSegment(*ctx, &types.SpacePathSegment{
|
||||
if err := spacePathStore.InsertSegment(ctx, &types.SpacePathSegment{
|
||||
ID: space.ID, Identifier: identifier, CreatedBy: userID, SpaceID: spaceID, IsPrimary: true,
|
||||
}); err != nil {
|
||||
t.Fatalf("failed to insert segment %v", err)
|
||||
|
|
|
@ -27,17 +27,9 @@ func TestDatabase_GetRootSpace(t *testing.T) {
|
|||
|
||||
ctx := context.Background()
|
||||
|
||||
createUser(t, &ctx, principalStore, 1)
|
||||
createUser(ctx, t, principalStore)
|
||||
|
||||
spaceTree, numSpaces := createSpaceTree()
|
||||
|
||||
createSpace(t, &ctx, spaceStore, spacePathStore, userID, 1, 0)
|
||||
for i := 1; i < numSpaces; i++ {
|
||||
parentID := int64(i)
|
||||
for _, spaceID := range spaceTree[parentID] {
|
||||
createSpace(t, &ctx, spaceStore, spacePathStore, 1, spaceID, parentID)
|
||||
}
|
||||
}
|
||||
numSpaces := createNestedSpaces(ctx, t, spaceStore, spacePathStore)
|
||||
|
||||
for i := 1; i <= numSpaces; i++ {
|
||||
rootSpc, err := spaceStore.GetRootSpace(ctx, int64(i))
|
||||
|
|
|
@ -36,7 +36,7 @@ func (e Order) String() string {
|
|||
case OrderAsc:
|
||||
return asc
|
||||
case OrderDefault:
|
||||
return defaultString
|
||||
return desc
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ type RepoFilter struct {
|
|||
Sort enum.RepoAttr `json:"sort"`
|
||||
Order enum.Order `json:"order"`
|
||||
DeletedBefore *int64 `json:"deleted_before,omitempty"`
|
||||
Recursive bool
|
||||
}
|
||||
|
||||
// RepositoryGitInfo holds git info for a repository.
|
||||
|
|
Loading…
Reference in New Issue