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  }