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

     1  package formationassignment
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/kyma-incubator/compass/components/director/pkg/pagination"
     7  
     8  	"github.com/kyma-incubator/compass/components/director/pkg/apperrors"
     9  
    10  	"github.com/kyma-incubator/compass/components/director/internal/model"
    11  	"github.com/kyma-incubator/compass/components/director/internal/repo"
    12  	"github.com/kyma-incubator/compass/components/director/pkg/log"
    13  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  const tableName string = `public.formation_assignments`
    18  
    19  var (
    20  	idTableColumns        = []string{"id"}
    21  	updatableTableColumns = []string{"state", "value"}
    22  	tableColumns          = []string{"id", "formation_id", "tenant_id", "source", "source_type", "target", "target_type", "state", "value"}
    23  	tenantColumn          = "tenant_id"
    24  )
    25  
    26  // EntityConverter converts between the internal model and entity
    27  //
    28  //go:generate mockery --name=EntityConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
    29  type EntityConverter interface {
    30  	ToEntity(in *model.FormationAssignment) *Entity
    31  	FromEntity(entity *Entity) *model.FormationAssignment
    32  }
    33  
    34  type repository struct {
    35  	creator               repo.CreatorGlobal
    36  	getter                repo.SingleGetter
    37  	globalGetter          repo.SingleGetterGlobal
    38  	pageableQuerierGlobal repo.PageableQuerier
    39  	unionLister           repo.UnionLister
    40  	lister                repo.Lister
    41  	conditionLister       repo.ConditionTreeLister
    42  	updaterGlobal         repo.UpdaterGlobal
    43  	deleter               repo.Deleter
    44  	deleteConditionTree   repo.DeleterConditionTree
    45  	existQuerier          repo.ExistQuerier
    46  	conv                  EntityConverter
    47  }
    48  
    49  // NewRepository creates a new FormationAssignment repository
    50  func NewRepository(conv EntityConverter) *repository {
    51  	return &repository{
    52  		creator:               repo.NewCreatorGlobal(resource.FormationAssignment, tableName, tableColumns),
    53  		getter:                repo.NewSingleGetterWithEmbeddedTenant(tableName, tenantColumn, tableColumns),
    54  		globalGetter:          repo.NewSingleGetterGlobal(resource.FormationAssignment, tableName, tableColumns),
    55  		pageableQuerierGlobal: repo.NewPageableQuerierWithEmbeddedTenant(tableName, tenantColumn, tableColumns),
    56  		unionLister:           repo.NewUnionListerWithEmbeddedTenant(tableName, tenantColumn, tableColumns),
    57  		lister:                repo.NewListerWithEmbeddedTenant(tableName, tenantColumn, tableColumns),
    58  		conditionLister:       repo.NewConditionTreeListerWithEmbeddedTenant(tableName, tenantColumn, tableColumns),
    59  		updaterGlobal:         repo.NewUpdaterWithEmbeddedTenant(resource.FormationAssignment, tableName, updatableTableColumns, tenantColumn, idTableColumns),
    60  		deleter:               repo.NewDeleterWithEmbeddedTenant(tableName, tenantColumn),
    61  		deleteConditionTree:   repo.NewDeleterConditionTreeWithEmbeddedTenant(tableName, tenantColumn),
    62  		existQuerier:          repo.NewExistQuerierWithEmbeddedTenant(tableName, tenantColumn),
    63  		conv:                  conv,
    64  	}
    65  }
    66  
    67  // Create creates a new Formation Assignment in the database with the fields from the model
    68  func (r *repository) Create(ctx context.Context, item *model.FormationAssignment) error {
    69  	if item == nil {
    70  		return apperrors.NewInternalError("model can not be empty")
    71  	}
    72  
    73  	log.C(ctx).Debugf("Persisting Formation Assignment entity with ID: %q", item.ID)
    74  	return r.creator.Create(ctx, r.conv.ToEntity(item))
    75  }
    76  
    77  // GetByTargetAndSource queries for a single Formation Assignment matching by a given Target, Source for the given Formation
    78  func (r *repository) GetByTargetAndSource(ctx context.Context, target, source, tenantID, formationID string) (*model.FormationAssignment, error) {
    79  	var entity Entity
    80  	if err := r.getter.Get(ctx, resource.FormationAssignment, tenantID, repo.Conditions{repo.NewEqualCondition("formation_id", formationID), repo.NewEqualCondition("target", target), repo.NewEqualCondition("source", source)}, repo.NoOrderBy, &entity); err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	return r.conv.FromEntity(&entity), nil
    85  }
    86  
    87  // Get queries for a single Formation Assignment matching by a given ID
    88  func (r *repository) Get(ctx context.Context, id, tenantID string) (*model.FormationAssignment, error) {
    89  	var entity Entity
    90  	if err := r.getter.Get(ctx, resource.FormationAssignment, tenantID, repo.Conditions{repo.NewEqualCondition("id", id)}, repo.NoOrderBy, &entity); err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	return r.conv.FromEntity(&entity), nil
    95  }
    96  
    97  // GetGlobalByID retrieves formation assignment matching ID `id` globally without tenant parameter
    98  func (r *repository) GetGlobalByID(ctx context.Context, id string) (*model.FormationAssignment, error) {
    99  	var entity Entity
   100  	if err := r.globalGetter.GetGlobal(ctx, repo.Conditions{repo.NewEqualCondition("id", id)}, repo.NoOrderBy, &entity); err != nil {
   101  		return nil, err
   102  	}
   103  
   104  	return r.conv.FromEntity(&entity), nil
   105  }
   106  
   107  // GetGlobalByIDAndFormationID retrieves formation assignment matching ID `id` and formation ID `formationID` globally, without tenant parameter
   108  func (r *repository) GetGlobalByIDAndFormationID(ctx context.Context, id, formationID string) (*model.FormationAssignment, error) {
   109  	var entity Entity
   110  	if err := r.globalGetter.GetGlobal(ctx, repo.Conditions{repo.NewEqualCondition("id", id), repo.NewEqualCondition("formation_id", formationID)}, repo.NoOrderBy, &entity); err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	return r.conv.FromEntity(&entity), nil
   115  }
   116  
   117  // GetForFormation retrieves Formation Assignment with the provided `id` associated to Formation with id `formationID` from the database if it exists
   118  func (r *repository) GetForFormation(ctx context.Context, tenantID, id, formationID string) (*model.FormationAssignment, error) {
   119  	var formationAssignmentEnt Entity
   120  
   121  	conditions := repo.Conditions{
   122  		repo.NewEqualCondition("id", id),
   123  		repo.NewEqualCondition("formation_id", formationID),
   124  	}
   125  
   126  	if err := r.getter.Get(ctx, resource.FormationAssignment, tenantID, conditions, repo.NoOrderBy, &formationAssignmentEnt); err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	return r.conv.FromEntity(&formationAssignmentEnt), nil
   131  }
   132  
   133  // GetAssignmentsForFormationWithStates retrieves formation assignments matching formation ID `formationID` and with state among `states` for tenant with ID `tenantID`
   134  func (r *repository) GetAssignmentsForFormationWithStates(ctx context.Context, tenantID, formationID string, states []string) ([]*model.FormationAssignment, error) {
   135  	if len(states) == 0 {
   136  		return nil, nil
   137  	}
   138  	var formationAssignmentCollection EntityCollection
   139  
   140  	conditions := repo.Conditions{
   141  		repo.NewEqualCondition("formation_id", formationID),
   142  		repo.NewInConditionForStringValues("state", states),
   143  	}
   144  
   145  	if err := r.lister.List(ctx, resource.FormationAssignment, tenantID, &formationAssignmentCollection, conditions...); err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	return r.multipleFromEntities(formationAssignmentCollection), nil
   150  }
   151  
   152  // GetBySourceAndTarget retrieves formation assignment by source and target
   153  func (r *repository) GetBySourceAndTarget(ctx context.Context, tenantID, formationID, sourceID, targetID string) (*model.FormationAssignment, error) {
   154  	var formationAssignmentEnt Entity
   155  
   156  	conditions := repo.Conditions{
   157  		repo.NewEqualCondition("formation_id", formationID),
   158  		repo.NewEqualCondition("source", sourceID),
   159  		repo.NewEqualCondition("target", targetID),
   160  	}
   161  
   162  	if err := r.getter.Get(ctx, resource.FormationAssignment, tenantID, conditions, repo.NoOrderBy, &formationAssignmentEnt); err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	return r.conv.FromEntity(&formationAssignmentEnt), nil
   167  }
   168  
   169  // GetReverseBySourceAndTarget retrieves reverse formation assignment by source and target
   170  func (r *repository) GetReverseBySourceAndTarget(ctx context.Context, tenantID, formationID, sourceID, targetID string) (*model.FormationAssignment, error) {
   171  	return r.GetBySourceAndTarget(ctx, tenantID, formationID, targetID, sourceID)
   172  }
   173  
   174  // List queries for all Formation Assignment sorted by ID and paginated by the pageSize and cursor parameters
   175  func (r *repository) List(ctx context.Context, pageSize int, cursor, tenantID string) (*model.FormationAssignmentPage, error) {
   176  	var entityCollection EntityCollection
   177  	page, totalCount, err := r.pageableQuerierGlobal.List(ctx, resource.FormationAssignment, tenantID, pageSize, cursor, "id", &entityCollection)
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  
   182  	return &model.FormationAssignmentPage{
   183  		Data:       r.multipleFromEntities(entityCollection),
   184  		TotalCount: totalCount,
   185  		PageInfo:   page,
   186  	}, nil
   187  }
   188  
   189  // ListByFormationIDs retrieves a page of Formation Assignment objects for each formationID from the database that are visible for `tenantID`
   190  func (r *repository) ListByFormationIDs(ctx context.Context, tenantID string, formationIDs []string, pageSize int, cursor string) ([]*model.FormationAssignmentPage, error) {
   191  	var formationAssignmentCollection EntityCollection
   192  
   193  	orderByColumns := repo.OrderByParams{repo.NewAscOrderBy("formation_id"), repo.NewAscOrderBy("id")}
   194  
   195  	counts, err := r.unionLister.List(ctx, resource.FormationAssignment, tenantID, formationIDs, "formation_id", pageSize, cursor, orderByColumns, &formationAssignmentCollection)
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  
   200  	formationAssignmentByFormationID := map[string][]*model.FormationAssignment{}
   201  	for _, faEntity := range formationAssignmentCollection {
   202  		formationAssignmentByFormationID[faEntity.FormationID] = append(formationAssignmentByFormationID[faEntity.FormationID], r.conv.FromEntity(faEntity))
   203  	}
   204  
   205  	offset, err := pagination.DecodeOffsetCursor(cursor)
   206  	if err != nil {
   207  		return nil, errors.Wrap(err, "while decoding page cursor")
   208  	}
   209  
   210  	faPages := make([]*model.FormationAssignmentPage, 0, len(formationIDs))
   211  	for _, formationID := range formationIDs {
   212  		totalCount := counts[formationID]
   213  		hasNextPage := false
   214  		endCursor := ""
   215  		if totalCount > offset+len(formationAssignmentByFormationID[formationID]) {
   216  			hasNextPage = true
   217  			endCursor = pagination.EncodeNextOffsetCursor(offset, pageSize)
   218  		}
   219  
   220  		page := &pagination.Page{
   221  			StartCursor: cursor,
   222  			EndCursor:   endCursor,
   223  			HasNextPage: hasNextPage,
   224  		}
   225  
   226  		faPages = append(faPages, &model.FormationAssignmentPage{Data: formationAssignmentByFormationID[formationID], TotalCount: totalCount, PageInfo: page})
   227  	}
   228  
   229  	return faPages, nil
   230  }
   231  
   232  // ListByFormationIDsNoPaging retrieves all Formation Assignment objects for each formationID from the database that are visible for `tenantID`
   233  func (r *repository) ListByFormationIDsNoPaging(ctx context.Context, tenantID string, formationIDs []string) ([][]*model.FormationAssignment, error) {
   234  	if len(formationIDs) == 0 {
   235  		return nil, nil
   236  	}
   237  
   238  	var formationAssignmentCollection EntityCollection
   239  
   240  	conditions := repo.NewInConditionForStringValues("formation_id", formationIDs)
   241  
   242  	if err := r.lister.List(ctx, resource.FormationAssignment, tenantID, &formationAssignmentCollection, conditions); err != nil {
   243  		return nil, err
   244  	}
   245  
   246  	formationAssignmentByFormationID := map[string][]*model.FormationAssignment{}
   247  	for _, faEntity := range formationAssignmentCollection {
   248  		formationAssignmentByFormationID[faEntity.FormationID] = append(formationAssignmentByFormationID[faEntity.FormationID], r.conv.FromEntity(faEntity))
   249  	}
   250  
   251  	formationAssignmentsPerFormation := make([][]*model.FormationAssignment, 0, len(formationIDs))
   252  	for _, formationID := range formationIDs {
   253  		formationAssignmentsPerFormation = append(formationAssignmentsPerFormation, formationAssignmentByFormationID[formationID])
   254  	}
   255  
   256  	return formationAssignmentsPerFormation, nil
   257  }
   258  
   259  // ListAllForObject retrieves all FormationAssignment objects for formation with ID `formationID` that have objectID as `target` or `source` from the database that are visible for `tenant`
   260  func (r *repository) ListAllForObject(ctx context.Context, tenant, formationID, objectID string) ([]*model.FormationAssignment, error) {
   261  	var entities EntityCollection
   262  	conditions := repo.And(
   263  		&repo.ConditionTree{Operand: repo.NewEqualCondition("formation_id", formationID)},
   264  		repo.Or(repo.ConditionTreesFromConditions([]repo.Condition{
   265  			repo.NewEqualCondition("source", objectID),
   266  			repo.NewEqualCondition("target", objectID),
   267  		})...))
   268  
   269  	if err := r.conditionLister.ListConditionTree(ctx, resource.FormationAssignment, tenant, &entities, conditions); err != nil {
   270  		return nil, err
   271  	}
   272  
   273  	return r.multipleFromEntities(entities), nil
   274  }
   275  
   276  // ListAllForObjectIDs retrieves all FormationAssignment objects for formation with ID `formationID` that have any of the objectIDs as `target` or `source` from the database that are visible for `tenant`
   277  func (r *repository) ListAllForObjectIDs(ctx context.Context, tenant, formationID string, objectIDs []string) ([]*model.FormationAssignment, error) {
   278  	var entities EntityCollection
   279  	conditions := repo.And(
   280  		&repo.ConditionTree{Operand: repo.NewEqualCondition("formation_id", formationID)},
   281  		repo.Or(repo.ConditionTreesFromConditions([]repo.Condition{
   282  			repo.NewInConditionForStringValues("source", objectIDs),
   283  			repo.NewInConditionForStringValues("target", objectIDs),
   284  		})...))
   285  
   286  	if err := r.conditionLister.ListConditionTree(ctx, resource.FormationAssignment, tenant, &entities, conditions); err != nil {
   287  		return nil, err
   288  	}
   289  
   290  	return r.multipleFromEntities(entities), nil
   291  }
   292  
   293  // ListForIDs missing godoc
   294  func (r *repository) ListForIDs(ctx context.Context, tenant string, ids []string) ([]*model.FormationAssignment, error) {
   295  	if len(ids) == 0 {
   296  		return nil, nil
   297  	}
   298  	var entitiesWithIDs EntityCollection
   299  	conditions := repo.NewInConditionForStringValues("id", ids)
   300  
   301  	if err := r.lister.List(ctx, resource.FormationAssignment, tenant, &entitiesWithIDs, conditions); err != nil {
   302  		return nil, err
   303  	}
   304  
   305  	return r.multipleFromEntities(entitiesWithIDs), nil
   306  }
   307  
   308  // Update updates the Formation Assignment matching the ID of the input model
   309  func (r *repository) Update(ctx context.Context, model *model.FormationAssignment) error {
   310  	if model == nil {
   311  		return apperrors.NewInternalError("model can not be empty")
   312  	}
   313  
   314  	return r.updaterGlobal.UpdateSingleGlobal(ctx, r.conv.ToEntity(model))
   315  }
   316  
   317  // Delete deletes a Formation Assignment with given ID
   318  func (r *repository) Delete(ctx context.Context, id, tenantID string) error {
   319  	return r.deleter.DeleteOne(ctx, resource.FormationAssignment, tenantID, repo.Conditions{repo.NewEqualCondition("id", id)})
   320  }
   321  
   322  func (r *repository) DeleteAssignmentsForObjectID(ctx context.Context, tenant, formationID, objectID string) error {
   323  	conditions := repo.And(
   324  		&repo.ConditionTree{Operand: repo.NewEqualCondition("formation_id", formationID)},
   325  		repo.Or(repo.ConditionTreesFromConditions([]repo.Condition{
   326  			repo.NewEqualCondition("source", objectID),
   327  			repo.NewEqualCondition("target", objectID),
   328  		})...))
   329  
   330  	return r.deleteConditionTree.DeleteConditionTree(ctx, resource.FormationAssignment, tenant, conditions)
   331  }
   332  
   333  // Exists check if a Formation Assignment with given ID exists
   334  func (r *repository) Exists(ctx context.Context, id, tenantID string) (bool, error) {
   335  	return r.existQuerier.Exists(ctx, resource.FormationAssignment, tenantID, repo.Conditions{repo.NewEqualCondition("id", id)})
   336  }
   337  
   338  func (r *repository) multipleFromEntities(entities EntityCollection) []*model.FormationAssignment {
   339  	items := make([]*model.FormationAssignment, 0, len(entities))
   340  	for _, ent := range entities {
   341  		items = append(items, r.conv.FromEntity(ent))
   342  	}
   343  	return items
   344  }