Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ protoc \
types.proto
```

This command will generate `types_terraform.go` in `tfschema` folder.
This command will generate `types_terraform.go` in `tfschema` folder.

See [Makefile](Makefile) for details.

Expand Down Expand Up @@ -109,7 +109,7 @@ validators:

plan_modifiers:
"Role.Options":
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.RequiresReplace()"
- "github.com/hashicorp/terraform-plugin-framework/resource.RequiresReplace()"
```

## UseStateForUnknown by default
Expand All @@ -120,7 +120,7 @@ The following setting:
use_state_for_unknown_by_default: true
```

will add `tfsdk.UseStateForUnknown()` PlanModifier to all computed fields.
will add `resource.UseStateForUnknown()` PlanModifier to all computed fields.

## Injecting fields into schema

Expand All @@ -143,7 +143,7 @@ If you need to rename field in schema, use `name_overrides` option:

```yaml
name_overrides:
"Role.Spec.AWSRoleARNs": aws_arns
"Role.Spec.AWSRoleARNs": aws_arns
```

## Custom fields
Expand Down Expand Up @@ -187,7 +187,7 @@ The signatures for `Test` resource would be the following:

```go
// CopyTestFromTerraform copies Terraform object fields to obj
// tf must have all the object attrs present (including null and unknown).
// tf must have all the object attrs present (including null and unknown).
// Hence, tf must be the result of req.Plan.Get or similar Terraform method.
// Otherwise, error would be returned.
func CopyTestFromTerraform(tf types.Object, obj *Test) diag.Diagnostics
Expand All @@ -197,7 +197,7 @@ They can be used as following:

```go
// Create template resource create method
func (r resource) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
func (r resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan types.Object
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
Expand All @@ -206,8 +206,8 @@ func (r resource) Create(ctx context.Context, req tfsdk.CreateResourceRequest, r
}

obj := types.Object{}
diags := tfschema.CopyObjFromTerraform(plan, &obj)
resp.Diagnostics.Append(diags...)
diags := tfschema.CopyObjFromTerraform(plan, &obj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
Expand Down
10 changes: 10 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ type SchemaType struct {
Type string `yaml:"type,omitempty"`
// ValueType is a Go attr.Value struct name
ValueType string `yaml:"value_type,omitempty"`
// ValueFromMethod is the method on attr.Value that will be called to get
// the underlying value
ValueFromMethod string `yaml:"value_from_method,omitempty"`
// ValueToMethod is the method that will be called to create the attr.Value
ValueToMethod string `yaml:"value_to_method,omitempty"`
// NullValueMethod is the method that will be called to create a null attr.Value
NullValueMethod string `yaml:"null_value_method,omitempty"`
// CastToType is a Go attr.Value .Value member type
CastToType string `yaml:"cast_to_type,omitempty"`
// CastToType is a go type of the object field
Expand All @@ -81,6 +88,9 @@ type InjectedField struct {
PlanModifiers []string `yaml:"plan_modifiers,omitempty"`
// PlanModifiers is the array of Validators
Validators []string `yaml:"validators,omitempty"`
// ValueMethod is the method that will be called to construct a placeholder
// value for the field in Copy*ToTerraform methods
ValueMethod string `yaml:"value_method,omitempty"`
}

// Config represents the plugin config
Expand Down
23 changes: 15 additions & 8 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,36 @@ func TestConfig(t *testing.T) {
require.Equal(t, cfg.Suffixes, map[string]string{"BoolCustom": "BoolSpecial"})
require.Equal(t, cfg.NameOverrides, map[string]string{"Test.Str": "str"})

require.Equal(t, cfg.PlanModifiers, map[string][]string{"Test.Str": {"github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"}})
require.Equal(t, cfg.PlanModifiers, map[string][]string{"Test.Str": {"github.com/hashicorp/terraform-plugin-framework/resource.UseStateForUnknown()"}})
require.Equal(t, cfg.Validators, map[string][]string{"Test.Str": {"UseMockValidator()"}})

require.Equal(t, cfg.TimeType, &SchemaType{
Type: "TimeType",
ValueType: "TimeValue",
ValueFromMethod: "ValueTime",
ValueToMethod: "ValueTime",
NullValueMethod: "NullTime",
CastToType: "time.Time",
CastFromType: "time.Time",
TypeConstructor: "UseRFC3339Time()",
})

require.Equal(t, cfg.DurationType, &SchemaType{
Type: "DurationType",
ValueType: "DurationValue",
CastToType: "time.Duration",
CastFromType: "time.Duration",
Type: "DurationType",
ValueType: "DurationValue",
ValueFromMethod: "ValueDuration",
ValueToMethod: "ValueDuration",
NullValueMethod: "NullDuration",
CastToType: "time.Duration",
CastFromType: "time.Duration",
})

require.Equal(t, cfg.InjectedFields, map[string][]InjectedField{
"Test": {{
Name: "id",
Type: "github.com/hashicorp/terraform-plugin-framework/types.StringType",
Computed: true,
Name: "id",
Type: "github.com/hashicorp/terraform-plugin-framework/types.StringType",
Computed: true,
ValueMethod: "github.com/hashicorp/terraform-plugin-framework/types.StringUnknown",
}},
})

Expand Down
14 changes: 12 additions & 2 deletions field.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,18 @@ type TerraformType struct {
Type string
// ValueType represents Terraform attr.Value name
ValueType string
// ValueFromMethod is the method called on an attr.Value to get the underlying value
ValueFromMethod string
// ValueToMethod is the method called to construct an attr.Value
ValueToMethod string
// NullValueMethod is the method called to construct a null attr.Value
NullValueMethod string
// ElemType represents Terraform attr.Type name for list/map Elem, equals Type by default
ElemType string
// ElemValueType represents Terraform attr.Value name for list/map Elem, equals ValueType by default
ElemValueType string
// IsTypeScalar is true when Type is not a real struct and is represented with numeric constant on Terraform side
IsTypeScalar bool
// IsTypeScalar is true when ElemType is not a real struct and is represented with numeric constant on Terraform side
IsElemTypeScalar bool
// ValueCastToType represents Go type of either ValueType.Value or ElemValueType.Value
ValueCastToType string
// ValueCastFromType represents Go type of a counterpart object field or field elem to cast from .Value
Expand Down Expand Up @@ -322,6 +326,9 @@ func (f *Field) setMapValues(c *FieldBuildContext) error {
f.ElemValueType = f.MapValueField.ElemValueType
f.ValueCastToType = f.MapValueField.ValueCastToType
f.ValueCastFromType = f.MapValueField.ValueCastFromType
f.ValueFromMethod = f.MapValueField.ValueFromMethod
f.ValueToMethod = f.MapValueField.ValueToMethod
f.NullValueMethod = f.MapValueField.NullValueMethod

f.GoElemType = f.MapValueField.GoElemType

Expand Down Expand Up @@ -409,6 +416,9 @@ func (f *Field) setTerraformTypeOverride(c *FieldBuildContext) {
if o != nil {
f.Type = o.Type
f.ValueType = o.ValueType
f.ValueFromMethod = o.ValueFromMethod
f.ValueToMethod = o.ValueToMethod
f.NullValueMethod = o.NullValueMethod
f.ValueCastToType = o.CastToType
f.ValueCastFromType = o.CastFromType
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Todo for me later:

  • fix the ValueCastFromType in Override
  • write a test for the override or deprecate it?! It looks like it has been broken since forever.

f.TypeConstructor = o.TypeConstructor
Expand Down
86 changes: 47 additions & 39 deletions field_build_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,47 +29,55 @@ import (

var (
float64Type = TerraformType{
Type: Types + ".Float64Type",
ValueType: Types + ".Float64",
ElemType: Types + ".Float64Type",
ElemValueType: Types + ".Float64",
ValueCastToType: "float64",
ZeroValue: "0",
IsTypeScalar: true,
IsElemTypeScalar: true,
Type: Types + ".Float64Type",
ValueType: Types + ".Float64",
ValueFromMethod: "ValueFloat64",
ValueToMethod: Types + ".Float64Value",
NullValueMethod: Types + ".Float64Null",
ElemType: Types + ".Float64Type",
ElemValueType: Types + ".Float64",
ValueCastToType: "float64",
ZeroValue: "0",
IsTypeScalar: true,
}

int64Type = TerraformType{
Type: Types + ".Int64Type",
ValueType: Types + ".Int64",
ElemType: Types + ".Int64Type",
ElemValueType: Types + ".Int64",
ValueCastToType: "int64",
ZeroValue: "0",
IsTypeScalar: true,
IsElemTypeScalar: true,
Type: Types + ".Int64Type",
ValueType: Types + ".Int64",
ValueFromMethod: "ValueInt64",
ValueToMethod: Types + ".Int64Value",
NullValueMethod: Types + ".Int64Null",
ElemType: Types + ".Int64Type",
ElemValueType: Types + ".Int64",
ValueCastToType: "int64",
ZeroValue: "0",
IsTypeScalar: true,
}

stringType = TerraformType{
Type: Types + ".StringType",
ValueType: Types + ".String",
ElemType: Types + ".StringType",
ElemValueType: Types + ".String",
ValueCastToType: "string",
ZeroValue: `""`,
IsTypeScalar: true,
IsElemTypeScalar: true,
Type: Types + ".StringType",
ValueType: Types + ".String",
ValueFromMethod: "ValueString",
ValueToMethod: Types + ".StringValue",
NullValueMethod: Types + ".StringNull",
ElemType: Types + ".StringType",
ElemValueType: Types + ".String",
ValueCastToType: "string",
ZeroValue: `""`,
IsTypeScalar: true,
}

boolType = TerraformType{
Type: Types + ".BoolType",
ValueType: Types + ".Bool",
ElemType: Types + ".BoolType",
ElemValueType: Types + ".Bool",
ValueCastToType: "bool",
ZeroValue: "false",
IsTypeScalar: true,
IsElemTypeScalar: true,
Type: Types + ".BoolType",
ValueType: Types + ".Bool",
ValueFromMethod: "ValueBool",
ValueToMethod: Types + ".BoolValue",
NullValueMethod: Types + ".BoolNull",
ElemType: Types + ".BoolType",
ElemValueType: Types + ".Bool",
ValueCastToType: "bool",
ZeroValue: "false",
IsTypeScalar: true,
}

objectType = TerraformType{
Expand Down Expand Up @@ -200,6 +208,9 @@ func (c *FieldBuildContext) GetTerraformType() (TerraformType, error) {
t = TerraformType{
Type: c.config.TimeType.Type,
ValueType: c.config.TimeType.ValueType,
ValueFromMethod: c.config.TimeType.ValueFromMethod,
ValueToMethod: c.config.TimeType.ValueToMethod,
NullValueMethod: c.config.TimeType.NullValueMethod,
ElemType: c.config.TimeType.Type,
ElemValueType: c.config.TimeType.ValueType,
ValueCastToType: c.config.TimeType.CastToType,
Expand All @@ -213,6 +224,9 @@ func (c *FieldBuildContext) GetTerraformType() (TerraformType, error) {
t = TerraformType{
Type: c.config.DurationType.Type,
ValueType: c.config.DurationType.ValueType,
ValueFromMethod: c.config.DurationType.ValueFromMethod,
ValueToMethod: c.config.DurationType.ValueToMethod,
NullValueMethod: c.config.DurationType.NullValueMethod,
ElemType: c.config.DurationType.Type,
ElemValueType: c.config.DurationType.ValueType,
ValueCastToType: c.config.DurationType.CastToType,
Expand All @@ -221,13 +235,11 @@ func (c *FieldBuildContext) GetTerraformType() (TerraformType, error) {
}
case c.field.IsTypeEq(descriptor.FieldDescriptorProto_TYPE_DOUBLE) || gogoproto.IsStdDouble(p):
t = float64Type
t.ValueCastFromType = "float64"
case c.field.IsTypeEq(descriptor.FieldDescriptorProto_TYPE_FLOAT) || gogoproto.IsStdFloat(p):
t = float64Type
t.ValueCastFromType = "float32"
case c.field.IsTypeEq(descriptor.FieldDescriptorProto_TYPE_INT64) || gogoproto.IsStdInt64(p):
t = int64Type
t.ValueCastFromType = "int64"
case c.field.IsTypeEq(descriptor.FieldDescriptorProto_TYPE_UINT64) || gogoproto.IsStdUInt64(p):
t = int64Type
t.ValueCastFromType = "uint64"
Expand All @@ -248,19 +260,15 @@ func (c *FieldBuildContext) GetTerraformType() (TerraformType, error) {
t.ValueCastFromType = "int32"
case c.field.IsTypeEq(descriptor.FieldDescriptorProto_TYPE_SFIXED64):
t = int64Type
t.ValueCastFromType = "int64"
case c.field.IsTypeEq(descriptor.FieldDescriptorProto_TYPE_SINT32):
t = int64Type
t.ValueCastFromType = "int32"
case c.field.IsTypeEq(descriptor.FieldDescriptorProto_TYPE_SINT64):
t = int64Type
t.ValueCastFromType = "int64"
case c.field.IsTypeEq(descriptor.FieldDescriptorProto_TYPE_BOOL) || gogoproto.IsStdBool(p):
t = boolType
t.ValueCastFromType = "bool"
case c.field.IsTypeEq(descriptor.FieldDescriptorProto_TYPE_STRING) || gogoproto.IsStdString(p):
t = stringType
t.ValueCastFromType = "string"
case c.field.IsTypeEq(descriptor.FieldDescriptorProto_TYPE_BYTES) || gogoproto.IsStdBytes(p):
t = stringType
t.ValueCastFromType = "[]byte"
Expand Down Expand Up @@ -442,7 +450,7 @@ func (c *FieldBuildContext) GetPlanModifiers() []string {
}

if c.config.UseStateForUnknownByDefault && c.IsComputed() {
return []string{"github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"}
return []string{"github.com/hashicorp/terraform-plugin-framework/resource.UseStateForUnknown()"}
}

return []string{}
Expand Down
Loading