Skip to content

Commit ad2b207

Browse files
authored
adds support for ephemeral resources (#157)
* adds support for ephemeral resources * adds changelog entry
1 parent 9a2312d commit ad2b207

File tree

5 files changed

+743
-0
lines changed

5 files changed

+743
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
kind: FEATURES
2+
body: 'ephemeral/timeouts: Adds functions and types for ephemeral resource timeouts'
3+
time: 2025-01-13T19:49:03.365811628-06:00
4+
custom:
5+
Issue: "157"

ephemeral/timeouts/schema.go

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package timeouts
5+
6+
import (
7+
"context"
8+
9+
"github.com/hashicorp/terraform-plugin-framework/attr"
10+
"github.com/hashicorp/terraform-plugin-framework/ephemeral/schema"
11+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
12+
"github.com/hashicorp/terraform-plugin-framework/types"
13+
14+
"github.com/hashicorp/terraform-plugin-framework-timeouts/internal/validators"
15+
)
16+
17+
const (
18+
attributeNameOpen = "open"
19+
)
20+
21+
// Opts is used as an argument to BlockWithOpts and AttributesWithOpts to indicate
22+
// whether supplied descriptions should override default descriptions.
23+
type Opts struct {
24+
OpenDescription string
25+
}
26+
27+
// BlockWithOpts returns a schema.Block containing attributes for `Open`, which is
28+
// defined as types.StringType and optional. A validator is used to verify
29+
// that the value assigned to `Open` can be parsed as time.Duration. The supplied
30+
// Opts are used to override defaults.
31+
func BlockWithOpts(ctx context.Context, opts Opts) schema.Block {
32+
return schema.SingleNestedBlock{
33+
Attributes: attributesMap(opts),
34+
CustomType: Type{
35+
ObjectType: types.ObjectType{
36+
AttrTypes: attrTypesMap(),
37+
},
38+
},
39+
}
40+
}
41+
42+
// Block returns a schema.Block containing attributes for `Open`, which is
43+
// defined as types.StringType and optional. A validator is used to verify
44+
// that the value assigned to `Open` can be parsed as time.Duration.
45+
func Block(ctx context.Context) schema.Block {
46+
return schema.SingleNestedBlock{
47+
Attributes: attributesMap(Opts{}),
48+
CustomType: Type{
49+
ObjectType: types.ObjectType{
50+
AttrTypes: attrTypesMap(),
51+
},
52+
},
53+
}
54+
}
55+
56+
// AttributesWithOpts returns a schema.SingleNestedAttribute which contains an
57+
// attribute for `Open`, which is defined as types.StringType and optional.
58+
// A validator is used to verify that the value assigned to an attribute
59+
// can be parsed as time.Duration. The supplied Opts are used to override defaults.
60+
func AttributesWithOpts(ctx context.Context, opts Opts) schema.Attribute {
61+
return schema.SingleNestedAttribute{
62+
Attributes: attributesMap(opts),
63+
CustomType: Type{
64+
ObjectType: types.ObjectType{
65+
AttrTypes: attrTypesMap(),
66+
},
67+
},
68+
Optional: true,
69+
}
70+
}
71+
72+
// Attributes returns a schema.SingleNestedAttribute which contains an
73+
// attribute for `Open`, which is defined as types.StringType and optional.
74+
// A validator is used to verify that the value assigned to an attribute
75+
// can be parsed as time.Duration.
76+
func Attributes(ctx context.Context) schema.Attribute {
77+
return schema.SingleNestedAttribute{
78+
Attributes: attributesMap(Opts{}),
79+
CustomType: Type{
80+
ObjectType: types.ObjectType{
81+
AttrTypes: attrTypesMap(),
82+
},
83+
},
84+
Optional: true,
85+
}
86+
}
87+
88+
func attributesMap(opts Opts) map[string]schema.Attribute {
89+
attribute := schema.StringAttribute{
90+
Optional: true,
91+
Description: `A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) ` +
92+
`consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are ` +
93+
`"s" (seconds), "m" (minutes), "h" (hours).`,
94+
Validators: []validator.String{
95+
validators.TimeDuration(),
96+
},
97+
}
98+
99+
if opts.OpenDescription != "" {
100+
attribute.Description = opts.OpenDescription
101+
}
102+
103+
return map[string]schema.Attribute{
104+
attributeNameOpen: attribute,
105+
}
106+
}
107+
108+
func attrTypesMap() map[string]attr.Type {
109+
return map[string]attr.Type{
110+
attributeNameOpen: types.StringType,
111+
}
112+
}

ephemeral/timeouts/schema_test.go

+247
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package timeouts_test
5+
6+
import (
7+
"context"
8+
"testing"
9+
10+
"github.com/google/go-cmp/cmp"
11+
"github.com/hashicorp/terraform-plugin-framework/attr"
12+
"github.com/hashicorp/terraform-plugin-framework/ephemeral/schema"
13+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
14+
"github.com/hashicorp/terraform-plugin-framework/types"
15+
16+
"github.com/hashicorp/terraform-plugin-framework-timeouts/ephemeral/timeouts"
17+
"github.com/hashicorp/terraform-plugin-framework-timeouts/internal/validators"
18+
)
19+
20+
func TestBlockWithOpts(t *testing.T) {
21+
t.Parallel()
22+
23+
type testCase struct {
24+
opts timeouts.Opts
25+
expected schema.Block
26+
}
27+
tests := map[string]testCase{
28+
"empty-opts": {
29+
opts: timeouts.Opts{},
30+
expected: schema.SingleNestedBlock{
31+
CustomType: timeouts.Type{
32+
ObjectType: types.ObjectType{
33+
AttrTypes: map[string]attr.Type{
34+
"open": types.StringType,
35+
},
36+
},
37+
},
38+
Attributes: map[string]schema.Attribute{
39+
"open": schema.StringAttribute{
40+
Optional: true,
41+
Description: `A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) ` +
42+
`consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are ` +
43+
`"s" (seconds), "m" (minutes), "h" (hours).`,
44+
Validators: []validator.String{
45+
validators.TimeDuration(),
46+
},
47+
},
48+
},
49+
},
50+
},
51+
"open-opts-description": {
52+
opts: timeouts.Opts{
53+
OpenDescription: "open description",
54+
},
55+
expected: schema.SingleNestedBlock{
56+
CustomType: timeouts.Type{
57+
ObjectType: types.ObjectType{
58+
AttrTypes: map[string]attr.Type{
59+
"open": types.StringType,
60+
},
61+
},
62+
},
63+
Attributes: map[string]schema.Attribute{
64+
"open": schema.StringAttribute{
65+
Optional: true,
66+
Description: "open description",
67+
Validators: []validator.String{
68+
validators.TimeDuration(),
69+
},
70+
},
71+
},
72+
},
73+
},
74+
}
75+
76+
for name, test := range tests {
77+
name, test := name, test
78+
t.Run(name, func(t *testing.T) {
79+
t.Parallel()
80+
actual := timeouts.BlockWithOpts(context.Background(), test.opts)
81+
82+
if diff := cmp.Diff(actual, test.expected); diff != "" {
83+
t.Errorf("unexpected block difference: %s", diff)
84+
}
85+
})
86+
}
87+
}
88+
89+
func TestBlock(t *testing.T) {
90+
t.Parallel()
91+
92+
type testCase struct {
93+
expected schema.Block
94+
}
95+
tests := map[string]testCase{
96+
"open": {
97+
expected: schema.SingleNestedBlock{
98+
CustomType: timeouts.Type{
99+
ObjectType: types.ObjectType{
100+
AttrTypes: map[string]attr.Type{
101+
"open": types.StringType,
102+
},
103+
},
104+
},
105+
Attributes: map[string]schema.Attribute{
106+
"open": schema.StringAttribute{
107+
Optional: true,
108+
Description: `A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) ` +
109+
`consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are ` +
110+
`"s" (seconds), "m" (minutes), "h" (hours).`,
111+
Validators: []validator.String{
112+
validators.TimeDuration(),
113+
},
114+
},
115+
},
116+
},
117+
},
118+
}
119+
120+
for name, test := range tests {
121+
name, test := name, test
122+
t.Run(name, func(t *testing.T) {
123+
t.Parallel()
124+
actual := timeouts.Block(context.Background())
125+
126+
if diff := cmp.Diff(actual, test.expected); diff != "" {
127+
t.Errorf("unexpected block difference: %s", diff)
128+
}
129+
})
130+
}
131+
}
132+
133+
func TestAttributesWithOpts(t *testing.T) {
134+
t.Parallel()
135+
136+
type testCase struct {
137+
opts timeouts.Opts
138+
expected schema.Attribute
139+
}
140+
tests := map[string]testCase{
141+
"empty-opts": {
142+
opts: timeouts.Opts{},
143+
expected: schema.SingleNestedAttribute{
144+
Attributes: map[string]schema.Attribute{
145+
"open": schema.StringAttribute{
146+
Optional: true,
147+
Description: `A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) ` +
148+
`consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are ` +
149+
`"s" (seconds), "m" (minutes), "h" (hours).`,
150+
Validators: []validator.String{
151+
validators.TimeDuration(),
152+
},
153+
},
154+
},
155+
CustomType: timeouts.Type{
156+
ObjectType: types.ObjectType{
157+
AttrTypes: map[string]attr.Type{
158+
"open": types.StringType,
159+
},
160+
},
161+
},
162+
Optional: true,
163+
},
164+
},
165+
"open-opts-description": {
166+
opts: timeouts.Opts{
167+
OpenDescription: "open description",
168+
},
169+
expected: schema.SingleNestedAttribute{
170+
Attributes: map[string]schema.Attribute{
171+
"open": schema.StringAttribute{
172+
Optional: true,
173+
Description: "open description",
174+
Validators: []validator.String{
175+
validators.TimeDuration(),
176+
},
177+
},
178+
},
179+
CustomType: timeouts.Type{
180+
ObjectType: types.ObjectType{
181+
AttrTypes: map[string]attr.Type{
182+
"open": types.StringType,
183+
},
184+
},
185+
},
186+
Optional: true,
187+
},
188+
},
189+
}
190+
191+
for name, test := range tests {
192+
name, test := name, test
193+
t.Run(name, func(t *testing.T) {
194+
t.Parallel()
195+
actual := timeouts.AttributesWithOpts(context.Background(), test.opts)
196+
197+
if diff := cmp.Diff(actual, test.expected); diff != "" {
198+
t.Errorf("unexpected block difference: %s", diff)
199+
}
200+
})
201+
}
202+
}
203+
204+
func TestAttributes(t *testing.T) {
205+
t.Parallel()
206+
207+
type testCase struct {
208+
expected schema.Attribute
209+
}
210+
tests := map[string]testCase{
211+
"open": {
212+
expected: schema.SingleNestedAttribute{
213+
Attributes: map[string]schema.Attribute{
214+
"open": schema.StringAttribute{
215+
Optional: true,
216+
Description: `A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) ` +
217+
`consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are ` +
218+
`"s" (seconds), "m" (minutes), "h" (hours).`,
219+
Validators: []validator.String{
220+
validators.TimeDuration(),
221+
},
222+
},
223+
},
224+
CustomType: timeouts.Type{
225+
ObjectType: types.ObjectType{
226+
AttrTypes: map[string]attr.Type{
227+
"open": types.StringType,
228+
},
229+
},
230+
},
231+
Optional: true,
232+
},
233+
},
234+
}
235+
236+
for name, test := range tests {
237+
name, test := name, test
238+
t.Run(name, func(t *testing.T) {
239+
t.Parallel()
240+
actual := timeouts.Attributes(context.Background())
241+
242+
if diff := cmp.Diff(actual, test.expected); diff != "" {
243+
t.Errorf("unexpected block difference: %s", diff)
244+
}
245+
})
246+
}
247+
}

0 commit comments

Comments
 (0)