github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/eventdef/service.go (about) 1 package eventdef 2 3 import ( 4 "context" 5 "github.com/kyma-incubator/compass/components/director/pkg/str" 6 7 "github.com/kyma-incubator/compass/components/director/pkg/log" 8 9 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 10 "github.com/kyma-incubator/compass/components/director/pkg/resource" 11 12 "github.com/kyma-incubator/compass/components/director/internal/domain/tenant" 13 "github.com/kyma-incubator/compass/components/director/internal/timestamp" 14 15 "github.com/kyma-incubator/compass/components/director/internal/model" 16 "github.com/pkg/errors" 17 ) 18 19 // EventAPIRepository is responsible for the repo-layer EventDefinition operations. 20 // 21 //go:generate mockery --name=EventAPIRepository --output=automock --outpkg=automock --case=underscore --disable-version-string 22 type EventAPIRepository interface { 23 GetByID(ctx context.Context, tenantID string, id string) (*model.EventDefinition, error) 24 GetByIDGlobal(ctx context.Context, id string) (*model.EventDefinition, error) 25 GetForBundle(ctx context.Context, tenant string, id string, bundleID string) (*model.EventDefinition, error) 26 ListByBundleIDs(ctx context.Context, tenantID string, bundleIDs []string, bundleRefs []*model.BundleReference, totalCounts map[string]int, pageSize int, cursor string) ([]*model.EventDefinitionPage, error) 27 ListByResourceID(ctx context.Context, tenantID, resourceID string, resourceType resource.Type) ([]*model.EventDefinition, error) 28 ListByApplicationIDPage(ctx context.Context, tenantID string, appID string, pageSize int, cursor string) (*model.EventDefinitionPage, error) 29 Create(ctx context.Context, tenant string, item *model.EventDefinition) error 30 CreateGlobal(ctx context.Context, item *model.EventDefinition) error 31 Update(ctx context.Context, tenant string, item *model.EventDefinition) error 32 UpdateGlobal(ctx context.Context, item *model.EventDefinition) error 33 Delete(ctx context.Context, tenantID string, id string) error 34 DeleteGlobal(ctx context.Context, id string) error 35 DeleteAllByBundleID(ctx context.Context, tenantID, bundleID string) error 36 } 37 38 // UIDService is responsible for generating GUIDs, which will be used as internal eventDefinition IDs when they are created. 39 // 40 //go:generate mockery --name=UIDService --output=automock --outpkg=automock --case=underscore --disable-version-string 41 type UIDService interface { 42 Generate() string 43 } 44 45 // SpecService is responsible for the service-layer Specification operations. 46 // 47 //go:generate mockery --name=SpecService --output=automock --outpkg=automock --case=underscore --disable-version-string 48 type SpecService interface { 49 CreateByReferenceObjectID(ctx context.Context, in model.SpecInput, resourceType resource.Type, objectType model.SpecReferenceObjectType, objectID string) (string, error) 50 UpdateByReferenceObjectID(ctx context.Context, id string, in model.SpecInput, resourceType resource.Type, objectType model.SpecReferenceObjectType, objectID string) error 51 GetByReferenceObjectID(ctx context.Context, resourceType resource.Type, objectType model.SpecReferenceObjectType, objectID string) (*model.Spec, error) 52 RefetchSpec(ctx context.Context, id string, objectType model.SpecReferenceObjectType) (*model.Spec, error) 53 ListFetchRequestsByReferenceObjectIDs(ctx context.Context, tenant string, objectIDs []string, objectType model.SpecReferenceObjectType) ([]*model.FetchRequest, error) 54 } 55 56 // BundleReferenceService is responsible for the service-layer BundleReference operations. 57 // 58 //go:generate mockery --name=BundleReferenceService --output=automock --outpkg=automock --case=underscore --disable-version-string 59 type BundleReferenceService interface { 60 GetForBundle(ctx context.Context, objectType model.BundleReferenceObjectType, objectID, bundleID *string) (*model.BundleReference, error) 61 CreateByReferenceObjectID(ctx context.Context, in model.BundleReferenceInput, objectType model.BundleReferenceObjectType, objectID, bundleID *string) error 62 UpdateByReferenceObjectID(ctx context.Context, in model.BundleReferenceInput, objectType model.BundleReferenceObjectType, objectID, bundleID *string) error 63 DeleteByReferenceObjectID(ctx context.Context, objectType model.BundleReferenceObjectType, objectID, bundleID *string) error 64 ListByBundleIDs(ctx context.Context, objectType model.BundleReferenceObjectType, bundleIDs []string, pageSize int, cursor string) ([]*model.BundleReference, map[string]int, error) 65 } 66 67 type service struct { 68 eventAPIRepo EventAPIRepository 69 uidService UIDService 70 specService SpecService 71 bundleReferenceService BundleReferenceService 72 timestampGen timestamp.Generator 73 } 74 75 // NewService returns a new object responsible for service-layer EventDefinition operations. 76 func NewService(eventAPIRepo EventAPIRepository, uidService UIDService, specService SpecService, bundleReferenceService BundleReferenceService) *service { 77 return &service{ 78 eventAPIRepo: eventAPIRepo, 79 uidService: uidService, 80 specService: specService, 81 bundleReferenceService: bundleReferenceService, 82 timestampGen: timestamp.DefaultGenerator, 83 } 84 } 85 86 // ListByBundleIDs lists all EventDefinitions in pages for a given array of bundle IDs. 87 func (s *service) ListByBundleIDs(ctx context.Context, bundleIDs []string, pageSize int, cursor string) ([]*model.EventDefinitionPage, error) { 88 tnt, err := tenant.LoadFromContext(ctx) 89 if err != nil { 90 return nil, errors.Wrap(err, "while loading tenant from context") 91 } 92 93 if pageSize < 1 || pageSize > 200 { 94 return nil, apperrors.NewInvalidDataError("page size must be between 1 and 200") 95 } 96 97 bundleRefs, counts, err := s.bundleReferenceService.ListByBundleIDs(ctx, model.BundleEventReference, bundleIDs, pageSize, cursor) 98 if err != nil { 99 return nil, err 100 } 101 102 return s.eventAPIRepo.ListByBundleIDs(ctx, tnt, bundleIDs, bundleRefs, counts, pageSize, cursor) 103 } 104 105 // ListByApplicationID lists all EventDefinitions for a given application ID. 106 func (s *service) ListByApplicationID(ctx context.Context, appID string) ([]*model.EventDefinition, error) { 107 tnt, err := tenant.LoadFromContext(ctx) 108 if err != nil { 109 return nil, err 110 } 111 112 return s.eventAPIRepo.ListByResourceID(ctx, tnt, appID, resource.Application) 113 } 114 115 // ListByApplicationTemplateVersionID lists all EventDefinitions for a given application template version ID. 116 func (s *service) ListByApplicationTemplateVersionID(ctx context.Context, appTemplateVersionID string) ([]*model.EventDefinition, error) { 117 return s.eventAPIRepo.ListByResourceID(ctx, "", appTemplateVersionID, resource.ApplicationTemplateVersion) 118 } 119 120 // ListByApplicationIDPage lists all EventDefinitions for a given application ID with paging. 121 func (s *service) ListByApplicationIDPage(ctx context.Context, appID string, pageSize int, cursor string) (*model.EventDefinitionPage, error) { 122 tnt, err := tenant.LoadFromContext(ctx) 123 if err != nil { 124 return nil, err 125 } 126 127 if pageSize < 1 || pageSize > 200 { 128 return nil, apperrors.NewInvalidDataError("page size must be between 1 and 200") 129 } 130 131 return s.eventAPIRepo.ListByApplicationIDPage(ctx, tnt, appID, pageSize, cursor) 132 } 133 134 // Get returns the EventDefinition by its ID. 135 func (s *service) Get(ctx context.Context, id string) (*model.EventDefinition, error) { 136 tnt, err := tenant.LoadFromContext(ctx) 137 if err != nil { 138 return nil, err 139 } 140 141 eventAPI, err := s.eventAPIRepo.GetByID(ctx, tnt, id) 142 if err != nil { 143 return nil, err 144 } 145 146 return eventAPI, nil 147 } 148 149 // GetForBundle returns an EventDefinition by its ID and a bundle ID. 150 func (s *service) GetForBundle(ctx context.Context, id string, bundleID string) (*model.EventDefinition, error) { 151 tnt, err := tenant.LoadFromContext(ctx) 152 if err != nil { 153 return nil, err 154 } 155 156 eventAPI, err := s.eventAPIRepo.GetForBundle(ctx, tnt, id, bundleID) 157 if err != nil { 158 return nil, errors.Wrap(err, "while getting API definition") 159 } 160 161 return eventAPI, nil 162 } 163 164 // CreateInBundle creates an EventDefinition. This function is used in the graphQL flow. 165 func (s *service) CreateInBundle(ctx context.Context, resourceType resource.Type, resourceID string, bundleID string, in model.EventDefinitionInput, spec *model.SpecInput) (string, error) { 166 return s.Create(ctx, resourceType, resourceID, &bundleID, nil, in, []*model.SpecInput{spec}, nil, 0, "") 167 } 168 169 // CreateInApplication creates an EventDefinition in the context of an Application without Bundle 170 func (s *service) CreateInApplication(ctx context.Context, appID string, in model.EventDefinitionInput, spec *model.SpecInput) (string, error) { 171 return s.Create(ctx, resource.Application, appID, nil, nil, in, []*model.SpecInput{spec}, nil, 0, "") 172 } 173 174 // Create creates EventDefinition/s. This function is used both in the ORD scenario and is re-used in CreateInBundle but with "null" ORD specific arguments. 175 func (s *service) Create(ctx context.Context, resourceType resource.Type, resourceID string, bundleID, packageID *string, in model.EventDefinitionInput, specs []*model.SpecInput, bundleIDs []string, eventHash uint64, defaultBundleID string) (string, error) { 176 id := s.uidService.Generate() 177 eventAPI := in.ToEventDefinition(id, resourceType, resourceID, packageID, eventHash) 178 179 if err := s.createEventDef(ctx, eventAPI, resourceType); err != nil { 180 return "", errors.Wrap(err, "while creating api") 181 } 182 183 if err := s.processSpecs(ctx, eventAPI.ID, specs, resourceType); err != nil { 184 return "", err 185 } 186 187 if err := s.createBundleReferenceObject(ctx, eventAPI.ID, bundleID, defaultBundleID, bundleIDs); err != nil { 188 return "", err 189 } 190 191 return id, nil 192 } 193 194 // Update updates an EventDefinition. This function is used in the graphQL flow. 195 func (s *service) Update(ctx context.Context, resourceType resource.Type, id string, in model.EventDefinitionInput, specIn *model.SpecInput) error { 196 return s.UpdateInManyBundles(ctx, resourceType, id, in, specIn, nil, nil, nil, 0, "") 197 } 198 199 // UpdateInManyBundles updates EventDefinition/s. This function is used both in the ORD scenario and is re-used in Update but with "null" ORD specific arguments. 200 func (s *service) UpdateInManyBundles(ctx context.Context, resourceType resource.Type, id string, in model.EventDefinitionInput, specIn *model.SpecInput, bundleIDsFromBundleReference, bundleIDsForCreation, bundleIDsForDeletion []string, eventHash uint64, defaultBundleID string) error { 201 eventDef, err := s.getEventDef(ctx, id, resourceType) 202 if err != nil { 203 return errors.Wrapf(err, "while getting EventDefinition with ID %s", id) 204 } 205 206 resourceID := getParentResourceID(eventDef) 207 eventDef = in.ToEventDefinition(id, resourceType, resourceID, eventDef.PackageID, eventHash) 208 209 if err = s.updateEventDef(ctx, eventDef, resourceType); err != nil { 210 return errors.Wrapf(err, "while updating EventDefinition with ID %s", id) 211 } 212 213 if err = s.handleReferenceObjects(ctx, eventDef.ID, bundleIDsForCreation, bundleIDsForDeletion, bundleIDsFromBundleReference, defaultBundleID); err != nil { 214 return errors.Wrapf(err, "while handling reference objects for EventDefinition with ID %s", eventDef.ID) 215 } 216 217 if specIn != nil { 218 return s.handleSpecsInEvent(ctx, resourceType, eventDef.ID, specIn) 219 } 220 221 return nil 222 } 223 224 // UpdateForApplication updates an EventDefinition for Application without being in a Bundle 225 func (s *service) UpdateForApplication(ctx context.Context, id string, in model.EventDefinitionInput, specIn *model.SpecInput) error { 226 tnt, err := tenant.LoadFromContext(ctx) 227 if err != nil { 228 return err 229 } 230 231 event, err := s.Get(ctx, id) 232 if err != nil { 233 return err 234 } 235 236 event = in.ToEventDefinition(id, resource.Application, str.PtrStrToStr(event.ApplicationID), event.PackageID, 0) 237 238 if err = s.eventAPIRepo.Update(ctx, tnt, event); err != nil { 239 return errors.Wrapf(err, "while updating EventDefinition with id %s", id) 240 } 241 242 if specIn != nil { 243 return s.handleSpecsInEvent(ctx, resource.Application, id, specIn) 244 } 245 246 return nil 247 } 248 249 // Delete deletes the EventDefinition by its ID. 250 func (s *service) Delete(ctx context.Context, resourceType resource.Type, id string) error { 251 if err := s.deleteEventDef(ctx, id, resourceType); err != nil { 252 return errors.Wrapf(err, "while deleting EventDefinition with id %s", id) 253 } 254 255 log.C(ctx).Infof("Successfully deleted EventDefinition with id %s", id) 256 257 return nil 258 } 259 260 // DeleteAllByBundleID deletes all EventDefinitions for a given bundle ID 261 func (s *service) DeleteAllByBundleID(ctx context.Context, bundleID string) error { 262 tnt, err := tenant.LoadFromContext(ctx) 263 if err != nil { 264 return errors.Wrapf(err, "while loading tenant from context") 265 } 266 267 err = s.eventAPIRepo.DeleteAllByBundleID(ctx, tnt, bundleID) 268 if err != nil { 269 return errors.Wrapf(err, "while deleting EventDefinitions for Bundle with id %q", bundleID) 270 } 271 272 return nil 273 } 274 275 // ListFetchRequests lists all FetchRequests for given specification IDs 276 func (s *service) ListFetchRequests(ctx context.Context, specIDs []string) ([]*model.FetchRequest, error) { 277 tnt, err := tenant.LoadFromContext(ctx) 278 if err != nil { 279 return nil, err 280 } 281 282 fetchRequests, err := s.specService.ListFetchRequestsByReferenceObjectIDs(ctx, tnt, specIDs, model.EventSpecReference) 283 if err != nil { 284 if apperrors.IsNotFoundError(err) { 285 return nil, nil 286 } 287 return nil, err 288 } 289 290 return fetchRequests, nil 291 } 292 293 func (s *service) handleSpecsInEvent(ctx context.Context, resourceType resource.Type, id string, specIn *model.SpecInput) error { 294 dbSpec, err := s.specService.GetByReferenceObjectID(ctx, resourceType, model.EventSpecReference, id) 295 if err != nil { 296 return errors.Wrapf(err, "while getting spec for EventDefinition with id %q", id) 297 } 298 299 if dbSpec == nil { 300 _, err = s.specService.CreateByReferenceObjectID(ctx, *specIn, resourceType, model.EventSpecReference, id) 301 return errors.Wrapf(err, "while creating spec for EventDefinition with ID %s", id) 302 } 303 304 if err = s.specService.UpdateByReferenceObjectID(ctx, dbSpec.ID, *specIn, resourceType, model.EventSpecReference, id); err != nil { 305 return errors.Wrapf(err, "while updating spec for EventDefinition with ID %s", id) 306 } 307 308 return nil 309 } 310 311 func (s *service) handleReferenceObjects(ctx context.Context, id string, bundleIDsForCreation, bundleIDsForDeletion, bundleIDsFromBundleReference []string, defaultBundleID string) error { 312 for _, bundleID := range bundleIDsForCreation { 313 createBundleRefInput := &model.BundleReferenceInput{} 314 if defaultBundleID != "" && bundleID == defaultBundleID { 315 isDefaultBundle := true 316 createBundleRefInput = &model.BundleReferenceInput{IsDefaultBundle: &isDefaultBundle} 317 } 318 if err := s.bundleReferenceService.CreateByReferenceObjectID(ctx, *createBundleRefInput, model.BundleEventReference, &id, &bundleID); err != nil { 319 return err 320 } 321 } 322 323 for _, bundleID := range bundleIDsForDeletion { 324 if err := s.bundleReferenceService.DeleteByReferenceObjectID(ctx, model.BundleEventReference, &id, &bundleID); err != nil { 325 return err 326 } 327 } 328 329 for _, bundleID := range bundleIDsFromBundleReference { 330 bundleRefInput := &model.BundleReferenceInput{} 331 if defaultBundleID != "" && bundleID == defaultBundleID { 332 isDefaultBundle := true 333 bundleRefInput = &model.BundleReferenceInput{IsDefaultBundle: &isDefaultBundle} 334 } 335 if err := s.bundleReferenceService.UpdateByReferenceObjectID(ctx, *bundleRefInput, model.BundleEventReference, &id, &bundleID); err != nil { 336 return err 337 } 338 } 339 340 return nil 341 } 342 343 func (s *service) processSpecs(ctx context.Context, eventID string, specs []*model.SpecInput, resourceType resource.Type) error { 344 for _, spec := range specs { 345 if spec == nil { 346 continue 347 } 348 349 if _, err := s.specService.CreateByReferenceObjectID(ctx, *spec, resourceType, model.EventSpecReference, eventID); err != nil { 350 return err 351 } 352 } 353 354 return nil 355 } 356 357 func (s *service) createBundleReferenceObject(ctx context.Context, eventID string, bundleID *string, defaultBundleID string, bundleIDs []string) error { 358 if bundleIDs == nil && bundleID != nil { 359 if err := s.bundleReferenceService.CreateByReferenceObjectID(ctx, model.BundleReferenceInput{}, model.BundleEventReference, &eventID, bundleID); err != nil { 360 return err 361 } 362 } 363 364 for _, bndlID := range bundleIDs { 365 bundleRefInput := &model.BundleReferenceInput{} 366 if defaultBundleID != "" && bndlID == defaultBundleID { 367 isDefaultBundle := true 368 bundleRefInput = &model.BundleReferenceInput{ 369 IsDefaultBundle: &isDefaultBundle, 370 } 371 } 372 if err := s.bundleReferenceService.CreateByReferenceObjectID(ctx, *bundleRefInput, model.BundleEventReference, &eventID, &bndlID); err != nil { 373 return err 374 } 375 } 376 377 return nil 378 } 379 380 func (s *service) createEventDef(ctx context.Context, eventAPI *model.EventDefinition, resourceType resource.Type) error { 381 if resourceType.IsTenantIgnorable() { 382 return s.eventAPIRepo.CreateGlobal(ctx, eventAPI) 383 } 384 385 tnt, err := tenant.LoadFromContext(ctx) 386 if err != nil { 387 return err 388 } 389 390 return s.eventAPIRepo.Create(ctx, tnt, eventAPI) 391 } 392 393 func (s *service) getEventDef(ctx context.Context, id string, resourceType resource.Type) (*model.EventDefinition, error) { 394 if resourceType.IsTenantIgnorable() { 395 return s.eventAPIRepo.GetByIDGlobal(ctx, id) 396 } 397 398 return s.Get(ctx, id) 399 } 400 401 func (s *service) updateEventDef(ctx context.Context, eventDef *model.EventDefinition, resourceType resource.Type) error { 402 if resourceType.IsTenantIgnorable() { 403 return s.eventAPIRepo.UpdateGlobal(ctx, eventDef) 404 } 405 406 tnt, err := tenant.LoadFromContext(ctx) 407 if err != nil { 408 return err 409 } 410 411 return s.eventAPIRepo.Update(ctx, tnt, eventDef) 412 } 413 414 func (s *service) deleteEventDef(ctx context.Context, id string, resourceType resource.Type) error { 415 if resourceType.IsTenantIgnorable() { 416 return s.eventAPIRepo.DeleteGlobal(ctx, id) 417 } 418 419 tnt, err := tenant.LoadFromContext(ctx) 420 if err != nil { 421 return errors.Wrapf(err, "while loading tenant from context") 422 } 423 424 return s.eventAPIRepo.Delete(ctx, tnt, id) 425 } 426 427 func getParentResourceID(api *model.EventDefinition) string { 428 if api.ApplicationTemplateVersionID != nil { 429 return *api.ApplicationTemplateVersionID 430 } else if api.ApplicationID != nil { 431 return *api.ApplicationID 432 } 433 434 return "" 435 }