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

     1  package bundle
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/kyma-incubator/compass/components/director/pkg/str"
     7  
     8  	"github.com/kyma-incubator/compass/components/director/pkg/log"
     9  	"github.com/kyma-incubator/compass/components/director/pkg/pagination"
    10  
    11  	"github.com/kyma-incubator/compass/components/director/pkg/apperrors"
    12  
    13  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
    14  
    15  	"github.com/kyma-incubator/compass/components/director/internal/model"
    16  	"github.com/kyma-incubator/compass/components/director/internal/repo"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  const (
    21  	bundleTable                string = `public.bundles`
    22  	correlationIDs             string = "correlation_ids"
    23  	appIDColumn                string = "app_id"
    24  	appTemplateVersionIDColumn string = "app_template_version_id"
    25  )
    26  
    27  var (
    28  	bundleColumns    = []string{"id", appIDColumn, "app_template_version_id", "name", "description", "version", "instance_auth_request_json_schema", "default_instance_auth", "ord_id", "local_tenant_id", "short_description", "links", "labels", "credential_exchange_strategies", "ready", "created_at", "updated_at", "deleted_at", "error", correlationIDs, "tags", "resource_hash", "documentation_labels"}
    29  	updatableColumns = []string{"name", "description", "version", "instance_auth_request_json_schema", "default_instance_auth", "ord_id", "local_tenant_id", "short_description", "links", "labels", "credential_exchange_strategies", "ready", "created_at", "updated_at", "deleted_at", "error", "correlation_ids", "tags", "resource_hash", "documentation_labels"}
    30  	orderByColumns   = repo.OrderByParams{repo.NewAscOrderBy(appIDColumn), repo.NewAscOrderBy("id")}
    31  )
    32  
    33  // EntityConverter missing godoc
    34  //
    35  //go:generate mockery --name=EntityConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
    36  type EntityConverter interface {
    37  	ToEntity(in *model.Bundle) (*Entity, error)
    38  	FromEntity(entity *Entity) (*model.Bundle, error)
    39  }
    40  
    41  type pgRepository struct {
    42  	existQuerier       repo.ExistQuerier
    43  	singleGetter       repo.SingleGetter
    44  	singleGlobalGetter repo.SingleGetterGlobal
    45  	deleter            repo.Deleter
    46  	deleterGlobal      repo.DeleterGlobal
    47  	lister             repo.Lister
    48  	globalLister       repo.ListerGlobal
    49  	unionLister        repo.UnionLister
    50  	creator            repo.Creator
    51  	creatorGlobal      repo.CreatorGlobal
    52  	updater            repo.Updater
    53  	updaterGlobal      repo.UpdaterGlobal
    54  	conv               EntityConverter
    55  }
    56  
    57  // NewRepository missing godoc
    58  func NewRepository(conv EntityConverter) *pgRepository {
    59  	return &pgRepository{
    60  		existQuerier:       repo.NewExistQuerier(bundleTable),
    61  		singleGetter:       repo.NewSingleGetter(bundleTable, bundleColumns),
    62  		singleGlobalGetter: repo.NewSingleGetterGlobal(resource.Bundle, bundleTable, bundleColumns),
    63  		deleter:            repo.NewDeleter(bundleTable),
    64  		deleterGlobal:      repo.NewDeleterGlobal(resource.Bundle, bundleTable),
    65  		lister:             repo.NewLister(bundleTable, bundleColumns),
    66  		globalLister:       repo.NewListerGlobal(resource.Bundle, bundleTable, bundleColumns),
    67  		unionLister:        repo.NewUnionLister(bundleTable, bundleColumns),
    68  		creator:            repo.NewCreator(bundleTable, bundleColumns),
    69  		creatorGlobal:      repo.NewCreatorGlobal(resource.Bundle, bundleTable, bundleColumns),
    70  		updater:            repo.NewUpdater(bundleTable, updatableColumns, []string{"id"}),
    71  		updaterGlobal:      repo.NewUpdaterGlobal(resource.Bundle, bundleTable, updatableColumns, []string{"id"}),
    72  		conv:               conv,
    73  	}
    74  }
    75  
    76  // BundleCollection missing godoc
    77  type BundleCollection []Entity
    78  
    79  // Len missing godoc
    80  func (r BundleCollection) Len() int {
    81  	return len(r)
    82  }
    83  
    84  // Create missing godoc
    85  func (r *pgRepository) Create(ctx context.Context, tenant string, model *model.Bundle) error {
    86  	if model == nil {
    87  		return apperrors.NewInternalError("model can not be nil")
    88  	}
    89  
    90  	bndlEnt, err := r.conv.ToEntity(model)
    91  	if err != nil {
    92  		return errors.Wrap(err, "while converting to Bundle entity")
    93  	}
    94  
    95  	log.C(ctx).Debugf("Persisting Bundle entity with id %s to db", model.ID)
    96  	return r.creator.Create(ctx, resource.Bundle, tenant, bndlEnt)
    97  }
    98  
    99  // CreateGlobal creates a bundle without tenant isolation
   100  func (r *pgRepository) CreateGlobal(ctx context.Context, model *model.Bundle) error {
   101  	if model == nil {
   102  		return apperrors.NewInternalError("model can not be nil")
   103  	}
   104  
   105  	bndlEnt, err := r.conv.ToEntity(model)
   106  	if err != nil {
   107  		return errors.Wrap(err, "while converting to Bundle entity")
   108  	}
   109  
   110  	log.C(ctx).Debugf("Persisting Bundle entity with id %s to db", model.ID)
   111  	return r.creatorGlobal.Create(ctx, bndlEnt)
   112  }
   113  
   114  // Update missing godoc
   115  func (r *pgRepository) Update(ctx context.Context, tenant string, model *model.Bundle) error {
   116  	if model == nil {
   117  		return apperrors.NewInternalError("model can not be nil")
   118  	}
   119  
   120  	bndlEnt, err := r.conv.ToEntity(model)
   121  
   122  	if err != nil {
   123  		return errors.Wrap(err, "while converting to Bundle entity")
   124  	}
   125  
   126  	return r.updater.UpdateSingle(ctx, resource.Bundle, tenant, bndlEnt)
   127  }
   128  
   129  // UpdateGlobal updates a bundle without tenant isolation
   130  func (r *pgRepository) UpdateGlobal(ctx context.Context, model *model.Bundle) error {
   131  	if model == nil {
   132  		return apperrors.NewInternalError("model can not be nil")
   133  	}
   134  
   135  	bndlEnt, err := r.conv.ToEntity(model)
   136  
   137  	if err != nil {
   138  		return errors.Wrap(err, "while converting to Bundle entity")
   139  	}
   140  
   141  	return r.updaterGlobal.UpdateSingleGlobal(ctx, bndlEnt)
   142  }
   143  
   144  // Delete missing godoc
   145  func (r *pgRepository) Delete(ctx context.Context, tenant, id string) error {
   146  	return r.deleter.DeleteOne(ctx, resource.Bundle, tenant, repo.Conditions{repo.NewEqualCondition("id", id)})
   147  }
   148  
   149  // DeleteGlobal deletes a bundles by ID without tenant isolation
   150  func (r *pgRepository) DeleteGlobal(ctx context.Context, id string) error {
   151  	return r.deleterGlobal.DeleteOneGlobal(ctx, repo.Conditions{repo.NewEqualCondition("id", id)})
   152  }
   153  
   154  // Exists missing godoc
   155  func (r *pgRepository) Exists(ctx context.Context, tenant, id string) (bool, error) {
   156  	return r.existQuerier.Exists(ctx, resource.Bundle, tenant, repo.Conditions{repo.NewEqualCondition("id", id)})
   157  }
   158  
   159  // GetByID missing godoc
   160  func (r *pgRepository) GetByID(ctx context.Context, tenant, id string) (*model.Bundle, error) {
   161  	var bndlEnt Entity
   162  	if err := r.singleGetter.Get(ctx, resource.Bundle, tenant, repo.Conditions{repo.NewEqualCondition("id", id)}, repo.NoOrderBy, &bndlEnt); err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	return convertToBundle(r, &bndlEnt)
   167  }
   168  
   169  // GetByIDGlobal gets a single bundle by ID without tenant isolation
   170  func (r *pgRepository) GetByIDGlobal(ctx context.Context, id string) (*model.Bundle, error) {
   171  	var bndlEnt Entity
   172  	if err := r.singleGlobalGetter.GetGlobal(ctx, repo.Conditions{repo.NewEqualCondition("id", id)}, repo.NoOrderBy, &bndlEnt); err != nil {
   173  		return nil, err
   174  	}
   175  
   176  	return convertToBundle(r, &bndlEnt)
   177  }
   178  
   179  func convertToBundle(r *pgRepository, bndlEnt *Entity) (*model.Bundle, error) {
   180  	bndlModel, err := r.conv.FromEntity(bndlEnt)
   181  	if err != nil {
   182  		return nil, errors.Wrap(err, "while converting Bundle from Entity")
   183  	}
   184  
   185  	return bndlModel, nil
   186  }
   187  
   188  // GetForApplication missing godoc
   189  func (r *pgRepository) GetForApplication(ctx context.Context, tenant string, id string, applicationID string) (*model.Bundle, error) {
   190  	var ent Entity
   191  
   192  	conditions := repo.Conditions{
   193  		repo.NewEqualCondition("id", id),
   194  		repo.NewEqualCondition(appIDColumn, applicationID),
   195  	}
   196  	if err := r.singleGetter.Get(ctx, resource.Bundle, tenant, conditions, repo.NoOrderBy, &ent); err != nil {
   197  		return nil, err
   198  	}
   199  
   200  	bndlModel, err := r.conv.FromEntity(&ent)
   201  	if err != nil {
   202  		return nil, errors.Wrap(err, "while creating Bundle model from entity")
   203  	}
   204  
   205  	return bndlModel, nil
   206  }
   207  
   208  // ListByApplicationIDs missing godoc
   209  func (r *pgRepository) ListByApplicationIDs(ctx context.Context, tenantID string, applicationIDs []string, pageSize int, cursor string) ([]*model.BundlePage, error) {
   210  	var bundleCollection BundleCollection
   211  	counts, err := r.unionLister.List(ctx, resource.Bundle, tenantID, applicationIDs, appIDColumn, pageSize, cursor, orderByColumns, &bundleCollection)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  
   216  	bundleByID := map[string][]*model.Bundle{}
   217  	for _, bundleEnt := range bundleCollection {
   218  		m, err := r.conv.FromEntity(&bundleEnt)
   219  		if err != nil {
   220  			return nil, errors.Wrap(err, "while creating Bundle model from entity")
   221  		}
   222  		applicationID := str.PtrStrToStr(repo.StringPtrFromNullableString(bundleEnt.ApplicationID))
   223  		bundleByID[applicationID] = append(bundleByID[applicationID], m)
   224  	}
   225  
   226  	offset, err := pagination.DecodeOffsetCursor(cursor)
   227  	if err != nil {
   228  		return nil, errors.Wrap(err, "while decoding page cursor")
   229  	}
   230  
   231  	bundlePages := make([]*model.BundlePage, 0, len(applicationIDs))
   232  	for _, appID := range applicationIDs {
   233  		totalCount := counts[appID]
   234  		hasNextPage := false
   235  		endCursor := ""
   236  		if totalCount > offset+len(bundleByID[appID]) {
   237  			hasNextPage = true
   238  			endCursor = pagination.EncodeNextOffsetCursor(offset, pageSize)
   239  		}
   240  
   241  		page := &pagination.Page{
   242  			StartCursor: cursor,
   243  			EndCursor:   endCursor,
   244  			HasNextPage: hasNextPage,
   245  		}
   246  
   247  		bundlePages = append(bundlePages, &model.BundlePage{Data: bundleByID[appID], TotalCount: totalCount, PageInfo: page})
   248  	}
   249  
   250  	return bundlePages, nil
   251  }
   252  
   253  // ListByResourceIDNoPaging lists bundles by resource type are resource ID without paging
   254  func (r *pgRepository) ListByResourceIDNoPaging(ctx context.Context, tenantID, resourceID string, resourceType resource.Type) ([]*model.Bundle, error) {
   255  	var condition repo.Condition
   256  	var err error
   257  	bundleCollection := BundleCollection{}
   258  
   259  	if resourceType == resource.Application {
   260  		condition = repo.NewEqualCondition(appIDColumn, resourceID)
   261  		err = r.lister.ListWithSelectForUpdate(ctx, resource.Bundle, tenantID, &bundleCollection, condition)
   262  	} else {
   263  		condition = repo.NewEqualCondition(appTemplateVersionIDColumn, resourceID)
   264  		err = r.globalLister.ListGlobalWithSelectForUpdate(ctx, &bundleCollection, condition)
   265  	}
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  
   270  	bundles := make([]*model.Bundle, 0, bundleCollection.Len())
   271  	for _, bundle := range bundleCollection {
   272  		bundleModel, err := r.conv.FromEntity(&bundle)
   273  		if err != nil {
   274  			return nil, err
   275  		}
   276  		bundles = append(bundles, bundleModel)
   277  	}
   278  
   279  	return bundles, nil
   280  }
   281  
   282  func (r *pgRepository) ListByDestination(ctx context.Context, tenantID string, destination model.DestinationInput) ([]*model.Bundle, error) {
   283  	bundleCollection := BundleCollection{}
   284  
   285  	var appIDInCondition repo.Condition
   286  	if destination.XSystemTenantID == "" {
   287  		appIDInCondition = repo.NewInConditionForSubQuery(appIDColumn, `
   288  			SELECT id
   289  			FROM public.applications
   290  			WHERE id IN (
   291  				SELECT id
   292  				FROM tenant_applications
   293  				WHERE tenant_id=(SELECT parent FROM business_tenant_mappings WHERE id = ? )
   294  			)
   295  			AND name = ? AND base_url = ?
   296  		`, []interface{}{tenantID, destination.XSystemTenantName, destination.XSystemBaseURL})
   297  	} else {
   298  		appIDInCondition = repo.NewInConditionForSubQuery(appIDColumn, `
   299  			SELECT DISTINCT pa.id as id
   300  			FROM public.applications pa JOIN labels l ON pa.id=l.app_id
   301  			WHERE pa.id IN (
   302  				SELECT id
   303  				FROM tenant_applications
   304  				WHERE tenant_id=(SELECT parent FROM business_tenant_mappings WHERE id = ? )
   305  			)
   306  			AND l.key='applicationType'
   307  			AND l.value ?| array[?]
   308  			AND pa.local_tenant_id = ?
   309  		`, []interface{}{tenantID, destination.XSystemType, destination.XSystemTenantID})
   310  	}
   311  
   312  	conditions := repo.Conditions{
   313  		appIDInCondition,
   314  		repo.NewJSONArrMatchAnyStringCondition(correlationIDs, destination.XCorrelationID),
   315  	}
   316  	err := r.globalLister.ListGlobal(ctx, &bundleCollection, conditions...)
   317  	if err != nil {
   318  		return nil, err
   319  	}
   320  	bundles := make([]*model.Bundle, 0, bundleCollection.Len())
   321  	for _, bundle := range bundleCollection {
   322  		bundleModel, err := r.conv.FromEntity(&bundle)
   323  		if err != nil {
   324  			return nil, errors.Wrap(err, "while creating Bundle model from entity")
   325  		}
   326  		bundles = append(bundles, bundleModel)
   327  	}
   328  	return bundles, nil
   329  }