@@ -4,15 +4,12 @@ import (
4
4
"context"
5
5
"testing"
6
6
7
- "github.com/hashicorp/terraform-plugin-framework/path"
8
7
"github.com/hashicorp/terraform-plugin-framework/resource"
9
8
rschema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
10
9
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
11
10
"github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier"
12
11
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
13
- "github.com/hashicorp/terraform-plugin-framework/tfsdk"
14
12
"github.com/hashicorp/terraform-plugin-framework/types"
15
- "github.com/hashicorp/terraform-plugin-go/tftypes"
16
13
"github.com/hexops/autogold/v2"
17
14
"github.com/zclconf/go-cty/cty"
18
15
@@ -154,140 +151,182 @@ func TestDetailedDiffSet(t *testing.T) {
154
151
},
155
152
})
156
153
154
+ computedCreateFunc := func (ctx context.Context , req resource.CreateRequest , resp * resource.CreateResponse ) {
155
+ type Nested struct {
156
+ Nested types.String `tfsdk:"nested"`
157
+ Computed types.String `tfsdk:"computed"`
158
+ }
159
+
160
+ type ObjectModel struct {
161
+ ID types.String `tfsdk:"id"`
162
+ Keys []Nested `tfsdk:"key"`
163
+ }
164
+
165
+ reqObj := ObjectModel {}
166
+ diags := req .Plan .Get (ctx , & reqObj )
167
+ contract .Assertf (diags .ErrorsCount () == 0 , "failed to get attribute: %v" , diags )
168
+
169
+ respObj := ObjectModel {
170
+ ID : types .StringValue ("test-id" ),
171
+ Keys : make ([]Nested , len (reqObj .Keys )),
172
+ }
173
+
174
+ for i , key := range reqObj .Keys {
175
+ newKey := Nested {}
176
+ if key .Computed .IsNull () || key .Computed .IsUnknown () {
177
+ nestedVal := ""
178
+ if ! key .Nested .IsNull () && ! key .Nested .IsUnknown () {
179
+ nestedVal = key .Nested .ValueString ()
180
+ }
181
+ computedVal := "computed-" + nestedVal
182
+ newKey .Nested = types .StringValue (nestedVal )
183
+ newKey .Computed = types .StringValue (computedVal )
184
+ } else {
185
+ newKey .Nested = key .Nested
186
+ newKey .Computed = key .Computed
187
+ }
188
+ respObj .Keys [i ] = newKey
189
+ }
190
+
191
+ diags = resp .State .Set (ctx , & respObj )
192
+ contract .Assertf (diags .ErrorsCount () == 0 , "failed to set attribute: %v" , diags )
193
+ }
194
+
195
+ computedUpdateFunc := func (ctx context.Context , req resource.UpdateRequest , resp * resource.UpdateResponse ) {
196
+ createResp := resource.CreateResponse {
197
+ State : resp .State ,
198
+ Diagnostics : resp .Diagnostics ,
199
+ }
200
+ computedCreateFunc (ctx , resource.CreateRequest {
201
+ Plan : req .Plan ,
202
+ Config : req .Config ,
203
+ ProviderMeta : req .ProviderMeta ,
204
+ }, & createResp )
205
+
206
+ resp .State = createResp .State
207
+ resp .Diagnostics = createResp .Diagnostics
208
+ }
209
+
157
210
blockSchemaWithComputed := pb .NewResource (pb.NewResourceArgs {
158
211
ResourceSchema : rschema.Schema {
159
212
Blocks : map [string ]rschema.Block {
160
213
"key" : rschema.SetNestedBlock {
161
214
NestedObject : rschema.NestedBlockObject {
162
215
Attributes : map [string ]rschema.Attribute {
163
- "nested" : rschema.StringAttribute {Optional : true },
164
- "computed" : rschema.StringAttribute {Computed : true , Optional : true },
216
+ "nested" : rschema.StringAttribute {Optional : true },
217
+ "computed" : rschema.StringAttribute {
218
+ Computed : true ,
219
+ Optional : true ,
220
+ PlanModifiers : []planmodifier.String {
221
+ stringplanmodifier .UseStateForUnknown (),
222
+ },
223
+ },
165
224
},
166
225
},
167
226
},
168
227
},
169
228
},
170
- CreateFunc : func (ctx context.Context , req resource.CreateRequest , resp * resource.CreateResponse ) {
171
- resp .State = tfsdk .State (req .Plan )
172
- diags := resp .State .SetAttribute (ctx , path .Root ("id" ), "test-id" )
173
- contract .Assertf (diags .ErrorsCount () == 0 , "failed to set attribute: %v" , diags )
174
-
175
- keys := make ([]tftypes.Value , 0 )
176
- diags = resp .State .GetAttribute (ctx , path .Root ("key" ), & keys )
177
- contract .Assertf (diags .ErrorsCount () == 0 , "failed to get attribute: %v" , diags )
178
-
179
- resultKeys := make ([]tftypes.Value , len (keys ))
180
- for i , key := range keys {
181
- keyMap := make (map [string ]tftypes.Value )
182
- if key .As (& keyMap ) == nil {
183
- nestedVal := ""
184
- if val , ok := keyMap ["nested" ]; ok {
185
- nestedVal = val .String ()
186
- }
187
- computedVal := ""
188
- if _ , ok := keyMap ["computed" ]; ! ok {
189
- computedVal = "computed-" + nestedVal
190
- } else {
191
- computedVal = keyMap ["computed" ].String ()
192
- }
193
- keyMap ["computed" ] = tftypes .NewValue (tftypes .String , computedVal )
194
- }
195
- resultKeys [i ] = tftypes .NewValue (tftypes.Object {
196
- AttributeTypes : map [string ]tftypes.Type {
197
- "nested" : tftypes .String ,
198
- "computed" : tftypes .String ,
229
+ CreateFunc : computedCreateFunc ,
230
+ UpdateFunc : computedUpdateFunc ,
231
+ })
232
+
233
+ blockSchemaWithComputedNoStateForUnknown := pb .NewResource (pb.NewResourceArgs {
234
+ ResourceSchema : rschema.Schema {
235
+ Blocks : map [string ]rschema.Block {
236
+ "key" : rschema.SetNestedBlock {
237
+ NestedObject : rschema.NestedBlockObject {
238
+ Attributes : map [string ]rschema.Attribute {
239
+ "nested" : rschema.StringAttribute {Optional : true },
240
+ "computed" : rschema.StringAttribute {
241
+ Computed : true ,
242
+ Optional : true ,
243
+ },
244
+ },
199
245
},
200
- }, keyMap )
201
- }
246
+ },
247
+ },
248
+ },
249
+ CreateFunc : computedCreateFunc ,
250
+ UpdateFunc : computedUpdateFunc ,
251
+ })
252
+
253
+ blockSchemaWithComputedReplace := pb .NewResource (pb.NewResourceArgs {
254
+ ResourceSchema : rschema.Schema {
255
+ Blocks : map [string ]rschema.Block {
256
+ "key" : rschema.SetNestedBlock {
257
+ NestedObject : rschema.NestedBlockObject {
258
+ Attributes : map [string ]rschema.Attribute {
259
+ "nested" : rschema.StringAttribute {Optional : true },
260
+ "computed" : rschema.StringAttribute {
261
+ Computed : true ,
262
+ Optional : true ,
263
+ PlanModifiers : []planmodifier.String {
264
+ stringplanmodifier .UseStateForUnknown (),
265
+ },
266
+ },
267
+ },
268
+ },
269
+ PlanModifiers : []planmodifier.Set {
270
+ setplanmodifier .RequiresReplace (),
271
+ },
272
+ },
273
+ },
274
+ },
275
+ CreateFunc : computedCreateFunc ,
276
+ UpdateFunc : computedUpdateFunc ,
277
+ })
202
278
203
- diags = resp .State .SetAttribute (ctx , path .Root ("key" ), resultKeys )
204
- contract .Assertf (diags .ErrorsCount () == 0 , "failed to set attribute: %v" , diags )
279
+ blockSchemaWithComputedNestedReplace := pb .NewResource (pb.NewResourceArgs {
280
+ ResourceSchema : rschema.Schema {
281
+ Blocks : map [string ]rschema.Block {
282
+ "key" : rschema.SetNestedBlock {
283
+ NestedObject : rschema.NestedBlockObject {
284
+ Attributes : map [string ]rschema.Attribute {
285
+ "nested" : rschema.StringAttribute {
286
+ Optional : true ,
287
+ PlanModifiers : []planmodifier.String {
288
+ stringplanmodifier .RequiresReplace (),
289
+ },
290
+ },
291
+ "computed" : rschema.StringAttribute {
292
+ Computed : true ,
293
+ Optional : true ,
294
+ PlanModifiers : []planmodifier.String {
295
+ stringplanmodifier .UseStateForUnknown (),
296
+ },
297
+ },
298
+ },
299
+ },
300
+ },
301
+ },
205
302
},
303
+ CreateFunc : computedCreateFunc ,
304
+ UpdateFunc : computedUpdateFunc ,
206
305
})
207
306
208
- // blockSchemaWithComputedAndStateForUnknown := pb.NewResource(pb.NewResourceArgs{
209
- // ResourceSchema: rschema.Schema{
210
- // Blocks: map[string]rschema.Block{
211
- // "key": rschema.SetNestedBlock{
212
- // NestedObject: rschema.NestedBlockObject{
213
- // Attributes: map[string]rschema.Attribute{
214
- // "nested": rschema.StringAttribute{Optional: true},
215
- // "computed": rschema.StringAttribute{
216
- // Computed: true,
217
- // Optional: true,
218
- // PlanModifiers: []planmodifier.String{
219
- // stringplanmodifier.UseStateForUnknown(),
220
- // },
221
- // },
222
- // },
223
- // },
224
- // },
225
- // },
226
- // },
227
- // })
228
-
229
- // blockSchemaWithComputedReplace := pb.NewResource(pb.NewResourceArgs{
230
- // ResourceSchema: rschema.Schema{
231
- // Blocks: map[string]rschema.Block{
232
- // "key": rschema.SetNestedBlock{
233
- // NestedObject: rschema.NestedBlockObject{
234
- // Attributes: map[string]rschema.Attribute{
235
- // "nested": rschema.StringAttribute{Optional: true},
236
- // "computed": rschema.StringAttribute{Computed: true, Optional: true},
237
- // },
238
- // },
239
- // PlanModifiers: []planmodifier.Set{
240
- // setplanmodifier.RequiresReplace(),
241
- // },
242
- // },
243
- // },
244
- // },
245
- // })
246
-
247
- // blockSchemaWithComputedNestedReplace := pb.NewResource(pb.NewResourceArgs{
248
- // ResourceSchema: rschema.Schema{
249
- // Blocks: map[string]rschema.Block{
250
- // "key": rschema.SetNestedBlock{
251
- // NestedObject: rschema.NestedBlockObject{
252
- // Attributes: map[string]rschema.Attribute{
253
- // "nested": rschema.StringAttribute{
254
- // Optional: true,
255
- // PlanModifiers: []planmodifier.String{
256
- // stringplanmodifier.RequiresReplace(),
257
- // },
258
- // },
259
- // "computed": rschema.StringAttribute{
260
- // Computed: true,
261
- // Optional: true,
262
- // },
263
- // },
264
- // },
265
- // },
266
- // },
267
- // },
268
- // })
269
-
270
- // blockSchemaWithComputedAndPlanModifierRequiresReplace := pb.NewResource(pb.NewResourceArgs{
271
- // ResourceSchema: rschema.Schema{
272
- // Blocks: map[string]rschema.Block{
273
- // "key": rschema.SetNestedBlock{
274
- // NestedObject: rschema.NestedBlockObject{
275
- // Attributes: map[string]rschema.Attribute{
276
- // "nested": rschema.StringAttribute{Optional: true},
277
- // "computed": rschema.StringAttribute{
278
- // Computed: true,
279
- // Optional: true,
280
- // PlanModifiers: []planmodifier.String{
281
- // stringplanmodifier.UseStateForUnknown(),
282
- // stringplanmodifier.RequiresReplace(),
283
- // },
284
- // },
285
- // },
286
- // },
287
- // },
288
- // },
289
- // },
290
- // })
307
+ blockSchemaWithComputedComputedRequiresReplace := pb .NewResource (pb.NewResourceArgs {
308
+ ResourceSchema : rschema.Schema {
309
+ Blocks : map [string ]rschema.Block {
310
+ "key" : rschema.SetNestedBlock {
311
+ NestedObject : rschema.NestedBlockObject {
312
+ Attributes : map [string ]rschema.Attribute {
313
+ "nested" : rschema.StringAttribute {Optional : true },
314
+ "computed" : rschema.StringAttribute {
315
+ Computed : true ,
316
+ Optional : true ,
317
+ PlanModifiers : []planmodifier.String {
318
+ stringplanmodifier .UseStateForUnknown (),
319
+ stringplanmodifier .RequiresReplace (),
320
+ },
321
+ },
322
+ },
323
+ },
324
+ },
325
+ },
326
+ },
327
+ CreateFunc : computedCreateFunc ,
328
+ UpdateFunc : computedUpdateFunc ,
329
+ })
291
330
292
331
attrList := func (arr * []string ) cty.Value {
293
332
if arr == nil {
@@ -354,8 +393,18 @@ func TestDetailedDiffSet(t *testing.T) {
354
393
{"block requires replace" , blockReplaceSchema , nestedAttrList },
355
394
{"block nested requires replace" , blockNestedReplaceSchema , nestedAttrList },
356
395
396
+ // Computed, each state we test both the behaviour when the computed value is specified in the program and when it is not.
357
397
{"block with computed no replace computed" , blockSchemaWithComputed , nestedAttrList },
358
398
{"block with computed no replace computed specified in program" , blockSchemaWithComputed , nestedAttrListWithComputedSpecified },
399
+ {"block with computed requires replace" , blockSchemaWithComputedReplace , nestedAttrList },
400
+ {"block with computed requires replace computed specified in program" , blockSchemaWithComputedReplace , nestedAttrListWithComputedSpecified },
401
+ {"block with computed and nested requires replace" , blockSchemaWithComputedNestedReplace , nestedAttrList },
402
+ {"block with computed and nested requires replace computed specified in program" , blockSchemaWithComputedNestedReplace , nestedAttrListWithComputedSpecified },
403
+ {"block with computed and computed requires replace" , blockSchemaWithComputedComputedRequiresReplace , nestedAttrList },
404
+ {"block with computed and computed requires replace computed specified in program" , blockSchemaWithComputedComputedRequiresReplace , nestedAttrListWithComputedSpecified },
405
+ // Rarely used, but supported
406
+ {"block with computed no state for unknown" , blockSchemaWithComputedNoStateForUnknown , nestedAttrList },
407
+ {"block with computed no state for unknown computed specified in program" , blockSchemaWithComputedNoStateForUnknown , nestedAttrListWithComputedSpecified },
359
408
}
360
409
361
410
scenarios := []struct {
0 commit comments