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

     1  package eventdef
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
     7  	"github.com/kyma-incubator/compass/components/director/pkg/str"
     8  
     9  	dataloader "github.com/kyma-incubator/compass/components/director/internal/dataloaders"
    10  
    11  	"github.com/kyma-incubator/compass/components/director/pkg/apperrors"
    12  
    13  	"github.com/kyma-incubator/compass/components/director/pkg/persistence"
    14  
    15  	"github.com/kyma-incubator/compass/components/director/pkg/log"
    16  	"github.com/pkg/errors"
    17  
    18  	"github.com/kyma-incubator/compass/components/director/internal/model"
    19  
    20  	"github.com/kyma-incubator/compass/components/director/pkg/graphql"
    21  )
    22  
    23  // EventDefService is responsible for the service-layer EventDefinition operations.
    24  //
    25  //go:generate mockery --name=EventDefService --output=automock --outpkg=automock --case=underscore --disable-version-string
    26  type EventDefService interface {
    27  	CreateInBundle(ctx context.Context, resourceType resource.Type, resourceID string, bundleID string, in model.EventDefinitionInput, spec *model.SpecInput) (string, error)
    28  	CreateInApplication(ctx context.Context, appID string, in model.EventDefinitionInput, spec *model.SpecInput) (string, error)
    29  	Update(ctx context.Context, resourceType resource.Type, id string, in model.EventDefinitionInput, spec *model.SpecInput) error
    30  	UpdateForApplication(ctx context.Context, id string, in model.EventDefinitionInput, specIn *model.SpecInput) error
    31  	Get(ctx context.Context, id string) (*model.EventDefinition, error)
    32  	Delete(ctx context.Context, resourceType resource.Type, id string) error
    33  	ListByApplicationIDPage(ctx context.Context, appID string, pageSize int, cursor string) (*model.EventDefinitionPage, error)
    34  	ListFetchRequests(ctx context.Context, eventDefIDs []string) ([]*model.FetchRequest, error)
    35  }
    36  
    37  // EventDefConverter converts EventDefinitions between the model.EventDefinition service-layer representation and the graphql-layer representation.
    38  //
    39  //go:generate mockery --name=EventDefConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
    40  type EventDefConverter interface {
    41  	ToGraphQL(in *model.EventDefinition, spec *model.Spec, bundleReference *model.BundleReference) (*graphql.EventDefinition, error)
    42  	MultipleToGraphQL(in []*model.EventDefinition, specs []*model.Spec, bundleRefs []*model.BundleReference) ([]*graphql.EventDefinition, error)
    43  	MultipleInputFromGraphQL(in []*graphql.EventDefinitionInput) ([]*model.EventDefinitionInput, []*model.SpecInput, error)
    44  	InputFromGraphQL(in *graphql.EventDefinitionInput) (*model.EventDefinitionInput, *model.SpecInput, error)
    45  }
    46  
    47  // FetchRequestConverter converts FetchRequest from the model.FetchRequest service-layer representation to the graphql-layer one.
    48  //
    49  //go:generate mockery --name=FetchRequestConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
    50  type FetchRequestConverter interface {
    51  	ToGraphQL(in *model.FetchRequest) (*graphql.FetchRequest, error)
    52  }
    53  
    54  // BundleService is responsible for the service-layer Bundle operations.
    55  //
    56  //go:generate mockery --name=BundleService --output=automock --outpkg=automock --case=underscore --disable-version-string
    57  type BundleService interface {
    58  	Get(ctx context.Context, id string) (*model.Bundle, error)
    59  }
    60  
    61  // Resolver is an object responsible for resolver-layer EventDefinition operations
    62  type Resolver struct {
    63  	transact      persistence.Transactioner
    64  	svc           EventDefService
    65  	bndlSvc       BundleService
    66  	bndlRefSvc    BundleReferenceService
    67  	converter     EventDefConverter
    68  	frConverter   FetchRequestConverter
    69  	specConverter SpecConverter
    70  	specService   SpecService
    71  }
    72  
    73  // NewResolver returns a new object responsible for resolver-layer EventDefinition operations.
    74  func NewResolver(transact persistence.Transactioner, svc EventDefService, bndlSvc BundleService, bndlRefSvc BundleReferenceService, converter EventDefConverter, frConverter FetchRequestConverter, specService SpecService, specConverter SpecConverter) *Resolver {
    75  	return &Resolver{
    76  		transact:      transact,
    77  		svc:           svc,
    78  		bndlSvc:       bndlSvc,
    79  		bndlRefSvc:    bndlRefSvc,
    80  		converter:     converter,
    81  		frConverter:   frConverter,
    82  		specConverter: specConverter,
    83  		specService:   specService,
    84  	}
    85  }
    86  
    87  // EventDefinitionsForApplication lists all EventDefinitions for a given application ID with paging.
    88  func (r *Resolver) EventDefinitionsForApplication(ctx context.Context, appID string, first *int, after *graphql.PageCursor) (*graphql.EventDefinitionPage, error) {
    89  	tx, err := r.transact.Begin()
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
    94  
    95  	log.C(ctx).Infof("Listing EventDefinition for Application with ID %s", appID)
    96  
    97  	ctx = persistence.SaveToContext(ctx, tx)
    98  
    99  	var cursor string
   100  	if after != nil {
   101  		cursor = string(*after)
   102  	}
   103  	if first == nil {
   104  		return nil, apperrors.NewInvalidDataError("missing required parameter 'first'")
   105  	}
   106  
   107  	eventPage, err := r.svc.ListByApplicationIDPage(ctx, appID, *first, cursor)
   108  	if err != nil {
   109  		return nil, errors.Wrapf(err, "while listing EventDefinition for Application with ID %s", appID)
   110  	}
   111  
   112  	gqlEvents := make([]*graphql.EventDefinition, 0, len(eventPage.Data))
   113  	for _, event := range eventPage.Data {
   114  		spec, err := r.specService.GetByReferenceObjectID(ctx, resource.Application, model.EventSpecReference, event.ID)
   115  		if err != nil {
   116  			return nil, errors.Wrapf(err, "while getting spec for EventDefinition with id %q", event.ID)
   117  		}
   118  
   119  		gqlEvent, err := r.converter.ToGraphQL(event, spec, nil)
   120  		if err != nil {
   121  			return nil, errors.Wrapf(err, "while converting EventDefinition with id %q to graphQL", event.ID)
   122  		}
   123  
   124  		gqlEvents = append(gqlEvents, gqlEvent)
   125  	}
   126  
   127  	if err = tx.Commit(); err != nil {
   128  		return nil, err
   129  	}
   130  
   131  	return &graphql.EventDefinitionPage{
   132  		Data:       gqlEvents,
   133  		TotalCount: eventPage.TotalCount,
   134  		PageInfo: &graphql.PageInfo{
   135  			StartCursor: graphql.PageCursor(eventPage.PageInfo.StartCursor),
   136  			EndCursor:   graphql.PageCursor(eventPage.PageInfo.EndCursor),
   137  			HasNextPage: eventPage.PageInfo.HasNextPage,
   138  		},
   139  	}, nil
   140  }
   141  
   142  // AddEventDefinitionToBundle adds an EventDefinition to a Bundle with a given ID.
   143  func (r *Resolver) AddEventDefinitionToBundle(ctx context.Context, bundleID string, in graphql.EventDefinitionInput) (*graphql.EventDefinition, error) {
   144  	tx, err := r.transact.Begin()
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   149  
   150  	log.C(ctx).Infof("Adding EventDefinition to bundle with id %s", bundleID)
   151  
   152  	ctx = persistence.SaveToContext(ctx, tx)
   153  
   154  	convertedIn, convertedSpec, err := r.converter.InputFromGraphQL(&in)
   155  	if err != nil {
   156  		return nil, errors.Wrap(err, "while converting GraphQL input to EventDefinition")
   157  	}
   158  
   159  	bndl, err := r.bndlSvc.Get(ctx, bundleID)
   160  	if err != nil {
   161  		if apperrors.IsNotFoundError(err) {
   162  			return nil, apperrors.NewInvalidDataError("cannot add Event Definition to not existing Bundle")
   163  		}
   164  		return nil, errors.Wrapf(err, "while checking existence of Bundle with id %s when adding EventDefinition", bundleID)
   165  	}
   166  
   167  	id, err := r.svc.CreateInBundle(ctx, resource.Application, str.PtrStrToStr(bndl.ApplicationID), bundleID, *convertedIn, convertedSpec)
   168  	if err != nil {
   169  		return nil, errors.Wrapf(err, "while creating EventDefinition in Bundle with id %s", bundleID)
   170  	}
   171  
   172  	event, err := r.svc.Get(ctx, id)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  
   177  	spec, err := r.specService.GetByReferenceObjectID(ctx, resource.Application, model.EventSpecReference, event.ID)
   178  	if err != nil {
   179  		return nil, errors.Wrapf(err, "while getting spec for EventDefinition with id %q", event.ID)
   180  	}
   181  
   182  	bndlRef, err := r.bndlRefSvc.GetForBundle(ctx, model.BundleEventReference, &event.ID, &bundleID)
   183  	if err != nil {
   184  		return nil, errors.Wrapf(err, "while getting bundle reference for EventDefinition with id %q", event.ID)
   185  	}
   186  
   187  	gqlEvent, err := r.converter.ToGraphQL(event, spec, bndlRef)
   188  	if err != nil {
   189  		return nil, errors.Wrapf(err, "while converting EventDefinition with id %q to graphQL", event.ID)
   190  	}
   191  
   192  	err = tx.Commit()
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  
   197  	log.C(ctx).Infof("EventDefinition with id %s successfully added to bundle with id %s", id, bundleID)
   198  	return gqlEvent, nil
   199  }
   200  
   201  // AddEventDefinitionToApplication adds an EventDefinition in the context of an Application without Bundle
   202  func (r *Resolver) AddEventDefinitionToApplication(ctx context.Context, appID string, in graphql.EventDefinitionInput) (*graphql.EventDefinition, error) {
   203  	tx, err := r.transact.Begin()
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   208  
   209  	log.C(ctx).Infof("Adding EventDefinition to Application with id %s", appID)
   210  
   211  	ctx = persistence.SaveToContext(ctx, tx)
   212  
   213  	convertedIn, convertedSpec, err := r.converter.InputFromGraphQL(&in)
   214  	if err != nil {
   215  		return nil, errors.Wrap(err, "while converting GraphQL input to EventDefinition")
   216  	}
   217  
   218  	id, err := r.svc.CreateInApplication(ctx, appID, *convertedIn, convertedSpec)
   219  	if err != nil {
   220  		return nil, errors.Wrapf(err, "while creating EventDefinition in Application with id %s", appID)
   221  	}
   222  
   223  	event, err := r.svc.Get(ctx, id)
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  
   228  	spec, err := r.specService.GetByReferenceObjectID(ctx, resource.Application, model.EventSpecReference, event.ID)
   229  	if err != nil {
   230  		return nil, errors.Wrapf(err, "while getting spec for EventDefinition with id %q", event.ID)
   231  	}
   232  
   233  	gqlEvent, err := r.converter.ToGraphQL(event, spec, nil)
   234  	if err != nil {
   235  		return nil, errors.Wrapf(err, "while converting EventDefinition with id %q to graphQL", event.ID)
   236  	}
   237  
   238  	if err = tx.Commit(); err != nil {
   239  		return nil, err
   240  	}
   241  
   242  	log.C(ctx).Infof("EventDefinition with id %s successfully added to Application with id %s", id, appID)
   243  	return gqlEvent, nil
   244  }
   245  
   246  // UpdateEventDefinition updates an EventDefinition by its ID.
   247  func (r *Resolver) UpdateEventDefinition(ctx context.Context, id string, in graphql.EventDefinitionInput) (*graphql.EventDefinition, error) {
   248  	tx, err := r.transact.Begin()
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   253  
   254  	log.C(ctx).Infof("Updating EventDefinition with id %s", id)
   255  
   256  	ctx = persistence.SaveToContext(ctx, tx)
   257  
   258  	convertedIn, convertedSpec, err := r.converter.InputFromGraphQL(&in)
   259  	if err != nil {
   260  		return nil, errors.Wrapf(err, "while converting GraphQL input to EventDefinition with id %s", id)
   261  	}
   262  
   263  	err = r.svc.Update(ctx, resource.Application, id, *convertedIn, convertedSpec)
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  
   268  	event, err := r.svc.Get(ctx, id)
   269  	if err != nil {
   270  		return nil, err
   271  	}
   272  
   273  	spec, err := r.specService.GetByReferenceObjectID(ctx, resource.Application, model.EventSpecReference, event.ID)
   274  	if err != nil {
   275  		return nil, errors.Wrapf(err, "while getting spec for EventDefinition with id %q", event.ID)
   276  	}
   277  
   278  	bndlRef, err := r.bndlRefSvc.GetForBundle(ctx, model.BundleEventReference, &event.ID, nil)
   279  	if err != nil {
   280  		return nil, errors.Wrapf(err, "while getting bundle reference for EventDefinition with id %q", event.ID)
   281  	}
   282  
   283  	gqlEvent, err := r.converter.ToGraphQL(event, spec, bndlRef)
   284  	if err != nil {
   285  		return nil, errors.Wrapf(err, "while converting EventDefinition with id %q to graphQL", event.ID)
   286  	}
   287  
   288  	err = tx.Commit()
   289  	if err != nil {
   290  		return nil, err
   291  	}
   292  
   293  	log.C(ctx).Infof("EventDefinition with id %s successfully updated.", id)
   294  	return gqlEvent, nil
   295  }
   296  
   297  // UpdateEventDefinitionForApplication updates an EventDefinition for Application without being in a Bundle
   298  func (r *Resolver) UpdateEventDefinitionForApplication(ctx context.Context, id string, in graphql.EventDefinitionInput) (*graphql.EventDefinition, error) {
   299  	tx, err := r.transact.Begin()
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   304  
   305  	log.C(ctx).Infof("Updating EventDefinition with id %s", id)
   306  
   307  	ctx = persistence.SaveToContext(ctx, tx)
   308  
   309  	convertedIn, convertedSpec, err := r.converter.InputFromGraphQL(&in)
   310  	if err != nil {
   311  		return nil, errors.Wrapf(err, "while converting GraphQL input to EventDefinition with id %s", id)
   312  	}
   313  
   314  	err = r.svc.UpdateForApplication(ctx, id, *convertedIn, convertedSpec)
   315  	if err != nil {
   316  		return nil, err
   317  	}
   318  
   319  	event, err := r.svc.Get(ctx, id)
   320  	if err != nil {
   321  		return nil, err
   322  	}
   323  
   324  	spec, err := r.specService.GetByReferenceObjectID(ctx, resource.Application, model.EventSpecReference, event.ID)
   325  	if err != nil {
   326  		return nil, errors.Wrapf(err, "while getting spec for EventDefinition with id %q", event.ID)
   327  	}
   328  
   329  	gqlEvent, err := r.converter.ToGraphQL(event, spec, nil)
   330  	if err != nil {
   331  		return nil, errors.Wrapf(err, "while converting EventDefinition with id %q to graphQL", event.ID)
   332  	}
   333  
   334  	if err = tx.Commit(); err != nil {
   335  		return nil, err
   336  	}
   337  
   338  	log.C(ctx).Infof("EventDefinition with id %s successfully updated.", id)
   339  	return gqlEvent, nil
   340  }
   341  
   342  // DeleteEventDefinition deletes an EventDefinition by its ID.
   343  func (r *Resolver) DeleteEventDefinition(ctx context.Context, id string) (*graphql.EventDefinition, error) {
   344  	tx, err := r.transact.Begin()
   345  	if err != nil {
   346  		return nil, err
   347  	}
   348  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   349  
   350  	log.C(ctx).Infof("Deleting EventDefinition with id %s", id)
   351  
   352  	ctx = persistence.SaveToContext(ctx, tx)
   353  
   354  	event, err := r.svc.Get(ctx, id)
   355  	if err != nil {
   356  		return nil, err
   357  	}
   358  
   359  	spec, err := r.specService.GetByReferenceObjectID(ctx, resource.Application, model.EventSpecReference, event.ID)
   360  	if err != nil {
   361  		return nil, errors.Wrapf(err, "while getting spec for EventDefinition with id %q", event.ID)
   362  	}
   363  
   364  	bndlRef, err := r.bndlRefSvc.GetForBundle(ctx, model.BundleEventReference, &event.ID, nil)
   365  	if err != nil {
   366  		return nil, errors.Wrapf(err, "while getting bundle reference for EventDefinition with id %q", event.ID)
   367  	}
   368  
   369  	gqlEvent, err := r.converter.ToGraphQL(event, spec, bndlRef)
   370  	if err != nil {
   371  		return nil, errors.Wrapf(err, "while converting EventDefinition with id %q to graphQL", event.ID)
   372  	}
   373  
   374  	err = r.svc.Delete(ctx, resource.Application, id)
   375  	if err != nil {
   376  		return nil, err
   377  	}
   378  
   379  	err = tx.Commit()
   380  	if err != nil {
   381  		return nil, err
   382  	}
   383  
   384  	log.C(ctx).Infof("EventDefinition with id %s successfully deleted.", id)
   385  	return gqlEvent, nil
   386  }
   387  
   388  // RefetchEventDefinitionSpec refetches an EventDefinitionSpec for EventDefinition with given ID.
   389  func (r *Resolver) RefetchEventDefinitionSpec(ctx context.Context, eventID string) (*graphql.EventSpec, error) {
   390  	tx, err := r.transact.Begin()
   391  	if err != nil {
   392  		return nil, err
   393  	}
   394  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   395  
   396  	log.C(ctx).Infof("Refetching EventDefinitionSpec for EventDefinition with id %s", eventID)
   397  
   398  	ctx = persistence.SaveToContext(ctx, tx)
   399  
   400  	dbSpec, err := r.specService.GetByReferenceObjectID(ctx, resource.Application, model.EventSpecReference, eventID)
   401  	if err != nil {
   402  		return nil, errors.Wrapf(err, "while getting spec for EventDefinition with id %q", eventID)
   403  	}
   404  
   405  	if dbSpec == nil {
   406  		return nil, errors.Errorf("spec for Event with id %q not found", eventID)
   407  	}
   408  
   409  	spec, err := r.specService.RefetchSpec(ctx, dbSpec.ID, model.EventSpecReference)
   410  	if err != nil {
   411  		return nil, err
   412  	}
   413  
   414  	converted, err := r.specConverter.ToGraphQLEventSpec(spec)
   415  	if err != nil {
   416  		return nil, err
   417  	}
   418  
   419  	err = tx.Commit()
   420  	if err != nil {
   421  		return nil, err
   422  	}
   423  
   424  	log.C(ctx).Infof("Successfully refetched EventDefinitionSpec for EventDefinition with id %s", eventID)
   425  	return converted, nil
   426  }
   427  
   428  // FetchRequest returns a FetchRequest by a given EventSpec via dataloaders.
   429  func (r *Resolver) FetchRequest(ctx context.Context, obj *graphql.EventSpec) (*graphql.FetchRequest, error) {
   430  	params := dataloader.ParamFetchRequestEventDef{ID: obj.ID, Ctx: ctx}
   431  	return dataloader.ForFetchRequestEventDef(ctx).FetchRequestEventDefByID.Load(params)
   432  }
   433  
   434  // FetchRequestEventDefDataLoader is the dataloader implementation.
   435  func (r *Resolver) FetchRequestEventDefDataLoader(keys []dataloader.ParamFetchRequestEventDef) ([]*graphql.FetchRequest, []error) {
   436  	if len(keys) == 0 {
   437  		return nil, []error{apperrors.NewInternalError("No EventDef specs found")}
   438  	}
   439  
   440  	ctx := keys[0].Ctx
   441  
   442  	specIDs := make([]string, 0, len(keys))
   443  	for _, key := range keys {
   444  		if key.ID == "" {
   445  			return nil, []error{apperrors.NewInternalError("Cannot fetch FetchRequest. EventDefinition Spec ID is empty")}
   446  		}
   447  		specIDs = append(specIDs, key.ID)
   448  	}
   449  
   450  	tx, err := r.transact.Begin()
   451  	if err != nil {
   452  		return nil, []error{err}
   453  	}
   454  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   455  
   456  	ctx = persistence.SaveToContext(ctx, tx)
   457  
   458  	fetchRequests, err := r.svc.ListFetchRequests(ctx, specIDs)
   459  	if err != nil {
   460  		return nil, []error{err}
   461  	}
   462  
   463  	if fetchRequests == nil {
   464  		return nil, nil
   465  	}
   466  
   467  	gqlFetchRequests := make([]*graphql.FetchRequest, 0, len(fetchRequests))
   468  	for _, fr := range fetchRequests {
   469  		fetchRequest, err := r.frConverter.ToGraphQL(fr)
   470  		if err != nil {
   471  			return nil, []error{err}
   472  		}
   473  		gqlFetchRequests = append(gqlFetchRequests, fetchRequest)
   474  	}
   475  
   476  	err = tx.Commit()
   477  	if err != nil {
   478  		return nil, []error{err}
   479  	}
   480  
   481  	log.C(ctx).Infof("Successfully fetched requests for Specifications %v", specIDs)
   482  	return gqlFetchRequests, nil
   483  }