github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/label/repository.go (about) 1 package label 2 3 import ( 4 "context" 5 6 "github.com/kyma-incubator/compass/components/director/internal/domain/tenant" 7 "github.com/kyma-incubator/compass/components/director/pkg/resource" 8 9 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 10 11 "github.com/kyma-incubator/compass/components/director/internal/repo" 12 13 "github.com/kyma-incubator/compass/components/director/internal/model" 14 "github.com/pkg/errors" 15 ) 16 17 const ( 18 tableName string = "public.labels" 19 tenantColumn string = "tenant_id" 20 keyColumn string = "key" 21 runtimeContextTable string = "public.tenant_runtime_contexts" 22 businessTenantMappingsTable string = `public.business_tenant_mappings` 23 ) 24 25 var ( 26 tableColumns = []string{"id", tenantColumn, "app_id", "runtime_id", "runtime_context_id", "app_template_id", keyColumn, "value", "version"} 27 updatableColumns = []string{"value"} 28 idColumns = []string{"id"} 29 versionedIDColumns = append(idColumns, "version") 30 ) 31 32 // Converter missing godoc 33 // 34 //go:generate mockery --name=Converter --output=automock --outpkg=automock --case=underscore --disable-version-string 35 type Converter interface { 36 ToEntity(in *model.Label) (*Entity, error) 37 FromEntity(in *Entity) (*model.Label, error) 38 } 39 40 type repository struct { 41 lister repo.Lister 42 listerGlobal repo.ListerGlobal 43 deleter repo.Deleter 44 deleterGlobal repo.DeleterGlobal 45 getter repo.SingleGetter 46 getterGlobal repo.SingleGetterGlobal 47 48 runtimeContextQueryBuilder repo.QueryBuilderGlobal 49 businessTenantMappingsQueryBuilder repo.QueryBuilderGlobal 50 51 embeddedTenantLister repo.Lister 52 embeddedTenantDeleter repo.Deleter 53 embeddedTenantGetter repo.SingleGetter 54 55 creator repo.Creator 56 globalCreator repo.CreatorGlobal 57 updater repo.Updater 58 versionedUpdater repo.Updater 59 updaterGlobal repo.UpdaterGlobal 60 embeddedTenantUpdater repo.UpdaterGlobal 61 versionedEmbeddedTenantUpdater repo.UpdaterGlobal 62 conv Converter 63 } 64 65 // NewRepository missing godoc 66 func NewRepository(conv Converter) *repository { 67 return &repository{ 68 lister: repo.NewLister(tableName, tableColumns), 69 listerGlobal: repo.NewListerGlobal(resource.Label, tableName, tableColumns), 70 deleter: repo.NewDeleter(tableName), 71 deleterGlobal: repo.NewDeleterGlobal(resource.Label, tableName), 72 getter: repo.NewSingleGetter(tableName, tableColumns), 73 getterGlobal: repo.NewSingleGetterGlobal(resource.Label, tableName, tableColumns), 74 75 runtimeContextQueryBuilder: repo.NewQueryBuilderGlobal(resource.RuntimeContext, runtimeContextTable, []string{"tenant_id"}), 76 businessTenantMappingsQueryBuilder: repo.NewQueryBuilderGlobal(resource.Tenant, businessTenantMappingsTable, []string{"id"}), 77 78 embeddedTenantLister: repo.NewListerWithEmbeddedTenant(tableName, tenantColumn, tableColumns), 79 embeddedTenantDeleter: repo.NewDeleterWithEmbeddedTenant(tableName, tenantColumn), 80 embeddedTenantGetter: repo.NewSingleGetterWithEmbeddedTenant(tableName, tenantColumn, tableColumns), 81 82 creator: repo.NewCreator(tableName, tableColumns), 83 globalCreator: repo.NewCreatorGlobal(resource.Label, tableName, tableColumns), 84 updater: repo.NewUpdater(tableName, updatableColumns, idColumns), 85 versionedUpdater: repo.NewUpdater(tableName, updatableColumns, versionedIDColumns), 86 updaterGlobal: repo.NewUpdaterGlobal(resource.Label, tableName, updatableColumns, idColumns), 87 embeddedTenantUpdater: repo.NewUpdaterWithEmbeddedTenant(resource.Label, tableName, updatableColumns, tenantColumn, idColumns), 88 versionedEmbeddedTenantUpdater: repo.NewUpdaterWithEmbeddedTenant(resource.Label, tableName, updatableColumns, tenantColumn, versionedIDColumns), 89 conv: conv, 90 } 91 } 92 93 // Upsert missing godoc 94 func (r *repository) Upsert(ctx context.Context, tenant string, label *model.Label) error { 95 if label == nil { 96 return apperrors.NewInternalError("item can not be empty") 97 } 98 99 l, err := r.GetByKey(ctx, tenant, label.ObjectType, label.ObjectID, label.Key) 100 if err != nil { 101 if apperrors.IsNotFoundError(err) { 102 return r.Create(ctx, tenant, label) 103 } 104 return err 105 } 106 107 l.Value = label.Value 108 labelEntity, err := r.conv.ToEntity(l) 109 if err != nil { 110 return errors.Wrap(err, "while creating label entity from model") 111 } 112 if label.ObjectType == model.TenantLabelableObject { 113 return r.embeddedTenantUpdater.UpdateSingleWithVersionGlobal(ctx, labelEntity) 114 } 115 if label.ObjectType == model.AppTemplateLabelableObject { 116 return r.updaterGlobal.UpdateSingleWithVersionGlobal(ctx, labelEntity) 117 } 118 119 return r.updater.UpdateSingleWithVersion(ctx, label.ObjectType.GetResourceType(), tenant, labelEntity) 120 } 121 122 // UpsertGlobal missing godoc 123 func (r *repository) UpsertGlobal(ctx context.Context, label *model.Label) error { 124 if label == nil { 125 return apperrors.NewInternalError("item can not be empty") 126 } 127 128 l, err := r.GetByKeyGlobal(ctx, label.ObjectType, label.ObjectID, label.Key) 129 if err != nil { 130 if apperrors.IsNotFoundError(err) { 131 return r.CreateGlobal(ctx, label) 132 } 133 return err 134 } 135 136 l.Value = label.Value 137 labelEntity, err := r.conv.ToEntity(l) 138 if err != nil { 139 return errors.Wrap(err, "while creating label entity from model") 140 } 141 142 return r.updaterGlobal.UpdateSingleWithVersionGlobal(ctx, labelEntity) 143 } 144 145 // UpdateWithVersion missing godoc 146 func (r *repository) UpdateWithVersion(ctx context.Context, tenant string, label *model.Label) error { 147 if label == nil { 148 return apperrors.NewInternalError("item can not be empty") 149 } 150 labelEntity, err := r.conv.ToEntity(label) 151 if err != nil { 152 return errors.Wrap(err, "while creating label entity from model") 153 } 154 if label.ObjectType == model.TenantLabelableObject { 155 return r.versionedEmbeddedTenantUpdater.UpdateSingleWithVersionGlobal(ctx, labelEntity) 156 } 157 return r.versionedUpdater.UpdateSingleWithVersion(ctx, label.ObjectType.GetResourceType(), tenant, labelEntity) 158 } 159 160 // Create missing godoc 161 func (r *repository) Create(ctx context.Context, tenant string, label *model.Label) error { 162 if label == nil { 163 return apperrors.NewInternalError("item can not be empty") 164 } 165 166 labelEntity, err := r.conv.ToEntity(label) 167 if err != nil { 168 return errors.Wrap(err, "while creating label entity from model") 169 } 170 171 if label.ObjectType == model.TenantLabelableObject || label.ObjectType == model.AppTemplateLabelableObject { 172 return r.globalCreator.Create(ctx, labelEntity) 173 } 174 175 return r.creator.Create(ctx, label.ObjectType.GetResourceType(), tenant, labelEntity) 176 } 177 178 // CreateGlobal missing godoc 179 func (r *repository) CreateGlobal(ctx context.Context, label *model.Label) error { 180 if label == nil { 181 return apperrors.NewInternalError("item can not be empty") 182 } 183 184 labelEntity, err := r.conv.ToEntity(label) 185 if err != nil { 186 return errors.Wrap(err, "while creating label entity from model") 187 } 188 189 return r.globalCreator.Create(ctx, labelEntity) 190 } 191 192 // GetByKey missing godoc 193 func (r *repository) GetByKey(ctx context.Context, tenant string, objectType model.LabelableObject, objectID, key string) (*model.Label, error) { 194 getter := r.getter 195 if objectType == model.TenantLabelableObject { 196 getter = r.embeddedTenantGetter 197 } 198 199 conds := repo.Conditions{repo.NewEqualCondition(keyColumn, key)} 200 if objectType != model.TenantLabelableObject { 201 conds = append(conds, repo.NewEqualCondition(labelableObjectField(objectType), objectID)) 202 } 203 204 var entity Entity 205 206 if objectType == model.AppTemplateLabelableObject { 207 if err := r.getterGlobal.GetGlobal(ctx, conds, repo.NoOrderBy, &entity); err != nil { 208 return nil, err 209 } 210 } else { 211 if err := getter.Get(ctx, objectType.GetResourceType(), tenant, conds, repo.NoOrderBy, &entity); err != nil { 212 return nil, err 213 } 214 } 215 216 labelModel, err := r.conv.FromEntity(&entity) 217 if err != nil { 218 return nil, errors.Wrap(err, "while converting Label entity to model") 219 } 220 221 return labelModel, nil 222 } 223 224 // GetByKeyGlobal missing godoc 225 func (r *repository) GetByKeyGlobal(ctx context.Context, objectType model.LabelableObject, objectID, key string) (*model.Label, error) { 226 conds := repo.Conditions{repo.NewEqualCondition(keyColumn, key)} 227 if objectType != model.TenantLabelableObject { 228 conds = append(conds, repo.NewEqualCondition(labelableObjectField(objectType), objectID)) 229 } 230 231 var entity Entity 232 233 if err := r.getterGlobal.GetGlobal(ctx, conds, repo.NoOrderBy, &entity); err != nil { 234 return nil, err 235 } 236 237 labelModel, err := r.conv.FromEntity(&entity) 238 if err != nil { 239 return nil, errors.Wrap(err, "while converting Label entity to model") 240 } 241 242 return labelModel, nil 243 } 244 245 // ListForGlobalObject missing godoc 246 func (r *repository) ListForGlobalObject(ctx context.Context, objectType model.LabelableObject, objectID string) (map[string]*model.Label, error) { 247 return r.ListForObject(ctx, "", objectType, objectID) 248 } 249 250 // ListForObject missing godoc 251 func (r *repository) ListForObject(ctx context.Context, tenant string, objectType model.LabelableObject, objectID string) (map[string]*model.Label, error) { 252 var entities Collection 253 254 var conditions []repo.Condition 255 256 lister := r.lister 257 if objectType == model.TenantLabelableObject { 258 lister = r.embeddedTenantLister 259 conditions = append(conditions, repo.NewNullCondition(labelableObjectField(model.ApplicationLabelableObject))) 260 conditions = append(conditions, repo.NewNullCondition(labelableObjectField(model.RuntimeContextLabelableObject))) 261 conditions = append(conditions, repo.NewNullCondition(labelableObjectField(model.RuntimeLabelableObject))) 262 conditions = append(conditions, repo.NewNullCondition(labelableObjectField(model.AppTemplateLabelableObject))) 263 } else { 264 conditions = append(conditions, repo.NewEqualCondition(labelableObjectField(objectType), objectID)) 265 } 266 267 if objectType == model.AppTemplateLabelableObject { 268 if err := r.listerGlobal.ListGlobal(ctx, &entities, conditions...); err != nil { 269 return nil, err 270 } 271 } else { 272 if err := lister.List(ctx, objectType.GetResourceType(), tenant, &entities, conditions...); err != nil { 273 return nil, err 274 } 275 } 276 277 labelsMap := make(map[string]*model.Label) 278 279 for _, entity := range entities { 280 m, err := r.conv.FromEntity(&entity) 281 if err != nil { 282 return nil, errors.Wrap(err, "while converting Label entity to model") 283 } 284 285 labelsMap[m.Key] = m 286 } 287 288 return labelsMap, nil 289 } 290 291 // ListForObjectIDs lists all labels for given object IDs 292 func (r *repository) ListForObjectIDs(ctx context.Context, tenant string, objectType model.LabelableObject, objectIDs []string) (map[string]map[string]interface{}, error) { 293 if len(objectIDs) == 0 { 294 return nil, nil 295 } 296 var entities Collection 297 298 conditions := []repo.Condition{repo.NewInConditionForStringValues(labelableObjectField(objectType), objectIDs)} 299 300 lister := r.lister 301 if objectType == model.TenantLabelableObject { 302 lister = r.embeddedTenantLister 303 conditions = append(conditions, repo.NewNullCondition(labelableObjectField(model.ApplicationLabelableObject))) 304 conditions = append(conditions, repo.NewNullCondition(labelableObjectField(model.RuntimeContextLabelableObject))) 305 conditions = append(conditions, repo.NewNullCondition(labelableObjectField(model.RuntimeLabelableObject))) 306 conditions = append(conditions, repo.NewNullCondition(labelableObjectField(model.AppTemplateLabelableObject))) 307 } 308 309 if objectType == model.AppTemplateLabelableObject { 310 if err := r.listerGlobal.ListGlobal(ctx, &entities, conditions...); err != nil { 311 return nil, err 312 } 313 } else { 314 if err := lister.List(ctx, objectType.GetResourceType(), tenant, &entities, conditions...); err != nil { 315 return nil, err 316 } 317 } 318 319 labelsMap := make(map[string]map[string]interface{}, len(entities)) 320 321 for _, entity := range entities { 322 m, err := r.conv.FromEntity(&entity) 323 if err != nil { 324 return nil, errors.Wrap(err, "while converting Label entity to model") 325 } 326 327 labelsForObject, ok := labelsMap[m.ObjectID] 328 if !ok { 329 labelsForObject = make(map[string]interface{}) 330 } 331 labelsForObject[m.Key] = m.Value 332 labelsMap[m.ObjectID] = labelsForObject 333 } 334 335 return labelsMap, nil 336 } 337 338 // ListByKey missing godoc 339 func (r *repository) ListByKey(ctx context.Context, tenant, key string) ([]*model.Label, error) { 340 var entities Collection 341 if err := r.lister.List(ctx, resource.Label, tenant, &entities, repo.NewEqualCondition(keyColumn, key)); err != nil { 342 return nil, err 343 } 344 return r.multipleFromEntity(entities) 345 } 346 347 // ListGlobalByKey lists all labels which are labeled with the provided key across tenants (global) 348 func (r *repository) ListGlobalByKey(ctx context.Context, key string) ([]*model.Label, error) { 349 var entities Collection 350 351 if err := r.listerGlobal.ListGlobal(ctx, &entities, repo.NewEqualCondition(keyColumn, key)); err != nil { 352 return nil, err 353 } 354 355 return r.multipleFromEntity(entities) 356 } 357 358 // ListGlobalByKeyAndObjects lists resources of objectType across tenants (global) which match the given objectIDs and labeled with the provided key 359 func (r *repository) ListGlobalByKeyAndObjects(ctx context.Context, objectType model.LabelableObject, objectIDs []string, key string) ([]*model.Label, error) { 360 var entities Collection 361 if err := r.listerGlobal.ListGlobalWithSelectForUpdate(ctx, &entities, repo.NewEqualCondition(keyColumn, key), repo.NewInConditionForStringValues(labelableObjectField(objectType), objectIDs)); err != nil { 362 return nil, err 363 } 364 return r.multipleFromEntity(entities) 365 } 366 367 // Delete missing godoc 368 func (r *repository) Delete(ctx context.Context, tenant string, objectType model.LabelableObject, objectID string, key string) error { 369 deleter := r.deleter 370 conds := repo.Conditions{repo.NewEqualCondition(keyColumn, key)} 371 if objectType == model.TenantLabelableObject { 372 deleter = r.embeddedTenantDeleter 373 } else { 374 conds = append(conds, repo.NewEqualCondition(labelableObjectField(objectType), objectID)) 375 } 376 if objectType == model.AppTemplateLabelableObject { 377 return r.deleterGlobal.DeleteManyGlobal(ctx, conds) 378 } 379 return deleter.DeleteMany(ctx, objectType.GetResourceType(), tenant, conds) 380 } 381 382 // DeleteAll missing godoc 383 func (r *repository) DeleteAll(ctx context.Context, tenant string, objectType model.LabelableObject, objectID string) error { 384 deleter := r.deleter 385 conds := repo.Conditions{} 386 if objectType == model.TenantLabelableObject { 387 deleter = r.embeddedTenantDeleter 388 } else { 389 conds = append(conds, repo.NewEqualCondition(labelableObjectField(objectType), objectID)) 390 } 391 if objectType == model.AppTemplateLabelableObject { 392 return r.deleterGlobal.DeleteManyGlobal(ctx, conds) 393 } 394 return deleter.DeleteMany(ctx, objectType.GetResourceType(), tenant, conds) 395 } 396 397 // DeleteByKeyNegationPattern missing godoc 398 func (r *repository) DeleteByKeyNegationPattern(ctx context.Context, tenant string, objectType model.LabelableObject, objectID string, labelKeyPattern string) error { 399 deleter := r.deleter 400 conds := repo.Conditions{repo.NewNotRegexConditionString(keyColumn, labelKeyPattern)} 401 if objectType == model.TenantLabelableObject { 402 deleter = r.embeddedTenantDeleter 403 } else { 404 conds = append(conds, repo.NewEqualCondition(labelableObjectField(objectType), objectID)) 405 } 406 if objectType == model.AppTemplateLabelableObject { 407 return r.deleterGlobal.DeleteManyGlobal(ctx, conds) 408 } 409 return deleter.DeleteMany(ctx, objectType.GetResourceType(), tenant, conds) 410 } 411 412 // DeleteByKey missing godoc 413 func (r *repository) DeleteByKey(ctx context.Context, tenant string, key string) error { 414 return r.deleter.DeleteMany(ctx, resource.Label, tenant, repo.Conditions{repo.NewEqualCondition(keyColumn, key)}) 415 } 416 417 // GetScenarioLabelsForRuntimes missing godoc 418 func (r *repository) GetScenarioLabelsForRuntimes(ctx context.Context, tenantID string, runtimesIDs []string) ([]model.Label, error) { 419 if len(runtimesIDs) == 0 { 420 return nil, apperrors.NewInvalidDataError("cannot execute query without runtimeIDs") 421 } 422 423 conditions := repo.Conditions{ 424 repo.NewEqualCondition(keyColumn, model.ScenariosKey), 425 repo.NewInConditionForStringValues("runtime_id", runtimesIDs), 426 } 427 428 var labels Collection 429 err := r.lister.List(ctx, resource.RuntimeLabel, tenantID, &labels, conditions...) 430 if err != nil { 431 return nil, errors.Wrap(err, "while fetching runtimes scenarios") 432 } 433 434 labelModels := make([]model.Label, 0, len(labels)) 435 for _, label := range labels { 436 labelModel, err := r.conv.FromEntity(&label) 437 if err != nil { 438 return nil, errors.Wrap(err, "while converting label entity to model") 439 } 440 labelModels = append(labelModels, *labelModel) 441 } 442 return labelModels, nil 443 } 444 445 func (r *repository) GetSubdomainLabelForSubscribedRuntime(ctx context.Context, tenantID string) (*model.Label, error) { 446 conds := repo.Conditions{ 447 repo.NewEqualCondition(keyColumn, tenant.SubdomainLabelKey), 448 repo.NewInConditionForSubQuery(tenantColumn, 449 "SELECT DISTINCT tenant_id FROM tenant_runtime_contexts WHERE tenant_id=?", []interface{}{tenantID}), 450 } 451 452 var entity Entity 453 err := r.embeddedTenantGetter.Get( 454 ctx, model.TenantLabelableObject.GetResourceType(), tenantID, conds, repo.NoOrderBy, &entity) 455 if err != nil { 456 return nil, err 457 } 458 459 labelModel, err := r.conv.FromEntity(&entity) 460 if err != nil { 461 return nil, errors.Wrap(err, "while converting Label entity to model") 462 } 463 464 return labelModel, nil 465 } 466 467 func (r *repository) multipleFromEntity(entities []Entity) ([]*model.Label, error) { 468 labels := make([]*model.Label, 0, len(entities)) 469 470 for _, entity := range entities { 471 m, err := r.conv.FromEntity(&entity) 472 if err != nil { 473 return nil, errors.Wrap(err, "while converting Label entity to model") 474 } 475 476 labels = append(labels, m) 477 } 478 479 return labels, nil 480 } 481 482 func labelableObjectField(objectType model.LabelableObject) string { 483 switch objectType { 484 case model.ApplicationLabelableObject: 485 return "app_id" 486 case model.RuntimeLabelableObject: 487 return "runtime_id" 488 case model.RuntimeContextLabelableObject: 489 return "runtime_context_id" 490 case model.TenantLabelableObject: 491 return "tenant_id" 492 case model.AppTemplateLabelableObject: 493 return "app_template_id" 494 } 495 496 return "" 497 }