You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
boundary/internal/alias/target/repository_alias_list.go

161 lines
5.6 KiB

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package target
import (
"context"
"database/sql"
"fmt"
"time"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/errors"
)
// listAliases lists aliases in the given scopes and supports WithLimit option.
func (r *Repository) listAliases(ctx context.Context, withScopeIds []string, opt ...Option) ([]*Alias, time.Time, error) {
const op = "target.(Repository).listAliases"
if len(withScopeIds) == 0 {
return nil, time.Time{}, errors.New(ctx, errors.InvalidParameter, op, "missing scope id")
}
opts, err := getOpts(opt...)
if err != nil {
return nil, time.Time{}, errors.Wrap(ctx, err, op)
}
limit := r.defaultLimit
switch {
case opts.withLimit > 0:
// non-zero signals an override of the default limit for the repo.
limit = opts.withLimit
case opts.withLimit < 0:
return nil, time.Time{}, errors.New(ctx, errors.InvalidParameter, op, "limit must be non-negative")
}
var args []any
whereClause := "scope_id in @scope_ids"
args = append(args, sql.Named("scope_ids", withScopeIds))
if opts.withStartPageAfterItem != nil {
whereClause = fmt.Sprintf("(create_time, public_id) < (@last_item_create_time, @last_item_id) and %s", whereClause)
args = append(args,
sql.Named("last_item_create_time", opts.withStartPageAfterItem.GetCreateTime()),
sql.Named("last_item_id", opts.withStartPageAfterItem.GetPublicId()),
)
}
dbOpts := []db.Option{db.WithLimit(limit), db.WithOrder("create_time desc, public_id desc")}
return r.queryAliases(ctx, whereClause, args, dbOpts...)
}
// listAliasesRefresh lists aliases limited by the list
// permissions of the repository.
// Supported options:
// - withTerminated
// - withLimit
// - withStartPageAfterItem
func (r *Repository) listAliasesRefresh(ctx context.Context, updatedAfter time.Time, withScopeIds []string, opt ...Option) ([]*Alias, time.Time, error) {
const op = "target.(Repository).listAliasesRefresh"
switch {
case updatedAfter.IsZero():
return nil, time.Time{}, errors.New(ctx, errors.InvalidParameter, op, "missing updated after time")
case len(withScopeIds) == 0:
return nil, time.Time{}, errors.New(ctx, errors.InvalidParameter, op, "missing scope id")
}
opts, err := getOpts(opt...)
if err != nil {
return nil, time.Time{}, errors.Wrap(ctx, err, op)
}
limit := r.defaultLimit
switch {
case opts.withLimit > 0:
// non-zero signals an override of the default limit for the repo.
limit = opts.withLimit
case opts.withLimit < 0:
return nil, time.Time{}, errors.New(ctx, errors.InvalidParameter, op, "limit must be non-negative")
}
var args []any
whereClause := "update_time > @updated_after_time and scope_id in @scope_ids"
args = append(args,
sql.Named("updated_after_time", timestamp.New(updatedAfter)),
sql.Named("scope_ids", withScopeIds),
)
if opts.withStartPageAfterItem != nil {
whereClause = fmt.Sprintf("(update_time, public_id) < (@last_item_update_time, @last_item_id) and %s", whereClause)
args = append(args,
sql.Named("last_item_update_time", opts.withStartPageAfterItem.GetUpdateTime()),
sql.Named("last_item_id", opts.withStartPageAfterItem.GetPublicId()),
)
}
dbOpts := []db.Option{db.WithLimit(limit), db.WithOrder("update_time desc, public_id desc")}
return r.queryAliases(ctx, whereClause, args, dbOpts...)
}
func (r *Repository) queryAliases(ctx context.Context, whereClause string, args []any, opt ...db.Option) ([]*Alias, time.Time, error) {
const op = "target.(Repository).queryAliases"
var ret []*Alias
var transactionTimestamp time.Time
if _, err := r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(rd db.Reader, w db.Writer) error {
var inRet []*Alias
if err := rd.SearchWhere(ctx, &inRet, whereClause, args, opt...); err != nil {
return errors.Wrap(ctx, err, op)
}
ret = inRet
var err error
transactionTimestamp, err = rd.Now(ctx)
return err
}); err != nil {
return nil, time.Time{}, err
}
return ret, transactionTimestamp, nil
}
// listDeletedIds lists the public IDs of any aliases deleted since the timestamp provided.
func (r *Repository) listDeletedIds(ctx context.Context, since time.Time) ([]string, time.Time, error) {
const op = "target.(Repository).listDeletedIds"
var deletedAliases []*deletedAlias
var transactionTimestamp time.Time
if _, err := r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(r db.Reader, _ db.Writer) error {
if err := r.SearchWhere(ctx, &deletedAliases, "delete_time >= ?", []any{since}); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("failed to query deleted aliases"))
}
var err error
transactionTimestamp, err = r.Now(ctx)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("failed to get transaction timestamp"))
}
return nil
}); err != nil {
return nil, time.Time{}, err
}
var aliasIds []string
for _, sess := range deletedAliases {
aliasIds = append(aliasIds, sess.PublicId)
}
return aliasIds, transactionTimestamp, nil
}
// estimatedCount returns an estimate of the total number of items in the alias table.
func (r *Repository) estimatedCount(ctx context.Context) (int, error) {
const op = "target.(Repository).estimatedCount"
rows, err := r.reader.Query(ctx, estimateCount, nil)
if err != nil {
return 0, errors.Wrap(ctx, err, op, errors.WithMsg("failed to query total aliases"))
}
var count int
for rows.Next() {
if err := r.reader.ScanRows(ctx, rows, &count); err != nil {
return 0, errors.Wrap(ctx, err, op, errors.WithMsg("failed to query total aliases"))
}
}
return count, nil
}