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

     1  package application
     2  
     3  import (
     4  	"context"
     5  	"crypto/sha256"
     6  	"fmt"
     7  	"strings"
     8  
     9  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
    10  
    11  	"github.com/kyma-incubator/compass/components/director/internal/domain/scenarioassignment"
    12  	"github.com/kyma-incubator/compass/components/director/pkg/consumer"
    13  	"github.com/kyma-incubator/compass/components/director/pkg/str"
    14  
    15  	pkgmodel "github.com/kyma-incubator/compass/components/director/pkg/model"
    16  
    17  	dataloader "github.com/kyma-incubator/compass/components/director/internal/dataloaders"
    18  
    19  	"github.com/kyma-incubator/compass/components/director/pkg/log"
    20  
    21  	"github.com/kyma-incubator/compass/components/director/pkg/inputvalidation"
    22  
    23  	"github.com/kyma-incubator/compass/components/director/internal/domain/tenant"
    24  
    25  	"github.com/kyma-incubator/compass/components/director/internal/domain/eventing"
    26  	"github.com/kyma-incubator/compass/components/director/pkg/apperrors"
    27  
    28  	"github.com/google/uuid"
    29  
    30  	"github.com/kyma-incubator/compass/components/director/pkg/persistence"
    31  
    32  	"github.com/kyma-incubator/compass/components/director/internal/labelfilter"
    33  	"github.com/kyma-incubator/compass/components/director/internal/model"
    34  	"github.com/kyma-incubator/compass/components/director/pkg/graphql"
    35  	"github.com/pkg/errors"
    36  )
    37  
    38  // ApplicationService missing godoc
    39  //
    40  //go:generate mockery --name=ApplicationService --output=automock --outpkg=automock --case=underscore --disable-version-string
    41  type ApplicationService interface {
    42  	Create(ctx context.Context, in model.ApplicationRegisterInput) (string, error)
    43  	Update(ctx context.Context, id string, in model.ApplicationUpdateInput) error
    44  	Get(ctx context.Context, id string) (*model.Application, error)
    45  	Delete(ctx context.Context, id string) error
    46  	List(ctx context.Context, filter []*labelfilter.LabelFilter, pageSize int, cursor string) (*model.ApplicationPage, error)
    47  	GetBySystemNumber(ctx context.Context, systemNumber string) (*model.Application, error)
    48  	ListByRuntimeID(ctx context.Context, runtimeUUID uuid.UUID, pageSize int, cursor string) (*model.ApplicationPage, error)
    49  	ListAll(ctx context.Context) ([]*model.Application, error)
    50  	SetLabel(ctx context.Context, label *model.LabelInput) error
    51  	GetLabel(ctx context.Context, applicationID string, key string) (*model.Label, error)
    52  	ListLabels(ctx context.Context, applicationID string) (map[string]*model.Label, error)
    53  	DeleteLabel(ctx context.Context, applicationID string, key string) error
    54  	Unpair(ctx context.Context, id string) error
    55  	Merge(ctx context.Context, destID, sourceID string) (*model.Application, error)
    56  }
    57  
    58  // ApplicationConverter missing godoc
    59  //
    60  //go:generate mockery --name=ApplicationConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
    61  type ApplicationConverter interface {
    62  	ToGraphQL(in *model.Application) *graphql.Application
    63  	MultipleToGraphQL(in []*model.Application) []*graphql.Application
    64  	CreateInputFromGraphQL(ctx context.Context, in graphql.ApplicationRegisterInput) (model.ApplicationRegisterInput, error)
    65  	UpdateInputFromGraphQL(in graphql.ApplicationUpdateInput) model.ApplicationUpdateInput
    66  	GraphQLToModel(obj *graphql.Application, tenantID string) *model.Application
    67  }
    68  
    69  // EventingService missing godoc
    70  //
    71  //go:generate mockery --name=EventingService --output=automock --outpkg=automock --case=underscore --disable-version-string
    72  type EventingService interface {
    73  	CleanupAfterUnregisteringApplication(ctx context.Context, appID uuid.UUID) (*model.ApplicationEventingConfiguration, error)
    74  	GetForApplication(ctx context.Context, app model.Application) (*model.ApplicationEventingConfiguration, error)
    75  }
    76  
    77  // WebhookService missing godoc
    78  //
    79  //go:generate mockery --name=WebhookService --output=automock --outpkg=automock --case=underscore --disable-version-string
    80  type WebhookService interface {
    81  	ListAllApplicationWebhooks(ctx context.Context, applicationTemplateID string) ([]*model.Webhook, error)
    82  }
    83  
    84  // SystemAuthService missing godoc
    85  //
    86  //go:generate mockery --name=SystemAuthService --output=automock --outpkg=automock --case=underscore --disable-version-string
    87  type SystemAuthService interface {
    88  	ListForObject(ctx context.Context, objectType pkgmodel.SystemAuthReferenceObjectType, objectID string) ([]pkgmodel.SystemAuth, error)
    89  	DeleteMultipleByIDForObject(ctx context.Context, systemAuths []pkgmodel.SystemAuth) error
    90  }
    91  
    92  // WebhookConverter missing godoc
    93  //
    94  //go:generate mockery --name=WebhookConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
    95  type WebhookConverter interface {
    96  	ToGraphQL(in *model.Webhook) (*graphql.Webhook, error)
    97  	MultipleToGraphQL(in []*model.Webhook) ([]*graphql.Webhook, error)
    98  	InputFromGraphQL(in *graphql.WebhookInput) (*model.WebhookInput, error)
    99  	MultipleInputFromGraphQL(in []*graphql.WebhookInput) ([]*model.WebhookInput, error)
   100  }
   101  
   102  // SystemAuthConverter missing godoc
   103  //
   104  //go:generate mockery --name=SystemAuthConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
   105  type SystemAuthConverter interface {
   106  	ToGraphQL(in *pkgmodel.SystemAuth) (graphql.SystemAuth, error)
   107  }
   108  
   109  // OAuth20Service missing godoc
   110  //
   111  //go:generate mockery --name=OAuth20Service --output=automock --outpkg=automock --case=underscore --disable-version-string
   112  type OAuth20Service interface {
   113  	DeleteMultipleClientCredentials(ctx context.Context, auths []pkgmodel.SystemAuth) error
   114  }
   115  
   116  // RuntimeService missing godoc
   117  //
   118  //go:generate mockery --name=RuntimeService --output=automock --outpkg=automock --case=underscore --disable-version-string
   119  type RuntimeService interface {
   120  	List(ctx context.Context, filter []*labelfilter.LabelFilter, pageSize int, cursor string) (*model.RuntimePage, error)
   121  	GetLabel(ctx context.Context, runtimeID string, key string) (*model.Label, error)
   122  }
   123  
   124  // BundleService missing godoc
   125  //
   126  //go:generate mockery --name=BundleService --output=automock --outpkg=automock --case=underscore --disable-version-string
   127  type BundleService interface {
   128  	GetForApplication(ctx context.Context, id string, applicationID string) (*model.Bundle, error)
   129  	ListByApplicationIDs(ctx context.Context, applicationIDs []string, pageSize int, cursor string) ([]*model.BundlePage, error)
   130  	CreateMultiple(ctx context.Context, resourceType resource.Type, resourceID string, in []*model.BundleCreateInput) error
   131  }
   132  
   133  // BundleConverter missing godoc
   134  //
   135  //go:generate mockery --name=BundleConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
   136  type BundleConverter interface {
   137  	ToGraphQL(in *model.Bundle) (*graphql.Bundle, error)
   138  	MultipleToGraphQL(in []*model.Bundle) ([]*graphql.Bundle, error)
   139  	MultipleCreateInputFromGraphQL(in []*graphql.BundleCreateInput) ([]*model.BundleCreateInput, error)
   140  }
   141  
   142  // OneTimeTokenService missing godoc
   143  //
   144  //go:generate mockery --name=OneTimeTokenService --output=automock --outpkg=automock --case=underscore --disable-version-string
   145  type OneTimeTokenService interface {
   146  	IsTokenValid(systemAuth *pkgmodel.SystemAuth) (bool, error)
   147  }
   148  
   149  // ApplicationTemplateService missing godoc
   150  //
   151  //go:generate mockery --name=ApplicationTemplateService --output=automock --outpkg=automock --case=underscore --disable-version-string
   152  type ApplicationTemplateService interface {
   153  	GetByFilters(ctx context.Context, filter []*labelfilter.LabelFilter) (*model.ApplicationTemplate, error)
   154  	Get(ctx context.Context, id string) (*model.ApplicationTemplate, error)
   155  }
   156  
   157  // ApplicationTemplateConverter converts between the graphql and model
   158  //
   159  //go:generate mockery --name=ApplicationTemplateConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
   160  type ApplicationTemplateConverter interface {
   161  	ToGraphQL(in *model.ApplicationTemplate) (*graphql.ApplicationTemplate, error)
   162  }
   163  
   164  // TenantBusinessTypeService is responsible for the service-layer Tenant Business Type operations.
   165  //
   166  //go:generate mockery --name=TenantBusinessTypeService --output=automock --outpkg=automock --case=underscore --exported=true --disable-version-string
   167  type TenantBusinessTypeService interface {
   168  	GetByID(ctx context.Context, id string) (*model.TenantBusinessType, error)
   169  }
   170  
   171  // TenantBusinessTypeConverter converts between the graphql and model
   172  //
   173  //go:generate mockery --name=TenantBusinessTypeConverter --output=automock --outpkg=automock --case=underscore --disable-version-string
   174  type TenantBusinessTypeConverter interface {
   175  	ToGraphQL(in *model.TenantBusinessType) *graphql.TenantBusinessType
   176  }
   177  
   178  // Resolver missing godoc
   179  type Resolver struct {
   180  	transact persistence.Transactioner
   181  
   182  	appSvc       ApplicationService
   183  	appConverter ApplicationConverter
   184  
   185  	appTemplateSvc       ApplicationTemplateService
   186  	appTemplateConverter ApplicationTemplateConverter
   187  
   188  	tenantBusinessTypeSvc       TenantBusinessTypeService
   189  	tenantBusinessTypeConverter TenantBusinessTypeConverter
   190  
   191  	webhookSvc WebhookService
   192  	oAuth20Svc OAuth20Service
   193  	sysAuthSvc SystemAuthService
   194  	bndlSvc    BundleService
   195  
   196  	webhookConverter WebhookConverter
   197  	sysAuthConv      SystemAuthConverter
   198  	eventingSvc      EventingService
   199  	bndlConv         BundleConverter
   200  
   201  	selfRegisterDistinguishLabelKey string
   202  	tokenPrefix                     string
   203  }
   204  
   205  // NewResolver missing godoc
   206  func NewResolver(transact persistence.Transactioner,
   207  	svc ApplicationService,
   208  	webhookSvc WebhookService,
   209  	oAuth20Svc OAuth20Service,
   210  	sysAuthSvc SystemAuthService,
   211  	appConverter ApplicationConverter,
   212  	webhookConverter WebhookConverter,
   213  	sysAuthConv SystemAuthConverter,
   214  	eventingSvc EventingService,
   215  	bndlSvc BundleService,
   216  	bndlConverter BundleConverter,
   217  	appTemplateSvc ApplicationTemplateService,
   218  	appTemplateConverter ApplicationTemplateConverter,
   219  	tenantBusinessTypeSvc TenantBusinessTypeService,
   220  	tenantBusinessTypeConverter TenantBusinessTypeConverter,
   221  	selfRegisterDistinguishLabelKey, tokenPrefix string) *Resolver {
   222  	return &Resolver{
   223  		transact:                        transact,
   224  		appSvc:                          svc,
   225  		webhookSvc:                      webhookSvc,
   226  		oAuth20Svc:                      oAuth20Svc,
   227  		sysAuthSvc:                      sysAuthSvc,
   228  		appConverter:                    appConverter,
   229  		webhookConverter:                webhookConverter,
   230  		sysAuthConv:                     sysAuthConv,
   231  		eventingSvc:                     eventingSvc,
   232  		bndlSvc:                         bndlSvc,
   233  		bndlConv:                        bndlConverter,
   234  		appTemplateSvc:                  appTemplateSvc,
   235  		appTemplateConverter:            appTemplateConverter,
   236  		tenantBusinessTypeSvc:           tenantBusinessTypeSvc,
   237  		tenantBusinessTypeConverter:     tenantBusinessTypeConverter,
   238  		selfRegisterDistinguishLabelKey: selfRegisterDistinguishLabelKey,
   239  		tokenPrefix:                     tokenPrefix,
   240  	}
   241  }
   242  
   243  // Applications retrieves all tenant scoped applications.
   244  // If this method is executed in a double authentication flow (i.e. consumerInfo.OnBehalfOf != nil)
   245  // then it would return 0 or 1 tenant applications - it would return 1 if there exists a tenant application
   246  // representing a tenant in an Application Provider and 0 if there is none such application.
   247  func (r *Resolver) Applications(ctx context.Context, filter []*graphql.LabelFilter, first *int, after *graphql.PageCursor) (*graphql.ApplicationPage, error) {
   248  	consumerInfo, err := consumer.LoadFromContext(ctx)
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  
   253  	tx, err := r.transact.Begin()
   254  	if err != nil {
   255  		return nil, err
   256  	}
   257  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   258  
   259  	ctx = persistence.SaveToContext(ctx, tx)
   260  
   261  	if consumerInfo.OnBehalfOf != "" {
   262  		log.C(ctx).Infof("External tenant with id %s is retrieving application on behalf of tenant with id REDACTED_%x", consumerInfo.ConsumerID, sha256.Sum256([]byte(consumerInfo.OnBehalfOf)))
   263  		tenantApp, err := r.getApplicationProviderTenant(ctx, consumerInfo)
   264  		if err != nil {
   265  			return nil, err
   266  		}
   267  
   268  		err = tx.Commit()
   269  		if err != nil {
   270  			return nil, err
   271  		}
   272  
   273  		return &graphql.ApplicationPage{
   274  			Data:       []*graphql.Application{tenantApp},
   275  			TotalCount: 1,
   276  			PageInfo: &graphql.PageInfo{
   277  				StartCursor: "1",
   278  				EndCursor:   "1",
   279  				HasNextPage: false,
   280  			},
   281  		}, nil
   282  	}
   283  
   284  	labelFilter := labelfilter.MultipleFromGraphQL(filter)
   285  
   286  	var cursor string
   287  	if after != nil {
   288  		cursor = string(*after)
   289  	}
   290  	if first == nil {
   291  		return nil, apperrors.NewInvalidDataError("missing required parameter 'first'")
   292  	}
   293  
   294  	appPage, err := r.appSvc.List(ctx, labelFilter, *first, cursor)
   295  	if err != nil {
   296  		return nil, err
   297  	}
   298  
   299  	err = tx.Commit()
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  
   304  	gqlApps := r.appConverter.MultipleToGraphQL(appPage.Data)
   305  
   306  	return &graphql.ApplicationPage{
   307  		Data:       gqlApps,
   308  		TotalCount: appPage.TotalCount,
   309  		PageInfo: &graphql.PageInfo{
   310  			StartCursor: graphql.PageCursor(appPage.PageInfo.StartCursor),
   311  			EndCursor:   graphql.PageCursor(appPage.PageInfo.EndCursor),
   312  			HasNextPage: appPage.PageInfo.HasNextPage,
   313  		},
   314  	}, nil
   315  }
   316  
   317  // ApplicationBySystemNumber returns an application retrieved by systemNumber
   318  func (r *Resolver) ApplicationBySystemNumber(ctx context.Context, systemNumber string) (*graphql.Application, error) {
   319  	return r.getApplication(ctx, func(ctx context.Context) (*model.Application, error) {
   320  		return r.appSvc.GetBySystemNumber(ctx, systemNumber)
   321  	})
   322  }
   323  
   324  // Application missing godoc
   325  func (r *Resolver) Application(ctx context.Context, id string) (*graphql.Application, error) {
   326  	return r.getApplication(ctx, func(ctx context.Context) (*model.Application, error) {
   327  		return r.appSvc.Get(ctx, id)
   328  	})
   329  }
   330  
   331  // ApplicationsForRuntime missing godoc
   332  func (r *Resolver) ApplicationsForRuntime(ctx context.Context, runtimeID string, first *int, after *graphql.PageCursor) (*graphql.ApplicationPage, error) {
   333  	var cursor string
   334  	if after != nil {
   335  		cursor = string(*after)
   336  	}
   337  
   338  	tx, err := r.transact.Begin()
   339  	if err != nil {
   340  		return nil, err
   341  	}
   342  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   343  
   344  	ctx = persistence.SaveToContext(ctx, tx)
   345  
   346  	if first == nil {
   347  		return nil, apperrors.NewInvalidDataError("missing required parameter 'first'")
   348  	}
   349  
   350  	runtimeUUID, err := uuid.Parse(runtimeID)
   351  	if err != nil {
   352  		return nil, errors.Wrap(err, "while converting runtimeID to UUID")
   353  	}
   354  
   355  	appPage, err := r.appSvc.ListByRuntimeID(ctx, runtimeUUID, *first, cursor)
   356  	if err != nil {
   357  		return nil, errors.Wrap(err, "while getting all Application for Runtime")
   358  	}
   359  
   360  	err = tx.Commit()
   361  	if err != nil {
   362  		return nil, err
   363  	}
   364  
   365  	gqlApps := r.appConverter.MultipleToGraphQL(appPage.Data)
   366  
   367  	return &graphql.ApplicationPage{
   368  		Data:       gqlApps,
   369  		TotalCount: appPage.TotalCount,
   370  		PageInfo: &graphql.PageInfo{
   371  			StartCursor: graphql.PageCursor(appPage.PageInfo.StartCursor),
   372  			EndCursor:   graphql.PageCursor(appPage.PageInfo.EndCursor),
   373  			HasNextPage: appPage.PageInfo.HasNextPage,
   374  		},
   375  	}, nil
   376  }
   377  
   378  // RegisterApplication missing godoc
   379  func (r *Resolver) RegisterApplication(ctx context.Context, in graphql.ApplicationRegisterInput) (*graphql.Application, error) {
   380  	log.C(ctx).Infof("Registering Application with name %s", in.Name)
   381  
   382  	convertedIn, err := r.appConverter.CreateInputFromGraphQL(ctx, in)
   383  	if err != nil {
   384  		return nil, errors.Wrap(err, "while converting ApplicationRegister input")
   385  	}
   386  
   387  	if convertedIn.Labels == nil {
   388  		convertedIn.Labels = make(map[string]interface{})
   389  	}
   390  	convertedIn.Labels["managed"] = "false"
   391  
   392  	id, err := r.appSvc.Create(ctx, convertedIn)
   393  	if err != nil {
   394  		return nil, err
   395  	}
   396  
   397  	app, err := r.appSvc.Get(ctx, id)
   398  	if err != nil {
   399  		return nil, err
   400  	}
   401  
   402  	gqlApp := r.appConverter.ToGraphQL(app)
   403  
   404  	log.C(ctx).Infof("Application with name %s and id %s successfully registered", in.Name, id)
   405  	return gqlApp, nil
   406  }
   407  
   408  // UpdateApplication missing godoc
   409  func (r *Resolver) UpdateApplication(ctx context.Context, id string, in graphql.ApplicationUpdateInput) (*graphql.Application, error) {
   410  	log.C(ctx).Infof("Updating Application with id %s", id)
   411  
   412  	convertedIn := r.appConverter.UpdateInputFromGraphQL(in)
   413  	err := r.appSvc.Update(ctx, id, convertedIn)
   414  	if err != nil {
   415  		return nil, err
   416  	}
   417  
   418  	app, err := r.appSvc.Get(ctx, id)
   419  	if err != nil {
   420  		return nil, err
   421  	}
   422  
   423  	gqlApp := r.appConverter.ToGraphQL(app)
   424  
   425  	log.C(ctx).Infof("Application with id %s successfully updated", id)
   426  
   427  	return gqlApp, nil
   428  }
   429  
   430  // UnregisterApplication missing godoc
   431  func (r *Resolver) UnregisterApplication(ctx context.Context, id string) (*graphql.Application, error) {
   432  	log.C(ctx).Infof("Unregistering Application with id %s", id)
   433  
   434  	app, err := r.appSvc.Get(ctx, id)
   435  	if err != nil {
   436  		return nil, err
   437  	}
   438  
   439  	appID, err := uuid.Parse(app.ID)
   440  	if err != nil {
   441  		return nil, errors.Wrap(err, "while parsing application ID as UUID")
   442  	}
   443  
   444  	if _, err = r.eventingSvc.CleanupAfterUnregisteringApplication(ctx, appID); err != nil {
   445  		return nil, err
   446  	}
   447  
   448  	auths, err := r.sysAuthSvc.ListForObject(ctx, pkgmodel.ApplicationReference, app.ID)
   449  	if err != nil {
   450  		return nil, err
   451  	}
   452  
   453  	err = r.oAuth20Svc.DeleteMultipleClientCredentials(ctx, auths)
   454  	if err != nil {
   455  		return nil, err
   456  	}
   457  	err = r.appSvc.Delete(ctx, id)
   458  	if err != nil {
   459  		return nil, err
   460  	}
   461  
   462  	deletedApp := r.appConverter.ToGraphQL(app)
   463  
   464  	log.C(ctx).Infof("Successfully unregistered Application with id %s", id)
   465  	return deletedApp, nil
   466  }
   467  
   468  // UnpairApplication Sets the UpdatedAt property for the given application, deletes associated []model.SystemAuth, deletes the hydra oauth clients.
   469  func (r *Resolver) UnpairApplication(ctx context.Context, id string) (*graphql.Application, error) {
   470  	log.C(ctx).Infof("Unpairing Application with id %s", id)
   471  
   472  	if err := r.appSvc.Unpair(ctx, id); err != nil {
   473  		return nil, err
   474  	}
   475  
   476  	app, err := r.appSvc.Get(ctx, id)
   477  	if err != nil {
   478  		return nil, err
   479  	}
   480  
   481  	auths, err := r.sysAuthSvc.ListForObject(ctx, pkgmodel.ApplicationReference, app.ID)
   482  	if err != nil {
   483  		return nil, err
   484  	}
   485  
   486  	if err = r.sysAuthSvc.DeleteMultipleByIDForObject(ctx, auths); err != nil {
   487  		return nil, err
   488  	}
   489  
   490  	if err = r.oAuth20Svc.DeleteMultipleClientCredentials(ctx, auths); err != nil {
   491  		return nil, err
   492  	}
   493  
   494  	gqlApp := r.appConverter.ToGraphQL(app)
   495  
   496  	log.C(ctx).Infof("Successfully Unpaired Application with id %s", id)
   497  	return gqlApp, nil
   498  }
   499  
   500  // SetApplicationLabel missing godoc
   501  func (r *Resolver) SetApplicationLabel(ctx context.Context, applicationID string, key string, value interface{}) (*graphql.Label, error) {
   502  	// TODO: Use @validation directive on input type instead, after resolving https://github.com/kyma-incubator/compass/issues/515
   503  	gqlLabel := graphql.LabelInput{Key: key, Value: value}
   504  	if err := inputvalidation.Validate(&gqlLabel); err != nil {
   505  		return nil, errors.Wrap(err, "validation error for type LabelInput")
   506  	}
   507  
   508  	tx, err := r.transact.Begin()
   509  	if err != nil {
   510  		return nil, err
   511  	}
   512  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   513  
   514  	ctx = persistence.SaveToContext(ctx, tx)
   515  
   516  	err = r.appSvc.SetLabel(ctx, &model.LabelInput{
   517  		Key:        key,
   518  		Value:      value,
   519  		ObjectType: model.ApplicationLabelableObject,
   520  		ObjectID:   applicationID,
   521  	})
   522  	if err != nil {
   523  		return nil, err
   524  	}
   525  
   526  	err = tx.Commit()
   527  	if err != nil {
   528  		return nil, err
   529  	}
   530  
   531  	return &graphql.Label{
   532  		Key:   key,
   533  		Value: value,
   534  	}, nil
   535  }
   536  
   537  // MergeApplications merges properties from Source Application into Destination Application, provided that the Destination's
   538  // Application does not have a value set for a given property. Then the Source Application is being deleted.
   539  func (r *Resolver) MergeApplications(ctx context.Context, destID string, sourceID string) (*graphql.Application, error) {
   540  	log.C(ctx).Infof("Merging source app with id %s into destination app with id %s", sourceID, destID)
   541  
   542  	tx, err := r.transact.Begin()
   543  	if err != nil {
   544  		return nil, err
   545  	}
   546  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   547  
   548  	ctx = persistence.SaveToContext(ctx, tx)
   549  
   550  	mergedApp, err := r.appSvc.Merge(ctx, destID, sourceID)
   551  	if err != nil {
   552  		return nil, err
   553  	}
   554  
   555  	err = tx.Commit()
   556  	if err != nil {
   557  		return nil, err
   558  	}
   559  
   560  	gqlApp := r.appConverter.ToGraphQL(mergedApp)
   561  
   562  	return gqlApp, nil
   563  }
   564  
   565  // DeleteApplicationLabel missing godoc
   566  func (r *Resolver) DeleteApplicationLabel(ctx context.Context, applicationID string, key string) (*graphql.Label, error) {
   567  	tx, err := r.transact.Begin()
   568  	if err != nil {
   569  		return nil, err
   570  	}
   571  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   572  
   573  	ctx = persistence.SaveToContext(ctx, tx)
   574  
   575  	label, err := r.appSvc.GetLabel(ctx, applicationID, key)
   576  	if err != nil {
   577  		return nil, err
   578  	}
   579  
   580  	err = r.appSvc.DeleteLabel(ctx, applicationID, key)
   581  	if err != nil {
   582  		return nil, err
   583  	}
   584  
   585  	err = tx.Commit()
   586  	if err != nil {
   587  		return nil, err
   588  	}
   589  
   590  	return &graphql.Label{
   591  		Key:   key,
   592  		Value: label.Value,
   593  	}, nil
   594  }
   595  
   596  // Webhooks missing godoc
   597  // TODO: Proper error handling
   598  func (r *Resolver) Webhooks(ctx context.Context, obj *graphql.Application) ([]*graphql.Webhook, error) {
   599  	tx, err := r.transact.Begin()
   600  	if err != nil {
   601  		return nil, err
   602  	}
   603  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   604  
   605  	ctx = persistence.SaveToContext(ctx, tx)
   606  
   607  	webhooks, err := r.webhookSvc.ListAllApplicationWebhooks(ctx, obj.ID)
   608  	if err != nil {
   609  		if apperrors.IsNotFoundError(err) {
   610  			return nil, tx.Commit()
   611  		}
   612  		return nil, err
   613  	}
   614  
   615  	gqlWebhooks, err := r.webhookConverter.MultipleToGraphQL(webhooks)
   616  	if err != nil {
   617  		return nil, err
   618  	}
   619  
   620  	if err = tx.Commit(); err != nil {
   621  		return nil, err
   622  	}
   623  
   624  	return gqlWebhooks, nil
   625  }
   626  
   627  // Labels missing godoc
   628  func (r *Resolver) Labels(ctx context.Context, obj *graphql.Application, key *string) (graphql.Labels, error) {
   629  	if obj == nil {
   630  		return nil, apperrors.NewInternalError("Application cannot be empty")
   631  	}
   632  
   633  	tx, err := r.transact.Begin()
   634  	if err != nil {
   635  		return nil, err
   636  	}
   637  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   638  
   639  	ctx = persistence.SaveToContext(ctx, tx)
   640  
   641  	itemMap, err := r.appSvc.ListLabels(ctx, obj.ID)
   642  	if err != nil {
   643  		if strings.Contains(err.Error(), "doesn't exist") {
   644  			return nil, tx.Commit()
   645  		}
   646  		return nil, err
   647  	}
   648  
   649  	err = tx.Commit()
   650  	if err != nil {
   651  		return nil, err
   652  	}
   653  
   654  	resultLabels := make(map[string]interface{})
   655  
   656  	for _, label := range itemMap {
   657  		if key == nil || label.Key == *key {
   658  			resultLabels[label.Key] = label.Value
   659  		}
   660  	}
   661  
   662  	var gqlLabels graphql.Labels = resultLabels
   663  	return gqlLabels, nil
   664  }
   665  
   666  // Auths missing godoc
   667  func (r *Resolver) Auths(ctx context.Context, obj *graphql.Application) ([]*graphql.AppSystemAuth, error) {
   668  	if obj == nil {
   669  		return nil, apperrors.NewInternalError("Application cannot be empty")
   670  	}
   671  
   672  	tx, err := r.transact.Begin()
   673  	if err != nil {
   674  		return nil, err
   675  	}
   676  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   677  	ctx = persistence.SaveToContext(ctx, tx)
   678  
   679  	sysAuths, err := r.sysAuthSvc.ListForObject(ctx, pkgmodel.ApplicationReference, obj.ID)
   680  	if err != nil {
   681  		return nil, err
   682  	}
   683  
   684  	err = tx.Commit()
   685  	if err != nil {
   686  		return nil, err
   687  	}
   688  
   689  	out := make([]*graphql.AppSystemAuth, 0, len(sysAuths))
   690  	for _, sa := range sysAuths {
   691  		c, err := r.sysAuthConv.ToGraphQL(&sa)
   692  		if err != nil {
   693  			return nil, err
   694  		}
   695  
   696  		out = append(out, c.(*graphql.AppSystemAuth))
   697  	}
   698  
   699  	return out, nil
   700  }
   701  
   702  // EventingConfiguration missing godoc
   703  func (r *Resolver) EventingConfiguration(ctx context.Context, obj *graphql.Application) (*graphql.ApplicationEventingConfiguration, error) {
   704  	if obj == nil {
   705  		return nil, apperrors.NewInternalError("Application cannot be empty")
   706  	}
   707  	tenantID, err := tenant.LoadFromContext(ctx)
   708  	if err != nil {
   709  		return nil, apperrors.NewCannotReadTenantError()
   710  	}
   711  
   712  	app := r.appConverter.GraphQLToModel(obj, tenantID)
   713  	if app == nil {
   714  		return nil, apperrors.NewInternalError("application cannot be empty")
   715  	}
   716  
   717  	tx, err := r.transact.Begin()
   718  	if err != nil {
   719  		return nil, errors.Wrap(err, "while opening the transaction")
   720  	}
   721  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   722  
   723  	ctx = persistence.SaveToContext(ctx, tx)
   724  
   725  	eventingCfg, err := r.eventingSvc.GetForApplication(ctx, *app)
   726  	if err != nil {
   727  		return nil, errors.Wrap(err, "while fetching eventing cofiguration for application")
   728  	}
   729  
   730  	if err = tx.Commit(); err != nil {
   731  		return nil, errors.Wrap(err, "while committing the transaction")
   732  	}
   733  
   734  	return eventing.ApplicationEventingConfigurationToGraphQL(eventingCfg), nil
   735  }
   736  
   737  // Bundles missing godoc
   738  func (r *Resolver) Bundles(ctx context.Context, obj *graphql.Application, first *int, after *graphql.PageCursor) (*graphql.BundlePage, error) {
   739  	param := dataloader.ParamBundle{ID: obj.ID, Ctx: ctx, First: first, After: after}
   740  	return dataloader.BundleFor(ctx).BundleByID.Load(param)
   741  }
   742  
   743  // BundlesDataLoader missing godoc
   744  func (r *Resolver) BundlesDataLoader(keys []dataloader.ParamBundle) ([]*graphql.BundlePage, []error) {
   745  	if len(keys) == 0 {
   746  		return nil, []error{apperrors.NewInternalError("No Applications found")}
   747  	}
   748  
   749  	ctx := keys[0].Ctx
   750  	applicationIDs := make([]string, 0, len(keys))
   751  	for _, key := range keys {
   752  		applicationIDs = append(applicationIDs, key.ID)
   753  	}
   754  
   755  	var cursor string
   756  	if keys[0].After != nil {
   757  		cursor = string(*keys[0].After)
   758  	}
   759  
   760  	if keys[0].First == nil {
   761  		return nil, []error{apperrors.NewInvalidDataError("missing required parameter 'first'")}
   762  	}
   763  
   764  	tx, err := r.transact.Begin()
   765  	if err != nil {
   766  		return nil, []error{err}
   767  	}
   768  
   769  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   770  
   771  	ctx = persistence.SaveToContext(ctx, tx)
   772  
   773  	bndlPages, err := r.bndlSvc.ListByApplicationIDs(ctx, applicationIDs, *keys[0].First, cursor)
   774  	if err != nil {
   775  		return nil, []error{err}
   776  	}
   777  
   778  	gqlBndls := make([]*graphql.BundlePage, 0, len(bndlPages))
   779  	for _, page := range bndlPages {
   780  		bndls, err := r.bndlConv.MultipleToGraphQL(page.Data)
   781  		if err != nil {
   782  			return nil, []error{err}
   783  		}
   784  
   785  		gqlBndls = append(gqlBndls, &graphql.BundlePage{Data: bndls, TotalCount: page.TotalCount, PageInfo: &graphql.PageInfo{
   786  			StartCursor: graphql.PageCursor(page.PageInfo.StartCursor),
   787  			EndCursor:   graphql.PageCursor(page.PageInfo.EndCursor),
   788  			HasNextPage: page.PageInfo.HasNextPage,
   789  		}})
   790  	}
   791  
   792  	err = tx.Commit()
   793  	if err != nil {
   794  		return nil, []error{err}
   795  	}
   796  
   797  	return gqlBndls, nil
   798  }
   799  
   800  // Bundle missing godoc
   801  func (r *Resolver) Bundle(ctx context.Context, obj *graphql.Application, id string) (*graphql.Bundle, error) {
   802  	if obj == nil {
   803  		return nil, apperrors.NewInternalError("Application cannot be empty")
   804  	}
   805  
   806  	tx, err := r.transact.Begin()
   807  	if err != nil {
   808  		return nil, err
   809  	}
   810  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   811  
   812  	ctx = persistence.SaveToContext(ctx, tx)
   813  
   814  	bndl, err := r.bndlSvc.GetForApplication(ctx, id, obj.ID)
   815  	if err != nil {
   816  		if apperrors.IsNotFoundError(err) {
   817  			return nil, tx.Commit()
   818  		}
   819  		return nil, err
   820  	}
   821  
   822  	gqlBundle, err := r.bndlConv.ToGraphQL(bndl)
   823  	if err != nil {
   824  		return nil, err
   825  	}
   826  
   827  	err = tx.Commit()
   828  	if err != nil {
   829  		return nil, err
   830  	}
   831  
   832  	return gqlBundle, nil
   833  }
   834  
   835  // ApplicationTemplate retrieves application template by given application
   836  func (r *Resolver) ApplicationTemplate(ctx context.Context, obj *graphql.Application) (*graphql.ApplicationTemplate, error) {
   837  	if obj == nil {
   838  		return nil, apperrors.NewInternalError("Application cannot be empty")
   839  	}
   840  	if obj.ApplicationTemplateID == nil {
   841  		return nil, nil
   842  	}
   843  
   844  	tx, err := r.transact.Begin()
   845  	if err != nil {
   846  		return nil, err
   847  	}
   848  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   849  
   850  	ctx = persistence.SaveToContext(ctx, tx)
   851  
   852  	appTemplate, err := r.appTemplateSvc.Get(ctx, *obj.ApplicationTemplateID)
   853  	if err != nil {
   854  		log.C(ctx).Infof("No app template found with id %s", *obj.ApplicationTemplateID)
   855  		return nil, errors.Wrapf(err, "no app template found with id %s", *obj.ApplicationTemplateID)
   856  	}
   857  
   858  	if err = tx.Commit(); err != nil {
   859  		return nil, err
   860  	}
   861  
   862  	return r.appTemplateConverter.ToGraphQL(appTemplate)
   863  }
   864  
   865  // TenantBusinessType retrieves tenant business type by given application
   866  func (r *Resolver) TenantBusinessType(ctx context.Context, obj *graphql.Application) (*graphql.TenantBusinessType, error) {
   867  	if obj == nil {
   868  		return nil, apperrors.NewInternalError("Application cannot be empty")
   869  	}
   870  	if obj.TenantBusinessTypeID == nil {
   871  		return nil, nil
   872  	}
   873  
   874  	tx, err := r.transact.Begin()
   875  	if err != nil {
   876  		return nil, err
   877  	}
   878  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   879  
   880  	ctx = persistence.SaveToContext(ctx, tx)
   881  
   882  	tenantBusinessType, err := r.tenantBusinessTypeSvc.GetByID(ctx, *obj.TenantBusinessTypeID)
   883  	if err != nil {
   884  		log.C(ctx).Infof("No tenant business type found with id %s", *obj.TenantBusinessTypeID)
   885  		return nil, errors.Wrapf(err, "no tenant business type found with id %s", *obj.TenantBusinessTypeID)
   886  	}
   887  
   888  	if err = tx.Commit(); err != nil {
   889  		return nil, err
   890  	}
   891  
   892  	return r.tenantBusinessTypeConverter.ToGraphQL(tenantBusinessType), nil
   893  }
   894  
   895  // getApplicationProviderTenant should be used when making requests with double authentication, i.e. consumerInfo.OnBehalfOf != nil;
   896  // The function leverages the knowledge of the provider tenant (it's in consumerInfo.ConsumerID) and consumer tenant (it's already set in the TenantCtx)
   897  // in order to derive the application template representing an Application Provider and then finding an application among those of the consumer tenant
   898  // which is associated with that application template.
   899  // In this way the getApplicationProviderTenant function finds a specific tenant application of a given Application Provider - there should only be one or none.
   900  func (r *Resolver) getApplicationProviderTenant(ctx context.Context, consumerInfo consumer.Consumer) (*graphql.Application, error) {
   901  	tokenClientID := strings.TrimPrefix(consumerInfo.TokenClientID, r.tokenPrefix)
   902  	filters := []*labelfilter.LabelFilter{
   903  		labelfilter.NewForKeyWithQuery(scenarioassignment.SubaccountIDKey, fmt.Sprintf("\"%s\"", consumerInfo.ConsumerID)),
   904  		labelfilter.NewForKeyWithQuery(tenant.RegionLabelKey, fmt.Sprintf("\"%s\"", consumerInfo.Region)),
   905  		labelfilter.NewForKeyWithQuery(r.selfRegisterDistinguishLabelKey, fmt.Sprintf("\"%s\"", tokenClientID)),
   906  	}
   907  
   908  	// Derive application provider's app template
   909  	appTemplate, err := r.appTemplateSvc.GetByFilters(ctx, filters)
   910  	if err != nil {
   911  		log.C(ctx).Infof("No app template found with filter %q = REDACTED_%x, %q = %q, %q = %q", scenarioassignment.SubaccountIDKey, sha256.Sum256([]byte(consumerInfo.ConsumerID)), tenant.RegionLabelKey, consumerInfo.Region, r.selfRegisterDistinguishLabelKey, tokenClientID)
   912  		return nil, errors.Wrapf(err, "no app template found with filter %q = %q, %q = %q, %q = %q", scenarioassignment.SubaccountIDKey, consumerInfo.ConsumerID, tenant.RegionLabelKey, consumerInfo.Region, r.selfRegisterDistinguishLabelKey, tokenClientID)
   913  	}
   914  
   915  	// Find the consuming tenant's applications
   916  	tntApplications, err := r.appSvc.ListAll(ctx)
   917  	if err != nil {
   918  		return nil, errors.Wrap(err, "while listing applications for tenant")
   919  	}
   920  
   921  	// Try to find the application matching the derived provider app template
   922  	var foundApp *model.Application
   923  	for _, app := range tntApplications {
   924  		if str.PtrStrToStr(app.ApplicationTemplateID) == appTemplate.ID {
   925  			foundApp = app
   926  			break
   927  		}
   928  	}
   929  
   930  	if foundApp == nil {
   931  		log.C(ctx).Infof("No application found for template with ID %q", appTemplate.ID)
   932  		return nil, errors.Errorf("No application found for template with ID %q", appTemplate.ID)
   933  	}
   934  
   935  	return r.appConverter.ToGraphQL(foundApp), nil
   936  }
   937  
   938  func (r *Resolver) getApplication(ctx context.Context, get func(context.Context) (*model.Application, error)) (*graphql.Application, error) {
   939  	tx, err := r.transact.Begin()
   940  	if err != nil {
   941  		return nil, err
   942  	}
   943  	defer r.transact.RollbackUnlessCommitted(ctx, tx)
   944  
   945  	ctx = persistence.SaveToContext(ctx, tx)
   946  	app, err := get(ctx)
   947  
   948  	if err != nil {
   949  		if apperrors.IsNotFoundError(err) {
   950  			return nil, tx.Commit()
   951  		}
   952  		return nil, err
   953  	}
   954  
   955  	err = tx.Commit()
   956  	if err != nil {
   957  		return nil, err
   958  	}
   959  
   960  	return r.appConverter.ToGraphQL(app), nil
   961  }