github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/command/format/object_id.go (about)

     1  package format
     2  
     3  import (
     4  	"github.com/zclconf/go-cty/cty"
     5  )
     6  
     7  // ObjectValueID takes a value that is assumed to be an object representation
     8  // of some resource instance object and attempts to heuristically find an
     9  // attribute of it that is likely to be a unique identifier in the remote
    10  // system that it belongs to which will be useful to the user.
    11  //
    12  // If such an attribute is found, its name and string value intended for
    13  // display are returned. Both returned strings are empty if no such attribute
    14  // exists, in which case the caller should assume that the resource instance
    15  // address within the Terraform configuration is the best available identifier.
    16  //
    17  // This is only a best-effort sort of thing, relying on naming conventions in
    18  // our resource type schemas. The result is not guaranteed to be unique, but
    19  // should generally be suitable for display to an end-user anyway.
    20  //
    21  // This function will panic if the given value is not of an object type.
    22  func ObjectValueID(obj cty.Value) (k, v string) {
    23  	if obj.IsNull() || !obj.IsKnown() {
    24  		return "", ""
    25  	}
    26  
    27  	atys := obj.Type().AttributeTypes()
    28  
    29  	switch {
    30  
    31  	case atys["id"] == cty.String:
    32  		v := obj.GetAttr("id")
    33  		if v.IsKnown() && !v.IsNull() {
    34  			return "id", v.AsString()
    35  		}
    36  
    37  	case atys["name"] == cty.String:
    38  		// "name" isn't always globally unique, but if there isn't also an
    39  		// "id" then it _often_ is, in practice.
    40  		v := obj.GetAttr("name")
    41  		if v.IsKnown() && !v.IsNull() {
    42  			return "name", v.AsString()
    43  		}
    44  	}
    45  
    46  	return "", ""
    47  }
    48  
    49  // ObjectValueName takes a value that is assumed to be an object representation
    50  // of some resource instance object and attempts to heuristically find an
    51  // attribute of it that is likely to be a human-friendly name in the remote
    52  // system that it belongs to which will be useful to the user.
    53  //
    54  // If such an attribute is found, its name and string value intended for
    55  // display are returned. Both returned strings are empty if no such attribute
    56  // exists, in which case the caller should assume that the resource instance
    57  // address within the Terraform configuration is the best available identifier.
    58  //
    59  // This is only a best-effort sort of thing, relying on naming conventions in
    60  // our resource type schemas. The result is not guaranteed to be unique, but
    61  // should generally be suitable for display to an end-user anyway.
    62  //
    63  // Callers that use both ObjectValueName and ObjectValueID at the same time
    64  // should be prepared to get the same attribute key and value from both in
    65  // some cases, since there is overlap betweek the id-extraction and
    66  // name-extraction heuristics.
    67  //
    68  // This function will panic if the given value is not of an object type.
    69  func ObjectValueName(obj cty.Value) (k, v string) {
    70  	if obj.IsNull() || !obj.IsKnown() {
    71  		return "", ""
    72  	}
    73  
    74  	atys := obj.Type().AttributeTypes()
    75  
    76  	switch {
    77  
    78  	case atys["name"] == cty.String:
    79  		v := obj.GetAttr("name")
    80  		if v.IsKnown() && !v.IsNull() {
    81  			return "name", v.AsString()
    82  		}
    83  
    84  	case atys["tags"].IsMapType() && atys["tags"].ElementType() == cty.String:
    85  		tags := obj.GetAttr("tags")
    86  		if tags.IsNull() || !tags.IsWhollyKnown() {
    87  			break
    88  		}
    89  
    90  		switch {
    91  		case tags.HasIndex(cty.StringVal("name")).RawEquals(cty.True):
    92  			v := tags.Index(cty.StringVal("name"))
    93  			if v.IsKnown() && !v.IsNull() {
    94  				return "tags.name", v.AsString()
    95  			}
    96  		case tags.HasIndex(cty.StringVal("Name")).RawEquals(cty.True):
    97  			// AWS-style naming convention
    98  			v := tags.Index(cty.StringVal("Name"))
    99  			if v.IsKnown() && !v.IsNull() {
   100  				return "tags.Name", v.AsString()
   101  			}
   102  		}
   103  	}
   104  
   105  	return "", ""
   106  }
   107  
   108  // ObjectValueIDOrName is a convenience wrapper around both ObjectValueID
   109  // and ObjectValueName (in that preference order) to try to extract some sort
   110  // of human-friendly descriptive string value for an object as additional
   111  // context about an object when it is being displayed in a compact way (where
   112  // not all of the attributes are visible.)
   113  //
   114  // Just as with the two functions it wraps, it is a best-effort and may return
   115  // two empty strings if no suitable attribute can be found for a given object.
   116  func ObjectValueIDOrName(obj cty.Value) (k, v string) {
   117  	k, v = ObjectValueID(obj)
   118  	if k != "" {
   119  		return
   120  	}
   121  	k, v = ObjectValueName(obj)
   122  	return
   123  }