github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/bundlereferences/repository.go (about) 1 package bundlereferences 2 3 import ( 4 "context" 5 6 "github.com/kyma-incubator/compass/components/director/pkg/log" 7 8 "github.com/kyma-incubator/compass/components/director/pkg/scope" 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/apperrors" 13 "github.com/kyma-incubator/compass/components/director/pkg/resource" 14 ) 15 16 const ( 17 // BundleReferenceTable represents the db name of the BundleReference table 18 BundleReferenceTable string = `public.bundle_references` 19 // APIDefTable represents the db name of the API Definitions table 20 APIDefTable string = `api_definitions` 21 // EventDefTable represents the db name of the Event Definitions table 22 EventDefTable string = `event_api_definitions` 23 24 // APIDefIDColumn represents the db column of the APIDefinition ID 25 APIDefIDColumn string = "api_def_id" 26 // APIDefURLColumn represents the db column of the APIDefinition default url 27 APIDefURLColumn string = "api_def_url" 28 // EventDefIDColumn represents the db column of the EventDefinition ID 29 EventDefIDColumn string = "event_def_id" 30 31 bundleIDColumn string = "bundle_id" 32 visibilityColumn string = "visibility" 33 internalVisibilityScope string = "internal_visibility:read" 34 publicVisibilityValue string = "public" 35 ) 36 37 var ( 38 bundleReferencesColumns = []string{"api_def_id", "event_def_id", "bundle_id", "api_def_url", "id", "is_default_bundle"} 39 updatableColumns = []string{"api_def_id", "event_def_id", "bundle_id", "api_def_url", "is_default_bundle"} 40 updatableColumnsWithoutBundleID = []string{"api_def_id", "event_def_id", "api_def_url"} 41 ) 42 43 // BundleReferenceConverter converts BundleReferences between the model.BundleReference service-layer representation and the repo-layer representation Entity. 44 //go:generate mockery --name=BundleReferenceConverter --output=automock --outpkg=automock --case=underscore --disable-version-string 45 type BundleReferenceConverter interface { 46 ToEntity(in model.BundleReference) Entity 47 FromEntity(in Entity) (model.BundleReference, error) 48 } 49 50 type repository struct { 51 creator repo.CreatorGlobal 52 unionLister repo.UnionListerGlobal 53 lister repo.ListerGlobal 54 getter repo.SingleGetterGlobal 55 deleter repo.DeleterGlobal 56 updater repo.UpdaterGlobal 57 queryBuilderAPIs repo.QueryBuilderGlobal 58 queryBuilderEvents repo.QueryBuilderGlobal 59 conv BundleReferenceConverter 60 } 61 62 // NewRepository returns a new entity responsible for repo-layer BundleReference operations. 63 func NewRepository(conv BundleReferenceConverter) *repository { 64 return &repository{ 65 creator: repo.NewCreatorGlobal(resource.BundleReference, BundleReferenceTable, bundleReferencesColumns), 66 unionLister: repo.NewUnionListerGlobal(resource.BundleReference, BundleReferenceTable, []string{}), 67 lister: repo.NewListerGlobal(resource.BundleReference, BundleReferenceTable, bundleReferencesColumns), 68 getter: repo.NewSingleGetterGlobal(resource.BundleReference, BundleReferenceTable, bundleReferencesColumns), 69 deleter: repo.NewDeleterGlobal(resource.BundleReference, BundleReferenceTable), 70 updater: repo.NewUpdaterGlobal(resource.BundleReference, BundleReferenceTable, updatableColumns, []string{}), 71 queryBuilderAPIs: repo.NewQueryBuilderGlobal(resource.API, APIDefTable, []string{"id"}), 72 queryBuilderEvents: repo.NewQueryBuilderGlobal(resource.EventDefinition, EventDefTable, []string{"id"}), 73 conv: conv, 74 } 75 } 76 77 // BundleReferencesCollection is an array of Entities 78 type BundleReferencesCollection []Entity 79 80 // Len returns the length of the collection 81 func (r BundleReferencesCollection) Len() int { 82 return len(r) 83 } 84 85 // GetByID retrieves the BundleReference with matching objectID/objectID and bundleID from the Compass storage. 86 func (r *repository) GetByID(ctx context.Context, objectType model.BundleReferenceObjectType, objectID, bundleID *string) (*model.BundleReference, error) { 87 fieldName, err := r.referenceObjectFieldName(objectType) 88 if err != nil { 89 return nil, err 90 } 91 92 var bundleReferenceEntity Entity 93 var conditions repo.Conditions 94 95 if bundleID == nil { 96 conditions = repo.Conditions{repo.NewEqualCondition(fieldName, objectID)} 97 } else { 98 conditions = repo.Conditions{ 99 repo.NewEqualCondition(fieldName, objectID), 100 repo.NewEqualCondition(bundleIDColumn, bundleID), 101 } 102 } 103 err = r.getter.GetGlobal(ctx, conditions, repo.NoOrderBy, &bundleReferenceEntity) 104 if err != nil { 105 return nil, err 106 } 107 108 bundleReferenceModel, err := r.conv.FromEntity(bundleReferenceEntity) 109 if err != nil { 110 return nil, err 111 } 112 113 return &bundleReferenceModel, nil 114 } 115 116 // GetBundleIDsForObject retrieves all BundleReference IDs for matching objectID from the Compass storage. 117 func (r *repository) GetBundleIDsForObject(ctx context.Context, objectType model.BundleReferenceObjectType, objectID *string) (ids []string, err error) { 118 fieldName, err := r.referenceObjectFieldName(objectType) 119 if err != nil { 120 return nil, err 121 } 122 123 var objectBundleIDs IDs 124 125 lister := r.lister.Clone() 126 lister.SetSelectedColumns([]string{bundleIDColumn}) 127 128 conditions := repo.Conditions{ 129 repo.NewEqualCondition(fieldName, objectID), 130 } 131 132 err = lister.ListGlobal(ctx, &objectBundleIDs, conditions...) 133 if err != nil { 134 return nil, err 135 } 136 137 return objectBundleIDs, nil 138 } 139 140 // Create adds the provided BundleReference into the Compass storage. 141 func (r *repository) Create(ctx context.Context, item *model.BundleReference) error { 142 if item == nil { 143 return apperrors.NewInternalError("item can not be empty") 144 } 145 146 entity := r.conv.ToEntity(*item) 147 148 return r.creator.Create(ctx, entity) 149 } 150 151 // Update updates the provided BundleReference. 152 func (r *repository) Update(ctx context.Context, item *model.BundleReference) error { 153 if item == nil { 154 return apperrors.NewInternalError("item cannot be nil") 155 } 156 157 fieldName, err := r.referenceObjectFieldName(item.ObjectType) 158 if err != nil { 159 return err 160 } 161 162 updater := r.updater.Clone() 163 164 idColumns := make([]string, 0) 165 if item.BundleID == nil { 166 idColumns = append(idColumns, fieldName) 167 updater.SetUpdatableColumns(updatableColumnsWithoutBundleID) 168 } else { 169 idColumns = append(idColumns, fieldName, bundleIDColumn) 170 } 171 172 updater.SetIDColumns(idColumns) 173 174 entity := r.conv.ToEntity(*item) 175 176 return updater.UpdateSingleGlobal(ctx, entity) 177 } 178 179 // DeleteByReferenceObjectID removes a BundleReference with matching objectID and bundleID from the Compass storage. 180 func (r *repository) DeleteByReferenceObjectID(ctx context.Context, bundleID string, objectType model.BundleReferenceObjectType, objectID string) error { 181 fieldName, err := r.referenceObjectFieldName(objectType) 182 if err != nil { 183 return err 184 } 185 186 conditions := repo.Conditions{ 187 repo.NewEqualCondition(fieldName, objectID), 188 repo.NewEqualCondition(bundleIDColumn, bundleID), 189 } 190 191 return r.deleter.DeleteOneGlobal(ctx, conditions) 192 } 193 194 // ListByBundleIDs retrieves all BundleReferences matching an array of bundleIDs from the Compass storage. 195 func (r *repository) ListByBundleIDs(ctx context.Context, objectType model.BundleReferenceObjectType, bundleIDs []string, pageSize int, cursor string) ([]*model.BundleReference, map[string]int, error) { 196 objectTable, objectIDCol, columns, err := getDetailsByObjectType(objectType) 197 if err != nil { 198 return nil, nil, err 199 } 200 201 unionLister := r.unionLister.Clone() 202 unionLister.SetSelectedColumns(columns) 203 204 objectFieldName, err := r.referenceObjectFieldName(objectType) 205 if err != nil { 206 return nil, nil, err 207 } 208 209 isInternalVisibilityScopePresent, err := scope.Contains(ctx, internalVisibilityScope) 210 if err != nil { 211 log.C(ctx).Infof("No scopes are present in the context meaning the flow is not user-initiated. Processing %ss without visibility check...", objectType) 212 isInternalVisibilityScopePresent = true 213 } 214 215 queryBuilder := r.queryBuilderAPIs 216 if objectTable == EventDefTable { 217 queryBuilder = r.queryBuilderEvents 218 } 219 220 var conditions repo.Conditions 221 if !isInternalVisibilityScopePresent { 222 log.C(ctx).Infof("No internal visibility scope is present in the context. Processing only public %ss...", objectType) 223 224 query, args, err := queryBuilder.BuildQueryGlobal(false, repo.NewEqualCondition(visibilityColumn, publicVisibilityValue)) 225 if err != nil { 226 return nil, nil, err 227 } 228 conditions = append(conditions, repo.NewInConditionForSubQuery(objectIDCol, query, args)) 229 } 230 231 log.C(ctx).Infof("Internal visibility scope is present in the context. Processing %ss without visibility check...", objectType) 232 conditions = append(conditions, repo.NewNotNullCondition(objectFieldName)) 233 234 orderByColumns, err := getOrderByColumnsByObjectType(objectType) 235 if err != nil { 236 return nil, nil, err 237 } 238 239 var objectBundleIDs BundleReferencesCollection 240 counts, err := unionLister.ListGlobal(ctx, bundleIDs, bundleIDColumn, pageSize, cursor, orderByColumns, &objectBundleIDs, conditions...) 241 if err != nil { 242 return nil, nil, err 243 } 244 245 bundleReferences := make([]*model.BundleReference, 0, len(objectBundleIDs)) 246 for _, d := range objectBundleIDs { 247 entity, err := r.conv.FromEntity(d) 248 if err != nil { 249 return nil, nil, err 250 } 251 bundleReferences = append(bundleReferences, &entity) 252 } 253 254 return bundleReferences, counts, nil 255 } 256 257 func (r *repository) referenceObjectFieldName(objectType model.BundleReferenceObjectType) (string, error) { 258 switch objectType { 259 case model.BundleAPIReference: 260 return APIDefIDColumn, nil 261 case model.BundleEventReference: 262 return EventDefIDColumn, nil 263 } 264 return "", apperrors.NewInternalError("Invalid type of the BundleReference object") 265 } 266 267 func getDetailsByObjectType(objectType model.BundleReferenceObjectType) (string, string, []string, error) { 268 switch objectType { 269 case model.BundleAPIReference: 270 return APIDefTable, APIDefIDColumn, []string{APIDefIDColumn, bundleIDColumn, APIDefURLColumn}, nil 271 case model.BundleEventReference: 272 return EventDefTable, EventDefIDColumn, []string{EventDefIDColumn, bundleIDColumn}, nil 273 } 274 return "", "", []string{""}, apperrors.NewInternalError("Invalid type of the BundleReference object") 275 } 276 277 func getOrderByColumnsByObjectType(objectType model.BundleReferenceObjectType) (repo.OrderByParams, error) { 278 switch objectType { 279 case model.BundleAPIReference: 280 return repo.OrderByParams{repo.NewAscOrderBy(APIDefIDColumn), repo.NewAscOrderBy(bundleIDColumn), repo.NewAscOrderBy(APIDefURLColumn)}, nil 281 case model.BundleEventReference: 282 return repo.OrderByParams{repo.NewAscOrderBy(EventDefIDColumn), repo.NewAscOrderBy(bundleIDColumn)}, nil 283 } 284 return nil, apperrors.NewInternalError("Invalid type of the BundleReference object") 285 } 286 287 // IDs keeps IDs retrieved from the Compass storage. 288 type IDs []string 289 290 // Len returns the length of the IDs 291 func (i IDs) Len() int { 292 return len(i) 293 }