github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/repo/list.go (about)

     1  package repo
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  
     7  	"github.com/kyma-incubator/compass/components/director/pkg/apperrors"
     8  
     9  	"github.com/kyma-incubator/compass/components/director/pkg/log"
    10  
    11  	"github.com/pkg/errors"
    12  
    13  	"github.com/kyma-incubator/compass/components/director/pkg/persistence"
    14  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
    15  )
    16  
    17  // Lister is an interface for listing tenant scoped entities with either externally managed tenant accesses (m2m table or view) or embedded tenant in them.
    18  type Lister interface {
    19  	List(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, additionalConditions ...Condition) error
    20  	ListWithSelectForUpdate(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, additionalConditions ...Condition) error
    21  	SetSelectedColumns(selectedColumns []string)
    22  	Clone() *universalLister
    23  }
    24  
    25  // ConditionTreeLister is an interface for listing tenant scoped entities with either externally managed tenant accesses (m2m table or view) or embedded tenant in them based on provided conditionTree.
    26  type ConditionTreeLister interface {
    27  	ListConditionTree(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, conditionTree *ConditionTree) error
    28  }
    29  
    30  // ConditionTreeListerGlobal is an interface for listing entities by conditionTree globally.
    31  type ConditionTreeListerGlobal interface {
    32  	ListConditionTreeGlobal(ctx context.Context, resourceType resource.Type, dest Collection, conditionTree *ConditionTree) error
    33  }
    34  
    35  // ListerGlobal is an interface for listing global entities.
    36  type ListerGlobal interface {
    37  	ListGlobal(ctx context.Context, dest Collection, additionalConditions ...Condition) error
    38  	ListGlobalWithSelectForUpdate(ctx context.Context, dest Collection, additionalConditions ...Condition) error
    39  	SetSelectedColumns(selectedColumns []string)
    40  	Clone() *universalLister
    41  }
    42  
    43  type universalLister struct {
    44  	tableName       string
    45  	selectedColumns string
    46  	tenantColumn    *string
    47  	resourceType    resource.Type
    48  	ownerCheck      bool
    49  
    50  	orderByParams OrderByParams
    51  }
    52  
    53  // NewListerWithEmbeddedTenant is a constructor for Lister about entities with tenant embedded in them.
    54  func NewListerWithEmbeddedTenant(tableName string, tenantColumn string, selectedColumns []string) Lister {
    55  	return &universalLister{
    56  		tableName:       tableName,
    57  		selectedColumns: strings.Join(selectedColumns, ", "),
    58  		tenantColumn:    &tenantColumn,
    59  		orderByParams:   NoOrderBy,
    60  	}
    61  }
    62  
    63  // NewLister is a constructor for Lister about entities with externally managed tenant accesses (m2m table or view)
    64  func NewLister(tableName string, selectedColumns []string) Lister {
    65  	return &universalLister{
    66  		tableName:       tableName,
    67  		selectedColumns: strings.Join(selectedColumns, ", "),
    68  		orderByParams:   NoOrderBy,
    69  	}
    70  }
    71  
    72  // NewConditionTreeListerWithEmbeddedTenant is a constructor for ConditionTreeLister about entities with tenant embedded in them.
    73  func NewConditionTreeListerWithEmbeddedTenant(tableName string, tenantColumn string, selectedColumns []string) ConditionTreeLister {
    74  	return &universalLister{
    75  		tableName:       tableName,
    76  		selectedColumns: strings.Join(selectedColumns, ", "),
    77  		tenantColumn:    &tenantColumn,
    78  		orderByParams:   NoOrderBy,
    79  	}
    80  }
    81  
    82  // NewConditionTreeLister is a constructor for ConditionTreeLister about entities with externally managed tenant accesses (m2m table or view)
    83  func NewConditionTreeLister(tableName string, selectedColumns []string) ConditionTreeLister {
    84  	return &universalLister{
    85  		tableName:       tableName,
    86  		selectedColumns: strings.Join(selectedColumns, ", "),
    87  		orderByParams:   NoOrderBy,
    88  	}
    89  }
    90  
    91  // NewConditionTreeListerGlobal is a constructor for ConditionTreeListerGlobal.
    92  func NewConditionTreeListerGlobal(tableName string, selectedColumns []string) ConditionTreeListerGlobal {
    93  	return &universalLister{
    94  		tableName:       tableName,
    95  		selectedColumns: strings.Join(selectedColumns, ", "),
    96  		orderByParams:   NoOrderBy,
    97  	}
    98  }
    99  
   100  // NewOwnerLister is a constructor for Lister about entities with externally managed tenant accesses (m2m table or view) with owner access check
   101  func NewOwnerLister(tableName string, selectedColumns []string, ownerCheck bool) Lister {
   102  	return &universalLister{
   103  		tableName:       tableName,
   104  		selectedColumns: strings.Join(selectedColumns, ", "),
   105  		ownerCheck:      ownerCheck,
   106  		orderByParams:   NoOrderBy,
   107  	}
   108  }
   109  
   110  // NewListerWithOrderBy is a constructor for Lister about entities with externally managed tenant accesses (m2m table or view) with additional order by clause.
   111  func NewListerWithOrderBy(tableName string, selectedColumns []string, orderByParams OrderByParams) Lister {
   112  	return &universalLister{
   113  		tableName:       tableName,
   114  		selectedColumns: strings.Join(selectedColumns, ", "),
   115  		orderByParams:   orderByParams,
   116  	}
   117  }
   118  
   119  // NewListerGlobalWithOrderBy is a constructor for ListerGlobal about global entities with additional order by clause.
   120  func NewListerGlobalWithOrderBy(resourceType resource.Type, tableName string, selectedColumns []string, orderByParams OrderByParams) ListerGlobal {
   121  	return &universalLister{
   122  		resourceType:    resourceType,
   123  		tableName:       tableName,
   124  		selectedColumns: strings.Join(selectedColumns, ", "),
   125  		orderByParams:   orderByParams,
   126  	}
   127  }
   128  
   129  // NewListerGlobal is a constructor for ListerGlobal about global entities.
   130  func NewListerGlobal(resourceType resource.Type, tableName string, selectedColumns []string) ListerGlobal {
   131  	return &universalLister{
   132  		resourceType:    resourceType,
   133  		tableName:       tableName,
   134  		selectedColumns: strings.Join(selectedColumns, ", "),
   135  		orderByParams:   NoOrderBy,
   136  	}
   137  }
   138  
   139  // List lists tenant scoped entities with tenant isolation subquery.
   140  // If the tenantColumn is configured the isolation is based on equal condition on tenantColumn.
   141  // If the tenantColumn is not configured an entity with externally managed tenant accesses in m2m table / view is assumed.
   142  func (l *universalLister) List(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, additionalConditions ...Condition) error {
   143  	return l.listWithTenantScope(ctx, resourceType, tenant, dest, NoLock, additionalConditions)
   144  }
   145  
   146  // ListConditionTree lists tenant scoped entities matching the provided condition tree with tenant isolation.
   147  // If the tenantColumn is configured the isolation is based on equal condition on tenantColumn.
   148  // If the tenantColumn is not configured an entity with externally managed tenant accesses in m2m table / view is assumed.
   149  func (l *universalLister) ListConditionTree(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, conditionTree *ConditionTree) error {
   150  	return l.listConditionTreeWithTenantScope(ctx, resourceType, tenant, dest, NoLock, conditionTree)
   151  }
   152  
   153  // ListConditionTreeGlobal lists entities matching the provided condition tree globally.
   154  func (l *universalLister) ListConditionTreeGlobal(ctx context.Context, resourceType resource.Type, dest Collection, conditionTree *ConditionTree) error {
   155  	return l.listWithConditionTree(ctx, resourceType, dest, NoLock, conditionTree)
   156  }
   157  
   158  // ListWithSelectForUpdate lists tenant scoped entities with tenant isolation subquery and
   159  func (l *universalLister) ListWithSelectForUpdate(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, additionalConditions ...Condition) error {
   160  	return l.listWithTenantScope(ctx, resourceType, tenant, dest, ForUpdateLock, additionalConditions)
   161  }
   162  
   163  func (l *universalLister) listWithTenantScope(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, lockClause string, additionalConditions []Condition) error {
   164  	if tenant == "" {
   165  		return apperrors.NewTenantRequiredError()
   166  	}
   167  
   168  	if l.tenantColumn != nil {
   169  		additionalConditions = append(Conditions{NewEqualCondition(*l.tenantColumn, tenant)}, additionalConditions...)
   170  		return l.list(ctx, resourceType, dest, lockClause, additionalConditions...)
   171  	}
   172  
   173  	tenantIsolation, err :=
   174  		newTenantIsolationConditionWithPlaceholder(resourceType, tenant, l.ownerCheck, true)
   175  	if err != nil {
   176  		return err
   177  	}
   178  
   179  	additionalConditions = append(additionalConditions, tenantIsolation)
   180  
   181  	return l.list(ctx, resourceType, dest, lockClause, additionalConditions...)
   182  }
   183  
   184  func (l *universalLister) listConditionTreeWithTenantScope(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, lockClause string, conditionTree *ConditionTree) error {
   185  	if tenant == "" {
   186  		return apperrors.NewTenantRequiredError()
   187  	}
   188  
   189  	if l.tenantColumn != nil {
   190  		conditions := And(&ConditionTree{Operand: NewEqualCondition(*l.tenantColumn, tenant)}, conditionTree)
   191  		return l.listWithConditionTree(ctx, resourceType, dest, lockClause, conditions)
   192  	}
   193  
   194  	tenantIsolation, err := newTenantIsolationConditionWithPlaceholder(resourceType, tenant, l.ownerCheck, true)
   195  	if err != nil {
   196  		return err
   197  	}
   198  
   199  	conditions := And(&ConditionTree{Operand: tenantIsolation}, conditionTree)
   200  
   201  	return l.listWithConditionTree(ctx, resourceType, dest, lockClause, conditions)
   202  }
   203  
   204  // SetSelectedColumns sets the selected columns for the query.
   205  func (l *universalLister) SetSelectedColumns(selectedColumns []string) {
   206  	l.selectedColumns = strings.Join(selectedColumns, ", ")
   207  }
   208  
   209  // Clone creates a new instance of the lister with the same configuration.
   210  func (l *universalLister) Clone() *universalLister {
   211  	var clonedLister universalLister
   212  
   213  	clonedLister.resourceType = l.resourceType
   214  	clonedLister.tableName = l.tableName
   215  	clonedLister.selectedColumns = l.selectedColumns
   216  	clonedLister.tenantColumn = l.tenantColumn
   217  	clonedLister.orderByParams = append(clonedLister.orderByParams, l.orderByParams...)
   218  
   219  	return &clonedLister
   220  }
   221  
   222  // ListGlobal lists global entities without tenant isolation.
   223  func (l *universalLister) ListGlobal(ctx context.Context, dest Collection, additionalConditions ...Condition) error {
   224  	return l.list(ctx, l.resourceType, dest, NoLock, additionalConditions...)
   225  }
   226  
   227  // ListGlobalWithSelectForUpdate lists global entities without tenant isolation.
   228  func (l *universalLister) ListGlobalWithSelectForUpdate(ctx context.Context, dest Collection, additionalConditions ...Condition) error {
   229  	return l.list(ctx, l.resourceType, dest, ForUpdateLock, additionalConditions...)
   230  }
   231  
   232  func (l *universalLister) list(ctx context.Context, resourceType resource.Type, dest Collection, lockClause string, conditions ...Condition) error {
   233  	persist, err := persistence.FromCtx(ctx)
   234  	if err != nil {
   235  		return err
   236  	}
   237  
   238  	query, args, err := buildSelectQuery(l.tableName, l.selectedColumns, conditions, l.orderByParams, lockClause, true)
   239  	if err != nil {
   240  		return errors.Wrap(err, "while building list query")
   241  	}
   242  
   243  	log.C(ctx).Debugf("Executing DB query: %s", query)
   244  	err = persist.SelectContext(ctx, dest, query, args...)
   245  
   246  	return persistence.MapSQLError(ctx, err, resourceType, resource.List, "while fetching list of objects from '%s' table '%+v'", l.tableName, err)
   247  }
   248  
   249  func (l *universalLister) listWithConditionTree(ctx context.Context, resourceType resource.Type, dest Collection, lockClause string, conditionTree *ConditionTree) error {
   250  	persist, err := persistence.FromCtx(ctx)
   251  	if err != nil {
   252  		return err
   253  	}
   254  
   255  	query, args, err := buildSelectQueryFromTree(l.tableName, l.selectedColumns, conditionTree, l.orderByParams, lockClause, true)
   256  	if err != nil {
   257  		return errors.Wrap(err, "while building list query")
   258  	}
   259  
   260  	log.C(ctx).Debugf("Executing DB query: %s", query)
   261  	err = persist.SelectContext(ctx, dest, query, args...)
   262  
   263  	return persistence.MapSQLError(ctx, err, resourceType, resource.List, "while fetching list of objects from '%s' table", l.tableName)
   264  }