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

     1  package formationtemplate
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/kyma-incubator/compass/components/director/pkg/apperrors"
     7  
     8  	"github.com/kyma-incubator/compass/components/director/internal/model"
     9  	"github.com/kyma-incubator/compass/components/director/internal/repo"
    10  	"github.com/kyma-incubator/compass/components/director/pkg/log"
    11  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  const (
    16  	tableName      string = `public.formation_templates`
    17  	tenantIDColumn string = "tenant_id"
    18  	idColumn       string = "id"
    19  )
    20  
    21  var (
    22  	idTableColumns            = []string{idColumn}
    23  	updatableTableColumns     = []string{"name", "application_types", "runtime_types", "runtime_type_display_name", "runtime_artifact_kind", "leading_product_ids"}
    24  	tenantTableColumn         = []string{tenantIDColumn}
    25  	tableColumnsWithoutTenant = append(idTableColumns, updatableTableColumns...)
    26  	tableColumns              = append(tableColumnsWithoutTenant, tenantTableColumn...)
    27  )
    28  
    29  // EntityConverter converts between the internal model and entity
    30  //
    31  //go:generate mockery --name=EntityConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
    32  type EntityConverter interface {
    33  	ToEntity(in *model.FormationTemplate) (*Entity, error)
    34  	FromEntity(entity *Entity) (*model.FormationTemplate, error)
    35  }
    36  
    37  type repository struct {
    38  	creator                        repo.CreatorGlobal
    39  	existQuerierGlobal             repo.ExistQuerierGlobal
    40  	singleGetterGlobal             repo.SingleGetterGlobal
    41  	singleGetterWithEmbeddedTenant repo.SingleGetter
    42  	pageableQuerierGlobal          repo.PageableQuerierGlobal
    43  	updaterGlobal                  repo.UpdaterGlobal
    44  	updaterWithEmbeddedTenant      repo.UpdaterGlobal
    45  	deleterGlobal                  repo.DeleterGlobal
    46  	deleterWithEmbeddedTenant      repo.Deleter
    47  	conv                           EntityConverter
    48  }
    49  
    50  // NewRepository creates a new FormationTemplate repository
    51  func NewRepository(conv EntityConverter) *repository {
    52  	return &repository{
    53  		creator:                        repo.NewCreatorGlobal(resource.FormationTemplate, tableName, tableColumns),
    54  		existQuerierGlobal:             repo.NewExistQuerierGlobal(resource.FormationTemplate, tableName),
    55  		singleGetterGlobal:             repo.NewSingleGetterGlobal(resource.FormationTemplate, tableName, tableColumns),
    56  		singleGetterWithEmbeddedTenant: repo.NewSingleGetterWithEmbeddedTenant(tableName, tenantIDColumn, tableColumns),
    57  		pageableQuerierGlobal:          repo.NewPageableQuerierGlobal(resource.FormationTemplate, tableName, tableColumns),
    58  		updaterGlobal:                  repo.NewUpdaterGlobal(resource.FormationTemplate, tableName, updatableTableColumns, idTableColumns),
    59  		updaterWithEmbeddedTenant:      repo.NewUpdaterWithEmbeddedTenant(resource.FormationTemplate, tableName, updatableTableColumns, tenantIDColumn, idTableColumns),
    60  		deleterGlobal:                  repo.NewDeleterGlobal(resource.FormationTemplate, tableName),
    61  		deleterWithEmbeddedTenant:      repo.NewDeleterWithEmbeddedTenant(tableName, tenantIDColumn),
    62  		conv:                           conv,
    63  	}
    64  }
    65  
    66  // Create creates a new FormationTemplate in the database with the fields in model
    67  func (r *repository) Create(ctx context.Context, item *model.FormationTemplate) error {
    68  	if item == nil {
    69  		return apperrors.NewInternalError("model can not be empty")
    70  	}
    71  
    72  	log.C(ctx).Debugf("Converting Formation Template with id %s to entity", item.ID)
    73  	entity, err := r.conv.ToEntity(item)
    74  	if err != nil {
    75  		return errors.Wrapf(err, "while converting Formation Template with ID %s", item.ID)
    76  	}
    77  
    78  	log.C(ctx).Debugf("Persisting Formation Template entity with id %s to db", item.ID)
    79  	return r.creator.Create(ctx, entity)
    80  }
    81  
    82  // Get queries for a single FormationTemplate matching the given id
    83  func (r *repository) Get(ctx context.Context, id string) (*model.FormationTemplate, error) {
    84  	var entity Entity
    85  	if err := r.singleGetterGlobal.GetGlobal(ctx, repo.Conditions{repo.NewEqualCondition("id", id)}, repo.NoOrderBy, &entity); err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	result, err := r.conv.FromEntity(&entity)
    90  	if err != nil {
    91  		return nil, errors.Wrapf(err, "while converting Formation Template with ID %s", id)
    92  	}
    93  
    94  	return result, nil
    95  }
    96  
    97  // GetByNameAndTenant check if the provided tenant is not empty and tries to retrieve the formation template with tenant. If it fails with not found error then the formation template is retrieved globally.
    98  // If the tenant is empty in the first place the formation template is retrieved globally.
    99  func (r *repository) GetByNameAndTenant(ctx context.Context, templateName, tenantID string) (*model.FormationTemplate, error) {
   100  	log.C(ctx).Debugf("Getting formation template by name: %q and tenant %q ...", templateName, tenantID)
   101  	var entity Entity
   102  
   103  	conditionsEqualName := repo.Conditions{repo.NewEqualCondition("name", templateName)}
   104  	conditionsEqualNameAndNullTenant := repo.Conditions{
   105  		repo.NewEqualCondition("name", templateName),
   106  		repo.NewNullCondition(tenantIDColumn),
   107  	}
   108  
   109  	// If the call is with tenant but the query (select * from FT where name = ? and tenant_id = ?) returns NOT FOUND that means that there is no such tenant scoped FT and the call should get the global FT with that name.
   110  	//
   111  	// With this approach we allow the client to create a tenant scoped FT with the same name as a global FT - so when we get the FT first we will try to match it by name and tenant
   112  	// and if there is no such FT, we will get the global one
   113  	if tenantID == "" {
   114  		if err := r.singleGetterGlobal.GetGlobal(ctx, conditionsEqualNameAndNullTenant, repo.NoOrderBy, &entity); err != nil {
   115  			return nil, err
   116  		}
   117  	} else {
   118  		if err := r.singleGetterWithEmbeddedTenant.Get(ctx, resource.FormationTemplate, tenantID, conditionsEqualName, repo.NoOrderBy, &entity); err != nil {
   119  			if !apperrors.IsNotFoundError(err) {
   120  				return nil, err
   121  			}
   122  			entity = Entity{}
   123  			if err = r.singleGetterGlobal.GetGlobal(ctx, conditionsEqualNameAndNullTenant, repo.NoOrderBy, &entity); err != nil {
   124  				return nil, err
   125  			}
   126  		}
   127  	}
   128  
   129  	result, err := r.conv.FromEntity(&entity)
   130  	if err != nil {
   131  		return nil, errors.Wrapf(err, "while converting Formation Template with name: %q", templateName)
   132  	}
   133  
   134  	return result, nil
   135  }
   136  
   137  // List queries for all FormationTemplate sorted by ID and paginated by the pageSize and cursor parameters
   138  func (r *repository) List(ctx context.Context, tenantID string, pageSize int, cursor string) (*model.FormationTemplatePage, error) {
   139  	var entityCollection EntityCollection
   140  
   141  	var conditions *repo.ConditionTree
   142  	if tenantID == "" {
   143  		conditions = &repo.ConditionTree{Operand: repo.NewNullCondition(tenantIDColumn)}
   144  	} else {
   145  		conditions = repo.Or(&repo.ConditionTree{Operand: repo.NewNullCondition(tenantIDColumn)}, &repo.ConditionTree{Operand: repo.NewEqualCondition(tenantIDColumn, tenantID)})
   146  	}
   147  
   148  	page, totalCount, err := r.pageableQuerierGlobal.ListGlobalWithAdditionalConditions(ctx, pageSize, cursor, idColumn, &entityCollection, conditions)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	items := make([]*model.FormationTemplate, 0, len(entityCollection))
   154  
   155  	for _, entity := range entityCollection {
   156  		isModel, err := r.conv.FromEntity(entity)
   157  		if err != nil {
   158  			return nil, errors.Wrapf(err, "while converting Formation Template entity with ID %s", entity.ID)
   159  		}
   160  
   161  		items = append(items, isModel)
   162  	}
   163  	return &model.FormationTemplatePage{
   164  		Data:       items,
   165  		TotalCount: totalCount,
   166  		PageInfo:   page,
   167  	}, nil
   168  }
   169  
   170  // Update updates the FormationTemplate matching the ID of the input model
   171  func (r *repository) Update(ctx context.Context, model *model.FormationTemplate) error {
   172  	if model == nil {
   173  		return apperrors.NewInternalError("model can not be empty")
   174  	}
   175  
   176  	entity, err := r.conv.ToEntity(model)
   177  	if err != nil {
   178  		return errors.Wrapf(err, "while converting Formation Template with ID %s", model.ID)
   179  	}
   180  
   181  	if model.TenantID != nil {
   182  		return r.updaterWithEmbeddedTenant.UpdateSingleGlobal(ctx, entity)
   183  	}
   184  	return r.updaterGlobal.UpdateSingleGlobal(ctx, entity)
   185  }
   186  
   187  // Delete deletes a formation template with given ID and tenantID
   188  func (r *repository) Delete(ctx context.Context, id, tenantID string) error {
   189  	conditions := repo.Conditions{repo.NewEqualCondition(idColumn, id)}
   190  
   191  	if tenantID == "" {
   192  		conditions = append(conditions, repo.NewNullCondition(tenantIDColumn))
   193  		return r.deleterGlobal.DeleteOneGlobal(ctx, conditions)
   194  	}
   195  
   196  	return r.deleterWithEmbeddedTenant.DeleteOne(ctx, resource.FormationTemplate, tenantID, conditions)
   197  }
   198  
   199  // Exists check if a formation template with given ID exists
   200  func (r *repository) Exists(ctx context.Context, id string) (bool, error) {
   201  	return r.existQuerierGlobal.ExistsGlobal(ctx, repo.Conditions{repo.NewEqualCondition(idColumn, id)})
   202  }