Skip to content

Commit 774af73

Browse files
committed
protov6 implementation of PlanResourceChange
1 parent 8873227 commit 774af73

File tree

7 files changed

+313
-4
lines changed

7 files changed

+313
-4
lines changed

internal/fromproto6/planresourcechange.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717

1818
// PlanResourceChangeRequest returns the *fwserver.PlanResourceChangeRequest
1919
// equivalent of a *tfprotov6.PlanResourceChangeRequest.
20-
func PlanResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.PlanResourceChangeRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, resourceBehavior resource.ResourceBehavior) (*fwserver.PlanResourceChangeRequest, diag.Diagnostics) {
20+
func PlanResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.PlanResourceChangeRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, resourceBehavior resource.ResourceBehavior, identitySchema fwschema.Schema) (*fwserver.PlanResourceChangeRequest, diag.Diagnostics) {
2121
if proto6 == nil {
2222
return nil, nil
2323
}
@@ -41,6 +41,7 @@ func PlanResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.PlanResour
4141
fw := &fwserver.PlanResourceChangeRequest{
4242
ResourceBehavior: resourceBehavior,
4343
ResourceSchema: resourceSchema,
44+
IdentitySchema: identitySchema,
4445
Resource: reqResource,
4546
ClientCapabilities: ModifyPlanClientCapabilities(proto6.ClientCapabilities),
4647
}
@@ -57,6 +58,12 @@ func PlanResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.PlanResour
5758

5859
fw.PriorState = priorState
5960

61+
priorIdentity, priorIdentityDiags := ResourceIdentity(ctx, proto6.PriorIdentity, identitySchema)
62+
63+
diags.Append(priorIdentityDiags...)
64+
65+
fw.PriorIdentity = priorIdentity
66+
6067
proposedNewState, proposedNewStateDiags := Plan(ctx, proto6.ProposedNewState, resourceSchema)
6168

6269
diags.Append(proposedNewStateDiags...)

internal/fromproto6/planresourcechange_test.go

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
1818
"github.com/hashicorp/terraform-plugin-framework/internal/privatestate"
1919
"github.com/hashicorp/terraform-plugin-framework/resource"
20+
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
2021
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
2122
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
2223
)
@@ -48,6 +49,30 @@ func TestPlanResourceChangeRequest(t *testing.T) {
4849
},
4950
}
5051

52+
testIdentityProto6Type := tftypes.Object{
53+
AttributeTypes: map[string]tftypes.Type{
54+
"test_identity_attribute": tftypes.String,
55+
},
56+
}
57+
58+
testIdentityProto6Value := tftypes.NewValue(testIdentityProto6Type, map[string]tftypes.Value{
59+
"test_identity_attribute": tftypes.NewValue(tftypes.String, "id-123"),
60+
})
61+
62+
testIdentityProto6DynamicValue, err := tfprotov6.NewDynamicValue(testIdentityProto6Type, testIdentityProto6Value)
63+
64+
if err != nil {
65+
t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err)
66+
}
67+
68+
testIdentitySchema := identityschema.Schema{
69+
Attributes: map[string]identityschema.Attribute{
70+
"test_identity_attribute": identityschema.StringAttribute{
71+
RequiredForImport: true,
72+
},
73+
},
74+
}
75+
5176
testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{
5277
"providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`),
5378
})
@@ -58,6 +83,7 @@ func TestPlanResourceChangeRequest(t *testing.T) {
5883
input *tfprotov6.PlanResourceChangeRequest
5984
resourceBehavior resource.ResourceBehavior
6085
resourceSchema fwschema.Schema
86+
identitySchema fwschema.Schema
6187
resource resource.Resource
6288
providerMetaSchema fwschema.Schema
6389
expected *fwserver.PlanResourceChangeRequest
@@ -182,6 +208,42 @@ func TestPlanResourceChangeRequest(t *testing.T) {
182208
ResourceSchema: testFwSchema,
183209
},
184210
},
211+
"prioridentity-missing-schema": {
212+
input: &tfprotov6.PlanResourceChangeRequest{
213+
PriorIdentity: &tfprotov6.ResourceIdentityData{
214+
IdentityData: &testIdentityProto6DynamicValue,
215+
},
216+
},
217+
resourceSchema: testFwSchema,
218+
expected: &fwserver.PlanResourceChangeRequest{
219+
ResourceSchema: testFwSchema,
220+
},
221+
expectedDiagnostics: diag.Diagnostics{
222+
diag.NewErrorDiagnostic(
223+
"Unable to Convert Resource Identity",
224+
"An unexpected error was encountered when converting the resource identity from the protocol type. "+
225+
"Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+
226+
"This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.",
227+
),
228+
},
229+
},
230+
"prioridentity": {
231+
input: &tfprotov6.PlanResourceChangeRequest{
232+
PriorIdentity: &tfprotov6.ResourceIdentityData{
233+
IdentityData: &testIdentityProto6DynamicValue,
234+
},
235+
},
236+
identitySchema: testIdentitySchema,
237+
resourceSchema: testFwSchema,
238+
expected: &fwserver.PlanResourceChangeRequest{
239+
IdentitySchema: testIdentitySchema,
240+
PriorIdentity: &tfsdk.ResourceIdentity{
241+
Raw: testIdentityProto6Value,
242+
Schema: testIdentitySchema,
243+
},
244+
ResourceSchema: testFwSchema,
245+
},
246+
},
185247
"providermeta-missing-data": {
186248
input: &tfprotov6.PlanResourceChangeRequest{},
187249
resourceSchema: testFwSchema,
@@ -265,7 +327,7 @@ func TestPlanResourceChangeRequest(t *testing.T) {
265327
t.Run(name, func(t *testing.T) {
266328
t.Parallel()
267329

268-
got, diags := fromproto6.PlanResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.resourceBehavior)
330+
got, diags := fromproto6.PlanResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.resourceBehavior, testCase.identitySchema)
269331

270332
if diff := cmp.Diff(got, testCase.expected, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" {
271333
t.Errorf("unexpected difference: %s", diff)

internal/proto6server/server_planresourcechange.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ func (s *Server) PlanResourceChange(ctx context.Context, proto6Req *tfprotov6.Pl
3737
return toproto6.PlanResourceChangeResponse(ctx, fwResp), nil
3838
}
3939

40+
identitySchema, diags := s.FrameworkServer.ResourceIdentitySchema(ctx, proto6Req.TypeName)
41+
42+
fwResp.Diagnostics.Append(diags...)
43+
44+
if fwResp.Diagnostics.HasError() {
45+
return toproto6.PlanResourceChangeResponse(ctx, fwResp), nil
46+
}
47+
4048
providerMetaSchema, diags := s.FrameworkServer.ProviderMetaSchema(ctx)
4149

4250
fwResp.Diagnostics.Append(diags...)
@@ -53,7 +61,7 @@ func (s *Server) PlanResourceChange(ctx context.Context, proto6Req *tfprotov6.Pl
5361
return toproto6.PlanResourceChangeResponse(ctx, fwResp), nil
5462
}
5563

56-
fwReq, diags := fromproto6.PlanResourceChangeRequest(ctx, proto6Req, resource, resourceSchema, providerMetaSchema, resourceBehavior)
64+
fwReq, diags := fromproto6.PlanResourceChangeRequest(ctx, proto6Req, resource, resourceSchema, providerMetaSchema, resourceBehavior, identitySchema)
5765

5866
fwResp.Diagnostics.Append(diags...)
5967

internal/proto6server/server_planresourcechange_test.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/hashicorp/terraform-plugin-framework/provider"
1515
"github.com/hashicorp/terraform-plugin-framework/provider/metaschema"
1616
"github.com/hashicorp/terraform-plugin-framework/resource"
17+
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
1718
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
1819
"github.com/hashicorp/terraform-plugin-framework/types"
1920
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
@@ -32,6 +33,12 @@ func TestServerPlanResourceChange(t *testing.T) {
3233

3334
testEmptyDynamicValue, _ := tfprotov6.NewDynamicValue(testSchemaType, tftypes.NewValue(testSchemaType, nil))
3435

36+
testIdentitySchemaType := tftypes.Object{
37+
AttributeTypes: map[string]tftypes.Type{
38+
"test_id": tftypes.String,
39+
},
40+
}
41+
3542
testSchema := schema.Schema{
3643
Attributes: map[string]schema.Attribute{
3744
"test_computed": schema.StringAttribute{
@@ -43,6 +50,18 @@ func TestServerPlanResourceChange(t *testing.T) {
4350
},
4451
}
4552

53+
testIdentitySchema := identityschema.Schema{
54+
Attributes: map[string]identityschema.Attribute{
55+
"test_id": identityschema.StringAttribute{
56+
RequiredForImport: true,
57+
},
58+
},
59+
}
60+
61+
type testIdentitySchemaData struct {
62+
TestID types.String `tfsdk:"test_id"`
63+
}
64+
4665
type testSchemaData struct {
4766
TestComputed types.String `tfsdk:"test_computed"`
4867
TestRequired types.String `tfsdk:"test_required"`
@@ -178,6 +197,70 @@ func TestServerPlanResourceChange(t *testing.T) {
178197
}),
179198
},
180199
},
200+
"create-request-plannedidentity": {
201+
server: &Server{
202+
FrameworkServer: fwserver.Server{
203+
Provider: &testprovider.Provider{
204+
ResourcesMethod: func(_ context.Context) []func() resource.Resource {
205+
return []func() resource.Resource{
206+
func() resource.Resource {
207+
return &testprovider.ResourceWithIdentityAndModifyPlan{
208+
Resource: &testprovider.Resource{
209+
SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
210+
resp.Schema = testSchema
211+
},
212+
MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
213+
resp.TypeName = "test_resource"
214+
},
215+
},
216+
IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) {
217+
resp.IdentitySchema = testIdentitySchema
218+
},
219+
ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) {
220+
var data testIdentitySchemaData
221+
222+
resp.Diagnostics.Append(req.Identity.Get(ctx, &data)...)
223+
224+
if data.TestID.ValueString() != "id-123" {
225+
resp.Diagnostics.AddError("Unexpected req.Identity", data.TestID.ValueString())
226+
}
227+
},
228+
}
229+
},
230+
}
231+
},
232+
},
233+
},
234+
},
235+
request: &tfprotov6.PlanResourceChangeRequest{
236+
Config: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{
237+
"test_computed": tftypes.NewValue(tftypes.String, nil),
238+
"test_required": tftypes.NewValue(tftypes.String, "test-config-value"),
239+
}),
240+
ProposedNewState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{
241+
"test_computed": tftypes.NewValue(tftypes.String, nil),
242+
"test_required": tftypes.NewValue(tftypes.String, "test-config-value"),
243+
}),
244+
PriorIdentity: &tfprotov6.ResourceIdentityData{
245+
IdentityData: testNewDynamicValue(t, testIdentitySchemaType, map[string]tftypes.Value{
246+
"test_id": tftypes.NewValue(tftypes.String, "id-123"),
247+
}),
248+
},
249+
PriorState: &testEmptyDynamicValue,
250+
TypeName: "test_resource",
251+
},
252+
expectedResponse: &tfprotov6.PlanResourceChangeResponse{
253+
PlannedState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{
254+
"test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue),
255+
"test_required": tftypes.NewValue(tftypes.String, "test-config-value"),
256+
}),
257+
PlannedIdentity: &tfprotov6.ResourceIdentityData{
258+
IdentityData: testNewDynamicValue(t, testIdentitySchemaType, map[string]tftypes.Value{
259+
"test_id": tftypes.NewValue(tftypes.String, "id-123"),
260+
}),
261+
},
262+
},
263+
},
181264
"create-request-providermeta": {
182265
server: &Server{
183266
FrameworkServer: fwserver.Server{
@@ -344,6 +427,70 @@ func TestServerPlanResourceChange(t *testing.T) {
344427
}),
345428
},
346429
},
430+
"create-response-plannedidentity": {
431+
server: &Server{
432+
FrameworkServer: fwserver.Server{
433+
Provider: &testprovider.Provider{
434+
ResourcesMethod: func(_ context.Context) []func() resource.Resource {
435+
return []func() resource.Resource{
436+
func() resource.Resource {
437+
return &testprovider.ResourceWithIdentityAndModifyPlan{
438+
Resource: &testprovider.Resource{
439+
SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
440+
resp.Schema = testSchema
441+
},
442+
MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
443+
resp.TypeName = "test_resource"
444+
},
445+
},
446+
ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) {
447+
var data testIdentitySchemaData
448+
449+
resp.Diagnostics.Append(req.Identity.Get(ctx, &data)...)
450+
451+
data.TestID = types.StringValue("new-id-123")
452+
453+
resp.Diagnostics.Append(resp.Identity.Set(ctx, &data)...)
454+
},
455+
IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) {
456+
resp.IdentitySchema = testIdentitySchema
457+
},
458+
}
459+
},
460+
}
461+
},
462+
},
463+
},
464+
},
465+
request: &tfprotov6.PlanResourceChangeRequest{
466+
Config: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{
467+
"test_computed": tftypes.NewValue(tftypes.String, nil),
468+
"test_required": tftypes.NewValue(tftypes.String, "test-config-value"),
469+
}),
470+
ProposedNewState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{
471+
"test_computed": tftypes.NewValue(tftypes.String, nil),
472+
"test_required": tftypes.NewValue(tftypes.String, "test-config-value"),
473+
}),
474+
PriorState: &testEmptyDynamicValue,
475+
PriorIdentity: &tfprotov6.ResourceIdentityData{
476+
IdentityData: testNewDynamicValue(t, testIdentitySchemaType, map[string]tftypes.Value{
477+
"test_id": tftypes.NewValue(tftypes.String, "id-123"),
478+
}),
479+
},
480+
TypeName: "test_resource",
481+
},
482+
expectedResponse: &tfprotov6.PlanResourceChangeResponse{
483+
PlannedState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{
484+
"test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue),
485+
"test_required": tftypes.NewValue(tftypes.String, "test-config-value"),
486+
}),
487+
PlannedIdentity: &tfprotov6.ResourceIdentityData{
488+
IdentityData: testNewDynamicValue(t, testIdentitySchemaType, map[string]tftypes.Value{
489+
"test_id": tftypes.NewValue(tftypes.String, "new-id-123"),
490+
}),
491+
},
492+
},
493+
},
347494
"create-response-requiresreplace": {
348495
server: &Server{
349496
FrameworkServer: fwserver.Server{

internal/proto6server/server_readresource_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ func TestServerReadResource(t *testing.T) {
456456
request: &tfprotov6.ReadResourceRequest{
457457
CurrentState: testEmptyDynamicValue,
458458
CurrentIdentity: &tfprotov6.ResourceIdentityData{
459-
IdentityData: testNewIdentityDynamicValue,
459+
IdentityData: testCurrentIdentityValue,
460460
},
461461
TypeName: "test_resource",
462462
},

internal/toproto6/planresourcechange.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ func PlanResourceChangeResponse(ctx context.Context, fw *fwserver.PlanResourceCh
2929
proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...)
3030
proto6.PlannedState = plannedState
3131

32+
plannedIdentity, diags := ResourceIdentity(ctx, fw.PlannedIdentity)
33+
34+
proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...)
35+
proto6.PlannedIdentity = plannedIdentity
36+
3237
requiresReplace, diags := totftypes.AttributePaths(ctx, fw.RequiresReplace)
3338

3439
proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...)

0 commit comments

Comments
 (0)