Skip to content

Commit

Permalink
tfsdk: Prevent Attribute misconfiguration by consolidating Computed, …
Browse files Browse the repository at this point in the history
…Optional, and Required fields into a single Configuration field

Reference: #31
Reference: #94
  • Loading branch information
bflad committed Aug 10, 2021
1 parent 8cc9eb6 commit 01c1fbf
Show file tree
Hide file tree
Showing 21 changed files with 516 additions and 484 deletions.
7 changes: 7 additions & 0 deletions .changelog/pending.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:breaking-change
tfsdk: The `Attribute` type `Computed`, `Optional`, and `Required` fields have been replaced by `Configuration`.
```

```release-note:enhancement
tfsdk: Prevent `Attribute` misconfiguration by consolidating `Computed`, `Optional`, and `Required` fields into a single `Configuration` field.
```
59 changes: 32 additions & 27 deletions tfsdk/attribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ type Attribute struct {
// If Attributes is set, Type cannot be.
Attributes NestedAttributes

// Configuration describes attribute value behaviors, such as whether the
// value must be configured by the practitioner. Must be defined.
//
// See AttributeConfiguration descriptions for additional information.
Configuration AttributeConfiguration

// Description is used in various tooling, like the language server, to
// give practitioners more information about what this attribute is,
// what it's for, and how it should be used. It should be written as
Expand All @@ -42,23 +48,6 @@ type Attribute struct {
// used. It should be formatted using Markdown.
MarkdownDescription string

// Required indicates whether the practitioner must enter a value for
// this attribute or not. Required and Optional cannot both be true,
// and Required and Computed cannot both be true.
Required bool

// Optional indicates whether the practitioner can choose not to enter
// a value for this attribute or not. Optional and Required cannot both
// be true.
Optional bool

// Computed indicates whether the provider may return its own value for
// this attribute or not. Required and Computed cannot both be true. If
// Required and Optional are both false, Computed must be true, and the
// attribute will be considered "read only" for the practitioner, with
// only the provider able to set its value.
Computed bool

// Sensitive indicates whether the value of this attribute should be
// considered sensitive data. Setting it to true will obscure the value
// in CLI output. Sensitive does not impact how values are stored, and
Expand Down Expand Up @@ -111,13 +100,7 @@ func (a Attribute) Equal(o Attribute) bool {
if a.MarkdownDescription != o.MarkdownDescription {
return false
}
if a.Required != o.Required {
return false
}
if a.Optional != o.Optional {
return false
}
if a.Computed != o.Computed {
if a.Configuration != o.Configuration {
return false
}
if a.Sensitive != o.Sensitive {
Expand All @@ -135,12 +118,23 @@ func (a Attribute) Equal(o Attribute) bool {
func (a Attribute) tfprotov6SchemaAttribute(ctx context.Context, name string, path *tftypes.AttributePath) (*tfprotov6.SchemaAttribute, error) {
schemaAttribute := &tfprotov6.SchemaAttribute{
Name: name,
Required: a.Required,
Optional: a.Optional,
Computed: a.Computed,
Sensitive: a.Sensitive,
}

switch a.Configuration {
case AttributeConfigurationRequired:
schemaAttribute.Required = true
case AttributeConfigurationOptional:
schemaAttribute.Optional = true
case AttributeConfigurationOptionalComputed:
schemaAttribute.Optional = true
schemaAttribute.Computed = true
case AttributeConfigurationComputed:
schemaAttribute.Computed = true
default:
return nil, path.NewErrorf("must configure Configuration")
}

if a.DeprecationMessage != "" {
schemaAttribute.Deprecated = true
}
Expand Down Expand Up @@ -238,6 +232,17 @@ func (a Attribute) validate(ctx context.Context, req ValidateAttributeRequest, r
return
}

if a.Configuration == AttributeConfigurationUnknown {
resp.Diagnostics = append(resp.Diagnostics, &tfprotov6.Diagnostic{
Severity: tfprotov6.DiagnosticSeverityError,
Summary: "Invalid Attribute Definition",
Detail: "Attribute missing Configuration definition. This is always a problem with the provider and should be reported to the provider developer.",
Attribute: req.AttributePath,
})

return
}

attributeConfig, diags := req.Config.GetAttribute(ctx, req.AttributePath)

resp.Diagnostics = append(resp.Diagnostics, diags...)
Expand Down
51 changes: 51 additions & 0 deletions tfsdk/attribute_configuration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package tfsdk

// AttributeConfiguration describes Attribute value behaviors.
type AttributeConfiguration int

const (
// AttributeConfigurationUnknown signals an invalid Attribute definition.
//
// This is the default value of this type and is used to signal an
// Attribute missing a proper Configuration definition.
AttributeConfigurationUnknown AttributeConfiguration = 0

// AttributeConfigurationRequired denotes a value that requires practitioner configuration.
//
// If the practitioner configuration does not include a known value, an
// error is returned.
AttributeConfigurationRequired AttributeConfiguration = 1

// AttributeConfigurationOptional denotes a value that can be optionally set in a practitioner configuration.
//
// A plan difference for the value will be shown if plugin returns a value
// and there is no practitioner configuration.
AttributeConfigurationOptional AttributeConfiguration = 2

// AttributeConfigurationOptionalComputed denotes a value that can be set either by practitioner configuration or the plugin.
//
// No plan difference for the value will be shown if plugin returns a value
// and there is no practitioner configuration.
AttributeConfigurationOptionalComputed AttributeConfiguration = 3

// AttributeConfigurationComputed denotes a value that can only be set by the plugin.
//
// Effectively, this is a read-only Attribute.
AttributeConfigurationComputed AttributeConfiguration = 4
)

// String returns a string representation of the AttributeConfiguration.
func (ac AttributeConfiguration) String() string {
switch ac {
case AttributeConfigurationRequired:
return "Required"
case AttributeConfigurationOptional:
return "Optional"
case AttributeConfigurationOptionalComputed:
return "OptionalComputed"
case AttributeConfigurationComputed:
return "Computed"
default:
return "Unknown"
}
}

0 comments on commit 01c1fbf

Please sign in to comment.