github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/defined_entity.go (about) 1 /* 2 * Copyright 2023 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. 3 */ 4 5 package govcd 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "github.com/vmware/go-vcloud-director/v2/types/v56" 11 "net/url" 12 "strings" 13 ) 14 15 const ( 16 labelDefinedEntity = "Defined Entity" 17 labelDefinedEntityType = "Defined Entity Type" 18 labelRdeBehavior = "RDE Behavior" 19 labelRdeBehaviorOverride = "RDE Behavior Override" 20 labelRdeBehaviorAccessControl = "RDE Behavior Access Control" 21 ) 22 23 // DefinedEntityType is a type for handling Runtime Defined Entity (RDE) Type definitions. 24 // Note. Running a few of these operations in parallel may corrupt database in VCD (at least <= 10.4.2) 25 type DefinedEntityType struct { 26 DefinedEntityType *types.DefinedEntityType 27 client *Client 28 } 29 30 // wrap is a hidden helper that facilitates the usage of a generic CRUD function 31 // 32 //lint:ignore U1000 this method is used in generic functions, but annoys staticcheck 33 func (d DefinedEntityType) wrap(inner *types.DefinedEntityType) *DefinedEntityType { 34 d.DefinedEntityType = inner 35 return &d 36 } 37 38 // DefinedEntity represents an instance of a Runtime Defined Entity (RDE) 39 type DefinedEntity struct { 40 DefinedEntity *types.DefinedEntity 41 Etag string // Populated by VCDClient.GetRdeById, DefinedEntityType.GetRdeById, DefinedEntity.Update 42 client *Client 43 } 44 45 // wrap is a hidden helper that facilitates the usage of a generic CRUD function 46 // 47 //lint:ignore U1000 this method is used in generic functions, but annoys staticcheck 48 func (d DefinedEntity) wrap(inner *types.DefinedEntity) *DefinedEntity { 49 d.DefinedEntity = inner 50 return &d 51 } 52 53 // CreateRdeType creates a Runtime Defined Entity Type. 54 // Only a System administrator can create RDE Types. 55 func (vcdClient *VCDClient) CreateRdeType(rde *types.DefinedEntityType) (*DefinedEntityType, error) { 56 c := crudConfig{ 57 endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntityTypes, 58 entityLabel: labelDefinedEntityType, 59 } 60 outerType := DefinedEntityType{client: &vcdClient.Client} 61 return createOuterEntity(&vcdClient.Client, outerType, c, rde) 62 } 63 64 // GetAllRdeTypes retrieves all Runtime Defined Entity Types. Query parameters can be supplied to perform additional filtering. 65 func (vcdClient *VCDClient) GetAllRdeTypes(queryParameters url.Values) ([]*DefinedEntityType, error) { 66 return getAllRdeTypes(&vcdClient.Client, queryParameters) 67 } 68 69 // getAllRdeTypes retrieves all Runtime Defined Entity Types. Query parameters can be supplied to perform additional filtering. 70 func getAllRdeTypes(client *Client, queryParameters url.Values) ([]*DefinedEntityType, error) { 71 c := crudConfig{ 72 endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntityTypes, 73 entityLabel: labelDefinedEntityType, 74 queryParameters: queryParameters, 75 } 76 77 outerType := DefinedEntityType{client: client} 78 return getAllOuterEntities[DefinedEntityType, types.DefinedEntityType](client, outerType, c) 79 } 80 81 // GetRdeType gets a Runtime Defined Entity Type by its unique combination of vendor, nss and version. 82 func (vcdClient *VCDClient) GetRdeType(vendor, nss, version string) (*DefinedEntityType, error) { 83 return getRdeType(&vcdClient.Client, vendor, nss, version) 84 } 85 86 // getRdeType gets a Runtime Defined Entity Type by its unique combination of vendor, nss and version. 87 func getRdeType(client *Client, vendor, nss, version string) (*DefinedEntityType, error) { 88 queryParameters := url.Values{} 89 queryParameters.Add("filter", fmt.Sprintf("vendor==%s;nss==%s;version==%s", vendor, nss, version)) 90 rdeTypes, err := getAllRdeTypes(client, queryParameters) 91 if err != nil { 92 return nil, err 93 } 94 95 if len(rdeTypes) == 0 { 96 return nil, fmt.Errorf("%s could not find the Runtime Defined Entity Type with vendor %s, nss %s and version %s", ErrorEntityNotFound, vendor, nss, version) 97 } 98 99 if len(rdeTypes) > 1 { 100 return nil, fmt.Errorf("found more than 1 Runtime Defined Entity Type with vendor %s, nss %s and version %s", vendor, nss, version) 101 } 102 103 return rdeTypes[0], nil 104 } 105 106 // GetRdeTypeById gets a Runtime Defined Entity Type by its ID. 107 func (vcdClient *VCDClient) GetRdeTypeById(id string) (*DefinedEntityType, error) { 108 c := crudConfig{ 109 entityLabel: labelDefinedEntityType, 110 endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntityTypes, 111 endpointParams: []string{id}, 112 } 113 114 outerType := DefinedEntityType{client: &vcdClient.Client} 115 return getOuterEntity[DefinedEntityType, types.DefinedEntityType](&vcdClient.Client, outerType, c) 116 } 117 118 // Update updates the receiver Runtime Defined Entity Type with the values given by the input. 119 // Only a System administrator can create RDE Types. 120 func (rdeType *DefinedEntityType) Update(rdeTypeToUpdate types.DefinedEntityType) error { 121 if rdeType.DefinedEntityType.ID == "" { 122 return fmt.Errorf("ID of the receiver Runtime Defined Entity Type is empty") 123 } 124 125 if rdeTypeToUpdate.ID != "" && rdeTypeToUpdate.ID != rdeType.DefinedEntityType.ID { 126 return fmt.Errorf("ID of the receiver Runtime Defined Entity and the input ID don't match") 127 } 128 129 // Name and schema are mandatory, even when we don't want to update them, so we populate them in this situation to avoid errors 130 // and make this method more user friendly. 131 if rdeTypeToUpdate.Name == "" { 132 rdeTypeToUpdate.Name = rdeType.DefinedEntityType.Name 133 } 134 if rdeTypeToUpdate.Schema == nil || len(rdeTypeToUpdate.Schema) == 0 { 135 rdeTypeToUpdate.Schema = rdeType.DefinedEntityType.Schema 136 } 137 rdeTypeToUpdate.Version = rdeType.DefinedEntityType.Version 138 rdeTypeToUpdate.Nss = rdeType.DefinedEntityType.Nss 139 rdeTypeToUpdate.Vendor = rdeType.DefinedEntityType.Vendor 140 141 c := crudConfig{ 142 endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntityTypes, 143 endpointParams: []string{rdeType.DefinedEntityType.ID}, 144 entityLabel: labelDefinedEntityType, 145 } 146 147 resultDefinedEntityType, err := updateInnerEntity(rdeType.client, c, &rdeTypeToUpdate) 148 if err != nil { 149 return err 150 } 151 // Only if there was no error in request we overwrite pointer receiver as otherwise it would 152 // wipe out existing data 153 rdeType.DefinedEntityType = resultDefinedEntityType 154 155 return nil 156 } 157 158 // Delete deletes the receiver Runtime Defined Entity Type. 159 // Only a System administrator can delete RDE Types. 160 func (rdeType *DefinedEntityType) Delete() error { 161 c := crudConfig{ 162 endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntityTypes, 163 endpointParams: []string{rdeType.DefinedEntityType.ID}, 164 entityLabel: labelDefinedEntityType, 165 } 166 167 if err := deleteEntityById(rdeType.client, c); err != nil { 168 return err 169 } 170 171 rdeType.DefinedEntityType = &types.DefinedEntityType{} 172 return nil 173 } 174 175 // GetAllBehaviors retrieves all the Behaviors of the receiver RDE Type. 176 func (rdeType *DefinedEntityType) GetAllBehaviors(queryParameters url.Values) ([]*types.Behavior, error) { 177 if rdeType.DefinedEntityType.ID == "" { 178 return nil, fmt.Errorf("ID of the receiver Defined Entity Type is empty") 179 } 180 return getAllBehaviors(rdeType.client, rdeType.DefinedEntityType.ID, types.OpenApiEndpointRdeTypeBehaviors, queryParameters) 181 } 182 183 // GetBehaviorById retrieves a unique Behavior that belongs to the receiver RDE Type and is determined by the 184 // input ID. The ID can be a RDE Interface Behavior ID or a RDE Type overridden Behavior ID. 185 func (rdeType *DefinedEntityType) GetBehaviorById(id string) (*types.Behavior, error) { 186 c := crudConfig{ 187 endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeTypeBehaviors, 188 endpointParams: []string{rdeType.DefinedEntityType.ID, id}, 189 entityLabel: labelRdeBehavior, 190 } 191 return getInnerEntity[types.Behavior](rdeType.client, c) 192 } 193 194 // GetBehaviorByName retrieves a unique Behavior that belongs to the receiver RDE Type and is named after 195 // the input. 196 func (rdeType *DefinedEntityType) GetBehaviorByName(name string) (*types.Behavior, error) { 197 behaviors, err := rdeType.GetAllBehaviors(nil) 198 if err != nil { 199 return nil, fmt.Errorf("could not get the Behaviors of the Defined Entity Type with ID '%s': %s", rdeType.DefinedEntityType.ID, err) 200 } 201 label := fmt.Sprintf("Defined Entity Behavior with name '%s' in Defined Entity Type with ID '%s': %s", name, rdeType.DefinedEntityType.ID, ErrorEntityNotFound) 202 return localFilterOneOrError(label, behaviors, "Name", name) 203 } 204 205 // UpdateBehaviorOverride overrides an Interface Behavior. Only Behavior description and execution can be overridden. 206 // It returns the new Behavior, result of the override (with a new ID). 207 func (rdeType *DefinedEntityType) UpdateBehaviorOverride(behavior types.Behavior) (*types.Behavior, error) { 208 if rdeType.DefinedEntityType.ID == "" { 209 return nil, fmt.Errorf("ID of the receiver Defined Entity Type is empty") 210 } 211 if behavior.ID == "" { 212 return nil, fmt.Errorf("ID of the Behavior to override is empty") 213 } 214 215 c := crudConfig{ 216 endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeTypeBehaviors, 217 endpointParams: []string{rdeType.DefinedEntityType.ID, behavior.ID}, 218 entityLabel: labelRdeBehaviorOverride, 219 } 220 return updateInnerEntity(rdeType.client, c, &behavior) 221 } 222 223 // DeleteBehaviorOverride removes a Behavior specified by its ID from the receiver Defined Entity Type. 224 // The ID can be the Interface Behavior ID or the Type Behavior ID (the overridden one). 225 func (rdeType *DefinedEntityType) DeleteBehaviorOverride(behaviorId string) error { 226 if rdeType.DefinedEntityType.ID == "" { 227 return fmt.Errorf("ID of the receiver Defined Entity Type is empty") 228 } 229 230 c := crudConfig{ 231 endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeTypeBehaviors, 232 endpointParams: []string{rdeType.DefinedEntityType.ID, behaviorId}, 233 entityLabel: labelRdeBehaviorOverride, 234 } 235 return deleteEntityById(rdeType.client, c) 236 } 237 238 // SetBehaviorAccessControls sets the given slice of BehaviorAccess to the receiver Defined Entity Type. 239 // If the input is nil, it removes all access controls from the receiver Defined Entity Type. 240 func (det *DefinedEntityType) SetBehaviorAccessControls(acls []*types.BehaviorAccess) error { 241 if det.DefinedEntityType.ID == "" { 242 return fmt.Errorf("ID of the receiver Defined Entity Type is empty") 243 } 244 245 sanitizedAcls := acls 246 if acls == nil { 247 sanitizedAcls = []*types.BehaviorAccess{} 248 } 249 250 // Wrap it in OpenAPI pages, this endpoint requires it 251 rawMessage, err := json.Marshal(sanitizedAcls) 252 if err != nil { 253 return fmt.Errorf("error setting Access controls in payload: %s", err) 254 } 255 payload := types.OpenApiPages{ 256 Values: rawMessage, 257 } 258 259 c := crudConfig{ 260 endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeTypeBehaviorAccessControls, 261 endpointParams: []string{det.DefinedEntityType.ID}, 262 entityLabel: labelRdeBehaviorAccessControl, 263 } 264 _, err = updateInnerEntity(det.client, c, &payload) 265 if err != nil { 266 return err 267 } 268 269 return nil 270 } 271 272 // GetAllBehaviorsAccessControls gets all the Behaviors Access Controls from the receiver DefinedEntityType. 273 // Query parameters can be supplied to modify pagination. 274 func (det *DefinedEntityType) GetAllBehaviorsAccessControls(queryParameters url.Values) ([]*types.BehaviorAccess, error) { 275 c := crudConfig{ 276 endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeTypeBehaviorAccessControls, 277 queryParameters: queryParameters, 278 endpointParams: []string{det.DefinedEntityType.ID}, 279 entityLabel: labelRdeBehaviorAccessControl, 280 } 281 return getAllInnerEntities[types.BehaviorAccess](det.client, c) 282 } 283 284 // GetAllRdes gets all the RDE instances of the given vendor, nss and version. 285 func (vcdClient *VCDClient) GetAllRdes(vendor, nss, version string, queryParameters url.Values) ([]*DefinedEntity, error) { 286 return getAllRdes(&vcdClient.Client, vendor, nss, version, queryParameters) 287 } 288 289 // GetAllRdes gets all the RDE instances of the receiver type. 290 func (rdeType *DefinedEntityType) GetAllRdes(queryParameters url.Values) ([]*DefinedEntity, error) { 291 return getAllRdes(rdeType.client, rdeType.DefinedEntityType.Vendor, rdeType.DefinedEntityType.Nss, rdeType.DefinedEntityType.Version, queryParameters) 292 } 293 294 // getAllRdes gets all the RDE instances of the given vendor, nss and version. 295 // Supports filtering with the given queryParameters. 296 func getAllRdes(client *Client, vendor, nss, version string, queryParameters url.Values) ([]*DefinedEntity, error) { 297 c := crudConfig{ 298 endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntitiesTypes, 299 entityLabel: labelDefinedEntityType, 300 queryParameters: queryParameters, 301 endpointParams: []string{vendor, "/", nss, "/", version}, 302 } 303 304 outerType := DefinedEntity{client: client} 305 return getAllOuterEntities[DefinedEntity, types.DefinedEntity](client, outerType, c) 306 } 307 308 // GetRdesByName gets RDE instances with the given name that belongs to the receiver type. 309 // VCD allows to have many RDEs with the same name, hence this function returns a slice. 310 func (rdeType *DefinedEntityType) GetRdesByName(name string) ([]*DefinedEntity, error) { 311 return getRdesByName(rdeType.client, rdeType.DefinedEntityType.Vendor, rdeType.DefinedEntityType.Nss, rdeType.DefinedEntityType.Version, name) 312 } 313 314 // GetRdesByName gets RDE instances with the given name and the given vendor, nss and version. 315 // VCD allows to have many RDEs with the same name, hence this function returns a slice. 316 func (vcdClient *VCDClient) GetRdesByName(vendor, nss, version, name string) ([]*DefinedEntity, error) { 317 return getRdesByName(&vcdClient.Client, vendor, nss, version, name) 318 } 319 320 // getRdesByName gets RDE instances with the given name and the given vendor, nss and version. 321 // VCD allows to have many RDEs with the same name, hence this function returns a slice. 322 func getRdesByName(client *Client, vendor, nss, version, name string) ([]*DefinedEntity, error) { 323 queryParameters := url.Values{} 324 queryParameters.Add("filter", fmt.Sprintf("name==%s", name)) 325 rdeTypes, err := getAllRdes(client, vendor, nss, version, queryParameters) 326 if err != nil { 327 return nil, err 328 } 329 330 if len(rdeTypes) == 0 { 331 return nil, fmt.Errorf("%s could not find the Runtime Defined Entity with name '%s'", ErrorEntityNotFound, name) 332 } 333 334 return rdeTypes, nil 335 } 336 337 // GetRdeById gets a Runtime Defined Entity by its ID. 338 // Getting a RDE by ID populates the ETag field in the returned object. 339 func (rdeType *DefinedEntityType) GetRdeById(id string) (*DefinedEntity, error) { 340 return getRdeById(rdeType.client, id) 341 } 342 343 // GetRdeById gets a Runtime Defined Entity by its ID. 344 // Getting a RDE by ID populates the ETag field in the returned object. 345 func (vcdClient *VCDClient) GetRdeById(id string) (*DefinedEntity, error) { 346 return getRdeById(&vcdClient.Client, id) 347 } 348 349 // getRdeById gets a Runtime Defined Entity by its ID. 350 // Getting a RDE by ID populates the ETag field in the returned object. 351 func getRdeById(client *Client, id string) (*DefinedEntity, error) { 352 c := crudConfig{ 353 entityLabel: labelDefinedEntityType, 354 endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntities, 355 endpointParams: []string{id}, 356 } 357 358 outerType := DefinedEntity{client: client} 359 result, headers, err := getOuterEntityWithHeaders(client, outerType, c) 360 if err != nil { 361 return nil, err 362 } 363 result.Etag = headers.Get("Etag") 364 return result, nil 365 } 366 367 // CreateRde creates an entity of the type of the receiver Runtime Defined Entity (RDE) type. 368 // The input doesn't need to specify the type ID, as it gets it from the receiver RDE type. 369 // The input tenant context allows to create the RDE in a given org if the creator is a System admin. 370 // NOTE: After RDE creation, some actor should Resolve it, otherwise the RDE state will be "PRE_CREATED" 371 // and the generated VCD task will remain at 1% until resolved. 372 func (rdeType *DefinedEntityType) CreateRde(entity types.DefinedEntity, tenantContext *TenantContext) (*DefinedEntity, error) { 373 entity.EntityType = rdeType.DefinedEntityType.ID 374 task, err := createRde(rdeType.client, entity, tenantContext) 375 if err != nil { 376 return nil, err 377 } 378 return getRdeFromTask(rdeType.client, task) 379 } 380 381 // CreateRde creates an entity of the type of the given vendor, nss and version. 382 // NOTE: After RDE creation, some actor should Resolve it, otherwise the RDE state will be "PRE_CREATED" 383 // and the generated VCD task will remain at 1% until resolved. 384 func (vcdClient *VCDClient) CreateRde(vendor, nss, version string, entity types.DefinedEntity, tenantContext *TenantContext) (*DefinedEntity, error) { 385 return createRdeAndGetFromTask(&vcdClient.Client, vendor, nss, version, entity, tenantContext) 386 } 387 388 // createRdeAndGetFromTask creates an entity of the type of the given vendor, nss and version. 389 // NOTE: After RDE creation, some actor should Resolve it, otherwise the RDE state will be "PRE_CREATED" 390 // and the generated VCD task will remain at 1% until resolved. 391 func createRdeAndGetFromTask(client *Client, vendor, nss, version string, entity types.DefinedEntity, tenantContext *TenantContext) (*DefinedEntity, error) { 392 entity.EntityType = fmt.Sprintf("urn:vcloud:type:%s:%s:%s", vendor, nss, version) 393 task, err := createRde(client, entity, tenantContext) 394 if err != nil { 395 return nil, err 396 } 397 return getRdeFromTask(client, task) 398 } 399 400 // CreateRde creates an entity of the type of the receiver Runtime Defined Entity (RDE) type. 401 // The input doesn't need to specify the type ID, as it gets it from the receiver RDE type. If it is specified anyway, 402 // it must match the type ID of the receiver RDE type. 403 // NOTE: After RDE creation, some actor should Resolve it, otherwise the RDE state will be "PRE_CREATED" 404 // and the generated VCD task will remain at 1% until resolved. 405 func createRde(client *Client, entity types.DefinedEntity, tenantContext *TenantContext) (*Task, error) { 406 if entity.EntityType == "" { 407 return nil, fmt.Errorf("ID of the Runtime Defined Entity type is empty") 408 } 409 410 if entity.Entity == nil || len(entity.Entity) == 0 { 411 return nil, fmt.Errorf("the entity JSON is empty") 412 } 413 414 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntityTypes 415 apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) 416 if err != nil { 417 return nil, err 418 } 419 420 urlRef, err := client.OpenApiBuildEndpoint(endpoint, entity.EntityType) 421 if err != nil { 422 return nil, err 423 } 424 425 task, err := client.OpenApiPostItemAsyncWithHeaders(apiVersion, urlRef, nil, entity, getTenantContextHeader(tenantContext)) 426 if err != nil { 427 return nil, err 428 } 429 // The refresh is needed as the task only has the HREF at the moment 430 err = task.Refresh() 431 if err != nil { 432 return nil, err 433 } 434 return &task, nil 435 } 436 437 // getRdeFromTask gets the Runtime Defined Entity from a given Task. This method is useful after RDE creation, as 438 // the API just returns a Task with the RDE details inside. 439 func getRdeFromTask(client *Client, task *Task) (*DefinedEntity, error) { 440 if task.Task == nil { 441 return nil, fmt.Errorf("could not retrieve the RDE from task, as it is nil") 442 } 443 rdeId := "" 444 if task.Task.Owner == nil { 445 // Try to retrieve the ID from the "Operation" field 446 beginning := strings.LastIndex(task.Task.Operation, "(") 447 end := strings.LastIndex(task.Task.Operation, ")") 448 if beginning < 0 || end < 0 || beginning >= end { 449 return nil, fmt.Errorf("could not retrieve the RDE from the task with ID '%s'", task.Task.ID) 450 } 451 rdeId = task.Task.Operation[beginning+1 : end] 452 } else { 453 rdeId = task.Task.Owner.ID 454 } 455 456 return getRdeById(client, rdeId) 457 } 458 459 // Resolve needs to be called after an RDE is successfully created. It makes the receiver RDE usable if the JSON entity 460 // is valid, reaching a state of RESOLVED. If it fails, the state will be RESOLUTION_ERROR, 461 // and it will need to Update the JSON entity. 462 // Resolving a RDE populates the ETag field in the receiver object. 463 func (rde *DefinedEntity) Resolve() error { 464 client := rde.client 465 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntitiesResolve 466 apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) 467 if err != nil { 468 return err 469 } 470 471 urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, rde.DefinedEntity.ID)) 472 if err != nil { 473 return err 474 } 475 476 headers, err := client.OpenApiPostItemAndGetHeaders(apiVersion, urlRef, nil, nil, rde.DefinedEntity, nil) 477 if err != nil { 478 return amendRdeApiError(client, err) 479 } 480 rde.Etag = headers.Get("Etag") 481 482 return nil 483 } 484 485 // Update updates the receiver Runtime Defined Entity with the values given by the input. This method is useful 486 // if rde.Resolve() failed and a JSON entity change is needed. 487 // Updating a RDE populates the ETag field in the receiver object. 488 func (rde *DefinedEntity) Update(rdeToUpdate types.DefinedEntity) error { 489 if rde.DefinedEntity.ID == "" { 490 return fmt.Errorf("ID of the receiver Runtime Defined Entity is empty") 491 } 492 493 // Name is mandatory, despite we don't want to update it, so we populate it in this situation to avoid errors 494 // and make this method more user friendly. 495 if rdeToUpdate.Name == "" { 496 rdeToUpdate.Name = rde.DefinedEntity.Name 497 } 498 499 if rde.Etag == "" { 500 // We need to get an Etag to perform the update 501 retrievedRde, err := getRdeById(rde.client, rde.DefinedEntity.ID) 502 if err != nil { 503 return err 504 } 505 if retrievedRde.Etag == "" { 506 return fmt.Errorf("could not retrieve a valid Etag to perform an update to RDE %s", retrievedRde.DefinedEntity.ID) 507 } 508 rde.Etag = retrievedRde.Etag 509 } 510 511 c := crudConfig{ 512 entityLabel: labelDefinedEntity, 513 endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntities, 514 endpointParams: []string{rde.DefinedEntity.ID}, 515 additionalHeader: map[string]string{"If-Match": rde.Etag}, 516 } 517 518 resultDefinedEntity, headers, err := updateInnerEntityWithHeaders(rde.client, c, &rdeToUpdate) 519 if err != nil { 520 return err 521 } 522 // Only if there was no error in request we overwrite pointer receiver as otherwise it would 523 // wipe out existing data 524 rde.DefinedEntity = resultDefinedEntity 525 rde.Etag = headers.Get("Etag") 526 527 return nil 528 } 529 530 // Delete deletes the receiver Runtime Defined Entity. 531 func (rde *DefinedEntity) Delete() error { 532 c := crudConfig{ 533 endpoint: types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntities, 534 endpointParams: []string{rde.DefinedEntity.ID}, 535 entityLabel: labelDefinedEntity, 536 } 537 538 if err := deleteEntityById(rde.client, c); err != nil { 539 return amendRdeApiError(rde.client, err) 540 } 541 542 rde.DefinedEntity = &types.DefinedEntity{} 543 rde.Etag = "" 544 return nil 545 } 546 547 // InvokeBehavior calls a Behavior identified by the given ID with the given execution parameters. 548 // Returns the invocation result as a raw string. 549 func (rde *DefinedEntity) InvokeBehavior(behaviorId string, invocation types.BehaviorInvocation) (string, error) { 550 client := rde.client 551 endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntitiesBehaviorsInvocations 552 apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) 553 if err != nil { 554 return "", err 555 } 556 557 urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, rde.DefinedEntity.ID, behaviorId)) 558 if err != nil { 559 return "", err 560 } 561 562 task, err := client.OpenApiPostItemAsync(apiVersion, urlRef, nil, invocation) 563 if err != nil { 564 return "", err 565 } 566 567 err = task.WaitTaskCompletion() 568 if err != nil { 569 return "", err 570 } 571 572 if task.Task.Result == nil { 573 return "", fmt.Errorf("the Task '%s' returned an empty Result content", task.Task.ID) 574 } 575 576 return task.Task.Result.ResultContent.Text, nil 577 } 578 579 // InvokeBehaviorAndMarshal calls a Behavior identified by the given ID with the given execution parameters. 580 // Returns the invocation result marshaled with the input object. 581 func (rde *DefinedEntity) InvokeBehaviorAndMarshal(behaviorId string, invocation types.BehaviorInvocation, output interface{}) error { 582 result, err := rde.InvokeBehavior(behaviorId, invocation) 583 if err != nil { 584 return err 585 } 586 587 err = json.Unmarshal([]byte(result), &output) 588 if err != nil { 589 return fmt.Errorf("error marshaling the invocation result '%s': %s", result, err) 590 } 591 592 return nil 593 }