Skip to content

Commit

Permalink
Add Description to Timeout Attributes (#52)
Browse files Browse the repository at this point in the history
* Adding description for each of the timeout attributes (#51)

* Adding changelog entries (#51)

* Amend attribute description to make valid time units clearer (#51)

* Amend attribute description for read and delete to indicate when these timeouts would be applicable (#51)

* Adding additional options to allow overriding of default descriptions (#51)

* Adding changelog entries (#51)

* Fixing changelog entry (#51)

* Updating README (#51)

* Apply suggestions from code review

Co-authored-by: Brian Flad <[email protected]>

* Updating to latest version of terraform-plugin-framework and fixing tests (#51)

---------

Co-authored-by: Brian Flad <[email protected]>
  • Loading branch information
bendbennett and bflad authored Jun 20, 2023
1 parent 177b693 commit 4ba612d
Show file tree
Hide file tree
Showing 9 changed files with 437 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changes/unreleased/ENHANCEMENTS-20230428-174331.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: ENHANCEMENTS
body: 'datasource/timeouts: Add default description for read'
time: 2023-04-28T17:43:31.884258+01:00
custom:
Issue: "51"
5 changes: 5 additions & 0 deletions .changes/unreleased/ENHANCEMENTS-20230428-174412.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: ENHANCEMENTS
body: 'resource/timeouts: Add default description for create, delete, read and update'
time: 2023-04-28T17:44:12.860337+01:00
custom:
Issue: "51"
6 changes: 6 additions & 0 deletions .changes/unreleased/ENHANCEMENTS-20230609-112245.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: ENHANCEMENTS
body: 'resource/timeouts: Add opts for `CreateDescription`, `ReadDescription`, `UpdateDescription`
and `DeleteDescription` to allow overriding of default description'
time: 2023-06-09T11:22:45.08138+01:00
custom:
Issue: "51"
6 changes: 6 additions & 0 deletions .changes/unreleased/ENHANCEMENTS-20230609-112409.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: ENHANCEMENTS
body: 'datasource/timeouts: Add `BlockWithOpts()` and `AttributesWithOpts()` functions
to allow overriding of default description'
time: 2023-06-09T11:24:09.988096+01:00
custom:
Issue: "51"
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ data "timeouts_example" "example" {

Use this module to mutate the `schema.Schema`:

You must supply `timeouts.Opts` when calling `timeouts.Block()` on a resource. Alternatively, `timeouts.BlockAll()` will generate attributes for `create`, `read`, `update` and `delete`.
You must supply `timeouts.Opts` when calling `timeouts.Block()` on a resource. The supplied `timeouts.Opts` allows specifying which timeouts to create and whether to override the default description for the timeout.

Alternatively, `timeouts.BlockAll()` will generate attributes for `create`, `read`, `update` and `delete`.

```go
import (
Expand All @@ -83,6 +85,8 @@ func (t *exampleResource) Schema(ctx context.Context, req resource.SchemaRequest
The `timeouts.Block()` call does not accept options on a data source as `read` is the only option.
However, the `timeouts.BlockWithOpts()` function is available for overriding the default description.
```go
import (
/* ... */
Expand Down Expand Up @@ -126,7 +130,9 @@ data "timeouts_example" "example" {
Use this module to mutate the `schema.Schema` as follows:
You must supply `timeouts.Opts` when calling `timeouts.Attributes()` on a resource.
You must supply `timeouts.Opts` when calling `timeouts.Attributes()` on a resource. The supplied `timeouts.Opts` allows specifying which timeouts to create and whether to override the default description for the timeout.
Alternatively, `timeouts.AttributesAll()` will generate attributes for `create`, `read`, `update` and `delete`.
```go
import (
Expand All @@ -144,7 +150,9 @@ func (t *exampleResource) Schema(ctx context.Context, req resource.SchemaRequest
},
```
The `timeouts.Attributes()` call does not accept options on a data source as `read` is the only option.
The `timeouts.Attributes()` call does not accept options on a data source as `read` is the only option.
However, the `timeouts.AttributesWithOpts()` function is available for overriding the default description.
```go
import (
Expand Down
64 changes: 55 additions & 9 deletions datasource/timeouts/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,54 @@ const (
attributeNameRead = "read"
)

// Opts is used as an argument to BlockWithOpts and AttributesWithOpts to indicate
// whether supplied descriptions should override default descriptions.
type Opts struct {
ReadDescription string
}

// BlockWithOpts returns a schema.Block containing attributes for `Read`, which is
// defined as types.StringType and optional. A validator is used to verify
// that the value assigned to `Read` can be parsed as time.Duration. The supplied
// Opts are used to override defaults.
func BlockWithOpts(ctx context.Context, opts Opts) schema.Block {
return schema.SingleNestedBlock{
Attributes: attributesMap(opts),
CustomType: Type{
ObjectType: types.ObjectType{
AttrTypes: attrTypesMap(),
},
},
}
}

// Block returns a schema.Block containing attributes for `Read`, which is
// defined as types.StringType and optional. A validator is used to verify
// that the value assigned to `Read` can be parsed as time.Duration.
func Block(ctx context.Context) schema.Block {
return schema.SingleNestedBlock{
Attributes: attributesMap(),
Attributes: attributesMap(Opts{}),
CustomType: Type{
ObjectType: types.ObjectType{
AttrTypes: attrTypesMap(),
},
},
}
}

// AttributesWithOpts returns a schema.SingleNestedAttribute which contains an
// attribute for `Read`, which is defined as types.StringType and optional.
// A validator is used to verify that the value assigned to an attribute
// can be parsed as time.Duration. The supplied Opts are used to override defaults.
func AttributesWithOpts(ctx context.Context, opts Opts) schema.Attribute {
return schema.SingleNestedAttribute{
Attributes: attributesMap(opts),
CustomType: Type{
ObjectType: types.ObjectType{
AttrTypes: attrTypesMap(),
},
},
Optional: true,
}
}

Expand All @@ -38,7 +75,7 @@ func Block(ctx context.Context) schema.Block {
// can be parsed as time.Duration.
func Attributes(ctx context.Context) schema.Attribute {
return schema.SingleNestedAttribute{
Attributes: attributesMap(),
Attributes: attributesMap(Opts{}),
CustomType: Type{
ObjectType: types.ObjectType{
AttrTypes: attrTypesMap(),
Expand All @@ -48,15 +85,24 @@ func Attributes(ctx context.Context) schema.Attribute {
}
}

func attributesMap() map[string]schema.Attribute {
return map[string]schema.Attribute{
attributeNameRead: schema.StringAttribute{
Optional: true,
Validators: []validator.String{
validators.TimeDuration(),
},
func attributesMap(opts Opts) map[string]schema.Attribute {
attribute := schema.StringAttribute{
Optional: true,
Description: `A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) ` +
`consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are ` +
`"s" (seconds), "m" (minutes), "h" (hours).`,
Validators: []validator.String{
validators.TimeDuration(),
},
}

if opts.ReadDescription != "" {
attribute.Description = opts.ReadDescription
}

return map[string]schema.Attribute{
attributeNameRead: attribute,
}
}

func attrTypesMap() map[string]attr.Type {
Expand Down
146 changes: 146 additions & 0 deletions datasource/timeouts/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,75 @@ import (
"github.com/hashicorp/terraform-plugin-framework-timeouts/internal/validators"
)

func TestBlockWithOpts(t *testing.T) {
t.Parallel()

type testCase struct {
opts timeouts.Opts
expected schema.Block
}
tests := map[string]testCase{
"empty-opts": {
opts: timeouts.Opts{},
expected: schema.SingleNestedBlock{
CustomType: timeouts.Type{
ObjectType: types.ObjectType{
AttrTypes: map[string]attr.Type{
"read": types.StringType,
},
},
},
Attributes: map[string]schema.Attribute{
"read": schema.StringAttribute{
Optional: true,
Description: `A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) ` +
`consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are ` +
`"s" (seconds), "m" (minutes), "h" (hours).`,
Validators: []validator.String{
validators.TimeDuration(),
},
},
},
},
},
"read-opts-description": {
opts: timeouts.Opts{
ReadDescription: "read description",
},
expected: schema.SingleNestedBlock{
CustomType: timeouts.Type{
ObjectType: types.ObjectType{
AttrTypes: map[string]attr.Type{
"read": types.StringType,
},
},
},
Attributes: map[string]schema.Attribute{
"read": schema.StringAttribute{
Optional: true,
Description: "read description",
Validators: []validator.String{
validators.TimeDuration(),
},
},
},
},
},
}

for name, test := range tests {
name, test := name, test
t.Run(name, func(t *testing.T) {
t.Parallel()
actual := timeouts.BlockWithOpts(context.Background(), test.opts)

if diff := cmp.Diff(actual, test.expected); diff != "" {
t.Errorf("unexpected block difference: %s", diff)
}
})
}
}

func TestBlock(t *testing.T) {
t.Parallel()

Expand All @@ -36,6 +105,9 @@ func TestBlock(t *testing.T) {
Attributes: map[string]schema.Attribute{
"read": schema.StringAttribute{
Optional: true,
Description: `A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) ` +
`consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are ` +
`"s" (seconds), "m" (minutes), "h" (hours).`,
Validators: []validator.String{
validators.TimeDuration(),
},
Expand All @@ -58,6 +130,77 @@ func TestBlock(t *testing.T) {
}
}

func TestAttributesWithOpts(t *testing.T) {
t.Parallel()

type testCase struct {
opts timeouts.Opts
expected schema.Attribute
}
tests := map[string]testCase{
"empty-opts": {
opts: timeouts.Opts{},
expected: schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"read": schema.StringAttribute{
Optional: true,
Description: `A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) ` +
`consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are ` +
`"s" (seconds), "m" (minutes), "h" (hours).`,
Validators: []validator.String{
validators.TimeDuration(),
},
},
},
CustomType: timeouts.Type{
ObjectType: types.ObjectType{
AttrTypes: map[string]attr.Type{
"read": types.StringType,
},
},
},
Optional: true,
},
},
"read-opts-description": {
opts: timeouts.Opts{
ReadDescription: "read description",
},
expected: schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"read": schema.StringAttribute{
Optional: true,
Description: "read description",
Validators: []validator.String{
validators.TimeDuration(),
},
},
},
CustomType: timeouts.Type{
ObjectType: types.ObjectType{
AttrTypes: map[string]attr.Type{
"read": types.StringType,
},
},
},
Optional: true,
},
},
}

for name, test := range tests {
name, test := name, test
t.Run(name, func(t *testing.T) {
t.Parallel()
actual := timeouts.AttributesWithOpts(context.Background(), test.opts)

if diff := cmp.Diff(actual, test.expected); diff != "" {
t.Errorf("unexpected block difference: %s", diff)
}
})
}
}

func TestAttributes(t *testing.T) {
t.Parallel()

Expand All @@ -70,6 +213,9 @@ func TestAttributes(t *testing.T) {
Attributes: map[string]schema.Attribute{
"read": schema.StringAttribute{
Optional: true,
Description: `A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) ` +
`consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are ` +
`"s" (seconds), "m" (minutes), "h" (hours).`,
Validators: []validator.String{
validators.TimeDuration(),
},
Expand Down
Loading

0 comments on commit 4ba612d

Please sign in to comment.