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

     1  package repo
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/kyma-incubator/compass/components/director/pkg/log"
     9  
    10  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
    11  
    12  	"github.com/kyma-incubator/compass/components/director/pkg/apperrors"
    13  
    14  	"github.com/kyma-incubator/compass/components/director/pkg/persistence"
    15  	"github.com/pkg/errors"
    16  )
    17  
    18  // ExistQuerier is an interface for checking existence of tenant scoped entities with either externally managed tenant accesses (m2m table or view) or embedded tenant in them.
    19  type ExistQuerier interface {
    20  	Exists(ctx context.Context, resourceType resource.Type, tenant string, conditions Conditions) (bool, error)
    21  }
    22  
    23  // ExistQuerierGlobal is an interface for checking existence of global entities.
    24  type ExistQuerierGlobal interface {
    25  	ExistsGlobal(ctx context.Context, conditions Conditions) (bool, error)
    26  }
    27  
    28  type universalExistQuerier struct {
    29  	tableName    string
    30  	tenantColumn *string
    31  	resourceType resource.Type
    32  	ownerCheck   bool
    33  }
    34  
    35  // NewExistQuerier is a constructor for ExistQuerier about entities with externally managed tenant accesses (m2m table or view)
    36  func NewExistQuerier(tableName string) ExistQuerier {
    37  	return &universalExistQuerier{tableName: tableName}
    38  }
    39  
    40  // NewExistQuerierWithOwnerCheck is a constructor for ExistQuerier about entities with externally managed tenant accesses (m2m table or view) with additional owner check.
    41  func NewExistQuerierWithOwnerCheck(tableName string) ExistQuerier {
    42  	return &universalExistQuerier{tableName: tableName, ownerCheck: true}
    43  }
    44  
    45  // NewExistQuerierWithEmbeddedTenant is a constructor for ExistQuerier about entities with tenant embedded in them.
    46  func NewExistQuerierWithEmbeddedTenant(tableName string, tenantColumn string) ExistQuerier {
    47  	return &universalExistQuerier{tableName: tableName, tenantColumn: &tenantColumn}
    48  }
    49  
    50  // NewExistQuerierGlobal is a constructor for ExistQuerierGlobal about global entities.
    51  func NewExistQuerierGlobal(resourceType resource.Type, tableName string) ExistQuerierGlobal {
    52  	return &universalExistQuerier{tableName: tableName, resourceType: resourceType}
    53  }
    54  
    55  // Exists checks for existence of tenant scoped entities with tenant isolation subquery.
    56  // If the tenantColumn is configured the isolation is based on equal condition on tenantColumn.
    57  // If the tenantColumn is not configured an entity with externally managed tenant accesses in m2m table / view is assumed.
    58  func (g *universalExistQuerier) Exists(ctx context.Context, resourceType resource.Type, tenant string, conditions Conditions) (bool, error) {
    59  	if tenant == "" {
    60  		return false, apperrors.NewTenantRequiredError()
    61  	}
    62  
    63  	if g.tenantColumn != nil {
    64  		conditions = append(Conditions{NewEqualCondition(*g.tenantColumn, tenant)}, conditions...)
    65  		return g.exists(ctx, resourceType, conditions)
    66  	}
    67  
    68  	tenantIsolation, err := NewTenantIsolationCondition(resourceType, tenant, g.ownerCheck)
    69  	if err != nil {
    70  		return false, err
    71  	}
    72  
    73  	conditions = append(conditions, tenantIsolation)
    74  
    75  	return g.exists(ctx, resourceType, conditions)
    76  }
    77  
    78  // ExistsGlobal checks for existence of global entities without tenant isolation.
    79  func (g *universalExistQuerier) ExistsGlobal(ctx context.Context, conditions Conditions) (bool, error) {
    80  	return g.exists(ctx, g.resourceType, conditions)
    81  }
    82  
    83  func (g *universalExistQuerier) exists(ctx context.Context, resourceType resource.Type, conditions Conditions) (bool, error) {
    84  	persist, err := persistence.FromCtx(ctx)
    85  	if err != nil {
    86  		return false, err
    87  	}
    88  
    89  	var stmtBuilder strings.Builder
    90  
    91  	stmtBuilder.WriteString(fmt.Sprintf("SELECT 1 FROM %s", g.tableName))
    92  	if len(conditions) > 0 {
    93  		stmtBuilder.WriteString(" WHERE")
    94  	}
    95  
    96  	err = writeEnumeratedConditions(&stmtBuilder, conditions)
    97  	if err != nil {
    98  		return false, errors.Wrap(err, "while writing enumerated conditions")
    99  	}
   100  	allArgs := getAllArgs(conditions)
   101  
   102  	query := getQueryFromBuilder(stmtBuilder)
   103  
   104  	log.C(ctx).Debugf("Executing DB query: %s", query)
   105  	var count int
   106  	err = persist.GetContext(ctx, &count, query, allArgs...)
   107  	err = persistence.MapSQLError(ctx, err, resourceType, resource.Exists, "while getting object from '%s' table", g.tableName)
   108  
   109  	if err != nil {
   110  		if apperrors.IsNotFoundError(err) {
   111  			return false, nil
   112  		}
   113  		return false, err
   114  	}
   115  	return true, nil
   116  }