github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/spec/repository.go (about)

     1  package spec
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
     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/pkg/errors"
    13  )
    14  
    15  const (
    16  	specificationsTable = `public.specifications`
    17  	apiDefIDColumn      = "api_def_id"
    18  	eventAPIDefIDColumn = "event_def_id"
    19  	pageSize            = 1
    20  	cursor              = ""
    21  )
    22  
    23  var (
    24  	specificationsColumns = []string{"id", apiDefIDColumn, eventAPIDefIDColumn, "spec_data", "api_spec_format", "api_spec_type", "event_spec_format", "event_spec_type", "custom_type"}
    25  	orderByColumns        = repo.OrderByParams{repo.NewAscOrderBy("created_at"), repo.NewAscOrderBy("id")}
    26  )
    27  
    28  // Converter missing godoc
    29  //
    30  //go:generate mockery --name=Converter --output=automock --outpkg=automock --case=underscore --disable-version-string
    31  type Converter interface {
    32  	ToEntity(in *model.Spec) *Entity
    33  	FromEntity(in *Entity) (*model.Spec, error)
    34  }
    35  
    36  type repository struct {
    37  	creator        repo.Creator
    38  	creatorGlobal  repo.CreatorGlobal
    39  	lister         repo.Lister
    40  	listerGlobal   repo.ListerGlobal
    41  	idLister       repo.Lister
    42  	idListerGlobal repo.ListerGlobal
    43  	unionLister    repo.UnionLister
    44  	getter         repo.SingleGetter
    45  	getterGlobal   repo.SingleGetterGlobal
    46  	deleter        repo.Deleter
    47  	deleterGlobal  repo.DeleterGlobal
    48  	updater        repo.Updater
    49  	updaterGlobal  repo.UpdaterGlobal
    50  	existQuerier   repo.ExistQuerier
    51  	conv           Converter
    52  }
    53  
    54  // NewRepository missing godoc
    55  func NewRepository(conv Converter) *repository {
    56  	return &repository{
    57  		creator:       repo.NewCreator(specificationsTable, specificationsColumns),
    58  		creatorGlobal: repo.NewCreatorGlobal(resource.Specification, specificationsTable, specificationsColumns),
    59  		getter:        repo.NewSingleGetter(specificationsTable, specificationsColumns),
    60  		getterGlobal:  repo.NewSingleGetterGlobal(resource.Specification, specificationsTable, specificationsColumns),
    61  		lister: repo.NewListerWithOrderBy(specificationsTable, specificationsColumns, repo.OrderByParams{
    62  			{
    63  				Field: "created_at",
    64  				Dir:   repo.AscOrderBy,
    65  			},
    66  		}),
    67  		listerGlobal: repo.NewListerGlobal(resource.Specification, specificationsTable, specificationsColumns),
    68  		idLister: repo.NewListerWithOrderBy(specificationsTable, []string{"id"}, repo.OrderByParams{
    69  			{
    70  				Field: "created_at",
    71  				Dir:   repo.AscOrderBy,
    72  			},
    73  		}),
    74  		idListerGlobal: repo.NewListerGlobalWithOrderBy(resource.Specification, specificationsTable, []string{"id"}, repo.OrderByParams{
    75  			{
    76  				Field: "created_at",
    77  				Dir:   repo.AscOrderBy,
    78  			},
    79  		}),
    80  		unionLister:   repo.NewUnionLister(specificationsTable, specificationsColumns),
    81  		deleter:       repo.NewDeleter(specificationsTable),
    82  		deleterGlobal: repo.NewDeleterGlobal(resource.Specification, specificationsTable),
    83  		updater:       repo.NewUpdater(specificationsTable, []string{"spec_data", "api_spec_format", "api_spec_type", "event_spec_format", "event_spec_type"}, []string{"id"}),
    84  		updaterGlobal: repo.NewUpdaterGlobal(resource.Specification, specificationsTable, []string{"spec_data", "api_spec_format", "api_spec_type", "event_spec_format", "event_spec_type"}, []string{"id"}),
    85  		existQuerier:  repo.NewExistQuerier(specificationsTable),
    86  		conv:          conv,
    87  	}
    88  }
    89  
    90  // GetByID missing godoc
    91  func (r *repository) GetByID(ctx context.Context, tenantID string, id string, objectType model.SpecReferenceObjectType) (*model.Spec, error) {
    92  	var specEntity Entity
    93  	err := r.getter.Get(ctx, objectType.GetResourceType(), tenantID, repo.Conditions{repo.NewEqualCondition("id", id)}, repo.NoOrderBy, &specEntity)
    94  	if err != nil {
    95  		return nil, errors.Wrapf(err, "while getting Specification with id %q", id)
    96  	}
    97  
    98  	specModel, err := r.conv.FromEntity(&specEntity)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	return specModel, nil
   104  }
   105  
   106  // GetByIDGlobal gets a Spec by ID without tenant isolation
   107  func (r *repository) GetByIDGlobal(ctx context.Context, id string) (*model.Spec, error) {
   108  	var specEntity Entity
   109  	err := r.getterGlobal.GetGlobal(ctx, repo.Conditions{repo.NewEqualCondition("id", id)}, repo.NoOrderBy, &specEntity)
   110  	if err != nil {
   111  		return nil, errors.Wrapf(err, "while getting Specification with id %q", id)
   112  	}
   113  
   114  	specModel, err := r.conv.FromEntity(&specEntity)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	return specModel, nil
   120  }
   121  
   122  // Create creates a spec in the scope of a tenant
   123  func (r *repository) Create(ctx context.Context, tenant string, item *model.Spec) error {
   124  	if item == nil {
   125  		return apperrors.NewInternalError("item can not be empty")
   126  	}
   127  
   128  	entity := r.conv.ToEntity(item)
   129  
   130  	return r.creator.Create(ctx, item.ObjectType.GetResourceType(), tenant, entity)
   131  }
   132  
   133  // CreateGlobal create a spec without a tenant isolation
   134  func (r *repository) CreateGlobal(ctx context.Context, item *model.Spec) error {
   135  	if item == nil {
   136  		return apperrors.NewInternalError("item can not be empty")
   137  	}
   138  
   139  	entity := r.conv.ToEntity(item)
   140  
   141  	return r.creatorGlobal.Create(ctx, entity)
   142  }
   143  
   144  // ListIDByReferenceObjectID retrieves all spec ids by objectType and objectID
   145  func (r *repository) ListIDByReferenceObjectID(ctx context.Context, tenant string, objectType model.SpecReferenceObjectType, objectID string) ([]string, error) {
   146  	conditions, err := r.buildReferenceObjectIDConditions(objectType, objectID)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	var specCollection SpecCollection
   152  	err = r.idLister.List(ctx, objectType.GetResourceType(), tenant, &specCollection, conditions...)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	return extractIDsFromCollection(specCollection), nil
   158  }
   159  
   160  // ListIDByReferenceObjectIDGlobal retrieves all spec ids by objectType and objectID
   161  func (r *repository) ListIDByReferenceObjectIDGlobal(ctx context.Context, objectType model.SpecReferenceObjectType, objectID string) ([]string, error) {
   162  	conditions, err := r.buildReferenceObjectIDConditions(objectType, objectID)
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  
   167  	var specCollection SpecCollection
   168  	err = r.idListerGlobal.ListGlobal(ctx, &specCollection, conditions...)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	return extractIDsFromCollection(specCollection), nil
   174  }
   175  
   176  // ListByReferenceObjectID missing godoc
   177  func (r *repository) ListByReferenceObjectID(ctx context.Context, tenant string, objectType model.SpecReferenceObjectType, objectID string) ([]*model.Spec, error) {
   178  	fieldName, err := r.referenceObjectFieldName(objectType)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	conditions := repo.Conditions{
   183  		repo.NewEqualCondition(fieldName, objectID),
   184  	}
   185  
   186  	var specCollection SpecCollection
   187  	err = r.lister.List(ctx, objectType.GetResourceType(), tenant, &specCollection, conditions...)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  
   192  	items := make([]*model.Spec, 0, len(specCollection))
   193  
   194  	for _, specEnt := range specCollection {
   195  		m, err := r.conv.FromEntity(&specEnt)
   196  		if err != nil {
   197  			return nil, err
   198  		}
   199  
   200  		items = append(items, m)
   201  	}
   202  
   203  	return items, nil
   204  }
   205  
   206  // ListByReferenceObjectIDGlobal lists specs by a model.SpecReferenceObjectType without tenant isolation
   207  func (r *repository) ListByReferenceObjectIDGlobal(ctx context.Context, objectType model.SpecReferenceObjectType, objectID string) ([]*model.Spec, error) {
   208  	fieldName, err := r.referenceObjectFieldName(objectType)
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  	conditions := repo.Conditions{
   213  		repo.NewEqualCondition(fieldName, objectID),
   214  	}
   215  
   216  	var specCollection SpecCollection
   217  	err = r.listerGlobal.ListGlobal(ctx, &specCollection, conditions...)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  
   222  	items := make([]*model.Spec, 0, len(specCollection))
   223  
   224  	for _, specEnt := range specCollection {
   225  		m, err := r.conv.FromEntity(&specEnt)
   226  		if err != nil {
   227  			return nil, err
   228  		}
   229  
   230  		items = append(items, m)
   231  	}
   232  
   233  	return items, nil
   234  }
   235  
   236  func (r *repository) ListByReferenceObjectIDs(ctx context.Context, tenant string, objectType model.SpecReferenceObjectType, objectIDs []string) ([]*model.Spec, error) {
   237  	objectFieldName, err := r.referenceObjectFieldName(objectType)
   238  	if err != nil {
   239  		return nil, err
   240  	}
   241  
   242  	conditions := repo.Conditions{
   243  		repo.NewNotNullCondition(objectFieldName),
   244  	}
   245  
   246  	var specs SpecCollection
   247  	_, err = r.unionLister.List(ctx, objectType.GetResourceType(), tenant, objectIDs, objectFieldName, pageSize, cursor, orderByColumns, &specs, conditions...)
   248  	if err != nil {
   249  		return nil, err
   250  	}
   251  
   252  	specifications := make([]*model.Spec, 0, len(specs))
   253  	for _, s := range specs {
   254  		entity, err := r.conv.FromEntity(&s)
   255  		if err != nil {
   256  			return nil, err
   257  		}
   258  		specifications = append(specifications, entity)
   259  	}
   260  
   261  	return specifications, nil
   262  }
   263  
   264  // Delete missing godoc
   265  func (r *repository) Delete(ctx context.Context, tenant, id string, objectType model.SpecReferenceObjectType) error {
   266  	return r.deleter.DeleteOne(ctx, objectType.GetResourceType(), tenant, repo.Conditions{repo.NewEqualCondition("id", id)})
   267  }
   268  
   269  // DeleteByReferenceObjectID missing godoc
   270  func (r *repository) DeleteByReferenceObjectID(ctx context.Context, tenant string, objectType model.SpecReferenceObjectType, objectID string) error {
   271  	fieldName, err := r.referenceObjectFieldName(objectType)
   272  	if err != nil {
   273  		return err
   274  	}
   275  
   276  	return r.deleter.DeleteMany(ctx, objectType.GetResourceType(), tenant, repo.Conditions{repo.NewEqualCondition(fieldName, objectID)})
   277  }
   278  
   279  // DeleteByReferenceObjectIDGlobal deletes a reference object with a given ID without tenant isolation
   280  func (r *repository) DeleteByReferenceObjectIDGlobal(ctx context.Context, objectType model.SpecReferenceObjectType, objectID string) error {
   281  	fieldName, err := r.referenceObjectFieldName(objectType)
   282  	if err != nil {
   283  		return err
   284  	}
   285  
   286  	return r.deleterGlobal.DeleteManyGlobal(ctx, repo.Conditions{repo.NewEqualCondition(fieldName, objectID)})
   287  }
   288  
   289  // Update missing godoc
   290  func (r *repository) Update(ctx context.Context, tenant string, item *model.Spec) error {
   291  	if item == nil {
   292  		return apperrors.NewInternalError("item cannot be nil")
   293  	}
   294  
   295  	entity := r.conv.ToEntity(item)
   296  
   297  	return r.updater.UpdateSingle(ctx, item.ObjectType.GetResourceType(), tenant, entity)
   298  }
   299  
   300  // UpdateGlobal updates a Spec without tenant isolation
   301  func (r *repository) UpdateGlobal(ctx context.Context, item *model.Spec) error {
   302  	if item == nil {
   303  		return apperrors.NewInternalError("item cannot be nil")
   304  	}
   305  
   306  	entity := r.conv.ToEntity(item)
   307  
   308  	return r.updaterGlobal.UpdateSingleGlobal(ctx, entity)
   309  }
   310  
   311  // Exists missing godoc
   312  func (r *repository) Exists(ctx context.Context, tenantID, id string, objectType model.SpecReferenceObjectType) (bool, error) {
   313  	return r.existQuerier.Exists(ctx, objectType.GetResourceType(), tenantID, repo.Conditions{repo.NewEqualCondition("id", id)})
   314  }
   315  
   316  func (r *repository) referenceObjectFieldName(objectType model.SpecReferenceObjectType) (string, error) {
   317  	switch objectType {
   318  	case model.APISpecReference:
   319  		return apiDefIDColumn, nil
   320  	case model.EventSpecReference:
   321  		return eventAPIDefIDColumn, nil
   322  	}
   323  
   324  	return "", apperrors.NewInternalError("Invalid type of the Specification reference object")
   325  }
   326  
   327  func (r *repository) buildReferenceObjectIDConditions(objectType model.SpecReferenceObjectType, objectID string) (repo.Conditions, error) {
   328  	fieldName, err := r.referenceObjectFieldName(objectType)
   329  	if err != nil {
   330  		return nil, err
   331  	}
   332  
   333  	return repo.Conditions{
   334  		repo.NewEqualCondition(fieldName, objectID),
   335  	}, nil
   336  }
   337  
   338  // SpecCollection missing godoc
   339  type SpecCollection []Entity
   340  
   341  // Len missing godoc
   342  func (r SpecCollection) Len() int {
   343  	return len(r)
   344  }
   345  
   346  func extractIDsFromCollection(specCollection SpecCollection) []string {
   347  	items := make([]string, 0, len(specCollection))
   348  
   349  	for _, specEnt := range specCollection {
   350  		items = append(items, specEnt.ID)
   351  	}
   352  
   353  	return items
   354  }