diff --git a/internal/api/controller/repo/list_paths.go b/internal/api/controller/repo/list_paths.go index 5a5c371f1..47380f106 100644 --- a/internal/api/controller/repo/list_paths.go +++ b/internal/api/controller/repo/list_paths.go @@ -6,6 +6,7 @@ package repo import ( "context" + "fmt" apiauth "github.com/harness/gitness/internal/api/auth" "github.com/harness/gitness/internal/auth" @@ -17,15 +18,25 @@ import ( * ListPaths lists all paths of a repo. */ func (c *Controller) ListPaths(ctx context.Context, session *auth.Session, - repoRef string, filter *types.PathFilter) ([]*types.Path, error) { + repoRef string, filter *types.PathFilter) ([]*types.Path, int64, error) { repo, err := findRepoFromRef(ctx, c.repoStore, repoRef) if err != nil { - return nil, err + return nil, 0, err } if err = apiauth.CheckRepo(ctx, c.authorizer, session, repo, enum.PermissionRepoView, false); err != nil { - return nil, err + return nil, 0, err } - return c.repoStore.ListAllPaths(ctx, repo.ID, filter) + count, err := c.repoStore.CountPaths(ctx, repo.ID, filter) + if err != nil { + return nil, 0, fmt.Errorf("failed to count paths: %w", err) + } + + paths, err := c.repoStore.ListPaths(ctx, repo.ID, filter) + if err != nil { + return nil, 0, fmt.Errorf("failed to list paths: %w", err) + } + + return paths, count, nil } diff --git a/internal/api/controller/space/list_paths.go b/internal/api/controller/space/list_paths.go index efa43e2ad..2b41aeb85 100644 --- a/internal/api/controller/space/list_paths.go +++ b/internal/api/controller/space/list_paths.go @@ -6,6 +6,7 @@ package space import ( "context" + "fmt" apiauth "github.com/harness/gitness/internal/api/auth" "github.com/harness/gitness/internal/auth" @@ -17,15 +18,25 @@ import ( * ListPaths lists all paths of a space. */ func (c *Controller) ListPaths(ctx context.Context, session *auth.Session, - spaceRef string, filter *types.PathFilter) ([]*types.Path, error) { + spaceRef string, filter *types.PathFilter) ([]*types.Path, int64, error) { space, err := findSpaceFromRef(ctx, c.spaceStore, spaceRef) if err != nil { - return nil, err + return nil, 0, err } if err = apiauth.CheckSpace(ctx, c.authorizer, session, space, enum.PermissionSpaceView, false); err != nil { - return nil, err + return nil, 0, err } - return c.spaceStore.ListAllPaths(ctx, space.ID, filter) + count, err := c.spaceStore.CountPaths(ctx, space.ID, filter) + if err != nil { + return nil, 0, fmt.Errorf("failed to count paths: %w", err) + } + + paths, err := c.spaceStore.ListPaths(ctx, space.ID, filter) + if err != nil { + return nil, 0, fmt.Errorf("failed to list paths: %w", err) + } + + return paths, count, nil } diff --git a/internal/api/controller/space/list_repositories.go b/internal/api/controller/space/list_repositories.go index 1bd426a11..01f01ccf1 100644 --- a/internal/api/controller/space/list_repositories.go +++ b/internal/api/controller/space/list_repositories.go @@ -18,28 +18,28 @@ import ( * ListRepositories lists the repositories of a space. */ func (c *Controller) ListRepositories(ctx context.Context, session *auth.Session, - spaceRef string, filter *types.RepoFilter) (int64, []*types.Repository, error) { + spaceRef string, filter *types.RepoFilter) ([]*types.Repository, int64, error) { space, err := findSpaceFromRef(ctx, c.spaceStore, spaceRef) if err != nil { - return 0, nil, err + return nil, 0, err } if err = apiauth.CheckSpace(ctx, c.authorizer, session, space, enum.PermissionRepoView, true); err != nil { - return 0, nil, err + return nil, 0, err } - count, err := c.repoStore.Count(ctx, space.ID) + count, err := c.repoStore.Count(ctx, space.ID, filter) if err != nil { - return 0, nil, fmt.Errorf("failed to count child repos: %w", err) + return nil, 0, fmt.Errorf("failed to count child repos: %w", err) } repos, err := c.repoStore.List(ctx, space.ID, filter) if err != nil { - return 0, nil, fmt.Errorf("failed to list child repos: %w", err) + return nil, 0, fmt.Errorf("failed to list child repos: %w", err) } /* * TODO: needs access control? Might want to avoid that (makes paging and performance hard) */ - return count, repos, nil + return repos, count, nil } diff --git a/internal/api/controller/space/list_spaces.go b/internal/api/controller/space/list_spaces.go index c73bef287..c7bde9556 100644 --- a/internal/api/controller/space/list_spaces.go +++ b/internal/api/controller/space/list_spaces.go @@ -18,28 +18,28 @@ import ( * ListSpaces lists the child spaces of a space. */ func (c *Controller) ListSpaces(ctx context.Context, session *auth.Session, - spaceRef string, filter *types.SpaceFilter) (int64, []*types.Space, error) { + spaceRef string, filter *types.SpaceFilter) ([]*types.Space, int64, error) { space, err := findSpaceFromRef(ctx, c.spaceStore, spaceRef) if err != nil { - return 0, nil, err + return nil, 0, err } if err = apiauth.CheckSpace(ctx, c.authorizer, session, space, enum.PermissionSpaceView, true); err != nil { - return 0, nil, err + return nil, 0, err } - count, err := c.spaceStore.Count(ctx, space.ID) + count, err := c.spaceStore.Count(ctx, space.ID, filter) if err != nil { - return 0, nil, fmt.Errorf("failed to count child spaces: %w", err) + return nil, 0, fmt.Errorf("failed to count child spaces: %w", err) } spaces, err := c.spaceStore.List(ctx, space.ID, filter) if err != nil { - return 0, nil, fmt.Errorf("failed to list child spaces: %w", err) + return nil, 0, fmt.Errorf("failed to list child spaces: %w", err) } /* * TODO: needs access control? Might want to avoid that (makes paging and performance hard) */ - return count, spaces, nil + return spaces, count, nil } diff --git a/internal/api/controller/user/list.go b/internal/api/controller/user/list.go index 39901fa38..0c7ae5a42 100644 --- a/internal/api/controller/user/list.go +++ b/internal/api/controller/user/list.go @@ -18,25 +18,25 @@ import ( * List lists all users of the system. */ func (c *Controller) List(ctx context.Context, session *auth.Session, - filter *types.UserFilter) (int64, []*types.User, error) { + filter *types.UserFilter) ([]*types.User, int64, error) { // Ensure principal has required permissions (user is global, no explicit resource) scope := &types.Scope{} resource := &types.Resource{ Type: enum.ResourceTypeUser, } if err := apiauth.Check(ctx, c.authorizer, session, scope, resource, enum.PermissionUserView); err != nil { - return 0, nil, err + return nil, 0, err } count, err := c.userStore.Count(ctx) if err != nil { - return 0, nil, fmt.Errorf("failed to count users: %w", err) + return nil, 0, fmt.Errorf("failed to count users: %w", err) } repos, err := c.userStore.List(ctx, filter) if err != nil { - return 0, nil, fmt.Errorf("failed to list users: %w", err) + return nil, 0, fmt.Errorf("failed to list users: %w", err) } - return count, repos, nil + return repos, count, nil } diff --git a/internal/api/handler/repo/list_paths.go b/internal/api/handler/repo/list_paths.go index d56e002d0..651e35558 100644 --- a/internal/api/handler/repo/list_paths.go +++ b/internal/api/handler/repo/list_paths.go @@ -31,13 +31,13 @@ func HandleListPaths(repoCtrl *repo.Controller) http.HandlerFunc { filter.Order = enum.OrderAsc } - paths, err := repoCtrl.ListPaths(ctx, session, repoRef, filter) + paths, totalCount, err := repoCtrl.ListPaths(ctx, session, repoRef, filter) if err != nil { render.TranslatedUserError(w, err) return } - // TODO: implement pagination - or should we block that many paths in the first place. + render.Pagination(r, w, filter.Page, filter.Size, int(totalCount)) render.JSON(w, http.StatusOK, paths) } } diff --git a/internal/api/handler/space/list.go b/internal/api/handler/space/list.go index 50e7f4c85..e378f4a80 100644 --- a/internal/api/handler/space/list.go +++ b/internal/api/handler/space/list.go @@ -29,7 +29,7 @@ func HandleListSpaces(spaceCtrl *space.Controller) http.HandlerFunc { spaceFilter.Order = enum.OrderAsc } - totalCount, spaces, err := spaceCtrl.ListSpaces(ctx, session, spaceRef, spaceFilter) + spaces, totalCount, err := spaceCtrl.ListSpaces(ctx, session, spaceRef, spaceFilter) if err != nil { render.TranslatedUserError(w, err) return diff --git a/internal/api/handler/space/list_paths.go b/internal/api/handler/space/list_paths.go index 94d9b5e3d..633610f0a 100644 --- a/internal/api/handler/space/list_paths.go +++ b/internal/api/handler/space/list_paths.go @@ -29,13 +29,13 @@ func HandleListPaths(spaceCtrl *space.Controller) http.HandlerFunc { filter.Order = enum.OrderAsc } - paths, err := spaceCtrl.ListPaths(ctx, session, spaceRef, filter) + paths, totalCount, err := spaceCtrl.ListPaths(ctx, session, spaceRef, filter) if err != nil { render.TranslatedUserError(w, err) return } - // TODO: do we need pagination? we should block that many paths in the first place. + render.Pagination(r, w, filter.Page, filter.Size, int(totalCount)) render.JSON(w, http.StatusOK, paths) } } diff --git a/internal/api/handler/space/list_repos.go b/internal/api/handler/space/list_repos.go index 060a2d921..1a73d6eb6 100644 --- a/internal/api/handler/space/list_repos.go +++ b/internal/api/handler/space/list_repos.go @@ -29,7 +29,7 @@ func HandleListRepos(spaceCtrl *space.Controller) http.HandlerFunc { filter.Order = enum.OrderAsc } - totalCount, repos, err := spaceCtrl.ListRepositories(ctx, session, spaceRef, filter) + repos, totalCount, err := spaceCtrl.ListRepositories(ctx, session, spaceRef, filter) if err != nil { render.TranslatedUserError(w, err) return diff --git a/internal/api/handler/users/list.go b/internal/api/handler/users/list.go index b82ff2ef0..812beab51 100644 --- a/internal/api/handler/users/list.go +++ b/internal/api/handler/users/list.go @@ -25,7 +25,7 @@ func HandleList(userCtrl *user.Controller) http.HandlerFunc { filter.Order = enum.OrderAsc } - totalCount, list, err := userCtrl.List(ctx, session, filter) + list, totalCount, err := userCtrl.List(ctx, session, filter) if err != nil { render.TranslatedUserError(w, err) return diff --git a/internal/api/openapi/repo.go b/internal/api/openapi/repo.go index aba947d69..17858c5f5 100644 --- a/internal/api/openapi/repo.go +++ b/internal/api/openapi/repo.go @@ -12,6 +12,7 @@ import ( "github.com/harness/gitness/internal/api/request" "github.com/harness/gitness/internal/api/usererror" "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" "github.com/swaggest/openapi-go/openapi3" ) @@ -138,6 +139,39 @@ var queryParameterSpacePath = openapi3.ParameterOrRef{ }, } +var queryParameterSortBranch = openapi3.ParameterOrRef{ + Parameter: &openapi3.Parameter{ + Name: request.QueryParamSort, + In: openapi3.ParameterInQuery, + Description: ptr.String("The data by which the branches are sorted."), + Required: ptr.Bool(false), + Schema: &openapi3.SchemaOrRef{ + Schema: &openapi3.Schema{ + Type: ptrSchemaType(openapi3.SchemaTypeString), + Default: ptrptr(enum.BranchSortOptionName.String()), + Enum: []interface{}{ + ptr.String(enum.BranchSortOptionName.String()), + ptr.String(enum.BranchSortOptionDate.String()), + }, + }, + }, + }, +} + +var queryParameterQueryBranch = openapi3.ParameterOrRef{ + Parameter: &openapi3.Parameter{ + Name: request.QueryParamQuery, + In: openapi3.ParameterInQuery, + Description: ptr.String("The substring by which the branches are filtered."), + Required: ptr.Bool(false), + Schema: &openapi3.SchemaOrRef{ + Schema: &openapi3.Schema{ + Type: ptrSchemaType(openapi3.SchemaTypeString), + }, + }, + }, +} + //nolint:funlen func repoOperations(reflector *openapi3.Reflector) { createRepository := openapi3.Operation{} @@ -211,6 +245,7 @@ func repoOperations(reflector *openapi3.Reflector) { opListPaths := openapi3.Operation{} opListPaths.WithTags("repository") opListPaths.WithMapOfAnything(map[string]interface{}{"operationId": "listRepositoryPaths"}) + opListPaths.WithParameters(queryParameterPage, queryParameterPerPage) _ = reflector.SetRequest(&opListPaths, new(repoRequest), http.MethodGet) _ = reflector.SetJSONResponse(&opListPaths, []types.Path{}, http.StatusOK) _ = reflector.SetJSONResponse(&opListPaths, new(usererror.Error), http.StatusInternalServerError) diff --git a/internal/api/openapi/shared.go b/internal/api/openapi/shared.go index 3bb27e1ce..511380576 100644 --- a/internal/api/openapi/shared.go +++ b/internal/api/openapi/shared.go @@ -70,36 +70,3 @@ var queryParameterDirection = openapi3.ParameterOrRef{ }, }, } - -var queryParameterSortBranch = openapi3.ParameterOrRef{ - Parameter: &openapi3.Parameter{ - Name: request.QueryParamSort, - In: openapi3.ParameterInQuery, - Description: ptr.String("The data by which the branches are sorted."), - Required: ptr.Bool(false), - Schema: &openapi3.SchemaOrRef{ - Schema: &openapi3.Schema{ - Type: ptrSchemaType(openapi3.SchemaTypeString), - Default: ptrptr(enum.BranchSortOptionName.String()), - Enum: []interface{}{ - ptr.String(enum.BranchSortOptionName.String()), - ptr.String(enum.BranchSortOptionDate.String()), - }, - }, - }, - }, -} - -var queryParameterQueryBranch = openapi3.ParameterOrRef{ - Parameter: &openapi3.Parameter{ - Name: request.QueryParamQuery, - In: openapi3.ParameterInQuery, - Description: ptr.String("The substring by which the branches are filtered."), - Required: ptr.Bool(false), - Schema: &openapi3.SchemaOrRef{ - Schema: &openapi3.Schema{ - Type: ptrSchemaType(openapi3.SchemaTypeString), - }, - }, - }, -} diff --git a/internal/api/openapi/space.go b/internal/api/openapi/space.go index bd57d0425..6ae05e581 100644 --- a/internal/api/openapi/space.go +++ b/internal/api/openapi/space.go @@ -7,9 +7,12 @@ package openapi import ( "net/http" + "github.com/gotidy/ptr" "github.com/harness/gitness/internal/api/controller/space" + "github.com/harness/gitness/internal/api/request" "github.com/harness/gitness/internal/api/usererror" "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" "github.com/swaggest/openapi-go/openapi3" ) @@ -41,6 +44,78 @@ type deletePathRequest struct { PathID string `json:"pathID" path:"pathID"` } +var queryParameterSortRepo = openapi3.ParameterOrRef{ + Parameter: &openapi3.Parameter{ + Name: request.QueryParamSort, + In: openapi3.ParameterInQuery, + Description: ptr.String("The data by which the repositories are sorted."), + Required: ptr.Bool(false), + Schema: &openapi3.SchemaOrRef{ + Schema: &openapi3.Schema{ + Type: ptrSchemaType(openapi3.SchemaTypeString), + Default: ptrptr(enum.RepoAttrName.String()), + Enum: []interface{}{ + ptr.String(enum.RepoAttrName.String()), + ptr.String(enum.RepoAttrPath.String()), + ptr.String(enum.RepoAttrPathName.String()), + ptr.String(enum.RepoAttrCreated.String()), + ptr.String(enum.RepoAttrUpdated.String()), + }, + }, + }, + }, +} + +var queryParameterQueryRepo = openapi3.ParameterOrRef{ + Parameter: &openapi3.Parameter{ + Name: request.QueryParamQuery, + In: openapi3.ParameterInQuery, + Description: ptr.String("The substring which is used to filter the repositories by their path name."), + Required: ptr.Bool(false), + Schema: &openapi3.SchemaOrRef{ + Schema: &openapi3.Schema{ + Type: ptrSchemaType(openapi3.SchemaTypeString), + }, + }, + }, +} + +var queryParameterSortSpace = openapi3.ParameterOrRef{ + Parameter: &openapi3.Parameter{ + Name: request.QueryParamSort, + In: openapi3.ParameterInQuery, + Description: ptr.String("The data by which the spaces are sorted."), + Required: ptr.Bool(false), + Schema: &openapi3.SchemaOrRef{ + Schema: &openapi3.Schema{ + Type: ptrSchemaType(openapi3.SchemaTypeString), + Default: ptrptr(enum.SpaceAttrName.String()), + Enum: []interface{}{ + ptr.String(enum.SpaceAttrName.String()), + ptr.String(enum.SpaceAttrPath.String()), + ptr.String(enum.SpaceAttrPathName.String()), + ptr.String(enum.SpaceAttrCreated.String()), + ptr.String(enum.SpaceAttrUpdated.String()), + }, + }, + }, + }, +} + +var queryParameterQuerySpace = openapi3.ParameterOrRef{ + Parameter: &openapi3.Parameter{ + Name: request.QueryParamQuery, + In: openapi3.ParameterInQuery, + Description: ptr.String("The substring which is used to filter the spaces by their path name."), + Required: ptr.Bool(false), + Schema: &openapi3.SchemaOrRef{ + Schema: &openapi3.Schema{ + Type: ptrSchemaType(openapi3.SchemaTypeString), + }, + }, + }, +} + //nolint:funlen // api spec generation no need for checking func complexity func spaceOperations(reflector *openapi3.Reflector) { opCreate := openapi3.Operation{} @@ -103,6 +178,8 @@ func spaceOperations(reflector *openapi3.Reflector) { opSpaces.WithTags("space") opSpaces.WithMapOfAnything(map[string]interface{}{"operationId": "listSpaces"}) opSpaces.WithParameters(queryParameterPage, queryParameterPerPage) + opSpaces.WithParameters(queryParameterQuerySpace, queryParameterSortSpace, queryParameterDirection, + queryParameterPage, queryParameterPerPage) _ = reflector.SetRequest(&opSpaces, new(spaceRequest), http.MethodGet) _ = reflector.SetJSONResponse(&opSpaces, []types.Space{}, http.StatusOK) _ = reflector.SetJSONResponse(&opSpaces, new(usererror.Error), http.StatusInternalServerError) @@ -114,7 +191,8 @@ func spaceOperations(reflector *openapi3.Reflector) { opRepos := openapi3.Operation{} opRepos.WithTags("space") opRepos.WithMapOfAnything(map[string]interface{}{"operationId": "listRepos"}) - opRepos.WithParameters(queryParameterPage, queryParameterPerPage) + opRepos.WithParameters(queryParameterQueryRepo, queryParameterSortRepo, queryParameterDirection, + queryParameterPage, queryParameterPerPage) _ = reflector.SetRequest(&opRepos, new(spaceRequest), http.MethodGet) _ = reflector.SetJSONResponse(&opRepos, []types.Repository{}, http.StatusOK) _ = reflector.SetJSONResponse(&opRepos, new(usererror.Error), http.StatusInternalServerError) @@ -137,6 +215,7 @@ func spaceOperations(reflector *openapi3.Reflector) { opListPaths := openapi3.Operation{} opListPaths.WithTags("space") opListPaths.WithMapOfAnything(map[string]interface{}{"operationId": "listPaths"}) + opListPaths.WithParameters(queryParameterPage, queryParameterPerPage) _ = reflector.SetRequest(&opListPaths, new(spaceRequest), http.MethodGet) _ = reflector.SetJSONResponse(&opListPaths, []types.Path{}, http.StatusOK) _ = reflector.SetJSONResponse(&opListPaths, new(usererror.Error), http.StatusInternalServerError) diff --git a/internal/api/request/util.go b/internal/api/request/util.go index 92385f72b..e9455221c 100644 --- a/internal/api/request/util.go +++ b/internal/api/request/util.go @@ -198,6 +198,7 @@ func ParseUserFilter(r *http.Request) *types.UserFilter { // ParseSpaceFilter extracts the space query parameter from the url. func ParseSpaceFilter(r *http.Request) *types.SpaceFilter { return &types.SpaceFilter{ + Query: ParseQuery(r), Order: ParseOrder(r), Page: ParsePage(r), Sort: ParseSortSpace(r), @@ -208,6 +209,7 @@ func ParseSpaceFilter(r *http.Request) *types.SpaceFilter { // ParseRepoFilter extracts the repository query parameter from the url. func ParseRepoFilter(r *http.Request) *types.RepoFilter { return &types.RepoFilter{ + Query: ParseQuery(r), Order: ParseOrder(r), Page: ParsePage(r), Sort: ParseSortRepo(r), diff --git a/internal/store/database/path.go b/internal/store/database/path.go index 26cbfa4fd..b8eb964ad 100644 --- a/internal/store/database/path.go +++ b/internal/store/database/path.go @@ -90,7 +90,7 @@ func ListPrimaryChildPathsTx(ctx context.Context, tx *sqlx.Tx, prefix string) ([ return childs, nil } -// ReplacePathTx replace the path for a target as part of a transaction - keeps the existing as alias if requested. +// ReplacePathTx replaces the path for a target as part of a transaction - keeps the existing as alias if requested. func ReplacePathTx(ctx context.Context, db *sqlx.DB, tx *sqlx.Tx, path *types.Path, keepAsAlias bool) error { if path.IsAlias { return store.ErrPrimaryPathRequired @@ -204,7 +204,7 @@ func FindPathTx(ctx context.Context, tx *sqlx.Tx, targetType enum.PathTargetType return dst, nil } -// Deletes a specific path alias (primary can't be deleted, only with delete all). +// DeletePath deletes a specific path alias (primary can't be deleted, only with delete all). func DeletePath(ctx context.Context, db *sqlx.DB, id int64) error { tx, err := db.BeginTxx(ctx, nil) if err != nil { @@ -234,7 +234,7 @@ func DeletePath(ctx context.Context, db *sqlx.DB, id int64) error { return nil } -// Deletes all paths for a target as part of a transaction. +// DeleteAllPaths deletes all paths for a target as part of a transaction. func DeleteAllPaths(ctx context.Context, tx *sqlx.Tx, targetType enum.PathTargetType, targetID int64) error { // delete all entries for the target if _, err := tx.ExecContext(ctx, pathDeleteTarget, string(targetType), fmt.Sprint(targetID)); err != nil { @@ -243,58 +243,54 @@ func DeleteAllPaths(ctx context.Context, tx *sqlx.Tx, targetType enum.PathTarget return nil } -// Lists all paths for a target. +// CountPaths returns the count of paths for a specified target. +func CountPaths(ctx context.Context, db *sqlx.DB, targetType enum.PathTargetType, targetID int64, + opts *types.PathFilter) (int64, error) { + var count int64 + err := db.QueryRowContext(ctx, pathCount, string(targetType), fmt.Sprint(targetID)).Scan(&count) + if err != nil { + return 0, processSQLErrorf(err, "Failed executing count query") + } + return count, nil +} + +// ListPaths lists all paths for a target. func ListPaths(ctx context.Context, db *sqlx.DB, targetType enum.PathTargetType, targetID int64, opts *types.PathFilter) ([]*types.Path, error) { dst := []*types.Path{} - - // if the principal does not provide any customer filter - // or sorting we use the default select statement. - if opts.Sort == enum.PathAttrNone { - err := db.SelectContext(ctx, &dst, pathSelect, string(targetType), fmt.Sprint(targetID), limit(opts.Size), - offset(opts.Page, opts.Size)) - if err != nil { - return nil, processSQLErrorf(err, "Default select query failed") - } - - return dst, nil - } - // else we construct the sql statement. - stmt := builder.Select("*").From("paths").Where("path_targetType = $1 AND path_targetId = $2", - string(targetType), fmt.Sprint(targetID)) + stmt := builder. + Select("*"). + From("paths"). + Where("path_targetType = ? AND path_targetId = ?", string(targetType), fmt.Sprint(targetID)) stmt = stmt.Limit(uint64(limit(opts.Size))) stmt = stmt.Offset(uint64(offset(opts.Page, opts.Size))) switch opts.Sort { - case enum.PathAttrCreated: + case enum.PathAttrPath, enum.PathAttrNone: // 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("path_value COLLATE NOCASE " + opts.Order.String()) + case enum.PathAttrCreated: stmt = stmt.OrderBy("path_created " + opts.Order.String()) case enum.PathAttrUpdated: stmt = stmt.OrderBy("path_updated " + opts.Order.String()) - case enum.PathAttrID: - stmt = stmt.OrderBy("path_id " + opts.Order.String()) - case enum.PathAttrPath: - stmt = stmt.OrderBy("path_value" + opts.Order.String()) - case enum.PathAttrNone: - // no sorting required } - sql, _, err := stmt.ToSql() + sql, args, err := stmt.ToSql() if err != nil { return nil, errors.Wrap(err, "Failed to convert query to sql") } - if err = db.SelectContext(ctx, &dst, sql); err != nil { + if err = db.SelectContext(ctx, &dst, sql, args...); err != nil { return nil, processSQLErrorf(err, "Customer select query failed") } return dst, nil } -// CountPathsTx Count paths for a target as part of a transaction. +// CountPathsTx counts paths for a target as part of a transaction. func CountPathsTx(ctx context.Context, tx *sqlx.Tx, targetType enum.PathTargetType, targetID int64) (int64, error) { var count int64 err := tx.QueryRowContext(ctx, pathCount, string(targetType), fmt.Sprint(targetID)).Scan(&count) @@ -316,11 +312,6 @@ path_id ,path_updated FROM paths ` -const pathSelect = pathBase + ` -WHERE path_targetType = $1 AND path_targetId = $2 -ORDER BY path_isAlias DESC, path_value ASC -LIMIT $3 OFFSET $4 -` // there's only one entry with a given target & targetId for isAlias -- false. const pathSelectPrimaryForTarget = pathBase + ` diff --git a/internal/store/database/repo.go b/internal/store/database/repo.go index dff4489af..51b958c70 100644 --- a/internal/store/database/repo.go +++ b/internal/store/database/repo.go @@ -208,9 +208,23 @@ func (s *RepoStore) Delete(ctx context.Context, id int64) error { } // Count of repos in a space. -func (s *RepoStore) Count(ctx context.Context, spaceID int64) (int64, error) { +func (s *RepoStore) Count(ctx context.Context, spaceID int64, opts *types.RepoFilter) (int64, error) { + stmt := builder. + Select("count(*)"). + From("repositories"). + Where("repo_spaceId = ?", spaceID) + + if opts.Query != "" { + stmt = stmt.Where("repo_pathName LIKE ?", fmt.Sprintf("%%%s%%", opts.Query)) + } + + sql, args, err := stmt.ToSql() + if err != nil { + return 0, errors.Wrap(err, "Failed to convert query to sql") + } + var count int64 - err := s.db.QueryRow(repoCount, spaceID).Scan(&count) + err = s.db.QueryRowContext(ctx, sql, args...).Scan(&count) if err != nil { return 0, processSQLErrorf(err, "Failed executing count query") } @@ -218,27 +232,21 @@ func (s *RepoStore) Count(ctx context.Context, spaceID int64) (int64, error) { } // List returns a list of repos in a space. -// TODO: speed up list - for some reason is 200ms for 1 repo as well as 1000 func (s *RepoStore) List(ctx context.Context, spaceID int64, opts *types.RepoFilter) ([]*types.Repository, error) { dst := []*types.Repository{} - // if the principal does not provide any customer filter - // or sorting we use the default select statement. - if opts.Sort == enum.RepoAttrNone { - err := s.db.SelectContext(ctx, &dst, repoSelect, spaceID, limit(opts.Size), offset(opts.Page, opts.Size)) - if err != nil { - return nil, processSQLErrorf(err, "Failed executing default list query") - } - return dst, nil - } - - // else we construct the sql statement. + // construct the sql statement. stmt := builder. Select("repositories.*,path_value AS repo_path"). From("repositories"). - InnerJoin("paths ON repositories.repo_id=paths.path_targetId AND paths.path_targetType='repo' " + + InnerJoin("paths ON repositories.repo_id=paths.path_targetId AND paths.path_targetType='repo' "+ "AND paths.path_isAlias=0"). - Where("repo_spaceId = " + fmt.Sprint(spaceID)) + Where("repo_spaceId = ?", fmt.Sprint(spaceID)) + + if opts.Query != "" { + stmt = stmt.Where("repo_pathName LIKE ?", fmt.Sprintf("%%%s%%", opts.Query)) + } + stmt = stmt.Limit(uint64(limit(opts.Size))) stmt = stmt.Offset(uint64(offset(opts.Page, opts.Size))) @@ -247,33 +255,36 @@ func (s *RepoStore) List(ctx context.Context, spaceID int64, opts *types.RepoFil // 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_name " + opts.Order.String()) + stmt = stmt.OrderBy("repo_name COLLATE NOCASE " + 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.RepoAttrID: - stmt = stmt.OrderBy("repo_id " + opts.Order.String()) case enum.RepoAttrPathName: - stmt = stmt.OrderBy("repo_pathName " + opts.Order.String()) + stmt = stmt.OrderBy("repo_pathName COLLATE NOCASE " + opts.Order.String()) case enum.RepoAttrPath: - stmt = stmt.OrderBy("repo_path " + opts.Order.String()) + stmt = stmt.OrderBy("repo_path COLLATE NOCASE " + opts.Order.String()) } - sql, _, err := stmt.ToSql() + sql, args, err := stmt.ToSql() if err != nil { return nil, errors.Wrap(err, "Failed to convert query to sql") } - if err = s.db.SelectContext(ctx, &dst, sql); err != nil { + if err = s.db.SelectContext(ctx, &dst, sql, args...); err != nil { return nil, processSQLErrorf(err, "Failed executing custom list query") } return dst, nil } -// ListAllPaths returns a list of all paths of a repo. -func (s *RepoStore) ListAllPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) { +// CountPaths returns a count of all paths of a repo. +func (s *RepoStore) CountPaths(ctx context.Context, id int64, opts *types.PathFilter) (int64, error) { + return CountPaths(ctx, s.db, enum.PathTargetTypeRepo, id, opts) +} + +// ListPaths returns a list of all paths of a repo. +func (s *RepoStore) ListPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) { return ListPaths(ctx, s.db, enum.PathTargetTypeRepo, id, opts) } @@ -326,18 +337,6 @@ INNER JOIN paths ON repositories.repo_id=paths.path_targetId AND paths.path_targetType='repo' AND paths.path_isAlias=0 ` -const repoSelect = repoSelectBaseWithJoin + ` -WHERE repo_spaceId = $1 -ORDER BY repo_pathName ASC -LIMIT $2 OFFSET $3 -` - -const repoCount = ` -SELECT count(*) -FROM repositories -WHERE repo_spaceId = $1 -` - const repoSelectByID = repoSelectBaseWithJoin + ` WHERE repo_id = $1 ` diff --git a/internal/store/database/repo_sync.go b/internal/store/database/repo_sync.go index dee76b94f..f86fd3afc 100644 --- a/internal/store/database/repo_sync.go +++ b/internal/store/database/repo_sync.go @@ -70,10 +70,10 @@ func (s *RepoStoreSync) Delete(ctx context.Context, id int64) error { } // Count of repos in a space. -func (s *RepoStoreSync) Count(ctx context.Context, spaceID int64) (int64, error) { +func (s *RepoStoreSync) Count(ctx context.Context, spaceID int64, opts *types.RepoFilter) (int64, error) { mutex.RLock() defer mutex.RUnlock() - return s.base.Count(ctx, spaceID) + return s.base.Count(ctx, spaceID, opts) } // List returns a list of repos in a space. @@ -83,9 +83,14 @@ func (s *RepoStoreSync) List(ctx context.Context, spaceID int64, opts *types.Rep return s.base.List(ctx, spaceID, opts) } -// ListAllPaths returns a list of all paths of a repo. -func (s *RepoStoreSync) ListAllPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) { - return s.base.ListAllPaths(ctx, id, opts) +// CountPaths returns a count of all paths of a repo. +func (s *RepoStoreSync) CountPaths(ctx context.Context, id int64, opts *types.PathFilter) (int64, error) { + return s.base.CountPaths(ctx, id, opts) +} + +// ListPaths returns a list of all paths of a repo. +func (s *RepoStoreSync) ListPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) { + return s.base.ListPaths(ctx, id, opts) } // CreatePath an alias for a repository. diff --git a/internal/store/database/space.go b/internal/store/database/space.go index c142dfe83..c8cc90b57 100644 --- a/internal/store/database/space.go +++ b/internal/store/database/space.go @@ -241,9 +241,23 @@ func (s *SpaceStore) Delete(ctx context.Context, id int64) error { } // Count the child spaces of a space. -func (s *SpaceStore) Count(ctx context.Context, id int64) (int64, error) { +func (s *SpaceStore) Count(ctx context.Context, id int64, opts *types.SpaceFilter) (int64, error) { + stmt := builder. + Select("count(*)"). + From("spaces"). + Where("space_parentId = ?", id) + + if opts.Query != "" { + stmt = stmt.Where("space_pathName LIKE ?", fmt.Sprintf("%%%s%%", opts.Query)) + } + + sql, args, err := stmt.ToSql() + if err != nil { + return 0, errors.Wrap(err, "Failed to convert query to sql") + } + var count int64 - err := s.db.QueryRowContext(ctx, spaceCount, id).Scan(&count) + err = s.db.QueryRowContext(ctx, sql, args...).Scan(&count) if err != nil { return 0, processSQLErrorf(err, "Failed executing count query") } @@ -251,61 +265,56 @@ func (s *SpaceStore) Count(ctx context.Context, id int64) (int64, error) { } // List returns a list of spaces under the parent space. -// TODO: speed up list - for some reason is 200ms for 1 space as well as 1000 func (s *SpaceStore) List(ctx context.Context, id int64, opts *types.SpaceFilter) ([]*types.Space, error) { dst := []*types.Space{} - // if the principal does not provide any customer filter - // or sorting we use the default select statement. - if opts.Sort == enum.SpaceAttrNone { - err := s.db.SelectContext(ctx, &dst, spaceSelect, id, limit(opts.Size), offset(opts.Page, opts.Size)) - if err != nil { - return nil, processSQLErrorf(err, "Failed executing default list query") - } - return dst, nil - } - - // else we construct the sql statement. stmt := builder. Select("spaces.*,path_value AS space_path"). From("spaces"). InnerJoin("paths ON spaces.space_id=paths.path_targetId AND paths.path_targetType='space' AND paths.path_isAlias=0"). - Where("space_parentId = " + fmt.Sprint(id)) + Where("space_parentId = ?", fmt.Sprint(id)) stmt = stmt.Limit(uint64(limit(opts.Size))) stmt = stmt.Offset(uint64(offset(opts.Page, opts.Size))) + if opts.Query != "" { + stmt = stmt.Where("space_pathName LIKE ?", fmt.Sprintf("%%%s%%", opts.Query)) + } + switch opts.Sort { case enum.SpaceAttrName, enum.SpaceAttrNone: // 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("space_name " + opts.Order.String()) + stmt = stmt.OrderBy("space_name COLLATE NOCASE " + opts.Order.String()) case enum.SpaceAttrCreated: stmt = stmt.OrderBy("space_created " + opts.Order.String()) case enum.SpaceAttrUpdated: stmt = stmt.OrderBy("space_updated " + opts.Order.String()) - case enum.SpaceAttrID: - stmt = stmt.OrderBy("space_id " + opts.Order.String()) case enum.SpaceAttrPathName: - stmt = stmt.OrderBy("space_pathName " + opts.Order.String()) + stmt = stmt.OrderBy("space_pathName COLLATE NOCASE " + opts.Order.String()) case enum.SpaceAttrPath: - stmt = stmt.OrderBy("space_path " + opts.Order.String()) + stmt = stmt.OrderBy("space_path COLLATE NOCASE " + opts.Order.String()) } - sql, _, err := stmt.ToSql() + sql, args, err := stmt.ToSql() if err != nil { return nil, errors.Wrap(err, "Failed to convert query to sql") } - if err = s.db.SelectContext(ctx, &dst, sql); err != nil { + if err = s.db.SelectContext(ctx, &dst, sql, args...); err != nil { return nil, processSQLErrorf(err, "Failed executing custom list query") } return dst, nil } -// ListAllPaths returns a list of all paths of a space. -func (s *SpaceStore) ListAllPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) { +// CountPaths returns a count of all paths of a space. +func (s *SpaceStore) CountPaths(ctx context.Context, id int64, opts *types.PathFilter) (int64, error) { + return CountPaths(ctx, s.db, enum.PathTargetTypeSpace, id, opts) +} + +// ListPaths returns a list of all paths of a space. +func (s *SpaceStore) ListPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) { return ListPaths(ctx, s.db, enum.PathTargetTypeSpace, id, opts) } @@ -351,18 +360,6 @@ INNER JOIN paths ON spaces.space_id=paths.path_targetId AND paths.path_targetType='space' AND paths.path_isAlias=0 ` -const spaceSelect = spaceSelectBaseWithJoin + ` -WHERE space_parentId = $1 -ORDER BY space_pathName ASC -LIMIT $2 OFFSET $3 -` - -const spaceCount = ` -SELECT count(*) -FROM spaces -WHERE space_parentId = $1 -` - const spaceSelectByID = spaceSelectBaseWithJoin + ` WHERE space_id = $1 ` diff --git a/internal/store/database/space_sync.go b/internal/store/database/space_sync.go index 4412ad4e5..603210826 100644 --- a/internal/store/database/space_sync.go +++ b/internal/store/database/space_sync.go @@ -70,10 +70,10 @@ func (s *SpaceStoreSync) Delete(ctx context.Context, id int64) error { } // Count the child spaces of a space. -func (s *SpaceStoreSync) Count(ctx context.Context, id int64) (int64, error) { +func (s *SpaceStoreSync) Count(ctx context.Context, id int64, opts *types.SpaceFilter) (int64, error) { mutex.RLock() defer mutex.RUnlock() - return s.base.Count(ctx, id) + return s.base.Count(ctx, id, opts) } // List returns a list of spaces under the parent space. @@ -83,9 +83,14 @@ func (s *SpaceStoreSync) List(ctx context.Context, id int64, opts *types.SpaceFi return s.base.List(ctx, id, opts) } -// ListAllPaths returns a list of all paths of a space. -func (s *SpaceStoreSync) ListAllPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) { - return s.base.ListAllPaths(ctx, id, opts) +// CountPaths returns a count of all paths of a space. +func (s *SpaceStoreSync) CountPaths(ctx context.Context, id int64, opts *types.PathFilter) (int64, error) { + return s.base.CountPaths(ctx, id, opts) +} + +// ListPaths returns a list of all paths of a space. +func (s *SpaceStoreSync) ListPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) { + return s.base.ListPaths(ctx, id, opts) } // CreatePath a path for a space. diff --git a/internal/store/store.go b/internal/store/store.go index eee438186..7eb29c063 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -110,13 +110,16 @@ type ( Delete(ctx context.Context, id int64) error // Count the child spaces of a space. - Count(ctx context.Context, id int64) (int64, error) + Count(ctx context.Context, id int64, opts *types.SpaceFilter) (int64, error) // List returns a list of child spaces in a space. List(ctx context.Context, id int64, opts *types.SpaceFilter) ([]*types.Space, error) - // ListAllPaths returns a list of all paths of a space. - ListAllPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) + // CountPaths returns a count of all paths of a space. + CountPaths(ctx context.Context, id int64, opts *types.PathFilter) (int64, error) + + // ListPaths returns a list of all paths of a space. + ListPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) // CreatePath create an alias for a space CreatePath(ctx context.Context, spaceID int64, params *types.PathParams) (*types.Path, error) @@ -147,13 +150,16 @@ type ( Delete(ctx context.Context, id int64) error // Count of repos in a space. - Count(ctx context.Context, spaceID int64) (int64, error) + Count(ctx context.Context, spaceID int64, opts *types.RepoFilter) (int64, error) // List returns a list of repos in a space. List(ctx context.Context, spaceID int64, opts *types.RepoFilter) ([]*types.Repository, error) - // ListAllPaths returns a list of all alias paths of a repo. - ListAllPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) + // CountPaths returns a count of all paths of a repo. + CountPaths(ctx context.Context, id int64, opts *types.PathFilter) (int64, error) + + // ListPaths returns a list of all paths of a repo. + ListPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) // CreatePath an alias for a repo CreatePath(ctx context.Context, repoID int64, params *types.PathParams) (*types.Path, error) diff --git a/mocks/mock_store.go b/mocks/mock_store.go index a1e502b7f..b543d7a7b 100644 --- a/mocks/mock_store.go +++ b/mocks/mock_store.go @@ -213,18 +213,33 @@ func (m *MockSpaceStore) EXPECT() *MockSpaceStoreMockRecorder { } // Count mocks base method. -func (m *MockSpaceStore) Count(arg0 context.Context, arg1 int64) (int64, error) { +func (m *MockSpaceStore) Count(arg0 context.Context, arg1 int64, arg2 *types.SpaceFilter) (int64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Count", arg0, arg1) + ret := m.ctrl.Call(m, "Count", arg0, arg1, arg2) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // Count indicates an expected call of Count. -func (mr *MockSpaceStoreMockRecorder) Count(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockSpaceStoreMockRecorder) Count(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Count", reflect.TypeOf((*MockSpaceStore)(nil).Count), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Count", reflect.TypeOf((*MockSpaceStore)(nil).Count), arg0, arg1, arg2) +} + +// CountPaths mocks base method. +func (m *MockSpaceStore) CountPaths(arg0 context.Context, arg1 int64, arg2 *types.PathFilter) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CountPaths", arg0, arg1, arg2) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CountPaths indicates an expected call of CountPaths. +func (mr *MockSpaceStoreMockRecorder) CountPaths(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountPaths", reflect.TypeOf((*MockSpaceStore)(nil).CountPaths), arg0, arg1, arg2) } // Create mocks base method. @@ -329,19 +344,19 @@ func (mr *MockSpaceStoreMockRecorder) List(arg0, arg1, arg2 interface{}) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockSpaceStore)(nil).List), arg0, arg1, arg2) } -// ListAllPaths mocks base method. -func (m *MockSpaceStore) ListAllPaths(arg0 context.Context, arg1 int64, arg2 *types.PathFilter) ([]*types.Path, error) { +// ListPaths mocks base method. +func (m *MockSpaceStore) ListPaths(arg0 context.Context, arg1 int64, arg2 *types.PathFilter) ([]*types.Path, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAllPaths", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "ListPaths", arg0, arg1, arg2) ret0, _ := ret[0].([]*types.Path) ret1, _ := ret[1].(error) return ret0, ret1 } -// ListAllPaths indicates an expected call of ListAllPaths. -func (mr *MockSpaceStoreMockRecorder) ListAllPaths(arg0, arg1, arg2 interface{}) *gomock.Call { +// ListPaths indicates an expected call of ListPaths. +func (mr *MockSpaceStoreMockRecorder) ListPaths(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAllPaths", reflect.TypeOf((*MockSpaceStore)(nil).ListAllPaths), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPaths", reflect.TypeOf((*MockSpaceStore)(nil).ListPaths), arg0, arg1, arg2) } // Move mocks base method. @@ -397,18 +412,33 @@ func (m *MockRepoStore) EXPECT() *MockRepoStoreMockRecorder { } // Count mocks base method. -func (m *MockRepoStore) Count(arg0 context.Context, arg1 int64) (int64, error) { +func (m *MockRepoStore) Count(arg0 context.Context, arg1 int64, arg2 *types.RepoFilter) (int64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Count", arg0, arg1) + ret := m.ctrl.Call(m, "Count", arg0, arg1, arg2) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // Count indicates an expected call of Count. -func (mr *MockRepoStoreMockRecorder) Count(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockRepoStoreMockRecorder) Count(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Count", reflect.TypeOf((*MockRepoStore)(nil).Count), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Count", reflect.TypeOf((*MockRepoStore)(nil).Count), arg0, arg1, arg2) +} + +// CountPaths mocks base method. +func (m *MockRepoStore) CountPaths(arg0 context.Context, arg1 int64, arg2 *types.PathFilter) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CountPaths", arg0, arg1, arg2) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CountPaths indicates an expected call of CountPaths. +func (mr *MockRepoStoreMockRecorder) CountPaths(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountPaths", reflect.TypeOf((*MockRepoStore)(nil).CountPaths), arg0, arg1, arg2) } // Create mocks base method. @@ -513,19 +543,19 @@ func (mr *MockRepoStoreMockRecorder) List(arg0, arg1, arg2 interface{}) *gomock. return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockRepoStore)(nil).List), arg0, arg1, arg2) } -// ListAllPaths mocks base method. -func (m *MockRepoStore) ListAllPaths(arg0 context.Context, arg1 int64, arg2 *types.PathFilter) ([]*types.Path, error) { +// ListPaths mocks base method. +func (m *MockRepoStore) ListPaths(arg0 context.Context, arg1 int64, arg2 *types.PathFilter) ([]*types.Path, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAllPaths", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "ListPaths", arg0, arg1, arg2) ret0, _ := ret[0].([]*types.Path) ret1, _ := ret[1].(error) return ret0, ret1 } -// ListAllPaths indicates an expected call of ListAllPaths. -func (mr *MockRepoStoreMockRecorder) ListAllPaths(arg0, arg1, arg2 interface{}) *gomock.Call { +// ListPaths indicates an expected call of ListPaths. +func (mr *MockRepoStoreMockRecorder) ListPaths(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAllPaths", reflect.TypeOf((*MockRepoStore)(nil).ListAllPaths), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPaths", reflect.TypeOf((*MockRepoStore)(nil).ListPaths), arg0, arg1, arg2) } // Move mocks base method. diff --git a/types/enum/git.go b/types/enum/git.go index afb4d881f..052d0ba07 100644 --- a/types/enum/git.go +++ b/types/enum/git.go @@ -38,6 +38,6 @@ func (o BranchSortOption) String() string { case BranchSortOptionDefault: return defaultString default: - return "" + return undefined } } diff --git a/types/enum/path.go b/types/enum/path.go index 0c4d945cb..70b0ed792 100644 --- a/types/enum/path.go +++ b/types/enum/path.go @@ -32,7 +32,6 @@ type PathAttr int // Order enumeration. const ( PathAttrNone PathAttr = iota - PathAttrID PathAttrPath PathAttrCreated PathAttrUpdated @@ -42,8 +41,6 @@ const ( // and returns the equivalent enumeration. func ParsePathAttr(s string) PathAttr { switch strings.ToLower(s) { - case id: - return PathAttrID case path: return PathAttrPath case created, createdAt: @@ -54,3 +51,19 @@ func ParsePathAttr(s string) PathAttr { return PathAttrNone } } + +// String returns the string representation of the attribute. +func (a PathAttr) String() string { + switch a { + case PathAttrPath: + return path + case PathAttrCreated: + return created + case PathAttrUpdated: + return updated + case PathAttrNone: + return "" + default: + return undefined + } +} diff --git a/types/enum/repo.go b/types/enum/repo.go index 4c392785d..92f7ba51b 100644 --- a/types/enum/repo.go +++ b/types/enum/repo.go @@ -4,7 +4,9 @@ package enum -import "strings" +import ( + "strings" +) // Defines repo attributes that can be used for sorting and filtering. type RepoAttr int @@ -12,7 +14,6 @@ type RepoAttr int // Order enumeration. const ( RepoAttrNone RepoAttr = iota - RepoAttrID RepoAttrPathName RepoAttrPath RepoAttrName @@ -24,8 +25,6 @@ const ( // and returns the equivalent enumeration. func ParseRepoAtrr(s string) RepoAttr { switch strings.ToLower(s) { - case id: - return RepoAttrID case name: return RepoAttrName case path: @@ -40,3 +39,23 @@ func ParseRepoAtrr(s string) RepoAttr { return RepoAttrNone } } + +// String returns the string representation of the attribute. +func (a RepoAttr) String() string { + switch a { + case RepoAttrPathName: + return pathName + case RepoAttrPath: + return path + case RepoAttrName: + return name + case RepoAttrCreated: + return created + case RepoAttrUpdated: + return updated + case RepoAttrNone: + return "" + default: + return undefined + } +} diff --git a/types/enum/shared.go b/types/enum/shared.go index 49c21c7b1..0caea211b 100644 --- a/types/enum/shared.go +++ b/types/enum/shared.go @@ -15,4 +15,5 @@ const ( updatedAt = "updated_at" date = "date" defaultString = "default" + undefined = "undefined" ) diff --git a/types/enum/space.go b/types/enum/space.go index fa87a7544..e32109f25 100644 --- a/types/enum/space.go +++ b/types/enum/space.go @@ -12,7 +12,6 @@ type SpaceAttr int // Order enumeration. const ( SpaceAttrNone SpaceAttr = iota - SpaceAttrID SpaceAttrPathName SpaceAttrPath SpaceAttrName @@ -24,19 +23,37 @@ const ( // and returns the equivalent enumeration. func ParseSpaceAttr(s string) SpaceAttr { switch strings.ToLower(s) { - case "id": - return SpaceAttrID - case "pathname", "path_name": - return SpaceAttrPathName - case "path": - return SpaceAttrPath - case "name": + case name: return SpaceAttrName - case "created", "created_at": + case path: + return SpaceAttrPath + case pathName: + return SpaceAttrPathName + case created, createdAt: return SpaceAttrCreated - case "updated", "updated_at": + case updated, updatedAt: return SpaceAttrUpdated default: return SpaceAttrNone } } + +// String returns the string representation of the attribute. +func (a SpaceAttr) String() string { + switch a { + case SpaceAttrPathName: + return pathName + case SpaceAttrPath: + return path + case SpaceAttrName: + return name + case SpaceAttrCreated: + return created + case SpaceAttrUpdated: + return updated + case SpaceAttrNone: + return "" + default: + return undefined + } +} diff --git a/types/repo.go b/types/repo.go index 8ab10f0f8..839ac1a02 100644 --- a/types/repo.go +++ b/types/repo.go @@ -38,6 +38,7 @@ type Repository struct { type RepoFilter struct { Page int `json:"page"` Size int `json:"size"` + Query string `json:"query"` Sort enum.RepoAttr `json:"sort"` Order enum.Order `json:"direction"` } diff --git a/types/space.go b/types/space.go index d3e148106..6c16f99ac 100644 --- a/types/space.go +++ b/types/space.go @@ -38,6 +38,7 @@ type Space struct { type SpaceFilter struct { Page int `json:"page"` Size int `json:"size"` + Query string `json:"query"` Sort enum.SpaceAttr `json:"sort"` Order enum.Order `json:"direction"` }