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  }