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 }