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 }