github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/runtime/repository.go (about) 1 package runtime 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 8 "github.com/kyma-incubator/compass/components/director/pkg/resource" 9 10 "github.com/google/uuid" 11 "github.com/kyma-incubator/compass/components/director/internal/domain/label" 12 "github.com/kyma-incubator/compass/components/director/internal/repo" 13 "github.com/pkg/errors" 14 15 "github.com/kyma-incubator/compass/components/director/internal/labelfilter" 16 "github.com/kyma-incubator/compass/components/director/internal/model" 17 ) 18 19 const runtimeTable string = `public.runtimes` 20 21 var ( 22 runtimeColumns = []string{"id", "name", "description", "status_condition", "status_timestamp", "creation_timestamp", "application_namespace"} 23 updatableColumns = []string{"name", "description", "status_condition", "status_timestamp", "application_namespace"} 24 ) 25 26 // EntityConverter missing godoc 27 //go:generate mockery --name=EntityConverter --output=automock --outpkg=automock --case=underscore --disable-version-string 28 type EntityConverter interface { 29 ToEntity(in *model.Runtime) (*Runtime, error) 30 FromEntity(entity *Runtime) *model.Runtime 31 } 32 33 type pgRepository struct { 34 existQuerier repo.ExistQuerier 35 ownerExistQuerier repo.ExistQuerier 36 singleGetter repo.SingleGetter 37 singleGetterGlobal repo.SingleGetterGlobal 38 deleter repo.Deleter 39 pageableQuerier repo.PageableQuerier 40 lister repo.Lister 41 ownerLister repo.Lister 42 listerGlobal repo.ListerGlobal 43 creator repo.Creator 44 updater repo.Updater 45 conv EntityConverter 46 } 47 48 // NewRepository missing godoc 49 func NewRepository(conv EntityConverter) *pgRepository { 50 return &pgRepository{ 51 existQuerier: repo.NewExistQuerier(runtimeTable), 52 ownerExistQuerier: repo.NewExistQuerierWithOwnerCheck(runtimeTable), 53 singleGetter: repo.NewSingleGetter(runtimeTable, runtimeColumns), 54 singleGetterGlobal: repo.NewSingleGetterGlobal(resource.Runtime, runtimeTable, runtimeColumns), 55 deleter: repo.NewDeleter(runtimeTable), 56 pageableQuerier: repo.NewPageableQuerier(runtimeTable, runtimeColumns), 57 lister: repo.NewLister(runtimeTable, runtimeColumns), 58 ownerLister: repo.NewOwnerLister(runtimeTable, runtimeColumns, true), 59 listerGlobal: repo.NewListerGlobal(resource.Runtime, runtimeTable, runtimeColumns), 60 creator: repo.NewCreator(runtimeTable, runtimeColumns), 61 updater: repo.NewUpdater(runtimeTable, updatableColumns, []string{"id"}), 62 conv: conv, 63 } 64 } 65 66 // Exists missing godoc 67 func (r *pgRepository) Exists(ctx context.Context, tenant, id string) (bool, error) { 68 return r.existQuerier.Exists(ctx, resource.Runtime, tenant, repo.Conditions{repo.NewEqualCondition("id", id)}) 69 } 70 71 // OwnerExists checks if runtime with given id and tenant exists and has owner access 72 func (r *pgRepository) OwnerExists(ctx context.Context, tenant, id string) (bool, error) { 73 return r.ownerExistQuerier.Exists(ctx, resource.Runtime, tenant, repo.Conditions{repo.NewEqualCondition("id", id)}) 74 } 75 76 // OwnerExistsByFiltersAndID checks if runtime with given id and filters in given tenant exists and has owner access. The results from the filter subqueries are combined using UNION 77 func (r *pgRepository) OwnerExistsByFiltersAndID(ctx context.Context, tenant, id string, filter []*labelfilter.LabelFilter) (bool, error) { 78 tenantID, err := uuid.Parse(tenant) 79 if err != nil { 80 return false, errors.Wrap(err, "while parsing tenant as UUID") 81 } 82 83 additionalConditions := repo.Conditions{repo.NewEqualCondition("id", id)} 84 85 filterSubquery, args, err := label.FilterQuery(model.RuntimeLabelableObject, label.UnionSet, tenantID, filter) 86 if err != nil { 87 return false, errors.Wrap(err, "while building filter query") 88 } 89 if filterSubquery != "" { 90 additionalConditions = append(additionalConditions, repo.NewInConditionForSubQuery("id", filterSubquery, args)) 91 } 92 93 return r.ownerExistQuerier.Exists(ctx, resource.Runtime, tenant, additionalConditions) 94 } 95 96 // Delete missing godoc 97 func (r *pgRepository) Delete(ctx context.Context, tenant string, id string) error { 98 return r.deleter.DeleteOne(ctx, resource.Runtime, tenant, repo.Conditions{repo.NewEqualCondition("id", id)}) 99 } 100 101 // GetByID missing godoc 102 func (r *pgRepository) GetByID(ctx context.Context, tenant, id string) (*model.Runtime, error) { 103 var runtimeEnt Runtime 104 if err := r.singleGetter.Get(ctx, resource.Runtime, tenant, repo.Conditions{repo.NewEqualCondition("id", id)}, repo.NoOrderBy, &runtimeEnt); err != nil { 105 return nil, err 106 } 107 108 runtimeModel := r.conv.FromEntity(&runtimeEnt) 109 110 return runtimeModel, nil 111 } 112 113 // GetByFiltersAndID missing godoc 114 func (r *pgRepository) GetByFiltersAndID(ctx context.Context, tenant, id string, filter []*labelfilter.LabelFilter) (*model.Runtime, error) { 115 tenantID, err := uuid.Parse(tenant) 116 if err != nil { 117 return nil, errors.Wrap(err, "while parsing tenant as UUID") 118 } 119 120 additionalConditions := repo.Conditions{repo.NewEqualCondition("id", id)} 121 122 filterSubquery, args, err := label.FilterQuery(model.RuntimeLabelableObject, label.IntersectSet, tenantID, filter) 123 if err != nil { 124 return nil, errors.Wrap(err, "while building filter query") 125 } 126 if filterSubquery != "" { 127 additionalConditions = append(additionalConditions, repo.NewInConditionForSubQuery("id", filterSubquery, args)) 128 } 129 130 var runtimeEnt Runtime 131 if err := r.singleGetter.Get(ctx, resource.Runtime, tenant, additionalConditions, repo.NoOrderBy, &runtimeEnt); err != nil { 132 return nil, err 133 } 134 135 runtimeModel := r.conv.FromEntity(&runtimeEnt) 136 137 return runtimeModel, nil 138 } 139 140 // GetByFiltersAndIDUsingUnion retrieves runtime by its ID if it matches the provided filters. The results from the filter subqueries are combined sing UNION 141 func (r *pgRepository) GetByFiltersAndIDUsingUnion(ctx context.Context, tenant, id string, filter []*labelfilter.LabelFilter) (*model.Runtime, error) { 142 tenantID, err := uuid.Parse(tenant) 143 if err != nil { 144 return nil, errors.Wrap(err, "while parsing tenant as UUID") 145 } 146 147 additionalConditions := repo.Conditions{repo.NewEqualCondition("id", id)} 148 149 filterSubquery, args, err := label.FilterQuery(model.RuntimeLabelableObject, label.UnionSet, tenantID, filter) 150 if err != nil { 151 return nil, errors.Wrap(err, "while building filter query") 152 } 153 if filterSubquery != "" { 154 additionalConditions = append(additionalConditions, repo.NewInConditionForSubQuery("id", filterSubquery, args)) 155 } 156 157 var runtimeEnt Runtime 158 if err := r.singleGetter.Get(ctx, resource.Runtime, tenant, additionalConditions, repo.NoOrderBy, &runtimeEnt); err != nil { 159 return nil, err 160 } 161 162 runtimeModel := r.conv.FromEntity(&runtimeEnt) 163 164 return runtimeModel, nil 165 } 166 167 // GetByFilters retrieves model.Runtime matching on the given label filters 168 func (r *pgRepository) GetByFilters(ctx context.Context, tenant string, filter []*labelfilter.LabelFilter) (*model.Runtime, error) { 169 var runtimeEnt Runtime 170 171 tenantID, err := uuid.Parse(tenant) 172 if err != nil { 173 return nil, errors.Wrap(err, "while parsing tenant as UUID") 174 } 175 176 filterSubquery, args, err := label.FilterQuery(model.RuntimeLabelableObject, label.IntersectSet, tenantID, filter) 177 if err != nil { 178 return nil, errors.Wrap(err, "while building filter query") 179 } 180 181 var conditions repo.Conditions 182 if filterSubquery != "" { 183 conditions = append(conditions, repo.NewInConditionForSubQuery("id", filterSubquery, args)) 184 } 185 186 if err = r.singleGetter.Get(ctx, resource.Runtime, tenant, conditions, repo.NoOrderBy, &runtimeEnt); err != nil { 187 return nil, err 188 } 189 190 appModel := r.conv.FromEntity(&runtimeEnt) 191 192 return appModel, nil 193 } 194 195 // GetByFiltersGlobal missing godoc 196 func (r *pgRepository) GetByFiltersGlobal(ctx context.Context, filter []*labelfilter.LabelFilter) (*model.Runtime, error) { 197 filterSubquery, args, err := label.FilterQueryGlobal(model.RuntimeLabelableObject, label.IntersectSet, filter) 198 if err != nil { 199 return nil, errors.Wrap(err, "while building filter query") 200 } 201 202 var additionalConditions repo.Conditions 203 if filterSubquery != "" { 204 additionalConditions = append(additionalConditions, repo.NewInConditionForSubQuery("id", filterSubquery, args)) 205 } 206 207 var runtimeEnt Runtime 208 if err := r.singleGetterGlobal.GetGlobal(ctx, additionalConditions, repo.NoOrderBy, &runtimeEnt); err != nil { 209 return nil, err 210 } 211 212 runtimeModel := r.conv.FromEntity(&runtimeEnt) 213 214 return runtimeModel, nil 215 } 216 217 // ListByFiltersGlobal missing godoc 218 func (r *pgRepository) ListByFiltersGlobal(ctx context.Context, filters []*labelfilter.LabelFilter) ([]*model.Runtime, error) { 219 filterSubquery, args, err := label.FilterQueryGlobal(model.RuntimeLabelableObject, label.IntersectSet, filters) 220 if err != nil { 221 return nil, errors.Wrap(err, "while building filter query") 222 } 223 224 var additionalConditions repo.Conditions 225 if filterSubquery != "" { 226 additionalConditions = append(additionalConditions, repo.NewInConditionForSubQuery("id", filterSubquery, args)) 227 } 228 229 var entities RuntimeCollection 230 if err := r.listerGlobal.ListGlobal(ctx, &entities, additionalConditions...); err != nil { 231 return nil, err 232 } 233 234 return r.multipleFromEntities(entities), nil 235 } 236 237 // ListAll returns all runtimes in a tenant that match given label filter and owner check to false. The results from the separate filters are combined using 'INTERSECT'. 238 func (r *pgRepository) ListAll(ctx context.Context, tenant string, filter []*labelfilter.LabelFilter) ([]*model.Runtime, error) { 239 return r.listRuntimes(ctx, tenant, filter, label.IntersectSet, false) 240 } 241 242 // ListAllWithUnionSetCombination returns all runtimes in a tenant that match given label filter and owner check to false. The results from the separate filters are combined using 'UNION'. 243 func (r *pgRepository) ListAllWithUnionSetCombination(ctx context.Context, tenant string, filter []*labelfilter.LabelFilter) ([]*model.Runtime, error) { 244 return r.listRuntimes(ctx, tenant, filter, label.UnionSet, false) 245 } 246 247 // ListOwnedRuntimes returns all runtimes in a tenant that match given label filter and owner check to true, The results from the separate filters are combined using 'UNION'. 248 func (r *pgRepository) ListOwnedRuntimes(ctx context.Context, tenant string, filter []*labelfilter.LabelFilter) ([]*model.Runtime, error) { 249 return r.listRuntimes(ctx, tenant, filter, label.UnionSet, true) 250 } 251 252 // RuntimeCollection missing godoc 253 type RuntimeCollection []Runtime 254 255 // Len missing godoc 256 func (r RuntimeCollection) Len() int { 257 return len(r) 258 } 259 260 // List missing godoc 261 func (r *pgRepository) List(ctx context.Context, tenant string, filter []*labelfilter.LabelFilter, pageSize int, cursor string) (*model.RuntimePage, error) { 262 var runtimesCollection RuntimeCollection 263 tenantID, err := uuid.Parse(tenant) 264 if err != nil { 265 return nil, errors.Wrap(err, "while parsing tenant as UUID") 266 } 267 filterSubquery, args, err := label.FilterQuery(model.RuntimeLabelableObject, label.IntersectSet, tenantID, filter) 268 if err != nil { 269 return nil, errors.Wrap(err, "while building filter query") 270 } 271 272 var conditions repo.Conditions 273 if filterSubquery != "" { 274 conditions = append(conditions, repo.NewInConditionForSubQuery("id", filterSubquery, args)) 275 } 276 277 page, totalCount, err := r.pageableQuerier.List(ctx, resource.Runtime, tenant, pageSize, cursor, "name", &runtimesCollection, conditions...) 278 279 if err != nil { 280 return nil, err 281 } 282 283 items := r.multipleFromEntities(runtimesCollection) 284 285 return &model.RuntimePage{ 286 Data: items, 287 TotalCount: totalCount, 288 PageInfo: page}, nil 289 } 290 291 // Create missing godoc 292 func (r *pgRepository) Create(ctx context.Context, tenant string, item *model.Runtime) error { 293 if item == nil { 294 return apperrors.NewInternalError("item can not be empty") 295 } 296 297 runtimeEnt, err := r.conv.ToEntity(item) 298 if err != nil { 299 return errors.Wrap(err, "while creating runtime entity from model") 300 } 301 302 return r.creator.Create(ctx, resource.Runtime, tenant, runtimeEnt) 303 } 304 305 // Update missing godoc 306 func (r *pgRepository) Update(ctx context.Context, tenant string, item *model.Runtime) error { 307 if item == nil { 308 return apperrors.NewInternalError("item cannot be nil") 309 } 310 runtimeEnt, err := r.conv.ToEntity(item) 311 if err != nil { 312 return errors.Wrap(err, "while creating runtime entity from model") 313 } 314 return r.updater.UpdateSingle(ctx, resource.Runtime, tenant, runtimeEnt) 315 } 316 317 // GetOldestForFilters missing godoc 318 func (r *pgRepository) GetOldestForFilters(ctx context.Context, tenant string, filter []*labelfilter.LabelFilter) (*model.Runtime, error) { 319 tenantID, err := uuid.Parse(tenant) 320 if err != nil { 321 return nil, errors.Wrap(err, "while parsing tenant as UUID") 322 } 323 324 var additionalConditions repo.Conditions 325 filterSubquery, args, err := label.FilterQuery(model.RuntimeLabelableObject, label.IntersectSet, tenantID, filter) 326 if err != nil { 327 return nil, errors.Wrap(err, "while building filter query") 328 } 329 if filterSubquery != "" { 330 additionalConditions = append(additionalConditions, repo.NewInConditionForSubQuery("id", filterSubquery, args)) 331 } 332 333 orderByParams := repo.OrderByParams{repo.NewAscOrderBy("creation_timestamp")} 334 335 var runtimeEnt Runtime 336 if err := r.singleGetter.Get(ctx, resource.Runtime, tenant, additionalConditions, orderByParams, &runtimeEnt); err != nil { 337 return nil, err 338 } 339 340 runtimeModel := r.conv.FromEntity(&runtimeEnt) 341 342 return runtimeModel, nil 343 } 344 345 // ListByScenariosAndIDs lists all runtimes with given IDs that are in any of the given scenarios 346 func (r *pgRepository) ListByScenariosAndIDs(ctx context.Context, tenant string, scenarios []string, ids []string) ([]*model.Runtime, error) { 347 if len(scenarios) == 0 || len(ids) == 0 { 348 return nil, nil 349 } 350 tenantUUID, err := uuid.Parse(tenant) 351 if err != nil { 352 return nil, apperrors.NewInvalidDataError("tenantID is not UUID") 353 } 354 355 var entities RuntimeCollection 356 357 // Scenarios query part 358 scenariosFilters := make([]*labelfilter.LabelFilter, 0, len(scenarios)) 359 for _, scenarioValue := range scenarios { 360 query := fmt.Sprintf(`$[*] ? (@ == "%s")`, scenarioValue) 361 scenariosFilters = append(scenariosFilters, labelfilter.NewForKeyWithQuery(model.ScenariosKey, query)) 362 } 363 364 scenariosSubquery, scenariosArgs, err := label.FilterQuery(model.RuntimeLabelableObject, label.UnionSet, tenantUUID, scenariosFilters) 365 if err != nil { 366 return nil, errors.Wrap(err, "while creating scenarios filter query") 367 } 368 369 var conditions repo.Conditions 370 if scenariosSubquery != "" { 371 conditions = append(conditions, repo.NewInConditionForSubQuery("id", scenariosSubquery, scenariosArgs)) 372 } 373 374 conditions = append(conditions, repo.NewInConditionForStringValues("id", ids)) 375 376 if err := r.lister.List(ctx, resource.Runtime, tenant, &entities, conditions...); err != nil { 377 return nil, err 378 } 379 380 return r.multipleFromEntities(entities), nil 381 } 382 383 // ListByScenarios lists all runtimes with given IDs that are in any of the given scenarios 384 func (r *pgRepository) ListByScenarios(ctx context.Context, tenant string, scenarios []string) ([]*model.Runtime, error) { 385 if len(scenarios) == 0 { 386 return nil, nil 387 } 388 tenantUUID, err := uuid.Parse(tenant) 389 if err != nil { 390 return nil, apperrors.NewInvalidDataError("tenantID is not UUID") 391 } 392 393 var entities RuntimeCollection 394 395 // Scenarios query part 396 scenariosFilters := make([]*labelfilter.LabelFilter, 0, len(scenarios)) 397 for _, scenarioValue := range scenarios { 398 query := fmt.Sprintf(`$[*] ? (@ == "%s")`, scenarioValue) 399 scenariosFilters = append(scenariosFilters, labelfilter.NewForKeyWithQuery(model.ScenariosKey, query)) 400 } 401 402 scenariosSubquery, scenariosArgs, err := label.FilterQuery(model.RuntimeLabelableObject, label.UnionSet, tenantUUID, scenariosFilters) 403 if err != nil { 404 return nil, errors.Wrap(err, "while creating scenarios filter query") 405 } 406 407 var conditions repo.Conditions 408 if scenariosSubquery != "" { 409 conditions = append(conditions, repo.NewInConditionForSubQuery("id", scenariosSubquery, scenariosArgs)) 410 } 411 412 if err := r.lister.List(ctx, resource.Runtime, tenant, &entities, conditions...); err != nil { 413 return nil, err 414 } 415 416 return r.multipleFromEntities(entities), nil 417 } 418 419 // ListByIDs lists all runtimes with given IDs 420 func (r *pgRepository) ListByIDs(ctx context.Context, tenant string, ids []string) ([]*model.Runtime, error) { 421 if len(ids) == 0 { 422 return nil, nil 423 } 424 var entities RuntimeCollection 425 426 if err := r.lister.List(ctx, resource.Runtime, tenant, &entities, repo.NewInConditionForStringValues("id", ids)); err != nil { 427 return nil, err 428 } 429 430 return r.multipleFromEntities(entities), nil 431 } 432 433 func (r *pgRepository) listRuntimes(ctx context.Context, tenant string, filter []*labelfilter.LabelFilter, setCombination label.SetCombination, ownerCheck bool) ([]*model.Runtime, error) { 434 var entities RuntimeCollection 435 436 tenantID, err := uuid.Parse(tenant) 437 if err != nil { 438 return nil, errors.Wrap(err, "while parsing tenant as UUID") 439 } 440 441 filterSubquery, args, err := label.FilterQuery(model.RuntimeLabelableObject, setCombination, tenantID, filter) 442 if err != nil { 443 return nil, errors.Wrap(err, "while building filter query") 444 } 445 446 var conditions repo.Conditions 447 if filterSubquery != "" { 448 conditions = append(conditions, repo.NewInConditionForSubQuery("id", filterSubquery, args)) 449 } 450 451 if ownerCheck { 452 if err = r.ownerLister.List(ctx, resource.Runtime, tenant, &entities, conditions...); err != nil { 453 return nil, err 454 } 455 } else { 456 if err = r.lister.List(ctx, resource.Runtime, tenant, &entities, conditions...); err != nil { 457 return nil, err 458 } 459 } 460 461 return r.multipleFromEntities(entities), nil 462 } 463 464 func (r *pgRepository) multipleFromEntities(entities RuntimeCollection) []*model.Runtime { 465 items := make([]*model.Runtime, 0, len(entities)) 466 for _, ent := range entities { 467 model := r.conv.FromEntity(&ent) 468 469 items = append(items, model) 470 } 471 return items 472 }