github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/webhook/datainputbuilder/builder.go (about) 1 package datainputbuilder 2 3 import ( 4 "context" 5 "encoding/json" 6 "strconv" 7 8 "github.com/kyma-incubator/compass/components/director/pkg/log" 9 10 "github.com/kyma-incubator/compass/components/director/pkg/webhook" 11 12 "github.com/kyma-incubator/compass/components/director/internal/model" 13 "github.com/pkg/errors" 14 ) 15 16 //go:generate mockery --exported --name=applicationRepository --output=automock --outpkg=automock --case=underscore --disable-version-string 17 type applicationRepository interface { 18 GetByID(ctx context.Context, tenant, id string) (*model.Application, error) 19 ListByScenariosNoPaging(ctx context.Context, tenant string, scenarios []string) ([]*model.Application, error) 20 } 21 22 //go:generate mockery --exported --name=applicationTemplateRepository --output=automock --outpkg=automock --case=underscore --disable-version-string 23 type applicationTemplateRepository interface { 24 Get(ctx context.Context, id string) (*model.ApplicationTemplate, error) 25 ListByIDs(ctx context.Context, ids []string) ([]*model.ApplicationTemplate, error) 26 } 27 28 //go:generate mockery --exported --name=runtimeRepository --output=automock --outpkg=automock --case=underscore --disable-version-string 29 type runtimeRepository interface { 30 GetByID(ctx context.Context, tenant, id string) (*model.Runtime, error) 31 ListByScenarios(ctx context.Context, tenant string, scenarios []string) ([]*model.Runtime, error) 32 ListByIDs(ctx context.Context, tenant string, ids []string) ([]*model.Runtime, error) 33 } 34 35 //go:generate mockery --exported --name=runtimeContextRepository --output=automock --outpkg=automock --case=underscore --disable-version-string 36 type runtimeContextRepository interface { 37 GetByID(ctx context.Context, tenant, id string) (*model.RuntimeContext, error) 38 GetByRuntimeID(ctx context.Context, tenant, runtimeID string) (*model.RuntimeContext, error) 39 ListByScenarios(ctx context.Context, tenant string, scenarios []string) ([]*model.RuntimeContext, error) 40 } 41 42 //go:generate mockery --exported --name=labelRepository --output=automock --outpkg=automock --case=underscore --disable-version-string 43 type labelRepository interface { 44 ListForObject(ctx context.Context, tenant string, objectType model.LabelableObject, objectID string) (map[string]*model.Label, error) 45 ListForObjectIDs(ctx context.Context, tenant string, objectType model.LabelableObject, objectIDs []string) (map[string]map[string]interface{}, error) 46 } 47 48 // DataInputBuilder is responsible to prepare and build different entity data needed for a webhook input 49 //go:generate mockery --exported --name=DataInputBuilder --output=automock --outpkg=automock --case=underscore --disable-version-string 50 type DataInputBuilder interface { 51 PrepareApplicationAndAppTemplateWithLabels(ctx context.Context, tenant, appID string) (*webhook.ApplicationWithLabels, *webhook.ApplicationTemplateWithLabels, error) 52 PrepareRuntimeWithLabels(ctx context.Context, tenant, runtimeID string) (*webhook.RuntimeWithLabels, error) 53 PrepareRuntimeContextWithLabels(ctx context.Context, tenant, runtimeCtxID string) (*webhook.RuntimeContextWithLabels, error) 54 PrepareRuntimeAndRuntimeContextWithLabels(ctx context.Context, tenant, runtimeID string) (*webhook.RuntimeWithLabels, *webhook.RuntimeContextWithLabels, error) 55 PrepareRuntimesAndRuntimeContextsMappingsInFormation(ctx context.Context, tenant string, scenario string) (map[string]*webhook.RuntimeWithLabels, map[string]*webhook.RuntimeContextWithLabels, error) 56 PrepareApplicationMappingsInFormation(ctx context.Context, tenant string, scenario string) (map[string]*webhook.ApplicationWithLabels, map[string]*webhook.ApplicationTemplateWithLabels, error) 57 } 58 59 // WebhookDataInputBuilder take cares to get and build different webhook input data such as application, runtime, runtime contexts 60 type WebhookDataInputBuilder struct { 61 applicationRepository applicationRepository 62 applicationTemplateRepository applicationTemplateRepository 63 runtimeRepo runtimeRepository 64 runtimeContextRepo runtimeContextRepository 65 labelRepository labelRepository 66 } 67 68 // NewWebhookDataInputBuilder creates a WebhookDataInputBuilder 69 func NewWebhookDataInputBuilder(applicationRepository applicationRepository, applicationTemplateRepository applicationTemplateRepository, runtimeRepo runtimeRepository, runtimeContextRepo runtimeContextRepository, labelRepository labelRepository) *WebhookDataInputBuilder { 70 return &WebhookDataInputBuilder{ 71 applicationRepository: applicationRepository, 72 applicationTemplateRepository: applicationTemplateRepository, 73 runtimeRepo: runtimeRepo, 74 runtimeContextRepo: runtimeContextRepo, 75 labelRepository: labelRepository, 76 } 77 } 78 79 // PrepareApplicationAndAppTemplateWithLabels construct ApplicationWithLabels and ApplicationTemplateWithLabels based on tenant and ID 80 func (b *WebhookDataInputBuilder) PrepareApplicationAndAppTemplateWithLabels(ctx context.Context, tenant, appID string) (*webhook.ApplicationWithLabels, *webhook.ApplicationTemplateWithLabels, error) { 81 application, err := b.applicationRepository.GetByID(ctx, tenant, appID) 82 if err != nil { 83 return nil, nil, errors.Wrapf(err, "while getting application by ID: %q", appID) 84 } 85 applicationLabels, err := b.getLabelsForObject(ctx, tenant, appID, model.ApplicationLabelableObject) 86 if err != nil { 87 return nil, nil, err 88 } 89 applicationWithLabels := &webhook.ApplicationWithLabels{ 90 Application: application, 91 Labels: applicationLabels, 92 } 93 94 var appTemplateWithLabels *webhook.ApplicationTemplateWithLabels 95 if application.ApplicationTemplateID != nil { 96 appTemplate, err := b.applicationTemplateRepository.Get(ctx, *application.ApplicationTemplateID) 97 if err != nil { 98 return nil, nil, errors.Wrapf(err, "while getting application template with ID: %q", *application.ApplicationTemplateID) 99 } 100 applicationTemplateLabels, err := b.getLabelsForObject(ctx, tenant, appTemplate.ID, model.AppTemplateLabelableObject) 101 if err != nil { 102 return nil, nil, err 103 } 104 appTemplateWithLabels = &webhook.ApplicationTemplateWithLabels{ 105 ApplicationTemplate: appTemplate, 106 Labels: applicationTemplateLabels, 107 } 108 } 109 return applicationWithLabels, appTemplateWithLabels, nil 110 } 111 112 // PrepareRuntimeWithLabels construct RuntimeWithLabels based on tenant and runtimeID 113 func (b *WebhookDataInputBuilder) PrepareRuntimeWithLabels(ctx context.Context, tenant, runtimeID string) (*webhook.RuntimeWithLabels, error) { 114 runtime, err := b.runtimeRepo.GetByID(ctx, tenant, runtimeID) 115 if err != nil { 116 return nil, errors.Wrapf(err, "while getting runtime by ID: %q", runtimeID) 117 } 118 119 runtimeLabels, err := b.getLabelsForObject(ctx, tenant, runtimeID, model.RuntimeLabelableObject) 120 if err != nil { 121 return nil, err 122 } 123 124 runtimeWithLabels := &webhook.RuntimeWithLabels{ 125 Runtime: runtime, 126 Labels: runtimeLabels, 127 } 128 129 return runtimeWithLabels, nil 130 } 131 132 // PrepareRuntimeContextWithLabels construct RuntimeContextWithLabels based on tenant and runtimeCtxID 133 func (b *WebhookDataInputBuilder) PrepareRuntimeContextWithLabels(ctx context.Context, tenant, runtimeCtxID string) (*webhook.RuntimeContextWithLabels, error) { 134 runtimeCtx, err := b.runtimeContextRepo.GetByID(ctx, tenant, runtimeCtxID) 135 if err != nil { 136 return nil, errors.Wrapf(err, "while getting runtime context by ID: %q", runtimeCtxID) 137 } 138 139 runtimeCtxLabels, err := b.getLabelsForObject(ctx, tenant, runtimeCtx.ID, model.RuntimeContextLabelableObject) 140 if err != nil { 141 return nil, err 142 } 143 144 runtimeContextWithLabels := &webhook.RuntimeContextWithLabels{ 145 RuntimeContext: runtimeCtx, 146 Labels: runtimeCtxLabels, 147 } 148 149 return runtimeContextWithLabels, nil 150 } 151 152 // PrepareRuntimeAndRuntimeContextWithLabels construct RuntimeWithLabels and RuntimeContextWithLabels based on tenant and runtimeID 153 func (b *WebhookDataInputBuilder) PrepareRuntimeAndRuntimeContextWithLabels(ctx context.Context, tenant, runtimeID string) (*webhook.RuntimeWithLabels, *webhook.RuntimeContextWithLabels, error) { 154 runtimeWithLabels, err := b.PrepareRuntimeWithLabels(ctx, tenant, runtimeID) 155 if err != nil { 156 return nil, nil, err 157 } 158 159 runtimeCtx, err := b.runtimeContextRepo.GetByRuntimeID(ctx, tenant, runtimeID) 160 if err != nil { 161 return nil, nil, errors.Wrapf(err, "while getting runtime context for runtime with ID: %q", runtimeID) 162 } 163 164 runtimeCtxLabels, err := b.getLabelsForObject(ctx, tenant, runtimeCtx.ID, model.RuntimeContextLabelableObject) 165 if err != nil { 166 return nil, nil, err 167 } 168 169 runtimeContextWithLabels := &webhook.RuntimeContextWithLabels{ 170 RuntimeContext: runtimeCtx, 171 Labels: runtimeCtxLabels, 172 } 173 174 return runtimeWithLabels, runtimeContextWithLabels, nil 175 } 176 177 // PrepareRuntimesAndRuntimeContextsMappingsInFormation constructs: 178 // map from runtime ID to RuntimeWithLabels with entries for each runtime part of the formation and for each runtime whose child runtime context is part of the formation 179 // map from parent runtime ID to RuntimeContextWithLabels with entries for all runtime contexts part of the formation. 180 func (b *WebhookDataInputBuilder) PrepareRuntimesAndRuntimeContextsMappingsInFormation(ctx context.Context, tenant string, scenario string) (map[string]*webhook.RuntimeWithLabels, map[string]*webhook.RuntimeContextWithLabels, error) { 181 runtimesInFormation, err := b.runtimeRepo.ListByScenarios(ctx, tenant, []string{scenario}) 182 if err != nil { 183 return nil, nil, errors.Wrapf(err, "while listing runtimes in scenario %s", scenario) 184 } 185 186 runtimeContextsInFormation, err := b.runtimeContextRepo.ListByScenarios(ctx, tenant, []string{scenario}) 187 if err != nil { 188 return nil, nil, errors.Wrapf(err, "while listing runtime contexts in scenario %s", scenario) 189 } 190 191 runtimeContextsIDs := make([]string, 0, len(runtimeContextsInFormation)) 192 parentRuntimeIDs := make([]string, 0, len(runtimeContextsInFormation)) 193 for _, rtCtx := range runtimeContextsInFormation { 194 runtimeContextsIDs = append(runtimeContextsIDs, rtCtx.ID) 195 parentRuntimeIDs = append(parentRuntimeIDs, rtCtx.RuntimeID) 196 } 197 198 // the parent runtime of the runtime context may not be in the formation - that's why we list them separately 199 parentRuntimesOfRuntimeContextsInFormation, err := b.runtimeRepo.ListByIDs(ctx, tenant, parentRuntimeIDs) 200 if err != nil { 201 return nil, nil, errors.Wrapf(err, "while listing parent runtimes of runtime contexts in scenario %s", scenario) 202 } 203 204 runtimes := append(runtimesInFormation, parentRuntimesOfRuntimeContextsInFormation...) 205 runtimesIDs := make([]string, 0, len(runtimes)) 206 for _, rt := range runtimes { 207 runtimesIDs = append(runtimesIDs, rt.ID) 208 } 209 210 runtimesLabels, err := b.getLabelsForObjects(ctx, tenant, runtimesIDs, model.RuntimeLabelableObject) 211 if err != nil { 212 return nil, nil, errors.Wrap(err, "while listing runtime labels") 213 } 214 215 runtimesMapping := make(map[string]*webhook.RuntimeWithLabels, len(runtimesLabels)) 216 for _, rt := range runtimes { 217 runtimesMapping[rt.ID] = &webhook.RuntimeWithLabels{ 218 Runtime: rt, 219 Labels: runtimesLabels[rt.ID], 220 } 221 } 222 223 runtimeContextsLabels, err := b.getLabelsForObjects(ctx, tenant, runtimeContextsIDs, model.RuntimeContextLabelableObject) 224 if err != nil { 225 return nil, nil, errors.Wrap(err, "while listing labels for runtime contexts") 226 } 227 228 runtimesToRuntimeContextsMapping := make(map[string]*webhook.RuntimeContextWithLabels, len(runtimeContextsInFormation)) 229 for _, rtCtx := range runtimeContextsInFormation { 230 runtimesToRuntimeContextsMapping[rtCtx.RuntimeID] = &webhook.RuntimeContextWithLabels{ 231 RuntimeContext: rtCtx, 232 Labels: runtimeContextsLabels[rtCtx.ID], 233 } 234 } 235 236 return runtimesMapping, runtimesToRuntimeContextsMapping, nil 237 } 238 239 // PrepareApplicationMappingsInFormation constructs: 240 // map from application ID to ApplicationWithLabels with entries for each application part of the formation 241 // map from applicationTemplate ID to ApplicationTemplateWithLabels with entries for each application template whose child application is part of the formation 242 func (b *WebhookDataInputBuilder) PrepareApplicationMappingsInFormation(ctx context.Context, tenant string, scenario string) (map[string]*webhook.ApplicationWithLabels, map[string]*webhook.ApplicationTemplateWithLabels, error) { 243 applicationsToBeNotifiedFor, err := b.applicationRepository.ListByScenariosNoPaging(ctx, tenant, []string{scenario}) 244 if err != nil { 245 return nil, nil, errors.Wrapf(err, "while listing applications in formation %s", scenario) 246 } 247 248 if len(applicationsToBeNotifiedFor) == 0 { 249 log.C(ctx).Infof("There are no applications in scenario %s.", scenario) 250 return make(map[string]*webhook.ApplicationWithLabels, 0), make(map[string]*webhook.ApplicationTemplateWithLabels, 0), nil 251 } 252 253 applicationsToBeNotifiedForIDs := make([]string, 0, len(applicationsToBeNotifiedFor)) 254 applicationsTemplateIDs := make([]string, 0, len(applicationsToBeNotifiedFor)) 255 for _, app := range applicationsToBeNotifiedFor { 256 applicationsToBeNotifiedForIDs = append(applicationsToBeNotifiedForIDs, app.ID) 257 if app.ApplicationTemplateID != nil { 258 applicationsTemplateIDs = append(applicationsTemplateIDs, *app.ApplicationTemplateID) 259 } 260 } 261 262 applicationsToBeNotifiedForLabels, err := b.getLabelsForObjects(ctx, tenant, applicationsToBeNotifiedForIDs, model.ApplicationLabelableObject) 263 if err != nil { 264 return nil, nil, errors.Wrap(err, "while listing labels for applications") 265 } 266 267 applicationMapping := make(map[string]*webhook.ApplicationWithLabels, len(applicationsToBeNotifiedForIDs)) 268 for i, app := range applicationsToBeNotifiedFor { 269 applicationMapping[app.ID] = &webhook.ApplicationWithLabels{ 270 Application: applicationsToBeNotifiedFor[i], 271 Labels: applicationsToBeNotifiedForLabels[app.ID], 272 } 273 } 274 275 applicationTemplates, err := b.applicationTemplateRepository.ListByIDs(ctx, applicationsTemplateIDs) 276 if err != nil { 277 return nil, nil, errors.Wrap(err, "while listing application templates") 278 } 279 280 applicationTemplatesLabels, err := b.getLabelsForObjects(ctx, tenant, applicationsTemplateIDs, model.AppTemplateLabelableObject) 281 if err != nil { 282 return nil, nil, errors.Wrap(err, "while listing labels for application templates") 283 } 284 285 applicationTemplatesMapping := make(map[string]*webhook.ApplicationTemplateWithLabels, len(applicationTemplates)) 286 for i, appTemplate := range applicationTemplates { 287 applicationTemplatesMapping[appTemplate.ID] = &webhook.ApplicationTemplateWithLabels{ 288 ApplicationTemplate: applicationTemplates[i], 289 Labels: applicationTemplatesLabels[appTemplate.ID], 290 } 291 } 292 293 return applicationMapping, applicationTemplatesMapping, nil 294 } 295 296 func (b *WebhookDataInputBuilder) getLabelsForObject(ctx context.Context, tenant, objectID string, objectType model.LabelableObject) (map[string]string, error) { 297 labels, err := b.labelRepository.ListForObject(ctx, tenant, objectType, objectID) 298 if err != nil { 299 return nil, errors.Wrapf(err, "while listing labels for %q with ID: %q", objectType, objectID) 300 } 301 labelsMap := make(map[string]string, len(labels)) 302 for _, l := range labels { 303 labelBytes, err := json.Marshal(l.Value) 304 if err != nil { 305 return nil, errors.Wrap(err, "while unmarshaling label value") 306 } 307 308 stringLabel := string(labelBytes) 309 unquotedLabel, err := strconv.Unquote(stringLabel) 310 if err != nil { 311 labelsMap[l.Key] = stringLabel 312 } else { 313 labelsMap[l.Key] = unquotedLabel 314 } 315 } 316 return labelsMap, nil 317 } 318 319 func (b *WebhookDataInputBuilder) getLabelsForObjects(ctx context.Context, tenant string, objectIDs []string, objectType model.LabelableObject) (map[string]map[string]string, error) { 320 labelsForResources, err := b.labelRepository.ListForObjectIDs(ctx, tenant, objectType, objectIDs) 321 if err != nil { 322 return nil, errors.Wrapf(err, "while listing labels for %q with IDs: %q", objectType, objectIDs) 323 } 324 labelsForResourcesMap := make(map[string]map[string]string, len(labelsForResources)) 325 for resourceID, labels := range labelsForResources { 326 for key, value := range labels { 327 labelBytes, err := json.Marshal(value) 328 if err != nil { 329 return nil, errors.Wrap(err, "while marshaling label value") 330 } 331 332 if _, ok := labelsForResourcesMap[resourceID]; !ok { 333 labelsForResourcesMap[resourceID] = make(map[string]string) 334 } 335 336 stringLabel := string(labelBytes) 337 unquotedLabel, err := strconv.Unquote(stringLabel) 338 if err != nil { 339 labelsForResourcesMap[resourceID][key] = stringLabel 340 } else { 341 labelsForResourcesMap[resourceID][key] = unquotedLabel 342 } 343 } 344 } 345 return labelsForResourcesMap, nil 346 }