github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/formation/notifications_generator.go (about) 1 package formation 2 3 import ( 4 "context" 5 6 "github.com/kyma-incubator/compass/components/director/internal/domain/formationassignment" 7 databuilder "github.com/kyma-incubator/compass/components/director/internal/domain/webhook/datainputbuilder" 8 "github.com/kyma-incubator/compass/components/director/internal/model" 9 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 10 "github.com/kyma-incubator/compass/components/director/pkg/formationconstraint" 11 "github.com/kyma-incubator/compass/components/director/pkg/log" 12 "github.com/kyma-incubator/compass/components/director/pkg/str" 13 webhookdir "github.com/kyma-incubator/compass/components/director/pkg/webhook" 14 webhookclient "github.com/kyma-incubator/compass/components/director/pkg/webhook_client" 15 "github.com/pkg/errors" 16 ) 17 18 //go:generate mockery --exported --name=applicationRepository --output=automock --outpkg=automock --case=underscore --disable-version-string 19 type applicationRepository interface { 20 GetByID(ctx context.Context, tenant, id string) (*model.Application, error) 21 ListAllByIDs(ctx context.Context, tenantID string, ids []string) ([]*model.Application, error) 22 ListByScenariosNoPaging(ctx context.Context, tenant string, scenarios []string) ([]*model.Application, error) 23 ListByScenariosAndIDs(ctx context.Context, tenant string, scenarios []string, ids []string) ([]*model.Application, error) 24 ListListeningApplications(ctx context.Context, tenant string, whType model.WebhookType) ([]*model.Application, error) 25 } 26 27 //go:generate mockery --exported --name=applicationTemplateRepository --output=automock --outpkg=automock --case=underscore --disable-version-string 28 type applicationTemplateRepository interface { 29 Get(ctx context.Context, id string) (*model.ApplicationTemplate, error) 30 ListByIDs(ctx context.Context, ids []string) ([]*model.ApplicationTemplate, error) 31 } 32 33 //go:generate mockery --exported --name=webhookRepository --output=automock --outpkg=automock --case=underscore --disable-version-string 34 type webhookRepository interface { 35 ListByReferenceObjectTypeAndWebhookType(ctx context.Context, tenant string, whType model.WebhookType, objType model.WebhookReferenceObjectType) ([]*model.Webhook, error) 36 ListByReferenceObjectTypesAndWebhookType(ctx context.Context, tenant string, whType model.WebhookType, objTypes []model.WebhookReferenceObjectType) ([]*model.Webhook, error) 37 ListByReferenceObjectIDGlobal(ctx context.Context, objID string, objType model.WebhookReferenceObjectType) ([]*model.Webhook, error) 38 GetByIDAndWebhookType(ctx context.Context, tenant, objectID string, objectType model.WebhookReferenceObjectType, webhookType model.WebhookType) (*model.Webhook, error) 39 } 40 41 //go:generate mockery --exported --name=notificationBuilder --output=automock --outpkg=automock --case=underscore --disable-version-string 42 type notificationBuilder interface { 43 BuildFormationAssignmentNotificationRequest(ctx context.Context, formationTemplateID string, joinPointDetails *formationconstraint.GenerateFormationAssignmentNotificationOperationDetails, webhook *model.Webhook) (*webhookclient.FormationAssignmentNotificationRequest, error) 44 BuildFormationNotificationRequests(ctx context.Context, joinPointDetails *formationconstraint.GenerateFormationNotificationOperationDetails, formation *model.Formation, formationTemplateWebhooks []*model.Webhook) ([]*webhookclient.FormationNotificationRequest, error) 45 PrepareDetailsForConfigurationChangeNotificationGeneration(operation model.FormationOperation, formationID string, formationTemplateID string, applicationTemplate *webhookdir.ApplicationTemplateWithLabels, application *webhookdir.ApplicationWithLabels, runtime *webhookdir.RuntimeWithLabels, runtimeContext *webhookdir.RuntimeContextWithLabels, assignment *webhookdir.FormationAssignment, reverseAssignment *webhookdir.FormationAssignment, targetType model.ResourceType, tenantContext *webhookdir.CustomerTenantContext, tenantID string) (*formationconstraint.GenerateFormationAssignmentNotificationOperationDetails, error) 46 PrepareDetailsForApplicationTenantMappingNotificationGeneration(operation model.FormationOperation, formationID string, formationTemplateID string, sourceApplicationTemplate *webhookdir.ApplicationTemplateWithLabels, sourceApplication *webhookdir.ApplicationWithLabels, targetApplicationTemplate *webhookdir.ApplicationTemplateWithLabels, targetApplication *webhookdir.ApplicationWithLabels, assignment *webhookdir.FormationAssignment, reverseAssignment *webhookdir.FormationAssignment, tenantContext *webhookdir.CustomerTenantContext, tenantID string) (*formationconstraint.GenerateFormationAssignmentNotificationOperationDetails, error) 47 } 48 49 // NotificationsGenerator is responsible for generation of notification requests 50 type NotificationsGenerator struct { 51 applicationRepository applicationRepository 52 applicationTemplateRepository applicationTemplateRepository 53 runtimeRepo runtimeRepository 54 runtimeContextRepo runtimeContextRepository 55 labelRepository labelRepository 56 webhookRepository webhookRepository 57 webhookDataInputBuilder databuilder.DataInputBuilder 58 notificationBuilder notificationBuilder 59 } 60 61 // NewNotificationsGenerator returns an instance of NotificationsGenerator 62 func NewNotificationsGenerator( 63 applicationRepository applicationRepository, 64 applicationTemplateRepository applicationTemplateRepository, 65 runtimeRepo runtimeRepository, 66 runtimeContextRepo runtimeContextRepository, 67 labelRepository labelRepository, 68 webhookRepository webhookRepository, 69 webhookDataInputBuilder databuilder.DataInputBuilder, 70 notificationBuilder notificationBuilder) *NotificationsGenerator { 71 return &NotificationsGenerator{ 72 applicationRepository: applicationRepository, 73 applicationTemplateRepository: applicationTemplateRepository, 74 runtimeRepo: runtimeRepo, 75 runtimeContextRepo: runtimeContextRepo, 76 labelRepository: labelRepository, 77 webhookRepository: webhookRepository, 78 webhookDataInputBuilder: webhookDataInputBuilder, 79 notificationBuilder: notificationBuilder, 80 } 81 } 82 83 // GenerateNotificationsAboutRuntimeAndRuntimeContextForTheApplicationThatIsAssigned generates notification with target the application that is assigned about for each runtime and each runtimeContext that is part of the formation 84 func (ns *NotificationsGenerator) GenerateNotificationsAboutRuntimeAndRuntimeContextForTheApplicationThatIsAssigned(ctx context.Context, tenant string, appID string, formation *model.Formation, operation model.FormationOperation, customerTenantContext *webhookdir.CustomerTenantContext) ([]*webhookclient.FormationAssignmentNotificationRequest, error) { 85 log.C(ctx).Infof("Generating %q notifications during %q operation about runtimes and runtime contexts in the same formation for application with ID: %q", model.WebhookTypeConfigurationChanged, operation, appID) 86 applicationWithLabels, appTemplateWithLabels, err := ns.webhookDataInputBuilder.PrepareApplicationAndAppTemplateWithLabels(ctx, tenant, appID) 87 if err != nil { 88 return nil, errors.Wrap(err, "while preparing application and application template with labels") 89 } 90 91 appTemplateID := "" 92 if appTemplateWithLabels != nil { 93 appTemplateID = appTemplateWithLabels.ID 94 } 95 96 webhook, err := formationassignment.GetWebhookForApplication(ctx, ns.webhookRepository, tenant, appID, appTemplateID, model.WebhookTypeConfigurationChanged) 97 if err != nil { 98 return nil, err 99 } 100 if webhook == nil { 101 return nil, nil 102 } 103 104 runtimesMapping, runtimesToRuntimeContextsMapping, err := ns.webhookDataInputBuilder.PrepareRuntimesAndRuntimeContextsMappingsInFormation(ctx, tenant, formation.Name) 105 if err != nil { 106 return nil, errors.Wrap(err, "while preparing runtime and runtime contexts mappings") 107 } 108 109 requests := make([]*webhookclient.FormationAssignmentNotificationRequest, 0, len(runtimesMapping)) 110 for rtID := range runtimesMapping { 111 rtCtx := runtimesToRuntimeContextsMapping[rtID] 112 if rtCtx == nil { 113 log.C(ctx).Infof("There is no runtime context for runtime with ID: %q in formation %q. Will proceed without runtime context in the input for webhook with ID: %q", rtID, formation.Name, webhook.ID) 114 } 115 runtime := runtimesMapping[rtID] 116 if appTemplateWithLabels == nil { 117 log.C(ctx).Infof("Application with ID: %q has no application template. Will proceed without application template in the input for webhook with ID: %q", appID, webhook.ID) 118 } 119 120 details, err := ns.notificationBuilder.PrepareDetailsForConfigurationChangeNotificationGeneration( 121 operation, 122 formation.ID, 123 formation.FormationTemplateID, 124 appTemplateWithLabels, 125 applicationWithLabels, 126 runtime, 127 rtCtx, 128 emptyFormationAssignment, 129 emptyFormationAssignment, 130 model.ApplicationResourceType, 131 customerTenantContext, 132 tenant) 133 if err != nil { 134 return nil, err 135 } 136 req, err := ns.notificationBuilder.BuildFormationAssignmentNotificationRequest(ctx, formation.FormationTemplateID, details, webhook) 137 if err != nil { 138 return nil, errors.Wrap(err, "Failed to build formation assignment notification request") 139 } else if req != nil { 140 requests = append(requests, req) 141 } 142 } 143 return requests, nil 144 } 145 146 // GenerateNotificationsForRuntimeAboutTheApplicationThatIsAssigned generates notification per runtime that is part of the formation with target the runtime and source the application on which `operation` is performed 147 func (ns *NotificationsGenerator) GenerateNotificationsForRuntimeAboutTheApplicationThatIsAssigned(ctx context.Context, tenant string, appID string, formation *model.Formation, operation model.FormationOperation, customerTenantContext *webhookdir.CustomerTenantContext) ([]*webhookclient.FormationAssignmentNotificationRequest, error) { 148 log.C(ctx).Infof("Generating %q notifications during %q operation about application with ID: %q for all listening runtimes in the same formation", model.WebhookTypeConfigurationChanged, operation, appID) 149 applicationWithLabels, appTemplateWithLabels, err := ns.webhookDataInputBuilder.PrepareApplicationAndAppTemplateWithLabels(ctx, tenant, appID) 150 if err != nil { 151 return nil, errors.Wrap(err, "while preparing application and application template with labels") 152 } 153 154 webhooks, err := ns.webhookRepository.ListByReferenceObjectTypeAndWebhookType(ctx, tenant, model.WebhookTypeConfigurationChanged, model.RuntimeWebhookReference) 155 if err != nil { 156 return nil, errors.Wrap(err, "when listing configuration changed webhooks for runtimes") 157 } 158 159 if len(webhooks) == 0 { 160 log.C(ctx).Infof("There are no runtimes listening for %q notifications in tenant %q for formation with name: %q", model.WebhookTypeConfigurationChanged, tenant, formation.Name) 161 return nil, nil 162 } 163 164 listeningRuntimeIDs := make([]string, 0, len(webhooks)) 165 for _, wh := range webhooks { 166 listeningRuntimeIDs = append(listeningRuntimeIDs, wh.ObjectID) 167 } 168 169 log.C(ctx).Infof("There is/are: %d runtimes listening for %q notifications in tenant %q for formation with name: %q", len(listeningRuntimeIDs), model.WebhookTypeConfigurationChanged, tenant, formation.Name) 170 171 runtimesInFormationMappings, runtimeIDToRuntimeContextInFormationMappings, err := ns.webhookDataInputBuilder.PrepareRuntimesAndRuntimeContextsMappingsInFormation(ctx, tenant, formation.Name) 172 if err != nil { 173 return nil, errors.Wrap(err, "while preparing runtime and runtime contexts mappings") 174 } 175 176 runtimeIDsToBeNotified := make(map[string]bool, len(listeningRuntimeIDs)) 177 for i := range listeningRuntimeIDs { 178 if runtimesInFormationMappings[listeningRuntimeIDs[i]] != nil { 179 runtimeIDsToBeNotified[listeningRuntimeIDs[i]] = true 180 } 181 } 182 183 webhooksToCall := make(map[string]*model.Webhook, len(runtimeIDsToBeNotified)) 184 for i := range webhooks { 185 if runtimeIDsToBeNotified[webhooks[i].ObjectID] { 186 webhooksToCall[webhooks[i].ObjectID] = webhooks[i] 187 } 188 } 189 190 requests := make([]*webhookclient.FormationAssignmentNotificationRequest, 0, len(runtimeIDsToBeNotified)) 191 for rtID := range runtimeIDsToBeNotified { 192 rtCtx := runtimeIDToRuntimeContextInFormationMappings[rtID] 193 if rtCtx == nil { 194 log.C(ctx).Infof("There is no runtime context for runtime with ID: %q in formation %q. Will proceed without runtime context in the input for webhook with ID: %q", rtID, formation.Name, webhooksToCall[rtID].ID) 195 } 196 runtime := runtimesInFormationMappings[rtID] 197 if appTemplateWithLabels == nil { 198 log.C(ctx).Infof("Application with ID: %q has no application template. Will proceed without application template in the input for webhook with ID: %q", appID, webhooksToCall[rtID].ID) 199 } 200 201 details, err := ns.notificationBuilder.PrepareDetailsForConfigurationChangeNotificationGeneration( 202 operation, 203 formation.ID, 204 formation.FormationTemplateID, 205 appTemplateWithLabels, 206 applicationWithLabels, 207 runtime, 208 rtCtx, 209 emptyFormationAssignment, 210 emptyFormationAssignment, 211 model.RuntimeResourceType, 212 customerTenantContext, 213 tenant) 214 if err != nil { 215 return nil, err 216 } 217 218 req, err := ns.notificationBuilder.BuildFormationAssignmentNotificationRequest(ctx, formation.FormationTemplateID, details, webhooksToCall[runtime.ID]) 219 if err != nil { 220 return nil, errors.Wrap(err, "Failed to build formation assignment notification request") 221 } else if req != nil { 222 requests = append(requests, req) 223 } 224 } 225 226 return requests, nil 227 } 228 229 // GenerateNotificationsForApplicationsAboutTheApplicationThatIsAssigned generates notification per application that is part of the formation with target the application and source the application on which `operation` is performed 230 func (ns *NotificationsGenerator) GenerateNotificationsForApplicationsAboutTheApplicationThatIsAssigned(ctx context.Context, tenant string, appID string, formation *model.Formation, operation model.FormationOperation, customerTenantContext *webhookdir.CustomerTenantContext) ([]*webhookclient.FormationAssignmentNotificationRequest, error) { 231 log.C(ctx).Infof("Generating %q notifications during %q operation for application with ID: %q", model.WebhookTypeApplicationTenantMapping, operation, appID) 232 applicationWithLabels, appTemplateWithLabels, err := ns.webhookDataInputBuilder.PrepareApplicationAndAppTemplateWithLabels(ctx, tenant, appID) 233 if err != nil { 234 return nil, errors.Wrap(err, "while preparing application and application template with labels") 235 } 236 237 webhooks, err := ns.webhookRepository.ListByReferenceObjectTypesAndWebhookType(ctx, tenant, model.WebhookTypeApplicationTenantMapping, []model.WebhookReferenceObjectType{model.ApplicationWebhookReference, model.ApplicationTemplateWebhookReference}) 238 if err != nil { 239 return nil, errors.Wrapf(err, "when listing %q webhooks for applications and their application templates", model.WebhookTypeApplicationTenantMapping) 240 } 241 242 resourceIDToWebhookMapping := make(map[string]*model.Webhook, len(webhooks)) 243 for _, webhook := range webhooks { 244 resourceIDToWebhookMapping[webhook.ObjectID] = webhook 245 } 246 247 // list applications that either have WebhookTypeApplicationTenantMapping webhook or their applicationTemplate has WebhookTypeApplicationTenantMapping webhook 248 listeningApps, err := ns.applicationRepository.ListListeningApplications(ctx, tenant, model.WebhookTypeApplicationTenantMapping) 249 if err != nil { 250 return nil, errors.Wrap(err, "while listing listening applications") 251 } 252 253 if len(listeningApps) == 0 { 254 log.C(ctx).Infof("There are no applications listening for %q notifications in tenant %q for formation with name: %q", tenant, model.WebhookTypeApplicationTenantMapping, formation.Name) 255 return nil, nil 256 } 257 258 listeningAppsByID := make(map[string]*model.Application, len(listeningApps)) 259 for i := range listeningApps { 260 listeningAppsByID[listeningApps[i].ID] = listeningApps[i] 261 } 262 263 appIDToWebhookMapping := make(map[string]*model.Webhook) 264 for _, app := range listeningApps { 265 // if webhook for the application exists use it 266 // if webhook for the application does not exist use the webhook for its application template 267 if resourceIDToWebhookMapping[app.ID] != nil { 268 appIDToWebhookMapping[app.ID] = resourceIDToWebhookMapping[app.ID] 269 } else { 270 appIDToWebhookMapping[app.ID] = resourceIDToWebhookMapping[str.PtrStrToStr(app.ApplicationTemplateID)] 271 } 272 } 273 274 log.C(ctx).Infof("There are %d applications listening for %q notifications in tenant %q for formation with name: %q", len(listeningAppsByID), model.WebhookTypeApplicationTenantMapping, tenant, formation.Name) 275 276 applicationsInFormationMapping, appTemplatesMapping, err := ns.webhookDataInputBuilder.PrepareApplicationMappingsInFormation(ctx, tenant, formation.Name) 277 if err != nil { 278 return nil, errors.Wrap(err, "while preparing application and application template mappings") 279 } 280 281 requests := make([]*webhookclient.FormationAssignmentNotificationRequest, 0, len(listeningAppsByID)) 282 if listeningAppsByID[appID] != nil { 283 log.C(ctx).Infof("The application with ID: %q that is being %q is also listening for %q notifications. Will create notifications about all other apps in the formation...", appID, operation, model.WebhookTypeApplicationTenantMapping) 284 webhook := appIDToWebhookMapping[appID] 285 286 appsInFormationCountExcludingAppCurrentlyAssigned := len(applicationsInFormationMapping) 287 if operation == model.AssignFormation { 288 appsInFormationCountExcludingAppCurrentlyAssigned -= 1 289 } 290 291 log.C(ctx).Infof("The number of other application(s) in formation %q is/are: %d. Notification(s) will be sent about them to application with ID: %q that is being %q.", formation.Name, appsInFormationCountExcludingAppCurrentlyAssigned, appID, operation) 292 293 for _, sourceApp := range applicationsInFormationMapping { 294 if sourceApp.ID == appID { 295 continue // Do not notify about itself 296 } 297 var appTemplate *webhookdir.ApplicationTemplateWithLabels 298 if sourceApp.ApplicationTemplateID != nil { 299 appTemplate = appTemplatesMapping[*sourceApp.ApplicationTemplateID] 300 } else { 301 log.C(ctx).Infof("Application with ID: %q has no application template. Will proceed without application template for the source application in the input for webhook with ID: %q", sourceApp.ID, webhook.ID) 302 } 303 if appTemplateWithLabels == nil { 304 log.C(ctx).Infof("Application with ID: %q has no application template. Will proceed without application template for the target application in the input for webhook with ID: %q", appID, webhook.ID) 305 } 306 307 details, err := ns.notificationBuilder.PrepareDetailsForApplicationTenantMappingNotificationGeneration( 308 operation, 309 formation.ID, 310 formation.FormationTemplateID, 311 appTemplate, 312 sourceApp, 313 appTemplateWithLabels, 314 applicationWithLabels, 315 emptyFormationAssignment, 316 emptyFormationAssignment, 317 customerTenantContext, 318 tenant, 319 ) 320 if err != nil { 321 return nil, err 322 } 323 324 req, err := ns.notificationBuilder.BuildFormationAssignmentNotificationRequest(ctx, formation.FormationTemplateID, details, webhook) 325 if err != nil { 326 return nil, errors.Wrap(err, "Failed to build formation assignment notification request") 327 } else if req != nil { 328 requests = append(requests, req) 329 } 330 } 331 332 delete(listeningAppsByID, appID) 333 } 334 335 listeningApplicationsInFormationIds := make([]string, 0, len(listeningAppsByID)) 336 for id := range listeningAppsByID { 337 if applicationsInFormationMapping[id] != nil { 338 listeningApplicationsInFormationIds = append(listeningApplicationsInFormationIds, id) 339 } 340 } 341 342 for _, appID := range listeningApplicationsInFormationIds { 343 targetApp := applicationsInFormationMapping[appID] 344 var targetAppTemplate *webhookdir.ApplicationTemplateWithLabels 345 if targetApp.ApplicationTemplateID != nil { 346 targetAppTemplate = appTemplatesMapping[*targetApp.ApplicationTemplateID] 347 } else { 348 log.C(ctx).Infof("Application with ID: %q has no application template. Will proceed without application template for the target application in the input for webhook with ID: %q", appID, appIDToWebhookMapping[appID].ID) 349 } 350 if appTemplateWithLabels == nil { 351 log.C(ctx).Infof("Application with ID: %q has no application template. Will proceed without application template for the source application in the input for webhook with ID: %q", appID, appIDToWebhookMapping[appID].ID) 352 } 353 354 details, err := ns.notificationBuilder.PrepareDetailsForApplicationTenantMappingNotificationGeneration( 355 operation, 356 formation.ID, 357 formation.FormationTemplateID, 358 appTemplateWithLabels, 359 applicationWithLabels, 360 targetAppTemplate, 361 targetApp, 362 emptyFormationAssignment, 363 emptyFormationAssignment, 364 customerTenantContext, 365 tenant, 366 ) 367 if err != nil { 368 return nil, err 369 } 370 371 req, err := ns.notificationBuilder.BuildFormationAssignmentNotificationRequest(ctx, formation.FormationTemplateID, details, appIDToWebhookMapping[appID]) 372 if err != nil { 373 return nil, errors.Wrap(err, "Failed to build formation assignment notification request") 374 } else if req != nil { 375 requests = append(requests, req) 376 } 377 } 378 379 log.C(ctx).Infof("Total number of %q notifications for application with ID: %q that is being %q is/are: %d", model.WebhookTypeApplicationTenantMapping, appID, operation, len(requests)) 380 381 return requests, nil 382 } 383 384 // GenerateNotificationsForApplicationsAboutTheRuntimeContextThatIsAssigned generates notification per application that is part of the formation with target the application and source the runtime context on which `operation` is performed 385 func (ns *NotificationsGenerator) GenerateNotificationsForApplicationsAboutTheRuntimeContextThatIsAssigned(ctx context.Context, tenant, runtimeCtxID string, formation *model.Formation, operation model.FormationOperation, customerTenantContext *webhookdir.CustomerTenantContext) ([]*webhookclient.FormationAssignmentNotificationRequest, error) { 386 log.C(ctx).Infof("Generating %q notifications about runtime context with ID: %q for all interested applications in the formation", operation, runtimeCtxID) 387 runtimeCtxWithLabels, err := ns.webhookDataInputBuilder.PrepareRuntimeContextWithLabels(ctx, tenant, runtimeCtxID) 388 if err != nil { 389 return nil, errors.Wrap(err, "while preparing runtime context with labels") 390 } 391 392 requests, err := ns.GenerateNotificationsForApplicationsAboutTheRuntimeThatIsAssigned(ctx, tenant, runtimeCtxWithLabels.RuntimeID, formation, operation, customerTenantContext) 393 if err != nil { 394 return nil, err 395 } 396 for _, request := range requests { 397 request.Object.(*webhookdir.FormationConfigurationChangeInput).RuntimeContext = runtimeCtxWithLabels 398 } 399 return requests, nil 400 } 401 402 // GenerateNotificationsForApplicationsAboutTheRuntimeThatIsAssigned generates notification per application that is part of the formation with target the application and source the runtime on which `operation` is performed 403 func (ns *NotificationsGenerator) GenerateNotificationsForApplicationsAboutTheRuntimeThatIsAssigned(ctx context.Context, tenant, runtimeID string, formation *model.Formation, operation model.FormationOperation, customerTenantContext *webhookdir.CustomerTenantContext) ([]*webhookclient.FormationAssignmentNotificationRequest, error) { 404 log.C(ctx).Infof("Generating %q notifications during %q operation about runtime with ID: %q for all interested applications in the formation", model.WebhookTypeConfigurationChanged, operation, runtimeID) 405 runtimeWithLabels, err := ns.webhookDataInputBuilder.PrepareRuntimeWithLabels(ctx, tenant, runtimeID) 406 if err != nil { 407 return nil, errors.Wrap(err, "while preparing runtime with labels") 408 } 409 410 webhooks, err := ns.webhookRepository.ListByReferenceObjectTypesAndWebhookType(ctx, tenant, model.WebhookTypeConfigurationChanged, []model.WebhookReferenceObjectType{model.ApplicationWebhookReference, model.ApplicationTemplateWebhookReference}) 411 if err != nil { 412 return nil, errors.Wrapf(err, "when listing %q webhooks for applications and their application templates", model.WebhookTypeConfigurationChanged) 413 } 414 415 resourceIDToWebhookMapping := make(map[string]*model.Webhook, len(webhooks)) 416 for _, webhook := range webhooks { 417 resourceIDToWebhookMapping[webhook.ObjectID] = webhook 418 } 419 420 listeningApps, err := ns.applicationRepository.ListListeningApplications(ctx, tenant, model.WebhookTypeConfigurationChanged) 421 if err != nil { 422 return nil, errors.Wrap(err, "while listing listening applications") 423 } 424 425 if len(listeningApps) == 0 { 426 log.C(ctx).Infof("There are no applications listening for %q notifications in tenant %q for formation with name: %q", tenant, model.WebhookTypeConfigurationChanged, formation.Name) 427 return nil, nil 428 } 429 430 listeningApplicationIDs := make([]string, 0, len(listeningApps)) 431 for _, app := range listeningApps { 432 listeningApplicationIDs = append(listeningApplicationIDs, app.ID) 433 } 434 435 appIDToWebhookMapping := make(map[string]*model.Webhook) 436 for _, app := range listeningApps { 437 // if webhook for the application exists use it 438 // if webhook for the application does not exist use the webhook for its application template 439 if resourceIDToWebhookMapping[app.ID] != nil { 440 appIDToWebhookMapping[app.ID] = resourceIDToWebhookMapping[app.ID] 441 } else { 442 appIDToWebhookMapping[app.ID] = resourceIDToWebhookMapping[str.PtrStrToStr(app.ApplicationTemplateID)] 443 } 444 } 445 446 log.C(ctx).Infof("There are %d applications listening for %q notifications in tenant %q for formation with ID: %q", len(listeningApplicationIDs), model.WebhookTypeConfigurationChanged, tenant, formation.Name) 447 448 applicationsInFormationMapping, appTemplatesMapping, err := ns.webhookDataInputBuilder.PrepareApplicationMappingsInFormation(ctx, tenant, formation.Name) 449 if err != nil { 450 return nil, err 451 } 452 453 listeningApplicationsInFormationIds := make([]string, 0, len(listeningApps)) 454 for i := range listeningApps { 455 if applicationsInFormationMapping[listeningApps[i].ID] != nil { 456 listeningApplicationsInFormationIds = append(listeningApplicationsInFormationIds, listeningApps[i].ID) 457 } 458 } 459 460 requests := make([]*webhookclient.FormationAssignmentNotificationRequest, 0, len(applicationsInFormationMapping)) 461 for _, appID := range listeningApplicationsInFormationIds { 462 app := applicationsInFormationMapping[appID] 463 var appTemplate *webhookdir.ApplicationTemplateWithLabels 464 if app.ApplicationTemplateID != nil { 465 appTemplate = appTemplatesMapping[*app.ApplicationTemplateID] 466 } else { 467 log.C(ctx).Infof("Application with ID: %q has no application template. Will proceed without application template in the input for webhook with ID: %q", appID, appIDToWebhookMapping[appID].ID) 468 } 469 470 details, err := ns.notificationBuilder.PrepareDetailsForConfigurationChangeNotificationGeneration( 471 operation, 472 formation.ID, 473 formation.FormationTemplateID, 474 appTemplate, 475 app, 476 runtimeWithLabels, 477 nil, 478 emptyFormationAssignment, 479 emptyFormationAssignment, 480 model.ApplicationResourceType, 481 customerTenantContext, 482 tenant, 483 ) 484 if err != nil { 485 return nil, err 486 } 487 488 req, err := ns.notificationBuilder.BuildFormationAssignmentNotificationRequest(ctx, formation.FormationTemplateID, details, appIDToWebhookMapping[appID]) 489 if err != nil { 490 return nil, errors.Wrap(err, "Failed to build formation assignment notification request") 491 } else if req != nil { 492 requests = append(requests, req) 493 } 494 } 495 return requests, nil 496 } 497 498 // GenerateNotificationsAboutApplicationsForTheRuntimeContextThatIsAssigned generates notification per runtime context that is part of the formation with target the runtime context and source the application on which `operation` is performed 499 func (ns *NotificationsGenerator) GenerateNotificationsAboutApplicationsForTheRuntimeContextThatIsAssigned(ctx context.Context, tenant, runtimeCtxID string, formation *model.Formation, operation model.FormationOperation, customerTenantContext *webhookdir.CustomerTenantContext) ([]*webhookclient.FormationAssignmentNotificationRequest, error) { 500 log.C(ctx).Infof("Generating %q notifications during %q operation for runtime context with ID: %q", model.WebhookTypeConfigurationChanged, operation, runtimeCtxID) 501 runtimeCtxWithLabels, err := ns.webhookDataInputBuilder.PrepareRuntimeContextWithLabels(ctx, tenant, runtimeCtxID) 502 if err != nil { 503 return nil, errors.Wrap(err, "while preparing runtime context with labels") 504 } 505 506 runtimeID := runtimeCtxWithLabels.RuntimeID 507 508 webhook, err := ns.webhookRepository.GetByIDAndWebhookType(ctx, tenant, runtimeID, model.RuntimeWebhookReference, model.WebhookTypeConfigurationChanged) 509 if err != nil { 510 if apperrors.IsNotFoundError(err) { 511 log.C(ctx).Infof("There is no %q webhook for runtime with ID: %q. No notifications will be generated.", model.WebhookTypeConfigurationChanged, runtimeID) 512 return nil, nil 513 } 514 return nil, errors.Wrapf(err, "while listing configuration changed webhooks for runtime %s", runtimeID) 515 } 516 517 runtimeWithLabels, err := ns.webhookDataInputBuilder.PrepareRuntimeWithLabels(ctx, tenant, runtimeID) 518 if err != nil { 519 return nil, errors.Wrap(err, "while preparing runtime with labels") 520 } 521 522 applicationMapping, applicationTemplatesMapping, err := ns.webhookDataInputBuilder.PrepareApplicationMappingsInFormation(ctx, tenant, formation.Name) 523 if err != nil { 524 return nil, err 525 } 526 527 requests := make([]*webhookclient.FormationAssignmentNotificationRequest, 0, len(applicationMapping)) 528 for _, app := range applicationMapping { 529 var appTemplate *webhookdir.ApplicationTemplateWithLabels 530 if app.ApplicationTemplateID != nil { 531 appTemplate = applicationTemplatesMapping[*app.ApplicationTemplateID] 532 } else { 533 log.C(ctx).Infof("Application with ID: %q has no application template. Will proceed without application template in the input for webhook with ID: %q", app.ID, webhook.ID) 534 } 535 536 details, err := ns.notificationBuilder.PrepareDetailsForConfigurationChangeNotificationGeneration( 537 operation, 538 formation.ID, 539 formation.FormationTemplateID, 540 appTemplate, 541 app, 542 runtimeWithLabels, 543 runtimeCtxWithLabels, 544 emptyFormationAssignment, 545 emptyFormationAssignment, 546 model.RuntimeContextResourceType, 547 customerTenantContext, 548 tenant, 549 ) 550 if err != nil { 551 return nil, err 552 } 553 554 req, err := ns.notificationBuilder.BuildFormationAssignmentNotificationRequest(ctx, formation.FormationTemplateID, details, webhook) 555 if err != nil { 556 return nil, errors.Wrap(err, "Failed to build formation assignment notification request") 557 } else if req != nil { 558 requests = append(requests, req) 559 } 560 } 561 562 return requests, nil 563 } 564 565 // GenerateNotificationsAboutApplicationsForTheRuntimeThatIsAssigned generates notification per runtime that is part of the formation with target the runtime and source the application on which `operation` is performed 566 func (ns *NotificationsGenerator) GenerateNotificationsAboutApplicationsForTheRuntimeThatIsAssigned(ctx context.Context, tenant, runtimeID string, formation *model.Formation, operation model.FormationOperation, customerTenantContext *webhookdir.CustomerTenantContext) ([]*webhookclient.FormationAssignmentNotificationRequest, error) { 567 log.C(ctx).Infof("Generating %q notifications during %q operation about all applications in the formation for runtime with ID: %q", model.WebhookTypeConfigurationChanged, operation, runtimeID) 568 runtimeWithLabels, err := ns.webhookDataInputBuilder.PrepareRuntimeWithLabels(ctx, tenant, runtimeID) 569 if err != nil { 570 return nil, errors.Wrap(err, "while preparing runtime with labels") 571 } 572 573 webhook, err := ns.webhookRepository.GetByIDAndWebhookType(ctx, tenant, runtimeID, model.RuntimeWebhookReference, model.WebhookTypeConfigurationChanged) 574 if err != nil { 575 if apperrors.IsNotFoundError(err) { 576 log.C(ctx).Infof("There is no %q webhook for runtime with ID: %q. No notifications will be generated.", model.WebhookTypeConfigurationChanged, runtimeID) 577 return nil, nil 578 } 579 return nil, errors.Wrapf(err, "while listing configuration changed webhooks for runtime %s", runtimeID) 580 } 581 582 applicationMapping, applicationTemplatesMapping, err := ns.webhookDataInputBuilder.PrepareApplicationMappingsInFormation(ctx, tenant, formation.Name) 583 if err != nil { 584 return nil, err 585 } 586 587 requests := make([]*webhookclient.FormationAssignmentNotificationRequest, 0, len(applicationMapping)) 588 for _, app := range applicationMapping { 589 var appTemplate *webhookdir.ApplicationTemplateWithLabels 590 if app.ApplicationTemplateID != nil { 591 appTemplate = applicationTemplatesMapping[*app.ApplicationTemplateID] 592 } else { 593 log.C(ctx).Infof("Application with ID: %q has no application template. Will proceed without application template in the input for webhook with ID: %q", app.ID, webhook.ID) 594 } 595 596 details, err := ns.notificationBuilder.PrepareDetailsForConfigurationChangeNotificationGeneration( 597 operation, 598 formation.ID, 599 formation.FormationTemplateID, 600 appTemplate, 601 app, 602 runtimeWithLabels, 603 nil, 604 emptyFormationAssignment, 605 emptyFormationAssignment, 606 model.RuntimeResourceType, 607 customerTenantContext, 608 tenant, 609 ) 610 if err != nil { 611 return nil, err 612 } 613 614 req, err := ns.notificationBuilder.BuildFormationAssignmentNotificationRequest(ctx, formation.FormationTemplateID, details, webhook) 615 if err != nil { 616 return nil, errors.Wrap(err, "Failed to build formation assignment notification request") 617 } else if req != nil { 618 requests = append(requests, req) 619 } 620 } 621 622 return requests, nil 623 } 624 625 // GenerateFormationLifecycleNotifications generates formation notifications for the provided webhooks 626 func (ns *NotificationsGenerator) GenerateFormationLifecycleNotifications(ctx context.Context, formationTemplateWebhooks []*model.Webhook, tenantID string, formation *model.Formation, formationTemplateName, formationTemplateID string, formationOperation model.FormationOperation, customerTenantContext *webhookdir.CustomerTenantContext) ([]*webhookclient.FormationNotificationRequest, error) { 627 details := &formationconstraint.GenerateFormationNotificationOperationDetails{ 628 Operation: formationOperation, 629 FormationID: formation.ID, 630 FormationName: formation.Name, 631 FormationType: formationTemplateName, 632 FormationTemplateID: formationTemplateID, 633 TenantID: tenantID, 634 CustomerTenantContext: customerTenantContext, 635 } 636 637 reqs, err := ns.notificationBuilder.BuildFormationNotificationRequests(ctx, details, formation, formationTemplateWebhooks) 638 if err != nil { 639 log.C(ctx).Errorf("Failed to build formation notification requests due to: %v", err) 640 } 641 642 return reqs, nil 643 }