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 }