github.com/cycloidio/terraform@v1.1.10-0.20220513142504-76d5c768dc63/plugin/convert/schema.go (about)

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