diff --git a/.changes/unreleased/FEATURES-20251014-173739.yaml b/.changes/unreleased/FEATURES-20251014-173739.yaml new file mode 100644 index 000000000..1023f9af3 --- /dev/null +++ b/.changes/unreleased/FEATURES-20251014-173739.yaml @@ -0,0 +1,5 @@ +kind: FEATURES +body: 'action/schema: Added `WriteOnly` schema field for action schemas.' +time: 2025-10-14T17:37:39.087475-04:00 +custom: + Issue: "1233" diff --git a/action/schema/bool_attribute.go b/action/schema/bool_attribute.go index c33070721..efbd0f71d 100644 --- a/action/schema/bool_attribute.go +++ b/action/schema/bool_attribute.go @@ -104,6 +104,10 @@ type BoolAttribute struct { // xattr.TypeWithValidate interface, the validators defined in this field // are run in addition to the validation defined by the type. Validators []validator.Bool + + // WriteOnly indicates whether this attribute can accept ephemeral values + // or not. If WriteOnly is true, either Optional or Required must also be true. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -166,9 +170,9 @@ func (a BoolAttribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns the WriteOnly field value. func (a BoolAttribute) IsWriteOnly() bool { - return false + return a.WriteOnly } // IsRequiredForImport returns false as this behavior is only relevant diff --git a/action/schema/bool_attribute_test.go b/action/schema/bool_attribute_test.go index a7b15890b..5d4e587cf 100644 --- a/action/schema/bool_attribute_test.go +++ b/action/schema/bool_attribute_test.go @@ -370,6 +370,12 @@ func TestBoolAttributeIsWriteOnly(t *testing.T) { attribute: schema.BoolAttribute{}, expected: false, }, + "writeOnly": { + attribute: schema.BoolAttribute{ + WriteOnly: true, + }, + expected: true, + }, } for name, testCase := range testCases { diff --git a/action/schema/dynamic_attribute.go b/action/schema/dynamic_attribute.go index 28837ef94..7f852b11c 100644 --- a/action/schema/dynamic_attribute.go +++ b/action/schema/dynamic_attribute.go @@ -105,6 +105,10 @@ type DynamicAttribute struct { // xattr.TypeWithValidate interface, the validators defined in this field // are run in addition to the validation defined by the type. Validators []validator.Dynamic + + // WriteOnly indicates whether this attribute can accept ephemeral values + // or not. If WriteOnly is true, either Optional or Required must also be true. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -167,9 +171,9 @@ func (a DynamicAttribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns the WriteOnly field value. func (a DynamicAttribute) IsWriteOnly() bool { - return false + return a.WriteOnly } // IsRequiredForImport returns false as this behavior is only relevant diff --git a/action/schema/dynamic_attribute_test.go b/action/schema/dynamic_attribute_test.go index d8d6b5b31..faa2a20c2 100644 --- a/action/schema/dynamic_attribute_test.go +++ b/action/schema/dynamic_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/action/schema" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestDynamicAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -369,6 +370,12 @@ func TestDynamicAttributeIsWriteOnly(t *testing.T) { attribute: schema.DynamicAttribute{}, expected: false, }, + "writeOnly": { + attribute: schema.DynamicAttribute{ + WriteOnly: true, + }, + expected: true, + }, } for name, testCase := range testCases { diff --git a/action/schema/float32_attribute.go b/action/schema/float32_attribute.go index 06f998a3d..9e1e7fde1 100644 --- a/action/schema/float32_attribute.go +++ b/action/schema/float32_attribute.go @@ -107,6 +107,10 @@ type Float32Attribute struct { // xattr.TypeWithValidate interface, the validators defined in this field // are run in addition to the validation defined by the type. Validators []validator.Float32 + + // WriteOnly indicates whether this attribute can accept ephemeral values + // or not. If WriteOnly is true, either Optional or Required must also be true. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -169,9 +173,9 @@ func (a Float32Attribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns the WriteOnly field value. func (a Float32Attribute) IsWriteOnly() bool { - return false + return a.WriteOnly } // IsRequiredForImport returns false as this behavior is only relevant diff --git a/action/schema/float32_attribute_test.go b/action/schema/float32_attribute_test.go index 78df1346c..918f4e9fe 100644 --- a/action/schema/float32_attribute_test.go +++ b/action/schema/float32_attribute_test.go @@ -370,6 +370,12 @@ func TestFloat32AttributeIsWriteOnly(t *testing.T) { attribute: schema.Float32Attribute{}, expected: false, }, + "writeOnly": { + attribute: schema.Float32Attribute{ + WriteOnly: true, + }, + expected: true, + }, } for name, testCase := range testCases { diff --git a/action/schema/float64_attribute.go b/action/schema/float64_attribute.go index aad03bc59..2aded5a5a 100644 --- a/action/schema/float64_attribute.go +++ b/action/schema/float64_attribute.go @@ -107,6 +107,10 @@ type Float64Attribute struct { // xattr.TypeWithValidate interface, the validators defined in this field // are run in addition to the validation defined by the type. Validators []validator.Float64 + + // WriteOnly indicates whether this attribute can accept ephemeral values + // or not. If WriteOnly is true, either Optional or Required must also be true. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -169,9 +173,9 @@ func (a Float64Attribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns the WriteOnly field value. func (a Float64Attribute) IsWriteOnly() bool { - return false + return a.WriteOnly } // IsRequiredForImport returns false as this behavior is only relevant diff --git a/action/schema/float64_attribute_test.go b/action/schema/float64_attribute_test.go index 631565ced..a5b99c79d 100644 --- a/action/schema/float64_attribute_test.go +++ b/action/schema/float64_attribute_test.go @@ -359,7 +359,7 @@ func TestFloat64AttributeIsSensitive(t *testing.T) { } } -func TestFloat54AttributeIsWriteOnly(t *testing.T) { +func TestFloat64AttributeIsWriteOnly(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -370,6 +370,12 @@ func TestFloat54AttributeIsWriteOnly(t *testing.T) { attribute: schema.Float64Attribute{}, expected: false, }, + "writeOnly": { + attribute: schema.Float64Attribute{ + WriteOnly: true, + }, + expected: true, + }, } for name, testCase := range testCases { diff --git a/action/schema/int32_attribute.go b/action/schema/int32_attribute.go index 4b57d6196..c53b85eb4 100644 --- a/action/schema/int32_attribute.go +++ b/action/schema/int32_attribute.go @@ -107,6 +107,10 @@ type Int32Attribute struct { // xattr.TypeWithValidate interface, the validators defined in this field // are run in addition to the validation defined by the type. Validators []validator.Int32 + + // WriteOnly indicates whether this attribute can accept ephemeral values + // or not. If WriteOnly is true, either Optional or Required must also be true. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -169,9 +173,9 @@ func (a Int32Attribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns the WriteOnly field value. func (a Int32Attribute) IsWriteOnly() bool { - return false + return a.WriteOnly } // IsRequiredForImport returns false as this behavior is only relevant diff --git a/action/schema/int32_attribute_test.go b/action/schema/int32_attribute_test.go index 88015bb5f..f5e251f03 100644 --- a/action/schema/int32_attribute_test.go +++ b/action/schema/int32_attribute_test.go @@ -359,7 +359,7 @@ func TestInt32AttributeIsSensitive(t *testing.T) { } } -func TestInt2AttributeIsWriteOnly(t *testing.T) { +func TestInt32AttributeIsWriteOnly(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -370,6 +370,12 @@ func TestInt2AttributeIsWriteOnly(t *testing.T) { attribute: schema.Int32Attribute{}, expected: false, }, + "writeOnly": { + attribute: schema.Int32Attribute{ + WriteOnly: true, + }, + expected: true, + }, } for name, testCase := range testCases { diff --git a/action/schema/int64_attribute.go b/action/schema/int64_attribute.go index e64f59447..4c5c58565 100644 --- a/action/schema/int64_attribute.go +++ b/action/schema/int64_attribute.go @@ -107,6 +107,10 @@ type Int64Attribute struct { // xattr.TypeWithValidate interface, the validators defined in this field // are run in addition to the validation defined by the type. Validators []validator.Int64 + + // WriteOnly indicates whether this attribute can accept ephemeral values + // or not. If WriteOnly is true, either Optional or Required must also be true. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -169,9 +173,9 @@ func (a Int64Attribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns the WriteOnly field value. func (a Int64Attribute) IsWriteOnly() bool { - return false + return a.WriteOnly } // IsRequiredForImport returns false as this behavior is only relevant diff --git a/action/schema/int64_attribute_test.go b/action/schema/int64_attribute_test.go index 9bd6cc524..cd5d82f85 100644 --- a/action/schema/int64_attribute_test.go +++ b/action/schema/int64_attribute_test.go @@ -370,6 +370,12 @@ func TestInt64AttributeIsWriteOnly(t *testing.T) { attribute: schema.Int64Attribute{}, expected: false, }, + "writeOnly": { + attribute: schema.Int64Attribute{ + WriteOnly: true, + }, + expected: true, + }, } for name, testCase := range testCases { diff --git a/action/schema/list_attribute.go b/action/schema/list_attribute.go index 86086d53e..0a47f6568 100644 --- a/action/schema/list_attribute.go +++ b/action/schema/list_attribute.go @@ -123,6 +123,10 @@ type ListAttribute struct { // xattr.TypeWithValidate interface, the validators defined in this field // are run in addition to the validation defined by the type. Validators []validator.List + + // WriteOnly indicates whether this attribute can accept ephemeral values + // or not. If WriteOnly is true, either Optional or Required must also be true. + WriteOnly bool } // ApplyTerraform5AttributePathStep returns the result of stepping into a list @@ -187,9 +191,9 @@ func (a ListAttribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns the WriteOnly field value. func (a ListAttribute) IsWriteOnly() bool { - return false + return a.WriteOnly } // IsRequiredForImport returns false as this behavior is only relevant diff --git a/action/schema/list_attribute_test.go b/action/schema/list_attribute_test.go index 69315448a..e5707abb5 100644 --- a/action/schema/list_attribute_test.go +++ b/action/schema/list_attribute_test.go @@ -378,6 +378,12 @@ func TestListAttributeIsWriteOnly(t *testing.T) { attribute: schema.ListAttribute{}, expected: false, }, + "writeOnly": { + attribute: schema.ListAttribute{ + WriteOnly: true, + }, + expected: true, + }, } for name, testCase := range testCases { @@ -392,7 +398,6 @@ func TestListAttributeIsWriteOnly(t *testing.T) { }) } } - func TestListAttributeListValidators(t *testing.T) { t.Parallel() diff --git a/action/schema/list_nested_attribute.go b/action/schema/list_nested_attribute.go index 3287270f5..af7013135 100644 --- a/action/schema/list_nested_attribute.go +++ b/action/schema/list_nested_attribute.go @@ -133,6 +133,13 @@ type ListNestedAttribute struct { // xattr.TypeWithValidate interface, the validators defined in this field // are run in addition to the validation defined by the type. Validators []validator.List + + // WriteOnly indicates whether this attribute can accept ephemeral values + // or not. If WriteOnly is true, either Optional or Required must also be true. + // + // If WriteOnly is true for a nested attribute, all of its child attributes + // must also set WriteOnly to true. + WriteOnly bool } // ApplyTerraform5AttributePathStep returns the Attributes field value if step @@ -215,9 +222,9 @@ func (a ListNestedAttribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns the WriteOnly field value. func (a ListNestedAttribute) IsWriteOnly() bool { - return false + return a.WriteOnly } // IsRequiredForImport returns false as this behavior is only relevant @@ -245,4 +252,7 @@ func (a ListNestedAttribute) ValidateImplementation(ctx context.Context, req fws if a.CustomType == nil && fwtype.ContainsCollectionWithDynamic(a.GetType()) { resp.Diagnostics.Append(fwtype.AttributeCollectionWithDynamicTypeDiag(req.Path)) } + if a.IsWriteOnly() && !fwschema.ContainsAllWriteOnlyChildAttributes(a) { + resp.Diagnostics.Append(fwschema.InvalidWriteOnlyNestedAttributeDiag(req.Path)) + } } diff --git a/action/schema/list_nested_attribute_test.go b/action/schema/list_nested_attribute_test.go index e863b12d2..cea30ddaf 100644 --- a/action/schema/list_nested_attribute_test.go +++ b/action/schema/list_nested_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/action/schema" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestListNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -546,6 +547,12 @@ func TestListNestedAttributeIsWriteOnly(t *testing.T) { attribute: schema.ListNestedAttribute{}, expected: false, }, + "writeOnly": { + attribute: schema.ListNestedAttribute{ + WriteOnly: true, + }, + expected: true, + }, } for name, testCase := range testCases { @@ -661,6 +668,50 @@ func TestListNestedAttributeValidateImplementation(t *testing.T) { }, }, }, + "writeOnly-with-child-writeOnly-no-error-diagnostic": { + attribute: schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "writeOnly-without-child-writeOnly-error-diagnostic": { + attribute: schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Schema Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" is a WriteOnly nested attribute that contains a non-WriteOnly child attribute.\n\n"+ + "Every child attribute of a WriteOnly nested attribute must also have WriteOnly set to true.", + ), + }, + }, + }, } for name, testCase := range testCases { diff --git a/action/schema/map_attribute.go b/action/schema/map_attribute.go index d8d97504f..dfd5cb471 100644 --- a/action/schema/map_attribute.go +++ b/action/schema/map_attribute.go @@ -126,6 +126,10 @@ type MapAttribute struct { // xattr.TypeWithValidate interface, the validators defined in this field // are run in addition to the validation defined by the type. Validators []validator.Map + + // WriteOnly indicates whether this attribute can accept ephemeral values + // or not. If WriteOnly is true, either Optional or Required must also be true. + WriteOnly bool } // ApplyTerraform5AttributePathStep returns the result of stepping into a map @@ -190,9 +194,9 @@ func (a MapAttribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns the WriteOnly field value. func (a MapAttribute) IsWriteOnly() bool { - return false + return a.WriteOnly } // IsRequiredForImport returns false as this behavior is only relevant diff --git a/action/schema/map_attribute_test.go b/action/schema/map_attribute_test.go index 855bf1673..087fa111c 100644 --- a/action/schema/map_attribute_test.go +++ b/action/schema/map_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/action/schema" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestMapAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -377,6 +378,12 @@ func TestMapAttributeIsWriteOnly(t *testing.T) { attribute: schema.MapAttribute{}, expected: false, }, + "writeOnly": { + attribute: schema.MapAttribute{ + WriteOnly: true, + }, + expected: true, + }, } for name, testCase := range testCases { diff --git a/action/schema/map_nested_attribute.go b/action/schema/map_nested_attribute.go index a6c84303a..d84cb75f8 100644 --- a/action/schema/map_nested_attribute.go +++ b/action/schema/map_nested_attribute.go @@ -133,6 +133,13 @@ type MapNestedAttribute struct { // xattr.TypeWithValidate interface, the validators defined in this field // are run in addition to the validation defined by the type. Validators []validator.Map + + // WriteOnly indicates whether this attribute can accept ephemeral values + // or not. If WriteOnly is true, either Optional or Required must also be true. + // + // If WriteOnly is true for a nested attribute, all of its child attributes + // must also set WriteOnly to true. + WriteOnly bool } // ApplyTerraform5AttributePathStep returns the Attributes field value if step @@ -215,9 +222,9 @@ func (a MapNestedAttribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns the WriteOnly field value. func (a MapNestedAttribute) IsWriteOnly() bool { - return false + return a.WriteOnly } // IsRequiredForImport returns false as this behavior is only relevant @@ -245,4 +252,8 @@ func (a MapNestedAttribute) ValidateImplementation(ctx context.Context, req fwsc if a.CustomType == nil && fwtype.ContainsCollectionWithDynamic(a.GetType()) { resp.Diagnostics.Append(fwtype.AttributeCollectionWithDynamicTypeDiag(req.Path)) } + + if a.IsWriteOnly() && !fwschema.ContainsAllWriteOnlyChildAttributes(a) { + resp.Diagnostics.Append(fwschema.InvalidWriteOnlyNestedAttributeDiag(req.Path)) + } } diff --git a/action/schema/map_nested_attribute_test.go b/action/schema/map_nested_attribute_test.go index bcb64fa6f..f7bdfa59f 100644 --- a/action/schema/map_nested_attribute_test.go +++ b/action/schema/map_nested_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/action/schema" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestMapNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -546,6 +547,12 @@ func TestMapNestedAttributeIsWriteOnly(t *testing.T) { attribute: schema.MapNestedAttribute{}, expected: false, }, + "writeOnly": { + attribute: schema.MapNestedAttribute{ + WriteOnly: true, + }, + expected: true, + }, } for name, testCase := range testCases { @@ -661,6 +668,50 @@ func TestMapNestedAttributeValidateImplementation(t *testing.T) { }, }, }, + "writeOnly-with-child-writeOnly-no-error-diagnostic": { + attribute: schema.MapNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "writeOnly-without-child-writeOnly-error-diagnostic": { + attribute: schema.MapNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Schema Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" is a WriteOnly nested attribute that contains a non-WriteOnly child attribute.\n\n"+ + "Every child attribute of a WriteOnly nested attribute must also have WriteOnly set to true.", + ), + }, + }, + }, } for name, testCase := range testCases { diff --git a/action/schema/number_attribute.go b/action/schema/number_attribute.go index 672d4371d..e4c194683 100644 --- a/action/schema/number_attribute.go +++ b/action/schema/number_attribute.go @@ -108,6 +108,10 @@ type NumberAttribute struct { // xattr.TypeWithValidate interface, the validators defined in this field // are run in addition to the validation defined by the type. Validators []validator.Number + + // WriteOnly indicates whether this attribute can accept ephemeral values + // or not. If WriteOnly is true, either Optional or Required must also be true. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -170,9 +174,9 @@ func (a NumberAttribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns the WriteOnly field value. func (a NumberAttribute) IsWriteOnly() bool { - return false + return a.WriteOnly } // IsRequiredForImport returns false as this behavior is only relevant diff --git a/action/schema/number_attribute_test.go b/action/schema/number_attribute_test.go index 5708b5e3f..0f989dc0e 100644 --- a/action/schema/number_attribute_test.go +++ b/action/schema/number_attribute_test.go @@ -370,6 +370,12 @@ func TestNumberAttributeIsWriteOnly(t *testing.T) { attribute: schema.NumberAttribute{}, expected: false, }, + "writeOnly": { + attribute: schema.NumberAttribute{ + WriteOnly: true, + }, + expected: true, + }, } for name, testCase := range testCases { diff --git a/action/schema/object_attribute.go b/action/schema/object_attribute.go index 5e9c656f7..a1d5ad467 100644 --- a/action/schema/object_attribute.go +++ b/action/schema/object_attribute.go @@ -125,6 +125,10 @@ type ObjectAttribute struct { // xattr.TypeWithValidate interface, the validators defined in this field // are run in addition to the validation defined by the type. Validators []validator.Object + + // WriteOnly indicates whether this attribute can accept ephemeral values + // or not. If WriteOnly is true, either Optional or Required must also be true.. + WriteOnly bool } // ApplyTerraform5AttributePathStep returns the result of stepping into an @@ -189,9 +193,9 @@ func (a ObjectAttribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns the WriteOnly field value. func (a ObjectAttribute) IsWriteOnly() bool { - return false + return a.WriteOnly } // IsRequiredForImport returns false as this behavior is only relevant diff --git a/action/schema/object_attribute_test.go b/action/schema/object_attribute_test.go index 4064b8ae2..17d65e504 100644 --- a/action/schema/object_attribute_test.go +++ b/action/schema/object_attribute_test.go @@ -384,6 +384,12 @@ func TestObjectAttributeIsWriteOnly(t *testing.T) { attribute: schema.ObjectAttribute{}, expected: false, }, + "writeOnly": { + attribute: schema.ObjectAttribute{ + WriteOnly: true, + }, + expected: true, + }, } for name, testCase := range testCases { diff --git a/action/schema/set_attribute.go b/action/schema/set_attribute.go index eecdb6215..a05e62f45 100644 --- a/action/schema/set_attribute.go +++ b/action/schema/set_attribute.go @@ -185,7 +185,7 @@ func (a SetAttribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns false as write-only attributes are not supported for sets and set-based data. func (a SetAttribute) IsWriteOnly() bool { return false } diff --git a/action/schema/set_nested_attribute.go b/action/schema/set_nested_attribute.go index a183409d7..2d199617b 100644 --- a/action/schema/set_nested_attribute.go +++ b/action/schema/set_nested_attribute.go @@ -210,7 +210,7 @@ func (a SetNestedAttribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns false as write-only attributes are not supported for sets and set-based data. func (a SetNestedAttribute) IsWriteOnly() bool { return false } diff --git a/action/schema/single_nested_attribute.go b/action/schema/single_nested_attribute.go index fcf08c1d9..4f3bafafb 100644 --- a/action/schema/single_nested_attribute.go +++ b/action/schema/single_nested_attribute.go @@ -120,6 +120,10 @@ type SingleNestedAttribute struct { // xattr.TypeWithValidate interface, the validators defined in this field // are run in addition to the validation defined by the type. Validators []validator.Object + + // WriteOnly indicates whether this attribute can accept ephemeral values + // or not. If WriteOnly is true, either Optional or Required must also be true. + WriteOnly bool } // ApplyTerraform5AttributePathStep returns the Attributes field value if step @@ -224,9 +228,9 @@ func (a SingleNestedAttribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns the WriteOnly field value. func (a SingleNestedAttribute) IsWriteOnly() bool { - return false + return a.WriteOnly } // IsRequiredForImport returns false as this behavior is only relevant diff --git a/action/schema/single_nested_attribute_test.go b/action/schema/single_nested_attribute_test.go index 4ceea6ca2..574da329c 100644 --- a/action/schema/single_nested_attribute_test.go +++ b/action/schema/single_nested_attribute_test.go @@ -508,6 +508,12 @@ func TestSingleNestedAttributeIsWriteOnly(t *testing.T) { attribute: schema.SingleNestedAttribute{}, expected: false, }, + "writeOnly": { + attribute: schema.SingleNestedAttribute{ + WriteOnly: true, + }, + expected: true, + }, } for name, testCase := range testCases { diff --git a/action/schema/string_attribute.go b/action/schema/string_attribute.go index cb284d1c6..2fa544068 100644 --- a/action/schema/string_attribute.go +++ b/action/schema/string_attribute.go @@ -104,6 +104,10 @@ type StringAttribute struct { // xattr.TypeWithValidate interface, the validators defined in this field // are run in addition to the validation defined by the type. Validators []validator.String + + // WriteOnly indicates whether this attribute can accept ephemeral values + // or not. If WriteOnly is true, either Optional or Required must also be true. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -166,9 +170,9 @@ func (a StringAttribute) IsSensitive() bool { return false } -// IsWriteOnly always returns false as action schema attributes cannot be WriteOnly. +// IsWriteOnly returns the WriteOnly field value. func (a StringAttribute) IsWriteOnly() bool { - return false + return a.WriteOnly } // IsRequiredForImport returns false as this behavior is only relevant diff --git a/action/schema/string_attribute_test.go b/action/schema/string_attribute_test.go index a930c3e16..772a08581 100644 --- a/action/schema/string_attribute_test.go +++ b/action/schema/string_attribute_test.go @@ -370,6 +370,12 @@ func TestStringAttributeIsWriteOnly(t *testing.T) { attribute: schema.StringAttribute{}, expected: false, }, + "writeOnly": { + attribute: schema.StringAttribute{ + WriteOnly: true, + }, + expected: true, + }, } for name, testCase := range testCases {