add UpdateOptLock

This commit is contained in:
Vistaar Juneja 2023-08-09 23:56:47 +01:00
parent e24eb858d5
commit 2bf73fbb5c
22 changed files with 247 additions and 176 deletions

View File

@ -19,7 +19,7 @@ func (c *Controller) List(
session *auth.Session, session *auth.Session,
spaceRef string, spaceRef string,
pipelineUID string, pipelineUID string,
filter *types.ExecutionFilter, pagination types.Pagination,
) ([]types.Execution, int64, error) { ) ([]types.Execution, int64, error) {
space, err := c.spaceStore.FindByRef(ctx, spaceRef) space, err := c.spaceStore.FindByRef(ctx, spaceRef)
if err != nil { if err != nil {
@ -45,7 +45,7 @@ func (c *Controller) List(
return fmt.Errorf("failed to count child executions: %w", err) return fmt.Errorf("failed to count child executions: %w", err)
} }
executions, dbErr = c.executionStore.List(ctx, pipeline.ID, filter) executions, dbErr = c.executionStore.List(ctx, pipeline.ID, pagination)
if dbErr != nil { if dbErr != nil {
return fmt.Errorf("failed to list child executions: %w", err) return fmt.Errorf("failed to list child executions: %w", err)
} }

View File

@ -45,9 +45,13 @@ func (c *Controller) Update(
return nil, fmt.Errorf("failed to find execution: %w", err) return nil, fmt.Errorf("failed to find execution: %w", err)
} }
if in.Status != "" { return c.executionStore.UpdateOptLock(ctx,
execution.Status = in.Status execution, func(original *types.Execution) error {
} // update values only if provided
if in.Status != "" {
original.Status = in.Status
}
return c.executionStore.Update(ctx, execution) return nil
})
} }

View File

@ -42,15 +42,17 @@ func (c *Controller) Update(
return nil, fmt.Errorf("could not find pipeline: %w", err) return nil, fmt.Errorf("could not find pipeline: %w", err)
} }
if in.Description != "" { return c.pipelineStore.UpdateOptLock(ctx, pipeline, func(pipeline *types.Pipeline) error {
pipeline.Description = in.Description if in.Description != "" {
} pipeline.Description = in.Description
if in.UID != "" { }
pipeline.UID = in.UID if in.UID != "" {
} pipeline.UID = in.UID
if in.ConfigPath != "" { }
pipeline.ConfigPath = in.ConfigPath if in.ConfigPath != "" {
} pipeline.ConfigPath = in.ConfigPath
}
return c.pipelineStore.Update(ctx, pipeline) return nil
})
} }

View File

@ -41,15 +41,17 @@ func (c *Controller) Update(
return nil, err return nil, err
} }
if in.Description != "" { return c.secretStore.UpdateOptLock(ctx, secret, func(original *types.Secret) error {
secret.Description = in.Description if in.Description != "" {
} original.Description = in.Description
if in.Data != "" { }
secret.Data = in.Data // will get encrypted at db layer if in.Data != "" {
} original.Data = in.Data // will get encrypted at db layer
if in.UID != "" { }
secret.UID = in.UID if in.UID != "" {
} original.UID = in.UID
}
return c.secretStore.Update(ctx, secret) return nil
})
} }

View File

@ -15,8 +15,12 @@ import (
) )
// ListPipelines lists the pipelines in a space. // ListPipelines lists the pipelines in a space.
func (c *Controller) ListPipelines(ctx context.Context, session *auth.Session, func (c *Controller) ListPipelines(
spaceRef string, filter *types.PipelineFilter) ([]types.Pipeline, int64, error) { ctx context.Context,
session *auth.Session,
spaceRef string,
pagination types.Pagination,
) ([]types.Pipeline, int64, error) {
space, err := c.spaceStore.FindByRef(ctx, spaceRef) space, err := c.spaceStore.FindByRef(ctx, spaceRef)
if err != nil { if err != nil {
return nil, 0, fmt.Errorf("failed to find parent space: %w", err) return nil, 0, fmt.Errorf("failed to find parent space: %w", err)
@ -32,12 +36,12 @@ func (c *Controller) ListPipelines(ctx context.Context, session *auth.Session,
err = dbtx.New(c.db).WithTx(ctx, func(ctx context.Context) (err error) { err = dbtx.New(c.db).WithTx(ctx, func(ctx context.Context) (err error) {
var dbErr error var dbErr error
count, dbErr = c.pipelineStore.Count(ctx, space.ID, filter) count, dbErr = c.pipelineStore.Count(ctx, space.ID, pagination)
if dbErr != nil { if dbErr != nil {
return fmt.Errorf("failed to count child executions: %w", err) return fmt.Errorf("failed to count child executions: %w", err)
} }
pipelines, dbErr = c.pipelineStore.List(ctx, space.ID, filter) pipelines, dbErr = c.pipelineStore.List(ctx, space.ID, pagination)
if dbErr != nil { if dbErr != nil {
return fmt.Errorf("failed to list child executions: %w", err) return fmt.Errorf("failed to list child executions: %w", err)
} }

View File

@ -15,8 +15,12 @@ import (
) )
// ListSecrets lists the secrets in a space. // ListSecrets lists the secrets in a space.
func (c *Controller) ListSecrets(ctx context.Context, session *auth.Session, func (c *Controller) ListSecrets(
spaceRef string, filter *types.SecretFilter) ([]types.Secret, int64, error) { ctx context.Context,
session *auth.Session,
spaceRef string,
pagination types.Pagination,
) ([]types.Secret, int64, error) {
space, err := c.spaceStore.FindByRef(ctx, spaceRef) space, err := c.spaceStore.FindByRef(ctx, spaceRef)
if err != nil { if err != nil {
return nil, 0, fmt.Errorf("failed to find parent space: %w", err) return nil, 0, fmt.Errorf("failed to find parent space: %w", err)
@ -32,12 +36,12 @@ func (c *Controller) ListSecrets(ctx context.Context, session *auth.Session,
err = dbtx.New(c.db).WithTx(ctx, func(ctx context.Context) (err error) { err = dbtx.New(c.db).WithTx(ctx, func(ctx context.Context) (err error) {
var dbErr error var dbErr error
count, dbErr = c.secretStore.Count(ctx, space.ID, filter) count, dbErr = c.secretStore.Count(ctx, space.ID, pagination)
if dbErr != nil { if dbErr != nil {
return fmt.Errorf("failed to count child executions: %w", err) return fmt.Errorf("failed to count child executions: %w", err)
} }
secrets, dbErr = c.secretStore.List(ctx, space.ID, filter) secrets, dbErr = c.secretStore.List(ctx, space.ID, pagination)
if dbErr != nil { if dbErr != nil {
return fmt.Errorf("failed to list child executions: %w", err) return fmt.Errorf("failed to list child executions: %w", err)
} }

View File

@ -28,15 +28,15 @@ func HandleList(executionCtrl *execution.Controller) http.HandlerFunc {
return return
} }
filter := request.ParseExecutionFilter(r) pagination := request.ParsePaginationFromRequest(r)
repos, totalCount, err := executionCtrl.List(ctx, session, spaceRef, pipelineUID, filter) repos, totalCount, err := executionCtrl.List(ctx, session, spaceRef, pipelineUID, pagination)
if err != nil { if err != nil {
render.TranslatedUserError(w, err) render.TranslatedUserError(w, err)
return return
} }
render.Pagination(r, w, filter.Page, filter.Size, int(totalCount)) render.Pagination(r, w, pagination.Page, pagination.Size, int(totalCount))
render.JSON(w, http.StatusOK, repos) render.JSON(w, http.StatusOK, repos)
} }
} }

View File

@ -22,14 +22,14 @@ func HandleListPipelines(spaceCtrl *space.Controller) http.HandlerFunc {
return return
} }
filter := request.ParsePipelineFilter(r) pagination := request.ParsePaginationFromRequest(r)
repos, totalCount, err := spaceCtrl.ListPipelines(ctx, session, spaceRef, filter) repos, totalCount, err := spaceCtrl.ListPipelines(ctx, session, spaceRef, pagination)
if err != nil { if err != nil {
render.TranslatedUserError(w, err) render.TranslatedUserError(w, err)
return return
} }
render.Pagination(r, w, filter.Page, filter.Size, int(totalCount)) render.Pagination(r, w, pagination.Page, pagination.Page, int(totalCount))
render.JSON(w, http.StatusOK, repos) render.JSON(w, http.StatusOK, repos)
} }
} }

View File

@ -23,8 +23,8 @@ func HandleListSecrets(spaceCtrl *space.Controller) http.HandlerFunc {
return return
} }
filter := request.ParseSecretFilter(r) pagination := request.ParsePaginationFromRequest(r)
ret, totalCount, err := spaceCtrl.ListSecrets(ctx, session, spaceRef, filter) ret, totalCount, err := spaceCtrl.ListSecrets(ctx, session, spaceRef, pagination)
if err != nil { if err != nil {
render.TranslatedUserError(w, err) render.TranslatedUserError(w, err)
return return
@ -36,7 +36,7 @@ func HandleListSecrets(spaceCtrl *space.Controller) http.HandlerFunc {
secrets = append(secrets, *s.CopyWithoutData()) secrets = append(secrets, *s.CopyWithoutData())
} }
render.Pagination(r, w, filter.Page, filter.Size, int(totalCount)) render.Pagination(r, w, pagination.Page, pagination.Size, int(totalCount))
render.JSON(w, http.StatusOK, secrets) render.JSON(w, http.StatusOK, secrets)
} }
} }

View File

@ -7,8 +7,6 @@ package request
import ( import (
"net/http" "net/http"
"net/url" "net/url"
"github.com/harness/gitness/types"
) )
const ( const (
@ -30,20 +28,3 @@ func GetPipelineRefFromPath(r *http.Request) (string, error) {
func GetExecutionNumberFromPath(r *http.Request) (int64, error) { func GetExecutionNumberFromPath(r *http.Request) (int64, error) {
return PathParamAsPositiveInt64(r, ExecutionNumber) return PathParamAsPositiveInt64(r, ExecutionNumber)
} }
// ParsePipelineFilter extracts the pipeline filter from the url.
func ParsePipelineFilter(r *http.Request) *types.PipelineFilter {
return &types.PipelineFilter{
Query: ParseQuery(r),
Page: ParsePage(r),
Size: ParseLimit(r),
}
}
// ParseExecutionFilter extracts the execution filter from the url.
func ParseExecutionFilter(r *http.Request) *types.ExecutionFilter {
return &types.ExecutionFilter{
Page: ParsePage(r),
Size: ParseLimit(r),
}
}

View File

@ -7,8 +7,6 @@ package request
import ( import (
"net/http" "net/http"
"net/url" "net/url"
"github.com/harness/gitness/types"
) )
const ( const (
@ -24,11 +22,3 @@ func GetSecretRefFromPath(r *http.Request) (string, error) {
// paths are unescaped // paths are unescaped
return url.PathUnescape(rawRef) return url.PathUnescape(rawRef)
} }
// ParseExecutionFilter extracts the execution filter from the url.
func ParseSecretFilter(r *http.Request) *types.SecretFilter {
return &types.SecretFilter{
Page: ParsePage(r),
Size: ParseLimit(r),
}
}

View File

@ -9,6 +9,7 @@ import (
"strconv" "strconv"
"github.com/harness/gitness/internal/api/usererror" "github.com/harness/gitness/internal/api/usererror"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum" "github.com/harness/gitness/types/enum"
"github.com/go-chi/chi" "github.com/go-chi/chi"
@ -17,11 +18,10 @@ import (
const ( const (
PathParamRemainder = "*" PathParamRemainder = "*"
QueryParamSort = "sort"
QueryParamOrder = "order"
QueryParamQuery = "query"
QueryParamCreatedBy = "created_by" QueryParamCreatedBy = "created_by"
QueryParamSort = "sort"
QueryParamOrder = "order"
QueryParamQuery = "query"
QueryParamState = "state" QueryParamState = "state"
QueryParamKind = "kind" QueryParamKind = "kind"
@ -166,26 +166,6 @@ func GetRemainderFromPath(r *http.Request) (string, error) {
return PathParamOrError(r, PathParamRemainder) return PathParamOrError(r, PathParamRemainder)
} }
// PipelineFilter stores pipeline query parameters.
type Pagination struct {
Page int `json:"page"`
Size int `json:"size"`
Query string `json:"query"`
Sort string `json:"sort"`
Order enum.Order `json:"order"`
}
// ParseExecutionFilter extracts the execution filter from the url.
func ParseFilterFromRequest(r *http.Request) Pagination {
return Pagination{
Page: ParsePage(r),
Size: ParseLimit(r),
Query: ParseQuery(r),
Sort: ParseSort(r),
Order: ParseOrder(r),
}
}
// ParseQuery extracts the query parameter from the url. // ParseQuery extracts the query parameter from the url.
func ParseQuery(r *http.Request) string { func ParseQuery(r *http.Request) string {
return r.URL.Query().Get(QueryParamQuery) return r.URL.Query().Get(QueryParamQuery)
@ -224,3 +204,14 @@ func ParseOrder(r *http.Request) enum.Order {
func ParseSort(r *http.Request) string { func ParseSort(r *http.Request) string {
return r.URL.Query().Get(QueryParamSort) return r.URL.Query().Get(QueryParamSort)
} }
// ParsePaginationFromRequest extracts the pagination info from the url.
func ParsePaginationFromRequest(r *http.Request) types.Pagination {
return types.Pagination{
Page: ParsePage(r),
Size: ParseLimit(r),
Query: ParseQuery(r),
Sort: ParseSort(r),
Order: ParseOrder(r),
}
}

View File

@ -449,17 +449,21 @@ type (
// Create creates a new pipeline in the datastore. // Create creates a new pipeline in the datastore.
Create(ctx context.Context, pipeline *types.Pipeline) error Create(ctx context.Context, pipeline *types.Pipeline) error
// Update tries to update a pipeline in the datastore with optimistic locking. // Update tries to update a pipeline in the datastore
Update(ctx context.Context, pipeline *types.Pipeline) (*types.Pipeline, error) Update(ctx context.Context, pipeline *types.Pipeline) (*types.Pipeline, error)
// List lists the pipelines present in a parent space ID in the datastore. // List lists the pipelines present in a parent space ID in the datastore.
List(ctx context.Context, spaceID int64, filter *types.PipelineFilter) ([]types.Pipeline, error) List(ctx context.Context, spaceID int64, pagination types.Pagination) ([]types.Pipeline, error)
// UpdateOptLock updates the pipeline using the optimistic locking mechanism.
UpdateOptLock(ctx context.Context, pipeline *types.Pipeline,
mutateFn func(pipeline *types.Pipeline) error) (*types.Pipeline, error)
// Delete deletes a pipeline ID from the datastore. // Delete deletes a pipeline ID from the datastore.
Delete(ctx context.Context, id int64) error Delete(ctx context.Context, id int64) error
// Count the number of pipelines in a space matching the given filter. // Count the number of pipelines in a space matching the given filter.
Count(ctx context.Context, spaceID int64, filter *types.PipelineFilter) (int64, error) Count(ctx context.Context, spaceID int64, filter types.Pagination) (int64, error)
// DeleteByUID deletes a pipeline with a given UID in a space // DeleteByUID deletes a pipeline with a given UID in a space
DeleteByUID(ctx context.Context, spaceID int64, uid string) error DeleteByUID(ctx context.Context, spaceID int64, uid string) error
@ -479,7 +483,11 @@ type (
Create(ctx context.Context, secret *types.Secret) error Create(ctx context.Context, secret *types.Secret) error
// Count the number of secrets in a space matching the given filter. // Count the number of secrets in a space matching the given filter.
Count(ctx context.Context, spaceID int64, filter *types.SecretFilter) (int64, error) Count(ctx context.Context, spaceID int64, pagination types.Pagination) (int64, error)
// UpdateOptLock updates the secret using the optimistic locking mechanism.
UpdateOptLock(ctx context.Context, secret *types.Secret,
mutateFn func(secret *types.Secret) error) (*types.Secret, error)
// Update tries to update a secret. // Update tries to update a secret.
Update(ctx context.Context, secret *types.Secret) (*types.Secret, error) Update(ctx context.Context, secret *types.Secret) (*types.Secret, error)
@ -491,7 +499,7 @@ type (
DeleteByUID(ctx context.Context, spaceID int64, uid string) error DeleteByUID(ctx context.Context, spaceID int64, uid string) error
// List lists the secrets in a given space // List lists the secrets in a given space
List(ctx context.Context, spaceID int64, filter *types.SecretFilter) ([]types.Secret, error) List(ctx context.Context, spaceID int64, filter types.Pagination) ([]types.Secret, error)
} }
ExecutionStore interface { ExecutionStore interface {
@ -501,11 +509,15 @@ type (
// Create creates a new execution in the datastore. // Create creates a new execution in the datastore.
Create(ctx context.Context, execution *types.Execution) error Create(ctx context.Context, execution *types.Execution) error
// Update tries to update an execution in the datastore with optimistic locking. // Update tries to update an execution.
Update(ctx context.Context, execution *types.Execution) (*types.Execution, error) Update(ctx context.Context, execution *types.Execution) (*types.Execution, error)
// UpdateOptLock updates the execution using the optimistic locking mechanism.
UpdateOptLock(ctx context.Context, exectuion *types.Execution,
mutateFn func(execution *types.Execution) error) (*types.Execution, error)
// List lists the executions for a given pipeline ID // List lists the executions for a given pipeline ID
List(ctx context.Context, pipelineID int64, filter *types.ExecutionFilter) ([]types.Execution, error) List(ctx context.Context, pipelineID int64, pagination types.Pagination) ([]types.Execution, error)
// Delete deletes an execution given a pipeline ID and an execution number // Delete deletes an execution given a pipeline ID and an execution number
Delete(ctx context.Context, pipelineID int64, num int64) error Delete(ctx context.Context, pipelineID int64, num int64) error

View File

@ -151,36 +151,36 @@ const (
executionUpdateStmt = ` executionUpdateStmt = `
UPDATE executions UPDATE executions
SET SET
execution_trigger = :execution_trigger, execution_trigger = :execution_trigger
execution_parent = :execution_parent, ,execution_parent = :execution_parent
execution_status = :execution_status, ,execution_status = :execution_status
execution_error = :execution_error, ,execution_error = :execution_error
execution_event = :execution_event, ,execution_event = :execution_event
execution_action = :execution_action, ,execution_action = :execution_action
execution_link = :execution_link, ,execution_link = :execution_link
execution_timestamp = :execution_timestamp, ,execution_timestamp = :execution_timestamp
execution_title = :execution_title, ,execution_title = :execution_title
execution_message = :execution_message, ,execution_message = :execution_message
execution_before = :execution_before, ,execution_before = :execution_before
execution_after = :execution_after, ,execution_after = :execution_after
execution_ref = :execution_ref, ,execution_ref = :execution_ref
execution_source_repo = :execution_source_repo, ,execution_source_repo = :execution_source_repo
execution_source = :execution_source, ,execution_source = :execution_source
execution_target = :execution_target, ,execution_target = :execution_target
execution_author = :execution_author, ,execution_author = :execution_author
execution_author_name = :execution_author_name, ,execution_author_name = :execution_author_name
execution_author_email = :execution_author_email, ,execution_author_email = :execution_author_email
execution_author_avatar = :execution_author_avatar, ,execution_author_avatar = :execution_author_avatar
execution_sender = :execution_sender, ,execution_sender = :execution_sender
execution_params = :execution_params, ,execution_params = :execution_params
execution_cron = :execution_cron, ,execution_cron = :execution_cron
execution_deploy = :execution_deploy, ,execution_deploy = :execution_deploy
execution_deploy_id = :execution_deploy_id, ,execution_deploy_id = :execution_deploy_id
execution_debug = :execution_debug, ,execution_debug = :execution_debug
execution_started = :execution_started, ,execution_started = :execution_started
execution_finished = :execution_finished, ,execution_finished = :execution_finished
execution_updated = :execution_updated, ,execution_updated = :execution_updated
execution_version = :execution_version ,execution_version = :execution_version
WHERE execution_id = :execution_id AND execution_version = :execution_version - 1` WHERE execution_id = :execution_id AND execution_version = :execution_version - 1`
) )
@ -244,19 +244,50 @@ func (s *executionStore) Update(ctx context.Context, execution *types.Execution)
return execution, nil return execution, nil
} }
// UpdateOptLock updates the pipeline using the optimistic locking mechanism.
func (s *executionStore) UpdateOptLock(ctx context.Context,
execution *types.Execution,
mutateFn func(execution *types.Execution) error) (*types.Execution, error) {
for {
dup := *execution
fmt.Println(dup.Status)
err := mutateFn(&dup)
if err != nil {
return nil, err
}
fmt.Println("dup.Status after: ", dup.Status)
execution, err = s.Update(ctx, &dup)
if err == nil {
return &dup, nil
}
if !errors.Is(err, gitness_store.ErrVersionConflict) {
return nil, err
}
execution, err = s.Find(ctx, execution.PipelineID, execution.Number)
if err != nil {
return nil, err
}
}
}
// List lists the executions for a given pipeline ID. // List lists the executions for a given pipeline ID.
func (s *executionStore) List( func (s *executionStore) List(
ctx context.Context, ctx context.Context,
pipelineID int64, pipelineID int64,
opts *types.ExecutionFilter, pagination types.Pagination,
) ([]types.Execution, error) { ) ([]types.Execution, error) {
stmt := database.Builder. stmt := database.Builder.
Select(executionColumns). Select(executionColumns).
From("executions"). From("executions").
Where("execution_pipeline_id = ?", fmt.Sprint(pipelineID)) Where("execution_pipeline_id = ?", fmt.Sprint(pipelineID))
stmt = stmt.Limit(database.Limit(opts.Size)) stmt = stmt.Limit(database.Limit(pagination.Size))
stmt = stmt.Offset(database.Offset(opts.Page, opts.Size)) stmt = stmt.Offset(database.Offset(pagination.Page, pagination.Size))
sql, args, err := stmt.ToSql() sql, args, err := stmt.ToSql()
if err != nil { if err != nil {

View File

@ -178,19 +178,19 @@ func (s *pipelineStore) Update(ctx context.Context, pipeline *types.Pipeline) (*
func (s *pipelineStore) List( func (s *pipelineStore) List(
ctx context.Context, ctx context.Context,
parentID int64, parentID int64,
opts *types.PipelineFilter, pagination types.Pagination,
) ([]types.Pipeline, error) { ) ([]types.Pipeline, error) {
stmt := database.Builder. stmt := database.Builder.
Select(pipelineColumns). Select(pipelineColumns).
From("pipelines"). From("pipelines").
Where("pipeline_space_id = ?", fmt.Sprint(parentID)) Where("pipeline_space_id = ?", fmt.Sprint(parentID))
if opts.Query != "" { if pagination.Query != "" {
stmt = stmt.Where("LOWER(pipeline_uid) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(opts.Query))) stmt = stmt.Where("LOWER(pipeline_uid) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(pagination.Query)))
} }
stmt = stmt.Limit(database.Limit(opts.Size)) stmt = stmt.Limit(database.Limit(pagination.Size))
stmt = stmt.Offset(database.Offset(opts.Page, opts.Size)) stmt = stmt.Offset(database.Offset(pagination.Page, pagination.Size))
sql, args, err := stmt.ToSql() sql, args, err := stmt.ToSql()
if err != nil { if err != nil {
@ -207,15 +207,42 @@ func (s *pipelineStore) List(
return dst, nil return dst, nil
} }
// UpdateOptLock updates the pipeline using the optimistic locking mechanism.
func (s *pipelineStore) UpdateOptLock(ctx context.Context,
pipeline *types.Pipeline,
mutateFn func(pipeline *types.Pipeline) error) (*types.Pipeline, error) {
for {
dup := *pipeline
err := mutateFn(&dup)
if err != nil {
return nil, err
}
pipeline, err = s.Update(ctx, &dup)
if err == nil {
return &dup, nil
}
if !errors.Is(err, gitness_store.ErrVersionConflict) {
return nil, err
}
pipeline, err = s.Find(ctx, pipeline.ID)
if err != nil {
return nil, err
}
}
}
// Count of pipelines in a space. // Count of pipelines in a space.
func (s *pipelineStore) Count(ctx context.Context, parentID int64, opts *types.PipelineFilter) (int64, error) { func (s *pipelineStore) Count(ctx context.Context, parentID int64, filter types.Pagination) (int64, error) {
stmt := database.Builder. stmt := database.Builder.
Select("count(*)"). Select("count(*)").
From("pipelines"). From("pipelines").
Where("pipeline_space_id = ?", parentID) Where("pipeline_space_id = ?", parentID)
if opts.Query != "" { if filter.Query != "" {
stmt = stmt.Where("pipeline_uid LIKE ?", fmt.Sprintf("%%%s%%", opts.Query)) stmt = stmt.Where("pipeline_uid LIKE ?", fmt.Sprintf("%%%s%%", filter.Query))
} }
sql, args, err := stmt.ToSql() sql, args, err := stmt.ToSql()

View File

@ -165,19 +165,46 @@ func (s *secretStore) Update(ctx context.Context, secret *types.Secret) (*types.
return secret, nil return secret, nil
} }
// UpdateOptLock updates the pipeline using the optimistic locking mechanism.
func (s *secretStore) UpdateOptLock(ctx context.Context,
secret *types.Secret,
mutateFn func(secret *types.Secret) error) (*types.Secret, error) {
for {
dup := *secret
err := mutateFn(&dup)
if err != nil {
return nil, err
}
secret, err = s.Update(ctx, &dup)
if err == nil {
return &dup, nil
}
if !errors.Is(err, gitness_store.ErrVersionConflict) {
return nil, err
}
secret, err = s.Find(ctx, secret.ID)
if err != nil {
return nil, err
}
}
}
// List lists all the secrets present in a space. // List lists all the secrets present in a space.
func (s *secretStore) List(ctx context.Context, parentID int64, opts *types.SecretFilter) ([]types.Secret, error) { func (s *secretStore) List(ctx context.Context, parentID int64, pagination types.Pagination) ([]types.Secret, error) {
stmt := database.Builder. stmt := database.Builder.
Select(secretColumns). Select(secretColumns).
From("secrets"). From("secrets").
Where("secret_space_id = ?", fmt.Sprint(parentID)) Where("secret_space_id = ?", fmt.Sprint(parentID))
if opts.Query != "" { if pagination.Query != "" {
stmt = stmt.Where("LOWER(secret_uid) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(opts.Query))) stmt = stmt.Where("LOWER(secret_uid) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(pagination.Query)))
} }
stmt = stmt.Limit(database.Limit(opts.Size)) stmt = stmt.Limit(database.Limit(pagination.Size))
stmt = stmt.Offset(database.Offset(opts.Page, opts.Size)) stmt = stmt.Offset(database.Offset(pagination.Page, pagination.Size))
sql, args, err := stmt.ToSql() sql, args, err := stmt.ToSql()
if err != nil { if err != nil {
@ -225,14 +252,14 @@ func (s *secretStore) DeleteByUID(ctx context.Context, spaceID int64, uid string
} }
// Count of secrets in a space. // Count of secrets in a space.
func (s *secretStore) Count(ctx context.Context, parentID int64, opts *types.SecretFilter) (int64, error) { func (s *secretStore) Count(ctx context.Context, parentID int64, filter types.Pagination) (int64, error) {
stmt := database.Builder. stmt := database.Builder.
Select("count(*)"). Select("count(*)").
From("secrets"). From("secrets").
Where("secret_space_id = ?", parentID) Where("secret_space_id = ?", parentID)
if opts.Query != "" { if filter.Query != "" {
stmt = stmt.Where("secret_uid LIKE ?", fmt.Sprintf("%%%s%%", opts.Query)) stmt = stmt.Where("secret_uid LIKE ?", fmt.Sprintf("%%%s%%", filter.Query))
} }
sql, args, err := stmt.ToSql() sql, args, err := stmt.ToSql()
@ -252,6 +279,9 @@ func (s *secretStore) Count(ctx context.Context, parentID int64, opts *types.Sec
// helper function returns the same secret with encrypted data. // helper function returns the same secret with encrypted data.
func enc(encrypt encrypt.Encrypter, secret *types.Secret) (*types.Secret, error) { func enc(encrypt encrypt.Encrypter, secret *types.Secret) (*types.Secret, error) {
if secret == nil {
return nil, fmt.Errorf("cannot encrypt a nil secret")
}
s := *secret s := *secret
ciphertext, err := encrypt.Encrypt(secret.Data) ciphertext, err := encrypt.Encrypt(secret.Data)
if err != nil { if err != nil {
@ -263,6 +293,9 @@ func enc(encrypt encrypt.Encrypter, secret *types.Secret) (*types.Secret, error)
// helper function returns the same secret with decrypted data. // helper function returns the same secret with decrypted data.
func dec(encrypt encrypt.Encrypter, secret *types.Secret) (*types.Secret, error) { func dec(encrypt encrypt.Encrypter, secret *types.Secret) (*types.Secret, error) {
if secret == nil {
return nil, fmt.Errorf("cannot decrypt a nil secret")
}
s := *secret s := *secret
plaintext, err := encrypt.Decrypt([]byte(secret.Data)) plaintext, err := encrypt.Decrypt([]byte(secret.Data))
if err != nil { if err != nil {

View File

@ -8,10 +8,9 @@ import (
context "context" context "context"
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock"
user "github.com/harness/gitness/internal/api/controller/user" user "github.com/harness/gitness/internal/api/controller/user"
types "github.com/harness/gitness/types" types "github.com/harness/gitness/types"
gomock "github.com/golang/mock/gomock"
) )
// MockClient is a mock of Client interface. // MockClient is a mock of Client interface.

View File

@ -8,10 +8,9 @@ import (
context "context" context "context"
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock"
types "github.com/harness/gitness/types" types "github.com/harness/gitness/types"
enum "github.com/harness/gitness/types/enum" enum "github.com/harness/gitness/types/enum"
gomock "github.com/golang/mock/gomock"
) )
// MockPrincipalStore is a mock of PrincipalStore interface. // MockPrincipalStore is a mock of PrincipalStore interface.

View File

@ -44,9 +44,3 @@ type Execution struct {
// TODO: (Vistaar) Add stages // TODO: (Vistaar) Add stages
// Stages []*Stage `db:"-" json:"stages,omitempty"` // Stages []*Stage `db:"-" json:"stages,omitempty"`
} }
// ExecutionFilter stores execution query parameters.
type ExecutionFilter struct {
Page int `json:"page"`
Size int `json:"size"`
}

12
types/pagination.go Normal file
View File

@ -0,0 +1,12 @@
package types
import "github.com/harness/gitness/types/enum"
// Pagination stores pagination related params
type Pagination struct {
Page int `json:"page"`
Size int `json:"size"`
Query string `json:"query"`
Sort string `json:"sort"`
Order enum.Order `json:"order"`
}

View File

@ -21,10 +21,3 @@ type Pipeline struct {
Updated int64 `db:"pipeline_updated" json:"updated"` Updated int64 `db:"pipeline_updated" json:"updated"`
Version int64 `db:"pipeline_version" json:"version"` Version int64 `db:"pipeline_version" json:"version"`
} }
// PipelineFilter stores pipeline query parameters.
type PipelineFilter struct {
Page int `json:"page"`
Size int `json:"size"`
Query string `json:"query"`
}

View File

@ -15,13 +15,6 @@ type Secret struct {
Version int64 `db:"secret_version" json:"version"` Version int64 `db:"secret_version" json:"version"`
} }
// SecretFilter stores secret query parameters.
type SecretFilter struct {
Page int `json:"page"`
Size int `json:"size"`
Query string `json:"query"`
}
// Copy makes a copy of the secret without the value. // Copy makes a copy of the secret without the value.
func (s *Secret) CopyWithoutData() *Secret { func (s *Secret) CopyWithoutData() *Secret {
return &Secret{ return &Secret{