github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/webhook/repository.go (about) 1 package webhook 2 3 import ( 4 "context" 5 "time" 6 7 "github.com/kyma-incubator/compass/components/director/pkg/log" 8 9 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 10 11 "github.com/kyma-incubator/compass/components/director/pkg/resource" 12 13 "github.com/pkg/errors" 14 15 "github.com/kyma-incubator/compass/components/director/internal/model" 16 "github.com/kyma-incubator/compass/components/director/internal/repo" 17 ) 18 19 const ( 20 tableName = "public.webhooks" 21 applicationID = "app_id" 22 runtimeID = "runtime_id" 23 formationTemplateID = "formation_template_id" 24 applicationTemplateID = "app_template_id" 25 ) 26 27 var ( 28 webhookColumns = []string{"id", "app_id", "app_template_id", "type", "url", "auth", "runtime_id", "integration_system_id", "mode", "correlation_id_key", "retry_interval", "timeout", "url_template", "input_template", "header_template", "output_template", "status_template", "created_at", "formation_template_id"} 29 updatableColumns = []string{"type", "url", "auth", "mode", "retry_interval", "timeout", "url_template", "input_template", "header_template", "output_template", "status_template"} 30 missingInputModelError = apperrors.NewInternalError("model has to be provided") 31 ) 32 33 // EntityConverter missing godoc 34 // 35 //go:generate mockery --name=EntityConverter --output=automock --outpkg=automock --case=underscore --disable-version-string 36 type EntityConverter interface { 37 FromEntity(in *Entity) (*model.Webhook, error) 38 ToEntity(in *model.Webhook) (*Entity, error) 39 } 40 41 type repository struct { 42 singleGetter repo.SingleGetter 43 singleGetterGlobal repo.SingleGetterGlobal 44 webhookUpdater repo.Updater 45 updaterGlobal repo.UpdaterGlobal 46 ftWebhookUpdaterGlobal repo.UpdaterGlobal 47 creator repo.Creator 48 globalCreator repo.CreatorGlobal 49 deleterGlobal repo.DeleterGlobal 50 deleter repo.Deleter 51 lister repo.Lister 52 conditionTreeLister repo.ConditionTreeLister 53 listerGlobal repo.ListerGlobal 54 listerGlobalOrderedByCreatedAt repo.ListerGlobal 55 conv EntityConverter 56 } 57 58 // NewRepository missing godoc 59 func NewRepository(conv EntityConverter) *repository { 60 return &repository{ 61 singleGetter: repo.NewSingleGetter(tableName, webhookColumns), 62 singleGetterGlobal: repo.NewSingleGetterGlobal(resource.Webhook, tableName, webhookColumns), 63 creator: repo.NewCreator(tableName, webhookColumns), 64 globalCreator: repo.NewCreatorGlobal(resource.Webhook, tableName, webhookColumns), 65 webhookUpdater: repo.NewUpdater(tableName, updatableColumns, []string{"id"}), 66 updaterGlobal: repo.NewUpdaterGlobal(resource.Webhook, tableName, updatableColumns, []string{"id", "app_template_id"}), 67 ftWebhookUpdaterGlobal: repo.NewUpdaterGlobal(resource.Webhook, tableName, updatableColumns, []string{"id", "formation_template_id"}), 68 deleterGlobal: repo.NewDeleterGlobal(resource.Webhook, tableName), 69 deleter: repo.NewDeleter(tableName), 70 lister: repo.NewLister(tableName, webhookColumns), 71 conditionTreeLister: repo.NewConditionTreeLister(tableName, webhookColumns), 72 listerGlobal: repo.NewListerGlobal(resource.Webhook, tableName, webhookColumns), 73 listerGlobalOrderedByCreatedAt: repo.NewListerGlobalWithOrderBy(resource.Webhook, tableName, webhookColumns, repo.OrderByParams{ 74 { 75 Field: "created_at", 76 Dir: repo.DescOrderBy, 77 }, 78 }), 79 conv: conv, 80 } 81 } 82 83 // GetByID missing godoc 84 func (r *repository) GetByID(ctx context.Context, tenant, id string, objectType model.WebhookReferenceObjectType) (*model.Webhook, error) { 85 var entity Entity 86 if err := r.singleGetter.Get(ctx, objectType.GetResourceType(), tenant, repo.Conditions{repo.NewEqualCondition("id", id)}, repo.NoOrderBy, &entity); err != nil { 87 return nil, err 88 } 89 m, err := r.conv.FromEntity(&entity) 90 if err != nil { 91 return nil, errors.Wrap(err, "while converting from entity to model") 92 } 93 return m, nil 94 } 95 96 // GetByIDGlobal missing godoc 97 func (r *repository) GetByIDGlobal(ctx context.Context, id string) (*model.Webhook, error) { 98 var entity Entity 99 if err := r.singleGetterGlobal.GetGlobal(ctx, repo.Conditions{repo.NewEqualCondition("id", id)}, repo.NoOrderBy, &entity); err != nil { 100 return nil, err 101 } 102 m, err := r.conv.FromEntity(&entity) 103 if err != nil { 104 return nil, errors.Wrap(err, "while converting from entity to model") 105 } 106 return m, nil 107 } 108 109 // ListByReferenceObjectID missing godoc 110 func (r *repository) ListByReferenceObjectID(ctx context.Context, tenant, objID string, objType model.WebhookReferenceObjectType) ([]*model.Webhook, error) { 111 var entities Collection 112 113 refColumn, err := getReferenceColumnForListByReferenceObjectType(objType) 114 if err != nil { 115 return nil, err 116 } 117 118 conditions := repo.Conditions{ 119 repo.NewEqualCondition(refColumn, objID), 120 } 121 122 if err := r.lister.List(ctx, objType.GetResourceType(), tenant, &entities, conditions...); err != nil { 123 return nil, err 124 } 125 126 return convertToWebhooks(entities, r) 127 } 128 129 // ListByReferenceObjectIDGlobal missing godoc 130 func (r *repository) ListByReferenceObjectIDGlobal(ctx context.Context, objID string, objType model.WebhookReferenceObjectType) ([]*model.Webhook, error) { 131 var entities Collection 132 133 refColumn, err := getReferenceColumnForListByReferenceObjectType(objType) 134 if err != nil { 135 return nil, err 136 } 137 138 conditions := repo.Conditions{ 139 repo.NewEqualCondition(refColumn, objID), 140 } 141 142 if err := r.listerGlobal.ListGlobal(ctx, &entities, conditions...); err != nil { 143 return nil, err 144 } 145 146 return convertToWebhooks(entities, r) 147 } 148 149 // ListByReferenceObjectTypeAndWebhookType lists all webhooks of a given type for a given object type 150 func (r *repository) ListByReferenceObjectTypeAndWebhookType(ctx context.Context, tenant string, whType model.WebhookType, objType model.WebhookReferenceObjectType) ([]*model.Webhook, error) { 151 var entities Collection 152 refColumn, err := getReferenceColumnForListByReferenceObjectType(objType) 153 if err != nil { 154 return nil, err 155 } 156 157 conditions := repo.Conditions{ 158 repo.NewNotNullCondition(refColumn), 159 repo.NewEqualCondition("type", whType), 160 } 161 162 if err := r.lister.List(ctx, objType.GetResourceType(), tenant, &entities, conditions...); err != nil { 163 return nil, err 164 } 165 166 return convertToWebhooks(entities, r) 167 } 168 169 // ListByReferenceObjectTypesAndWebhookType lists all webhooks of a given type for a given object types 170 func (r *repository) ListByReferenceObjectTypesAndWebhookType(ctx context.Context, tenant string, whType model.WebhookType, objTypes []model.WebhookReferenceObjectType) ([]*model.Webhook, error) { 171 var entities Collection 172 173 objTypesConditions := repo.Conditions{} 174 for _, objType := range objTypes { 175 refColumn, err := getReferenceColumnForListByReferenceObjectType(objType) 176 if err != nil { 177 return nil, err 178 } 179 objTypesConditions = append(objTypesConditions, repo.NewNotNullCondition(refColumn)) 180 } 181 182 conditions := repo.And( 183 append( 184 repo.ConditionTreesFromConditions( 185 []repo.Condition{ 186 repo.NewEqualCondition("type", whType), 187 }, 188 ), 189 repo.Or(repo.ConditionTreesFromConditions( 190 objTypesConditions, 191 )...), 192 )..., 193 ) 194 195 if err := r.conditionTreeLister.ListConditionTree(ctx, resource.Webhook, tenant, &entities, conditions); err != nil { 196 return nil, err 197 } 198 199 if containsWebhookType(objTypes, model.ApplicationTemplateWebhookReference) { 200 var appTemplateWebhooks Collection 201 202 refColumn, err := getReferenceColumnForListByReferenceObjectType(model.ApplicationTemplateWebhookReference) 203 if err != nil { 204 return nil, err 205 } 206 conditionsForAppTemplate := repo.Conditions{ 207 repo.NewNotNullCondition(refColumn), 208 repo.NewEqualCondition("type", whType), 209 } 210 211 if err := r.listerGlobal.ListGlobal(ctx, &appTemplateWebhooks, conditionsForAppTemplate...); err != nil { 212 return nil, err 213 } 214 215 entities = append(entities, appTemplateWebhooks...) 216 } 217 218 return convertToWebhooks(entities, r) 219 } 220 221 // GetByIDAndWebhookType returns a webhook given an objectID, objectType and webhookType. 222 // Global getter is used for object type ApplicationTemplateWebhookReference as the application template is not tenant scoped 223 // and single getter is used for all other object types. 224 func (r *repository) GetByIDAndWebhookType(ctx context.Context, tenant, objectID string, objectType model.WebhookReferenceObjectType, webhookType model.WebhookType) (*model.Webhook, error) { 225 var entity Entity 226 refColumn, err := getReferenceColumnForListByReferenceObjectType(objectType) 227 if err != nil { 228 return nil, err 229 } 230 231 conditions := repo.Conditions{ 232 repo.NewEqualCondition(refColumn, objectID), 233 repo.NewEqualCondition("type", webhookType), 234 } 235 236 switch objectType { 237 case model.ApplicationTemplateWebhookReference: 238 if err := r.singleGetterGlobal.GetGlobal(ctx, conditions, repo.NoOrderBy, &entity); err != nil { 239 return nil, err 240 } 241 default: 242 if err := r.singleGetter.Get(ctx, objectType.GetResourceType(), tenant, conditions, repo.NoOrderBy, &entity); err != nil { 243 return nil, err 244 } 245 } 246 247 m, err := r.conv.FromEntity(&entity) 248 if err != nil { 249 return nil, errors.Wrap(err, "while converting from entity to model") 250 } 251 return m, nil 252 } 253 254 func (r *repository) ListByApplicationIDWithSelectForUpdate(ctx context.Context, tenant, applicationID string) ([]*model.Webhook, error) { 255 var entities Collection 256 257 conditions := repo.Conditions{ 258 repo.NewEqualCondition("app_id", applicationID), 259 } 260 261 if err := r.lister.ListWithSelectForUpdate(ctx, resource.AppWebhook, tenant, &entities, conditions...); err != nil { 262 return nil, err 263 } 264 265 return convertToWebhooks(entities, r) 266 } 267 268 // ListByWebhookType retrieves all webhooks which have the given webhook type in descending order 269 func (r *repository) ListByWebhookType(ctx context.Context, webhookType model.WebhookType) ([]*model.Webhook, error) { 270 var entities Collection 271 272 conditions := repo.Conditions{ 273 repo.NewEqualCondition("type", webhookType), 274 } 275 276 if err := r.listerGlobalOrderedByCreatedAt.ListGlobal(ctx, &entities, conditions...); err != nil { 277 return nil, err 278 } 279 280 return convertToWebhooks(entities, r) 281 } 282 283 // ListByApplicationTemplateID missing godoc 284 func (r *repository) ListByApplicationTemplateID(ctx context.Context, applicationTemplateID string) ([]*model.Webhook, error) { 285 var entities Collection 286 287 conditions := repo.Conditions{ 288 repo.NewEqualCondition("app_template_id", applicationTemplateID), 289 } 290 291 if err := r.listerGlobal.ListGlobal(ctx, &entities, conditions...); err != nil { 292 return nil, err 293 } 294 295 return convertToWebhooks(entities, r) 296 } 297 298 // Create missing godoc 299 func (r *repository) Create(ctx context.Context, tenant string, item *model.Webhook) error { 300 if item == nil { 301 return missingInputModelError 302 } 303 entity, err := r.conv.ToEntity(item) 304 if err != nil { 305 return errors.Wrap(err, "while converting model to entity") 306 } 307 308 if entity.CreatedAt == nil || entity.CreatedAt.IsZero() { 309 now := time.Now() 310 entity.CreatedAt = &now 311 } 312 313 log.C(ctx).Debugf("Persisting Webhook entity with type %s and id %s for %s to db", item.Type, item.ID, item.ObjectType) 314 if len(tenant) == 0 { 315 return r.globalCreator.Create(ctx, entity) 316 } 317 return r.creator.Create(ctx, item.ObjectType.GetResourceType(), tenant, entity) 318 } 319 320 // CreateMany missing godoc 321 func (r *repository) CreateMany(ctx context.Context, tenant string, items []*model.Webhook) error { 322 for _, item := range items { 323 if err := r.Create(ctx, tenant, item); err != nil { 324 return errors.Wrapf(err, "while creating Webhook with type %s and id %s for %s", item.Type, item.ID, item.ObjectType) 325 } 326 log.C(ctx).Infof("Successfully created Webhook with type %s and id %s for %s", item.Type, item.ID, item.ObjectType) 327 } 328 return nil 329 } 330 331 // Update missing godoc 332 func (r *repository) Update(ctx context.Context, tenant string, item *model.Webhook) error { 333 if item == nil { 334 return missingInputModelError 335 } 336 entity, err := r.conv.ToEntity(item) 337 if err != nil { 338 return errors.Wrap(err, "while converting model to entity") 339 } 340 if item.ObjectType.GetResourceType() == resource.Webhook { // Global resource webhook 341 return r.updaterGlobal.UpdateSingleGlobal(ctx, entity) 342 } 343 if item.ObjectType.GetResourceType() == resource.FormationTemplateWebhook && tenant == "" { 344 return r.ftWebhookUpdaterGlobal.UpdateSingleGlobal(ctx, entity) 345 } 346 return r.webhookUpdater.UpdateSingle(ctx, item.ObjectType.GetResourceType(), tenant, entity) 347 } 348 349 // Delete missing godoc 350 func (r *repository) Delete(ctx context.Context, id string) error { 351 return r.deleterGlobal.DeleteOneGlobal(ctx, repo.Conditions{repo.NewEqualCondition("id", id)}) 352 } 353 354 // DeleteAllByApplicationID missing godoc 355 func (r *repository) DeleteAllByApplicationID(ctx context.Context, tenant, applicationID string) error { 356 return r.deleter.DeleteMany(ctx, resource.AppWebhook, tenant, repo.Conditions{repo.NewEqualCondition("app_id", applicationID)}) 357 } 358 359 // DeleteAllByApplicationTemplateID missing godoc 360 func (r *repository) DeleteAllByApplicationTemplateID(ctx context.Context, applicationTemplateID string) error { 361 return r.deleterGlobal.DeleteManyGlobal(ctx, repo.Conditions{repo.NewEqualCondition("app_template_id", applicationTemplateID)}) 362 } 363 364 func convertToWebhooks(entities Collection, r *repository) ([]*model.Webhook, error) { 365 out := make([]*model.Webhook, 0, len(entities)) 366 for _, ent := range entities { 367 w, err := r.conv.FromEntity(&ent) 368 if err != nil { 369 return nil, errors.Wrap(err, "while converting Webhook to model") 370 } 371 out = append(out, w) 372 } 373 374 return out, nil 375 } 376 377 func getReferenceColumnForListByReferenceObjectType(objType model.WebhookReferenceObjectType) (string, error) { 378 switch objType { 379 case model.ApplicationWebhookReference: 380 return applicationID, nil 381 case model.RuntimeWebhookReference: 382 return runtimeID, nil 383 case model.FormationTemplateWebhookReference: 384 return formationTemplateID, nil 385 case model.ApplicationTemplateWebhookReference: 386 return applicationTemplateID, nil 387 default: 388 return "", errors.New("referenced object should be one of application, application template, runtime or formation template") 389 } 390 } 391 392 func containsWebhookType(objTypes []model.WebhookReferenceObjectType, objType model.WebhookReferenceObjectType) bool { 393 for _, t := range objTypes { 394 if t == objType { 395 return true 396 } 397 } 398 return false 399 }