github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/plugin/convert/schema.go (about)

     1  package convert
     2  
     3  import (
     4  	"encoding/json"
     5  	"log"
     6  	"reflect"
     7  	"sort"
     8  
     9  	"github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema"
    10  	"github.com/hashicorp/terraform-plugin-sdk/internal/providers"
    11  	proto "github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5"
    12  )
    13  
    14  // ConfigSchemaToProto takes a *configschema.Block and converts it to a
    15  // proto.Schema_Block for a grpc response.
    16  func ConfigSchemaToProto(b *configschema.Block) *proto.Schema_Block {
    17  	block := &proto.Schema_Block{
    18  		Description:     b.Description,
    19  		DescriptionKind: protoStringKind(b.DescriptionKind),
    20  		Deprecated:      b.Deprecated,
    21  	}
    22  
    23  	for _, name := range sortedKeys(b.Attributes) {
    24  		a := b.Attributes[name]
    25  
    26  		attr := &proto.Schema_Attribute{
    27  			Name:            name,
    28  			Description:     a.Description,
    29  			DescriptionKind: protoStringKind(a.DescriptionKind),
    30  			Optional:        a.Optional,
    31  			Computed:        a.Computed,
    32  			Required:        a.Required,
    33  			Sensitive:       a.Sensitive,
    34  			Deprecated:      a.Deprecated,
    35  		}
    36  
    37  		ty, err := json.Marshal(a.Type)
    38  		if err != nil {
    39  			panic(err)
    40  		}
    41  
    42  		attr.Type = ty
    43  
    44  		block.Attributes = append(block.Attributes, attr)
    45  	}
    46  
    47  	for _, name := range sortedKeys(b.BlockTypes) {
    48  		b := b.BlockTypes[name]
    49  		block.BlockTypes = append(block.BlockTypes, protoSchemaNestedBlock(name, b))
    50  	}
    51  
    52  	return block
    53  }
    54  
    55  func protoStringKind(k configschema.StringKind) proto.StringKind {
    56  	switch k {
    57  	default:
    58  		log.Printf("[TRACE] unexpected configschema.StringKind: %d", k)
    59  		return proto.StringKind_PLAIN
    60  	case configschema.StringPlain:
    61  		return proto.StringKind_PLAIN
    62  	case configschema.StringMarkdown:
    63  		return proto.StringKind_MARKDOWN
    64  	}
    65  }
    66  
    67  func protoSchemaNestedBlock(name string, b *configschema.NestedBlock) *proto.Schema_NestedBlock {
    68  	var nesting proto.Schema_NestedBlock_NestingMode
    69  	switch b.Nesting {
    70  	case configschema.NestingSingle:
    71  		nesting = proto.Schema_NestedBlock_SINGLE
    72  	case configschema.NestingGroup:
    73  		nesting = proto.Schema_NestedBlock_GROUP
    74  	case configschema.NestingList:
    75  		nesting = proto.Schema_NestedBlock_LIST
    76  	case configschema.NestingSet:
    77  		nesting = proto.Schema_NestedBlock_SET
    78  	case configschema.NestingMap:
    79  		nesting = proto.Schema_NestedBlock_MAP
    80  	default:
    81  		nesting = proto.Schema_NestedBlock_INVALID
    82  	}
    83  	return &proto.Schema_NestedBlock{
    84  		TypeName: name,
    85  		Block:    ConfigSchemaToProto(&b.Block),
    86  		Nesting:  nesting,
    87  		MinItems: int64(b.MinItems),
    88  		MaxItems: int64(b.MaxItems),
    89  	}
    90  }
    91  
    92  // ProtoToProviderSchema takes a proto.Schema and converts it to a providers.Schema.
    93  func ProtoToProviderSchema(s *proto.Schema) providers.Schema {
    94  	return providers.Schema{
    95  		Version: s.Version,
    96  		Block:   ProtoToConfigSchema(s.Block),
    97  	}
    98  }
    99  
   100  // ProtoToConfigSchema takes the GetSchcema_Block from a grpc response and converts it
   101  // to a terraform *configschema.Block.
   102  func ProtoToConfigSchema(b *proto.Schema_Block) *configschema.Block {
   103  	block := &configschema.Block{
   104  		Attributes: make(map[string]*configschema.Attribute),
   105  		BlockTypes: make(map[string]*configschema.NestedBlock),
   106  
   107  		Description:     b.Description,
   108  		DescriptionKind: schemaStringKind(b.DescriptionKind),
   109  		Deprecated:      b.Deprecated,
   110  	}
   111  
   112  	for _, a := range b.Attributes {
   113  		attr := &configschema.Attribute{
   114  			Description:     a.Description,
   115  			DescriptionKind: schemaStringKind(a.DescriptionKind),
   116  			Required:        a.Required,
   117  			Optional:        a.Optional,
   118  			Computed:        a.Computed,
   119  			Sensitive:       a.Sensitive,
   120  			Deprecated:      a.Deprecated,
   121  		}
   122  
   123  		if err := json.Unmarshal(a.Type, &attr.Type); err != nil {
   124  			panic(err)
   125  		}
   126  
   127  		block.Attributes[a.Name] = attr
   128  	}
   129  
   130  	for _, b := range b.BlockTypes {
   131  		block.BlockTypes[b.TypeName] = schemaNestedBlock(b)
   132  	}
   133  
   134  	return block
   135  }
   136  
   137  func schemaStringKind(k proto.StringKind) configschema.StringKind {
   138  	switch k {
   139  	default:
   140  		log.Printf("[TRACE] unexpected proto.StringKind: %d", k)
   141  		return configschema.StringPlain
   142  	case proto.StringKind_PLAIN:
   143  		return configschema.StringPlain
   144  	case proto.StringKind_MARKDOWN:
   145  		return configschema.StringMarkdown
   146  	}
   147  }
   148  
   149  func schemaNestedBlock(b *proto.Schema_NestedBlock) *configschema.NestedBlock {
   150  	var nesting configschema.NestingMode
   151  	switch b.Nesting {
   152  	case proto.Schema_NestedBlock_SINGLE:
   153  		nesting = configschema.NestingSingle
   154  	case proto.Schema_NestedBlock_GROUP:
   155  		nesting = configschema.NestingGroup
   156  	case proto.Schema_NestedBlock_LIST:
   157  		nesting = configschema.NestingList
   158  	case proto.Schema_NestedBlock_MAP:
   159  		nesting = configschema.NestingMap
   160  	case proto.Schema_NestedBlock_SET:
   161  		nesting = configschema.NestingSet
   162  	default:
   163  		// In all other cases we'll leave it as the zero value (invalid) and
   164  		// let the caller validate it and deal with this.
   165  	}
   166  
   167  	nb := &configschema.NestedBlock{
   168  		Nesting:  nesting,
   169  		MinItems: int(b.MinItems),
   170  		MaxItems: int(b.MaxItems),
   171  	}
   172  
   173  	nested := ProtoToConfigSchema(b.Block)
   174  	nb.Block = *nested
   175  	return nb
   176  }
   177  
   178  // sortedKeys returns the lexically sorted keys from the given map. This is
   179  // used to make schema conversions are deterministic. This panics if map keys
   180  // are not a string.
   181  func sortedKeys(m interface{}) []string {
   182  	v := reflect.ValueOf(m)
   183  	keys := make([]string, v.Len())
   184  
   185  	mapKeys := v.MapKeys()
   186  	for i, k := range mapKeys {
   187  		keys[i] = k.Interface().(string)
   188  	}
   189  
   190  	sort.Strings(keys)
   191  	return keys
   192  }