github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/formationtemplate/repository.go (about) 1 package formationtemplate 2 3 import ( 4 "context" 5 6 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 7 8 "github.com/kyma-incubator/compass/components/director/internal/model" 9 "github.com/kyma-incubator/compass/components/director/internal/repo" 10 "github.com/kyma-incubator/compass/components/director/pkg/log" 11 "github.com/kyma-incubator/compass/components/director/pkg/resource" 12 "github.com/pkg/errors" 13 ) 14 15 const ( 16 tableName string = `public.formation_templates` 17 tenantIDColumn string = "tenant_id" 18 idColumn string = "id" 19 ) 20 21 var ( 22 idTableColumns = []string{idColumn} 23 updatableTableColumns = []string{"name", "application_types", "runtime_types", "runtime_type_display_name", "runtime_artifact_kind", "leading_product_ids"} 24 tenantTableColumn = []string{tenantIDColumn} 25 tableColumnsWithoutTenant = append(idTableColumns, updatableTableColumns...) 26 tableColumns = append(tableColumnsWithoutTenant, tenantTableColumn...) 27 ) 28 29 // EntityConverter converts between the internal model and entity 30 // 31 //go:generate mockery --name=EntityConverter --output=automock --outpkg=automock --case=underscore --disable-version-string 32 type EntityConverter interface { 33 ToEntity(in *model.FormationTemplate) (*Entity, error) 34 FromEntity(entity *Entity) (*model.FormationTemplate, error) 35 } 36 37 type repository struct { 38 creator repo.CreatorGlobal 39 existQuerierGlobal repo.ExistQuerierGlobal 40 singleGetterGlobal repo.SingleGetterGlobal 41 singleGetterWithEmbeddedTenant repo.SingleGetter 42 pageableQuerierGlobal repo.PageableQuerierGlobal 43 updaterGlobal repo.UpdaterGlobal 44 updaterWithEmbeddedTenant repo.UpdaterGlobal 45 deleterGlobal repo.DeleterGlobal 46 deleterWithEmbeddedTenant repo.Deleter 47 conv EntityConverter 48 } 49 50 // NewRepository creates a new FormationTemplate repository 51 func NewRepository(conv EntityConverter) *repository { 52 return &repository{ 53 creator: repo.NewCreatorGlobal(resource.FormationTemplate, tableName, tableColumns), 54 existQuerierGlobal: repo.NewExistQuerierGlobal(resource.FormationTemplate, tableName), 55 singleGetterGlobal: repo.NewSingleGetterGlobal(resource.FormationTemplate, tableName, tableColumns), 56 singleGetterWithEmbeddedTenant: repo.NewSingleGetterWithEmbeddedTenant(tableName, tenantIDColumn, tableColumns), 57 pageableQuerierGlobal: repo.NewPageableQuerierGlobal(resource.FormationTemplate, tableName, tableColumns), 58 updaterGlobal: repo.NewUpdaterGlobal(resource.FormationTemplate, tableName, updatableTableColumns, idTableColumns), 59 updaterWithEmbeddedTenant: repo.NewUpdaterWithEmbeddedTenant(resource.FormationTemplate, tableName, updatableTableColumns, tenantIDColumn, idTableColumns), 60 deleterGlobal: repo.NewDeleterGlobal(resource.FormationTemplate, tableName), 61 deleterWithEmbeddedTenant: repo.NewDeleterWithEmbeddedTenant(tableName, tenantIDColumn), 62 conv: conv, 63 } 64 } 65 66 // Create creates a new FormationTemplate in the database with the fields in model 67 func (r *repository) Create(ctx context.Context, item *model.FormationTemplate) error { 68 if item == nil { 69 return apperrors.NewInternalError("model can not be empty") 70 } 71 72 log.C(ctx).Debugf("Converting Formation Template with id %s to entity", item.ID) 73 entity, err := r.conv.ToEntity(item) 74 if err != nil { 75 return errors.Wrapf(err, "while converting Formation Template with ID %s", item.ID) 76 } 77 78 log.C(ctx).Debugf("Persisting Formation Template entity with id %s to db", item.ID) 79 return r.creator.Create(ctx, entity) 80 } 81 82 // Get queries for a single FormationTemplate matching the given id 83 func (r *repository) Get(ctx context.Context, id string) (*model.FormationTemplate, error) { 84 var entity Entity 85 if err := r.singleGetterGlobal.GetGlobal(ctx, repo.Conditions{repo.NewEqualCondition("id", id)}, repo.NoOrderBy, &entity); err != nil { 86 return nil, err 87 } 88 89 result, err := r.conv.FromEntity(&entity) 90 if err != nil { 91 return nil, errors.Wrapf(err, "while converting Formation Template with ID %s", id) 92 } 93 94 return result, nil 95 } 96 97 // GetByNameAndTenant check if the provided tenant is not empty and tries to retrieve the formation template with tenant. If it fails with not found error then the formation template is retrieved globally. 98 // If the tenant is empty in the first place the formation template is retrieved globally. 99 func (r *repository) GetByNameAndTenant(ctx context.Context, templateName, tenantID string) (*model.FormationTemplate, error) { 100 log.C(ctx).Debugf("Getting formation template by name: %q and tenant %q ...", templateName, tenantID) 101 var entity Entity 102 103 conditionsEqualName := repo.Conditions{repo.NewEqualCondition("name", templateName)} 104 conditionsEqualNameAndNullTenant := repo.Conditions{ 105 repo.NewEqualCondition("name", templateName), 106 repo.NewNullCondition(tenantIDColumn), 107 } 108 109 // If the call is with tenant but the query (select * from FT where name = ? and tenant_id = ?) returns NOT FOUND that means that there is no such tenant scoped FT and the call should get the global FT with that name. 110 // 111 // With this approach we allow the client to create a tenant scoped FT with the same name as a global FT - so when we get the FT first we will try to match it by name and tenant 112 // and if there is no such FT, we will get the global one 113 if tenantID == "" { 114 if err := r.singleGetterGlobal.GetGlobal(ctx, conditionsEqualNameAndNullTenant, repo.NoOrderBy, &entity); err != nil { 115 return nil, err 116 } 117 } else { 118 if err := r.singleGetterWithEmbeddedTenant.Get(ctx, resource.FormationTemplate, tenantID, conditionsEqualName, repo.NoOrderBy, &entity); err != nil { 119 if !apperrors.IsNotFoundError(err) { 120 return nil, err 121 } 122 entity = Entity{} 123 if err = r.singleGetterGlobal.GetGlobal(ctx, conditionsEqualNameAndNullTenant, repo.NoOrderBy, &entity); err != nil { 124 return nil, err 125 } 126 } 127 } 128 129 result, err := r.conv.FromEntity(&entity) 130 if err != nil { 131 return nil, errors.Wrapf(err, "while converting Formation Template with name: %q", templateName) 132 } 133 134 return result, nil 135 } 136 137 // List queries for all FormationTemplate sorted by ID and paginated by the pageSize and cursor parameters 138 func (r *repository) List(ctx context.Context, tenantID string, pageSize int, cursor string) (*model.FormationTemplatePage, error) { 139 var entityCollection EntityCollection 140 141 var conditions *repo.ConditionTree 142 if tenantID == "" { 143 conditions = &repo.ConditionTree{Operand: repo.NewNullCondition(tenantIDColumn)} 144 } else { 145 conditions = repo.Or(&repo.ConditionTree{Operand: repo.NewNullCondition(tenantIDColumn)}, &repo.ConditionTree{Operand: repo.NewEqualCondition(tenantIDColumn, tenantID)}) 146 } 147 148 page, totalCount, err := r.pageableQuerierGlobal.ListGlobalWithAdditionalConditions(ctx, pageSize, cursor, idColumn, &entityCollection, conditions) 149 if err != nil { 150 return nil, err 151 } 152 153 items := make([]*model.FormationTemplate, 0, len(entityCollection)) 154 155 for _, entity := range entityCollection { 156 isModel, err := r.conv.FromEntity(entity) 157 if err != nil { 158 return nil, errors.Wrapf(err, "while converting Formation Template entity with ID %s", entity.ID) 159 } 160 161 items = append(items, isModel) 162 } 163 return &model.FormationTemplatePage{ 164 Data: items, 165 TotalCount: totalCount, 166 PageInfo: page, 167 }, nil 168 } 169 170 // Update updates the FormationTemplate matching the ID of the input model 171 func (r *repository) Update(ctx context.Context, model *model.FormationTemplate) error { 172 if model == nil { 173 return apperrors.NewInternalError("model can not be empty") 174 } 175 176 entity, err := r.conv.ToEntity(model) 177 if err != nil { 178 return errors.Wrapf(err, "while converting Formation Template with ID %s", model.ID) 179 } 180 181 if model.TenantID != nil { 182 return r.updaterWithEmbeddedTenant.UpdateSingleGlobal(ctx, entity) 183 } 184 return r.updaterGlobal.UpdateSingleGlobal(ctx, entity) 185 } 186 187 // Delete deletes a formation template with given ID and tenantID 188 func (r *repository) Delete(ctx context.Context, id, tenantID string) error { 189 conditions := repo.Conditions{repo.NewEqualCondition(idColumn, id)} 190 191 if tenantID == "" { 192 conditions = append(conditions, repo.NewNullCondition(tenantIDColumn)) 193 return r.deleterGlobal.DeleteOneGlobal(ctx, conditions) 194 } 195 196 return r.deleterWithEmbeddedTenant.DeleteOne(ctx, resource.FormationTemplate, tenantID, conditions) 197 } 198 199 // Exists check if a formation template with given ID exists 200 func (r *repository) Exists(ctx context.Context, id string) (bool, error) { 201 return r.existQuerierGlobal.ExistsGlobal(ctx, repo.Conditions{repo.NewEqualCondition(idColumn, id)}) 202 }