Skip to content

Commit b3fd40e

Browse files
authored
Fix usage of resource config on vercel_project resource. (#286)
- properly support null or unknown values for this field. - remove false defaults of `null` for the fields. - Fix validation allowing `0` or `901` as timeout values. - Remove a lot of unnecessary code to suppress diffs. - Allow setting resourceConfig on project creation.
1 parent 4d27076 commit b3fd40e

File tree

4 files changed

+64
-78
lines changed

4 files changed

+64
-78
lines changed

client/project.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ type Security struct {
218218
}
219219

220220
type ResourceConfig struct {
221-
FunctionDefaultMemoryType string `json:"functionDefaultMemoryType,omitempty"`
222-
FunctionDefaultTimeout int64 `json:"functionDefaultTimeout,omitempty"`
221+
FunctionDefaultMemoryType *string `json:"functionDefaultMemoryType,omitempty"`
222+
FunctionDefaultTimeout *int64 `json:"functionDefaultTimeout,omitempty"`
223223
}
224224

225225
// GetProject retrieves information about an existing project from Vercel.

vercel/data_source_project.go

+1-8
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ type ProjectDataSource struct {
414414
DirectoryListing types.Bool `tfsdk:"directory_listing"`
415415
EnableAffectedProjectsDeployments types.Bool `tfsdk:"enable_affected_projects_deployments"`
416416
SkewProtection types.String `tfsdk:"skew_protection"`
417-
ResourceConfig *ResourceConfig `tfsdk:"resource_config"`
417+
ResourceConfig types.Object `tfsdk:"resource_config"`
418418
NodeVersion types.String `tfsdk:"node_version"`
419419
}
420420

@@ -431,13 +431,6 @@ func convertResponseToProjectDataSource(ctx context.Context, response client.Pro
431431
})
432432
}
433433

434-
if response.ResourceConfig != nil {
435-
plan.ResourceConfig = &ResourceConfig{
436-
FunctionDefaultMemoryType: types.StringValue(response.ResourceConfig.FunctionDefaultMemoryType),
437-
FunctionDefaultTimeout: types.Int64Value(response.ResourceConfig.FunctionDefaultTimeout),
438-
}
439-
}
440-
441434
project, err := convertResponseToProject(ctx, response, plan, environmentVariables)
442435
if err != nil {
443436
return ProjectDataSource{}, err

vercel/resource_project.go

+55-68
Original file line numberDiff line numberDiff line change
@@ -518,9 +518,10 @@ At this time you cannot use a Vercel Project resource with in-line ` + "`environ
518518
},
519519
},
520520
"resource_config": schema.SingleNestedAttribute{
521-
Description: "Resource Configuration for the project.",
522-
Optional: true,
523-
Computed: true,
521+
Description: "Resource Configuration for the project.",
522+
Optional: true,
523+
Computed: true,
524+
PlanModifiers: []planmodifier.Object{objectplanmodifier.UseStateForUnknown()},
524525
Attributes: map[string]schema.Attribute{
525526
// This is actually "function_default_memory_type" in the API schema, but for better convention, we use "cpu" and do translation in the provider.
526527
"function_default_cpu_type": schema.StringAttribute{
@@ -530,64 +531,24 @@ At this time you cannot use a Vercel Project resource with in-line ` + "`environ
530531
Validators: []validator.String{
531532
stringvalidator.OneOf("standard_legacy", "standard", "performance"),
532533
},
533-
PlanModifiers: []planmodifier.String{SuppressDiffIfNotConfigured(), stringplanmodifier.UseStateForUnknown()},
534+
PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()},
534535
},
535536
"function_default_timeout": schema.Int64Attribute{
536537
Description: "The default timeout for Serverless Functions.",
537538
Optional: true,
538539
Computed: true,
539540
Validators: []validator.Int64{
540-
int64validator.AtLeast(0),
541-
int64validator.AtMost(901),
541+
int64validator.AtLeast(1),
542+
int64validator.AtMost(900),
542543
},
543-
PlanModifiers: []planmodifier.Int64{SuppressDiffIfNotConfigured(), int64planmodifier.UseStateForUnknown()},
544+
PlanModifiers: []planmodifier.Int64{int64planmodifier.UseStateForUnknown()},
544545
},
545546
},
546-
Default: objectdefault.StaticValue(types.ObjectValueMust(
547-
map[string]attr.Type{
548-
"function_default_cpu_type": types.StringType,
549-
"function_default_timeout": types.Int64Type,
550-
},
551-
map[string]attr.Value{
552-
"function_default_cpu_type": types.StringNull(),
553-
"function_default_timeout": types.Int64Null(),
554-
},
555-
)),
556547
},
557548
},
558549
}
559550
}
560551

561-
type suppressDiffIfNotConfigured struct{}
562-
563-
func SuppressDiffIfNotConfigured() *suppressDiffIfNotConfigured {
564-
return &suppressDiffIfNotConfigured{}
565-
}
566-
567-
func (m *suppressDiffIfNotConfigured) Description(ctx context.Context) string {
568-
return "Ensures that the diff is suppressed if the attribute is not explicitly configured by users."
569-
}
570-
571-
func (m *suppressDiffIfNotConfigured) MarkdownDescription(ctx context.Context) string {
572-
return m.Description(ctx)
573-
}
574-
575-
func (m *suppressDiffIfNotConfigured) PlanModifyString(ctx context.Context, req planmodifier.StringRequest, resp *planmodifier.StringResponse) {
576-
if req.ConfigValue.IsNull() {
577-
resp.PlanValue = req.StateValue
578-
resp.RequiresReplace = false
579-
return
580-
}
581-
}
582-
583-
func (m *suppressDiffIfNotConfigured) PlanModifyInt64(ctx context.Context, req planmodifier.Int64Request, resp *planmodifier.Int64Response) {
584-
if req.ConfigValue.IsNull() {
585-
resp.PlanValue = req.StateValue
586-
resp.RequiresReplace = false
587-
return
588-
}
589-
}
590-
591552
// Project reflects the state terraform stores internally for a project.
592553
type Project struct {
593554
BuildCommand types.String `tfsdk:"build_command"`
@@ -624,7 +585,7 @@ type Project struct {
624585
DirectoryListing types.Bool `tfsdk:"directory_listing"`
625586
EnableAffectedProjectsDeployments types.Bool `tfsdk:"enable_affected_projects_deployments"`
626587
SkewProtection types.String `tfsdk:"skew_protection"`
627-
ResourceConfig *ResourceConfig `tfsdk:"resource_config"`
588+
ResourceConfig types.Object `tfsdk:"resource_config"`
628589
}
629590

630591
type GitComments struct {
@@ -659,7 +620,6 @@ func (p Project) RequiresUpdateAfterCreation() bool {
659620
!p.PrioritiseProductionBuilds.IsNull() ||
660621
!p.DirectoryListing.IsNull() ||
661622
!p.SkewProtection.IsNull() ||
662-
p.ResourceConfig != nil ||
663623
!p.NodeVersion.IsNull()
664624

665625
}
@@ -723,6 +683,14 @@ func parseEnvironment(ctx context.Context, vars []EnvironmentItem) (out []client
723683

724684
func (p *Project) toCreateProjectRequest(ctx context.Context, envs []EnvironmentItem) (req client.CreateProjectRequest, diags diag.Diagnostics) {
725685
clientEnvs, diags := parseEnvironment(ctx, envs)
686+
if diags.HasError() {
687+
return req, diags
688+
}
689+
resourceConfig, diags := p.resourceConfig(ctx)
690+
if diags.HasError() {
691+
return req, diags
692+
}
693+
726694
return client.CreateProjectRequest{
727695
BuildCommand: p.BuildCommand.ValueStringPointer(),
728696
CommandForIgnoringBuildStep: p.IgnoreCommand.ValueStringPointer(),
@@ -738,6 +706,7 @@ func (p *Project) toCreateProjectRequest(ctx context.Context, envs []Environment
738706
PublicSource: p.PublicSource.ValueBoolPointer(),
739707
RootDirectory: p.RootDirectory.ValueStringPointer(),
740708
ServerlessFunctionRegion: p.ServerlessFunctionRegion.ValueString(),
709+
ResourceConfig: resourceConfig.toClientResourceConfig(),
741710
}, diags
742711
}
743712

@@ -768,6 +737,10 @@ func (p *Project) toUpdateProjectRequest(ctx context.Context, oldName string) (r
768737
if diags.HasError() {
769738
return req, diags
770739
}
740+
resourceConfig, diags := p.resourceConfig(ctx)
741+
if diags.HasError() {
742+
return req, diags
743+
}
771744
return client.UpdateProjectRequest{
772745
BuildCommand: p.BuildCommand.ValueStringPointer(),
773746
CommandForIgnoringBuildStep: p.IgnoreCommand.ValueStringPointer(),
@@ -796,7 +769,7 @@ func (p *Project) toUpdateProjectRequest(ctx context.Context, oldName string) (r
796769
DirectoryListing: p.DirectoryListing.ValueBool(),
797770
SkewProtectionMaxAge: toSkewProtectionAge(p.SkewProtection),
798771
GitComments: gc.toUpdateProjectRequest(),
799-
ResourceConfig: p.ResourceConfig.toUpdateProjectRequest(),
772+
ResourceConfig: resourceConfig.toClientResourceConfig(),
800773
NodeVersion: p.NodeVersion.ValueString(),
801774
}, nil
802775
}
@@ -1026,22 +999,37 @@ func (o *OIDCTokenConfig) toUpdateProjectRequest() *client.OIDCTokenConfig {
1026999
}
10271000
}
10281001

1002+
var resourceConfigAttrType = types.ObjectType{
1003+
AttrTypes: map[string]attr.Type{
1004+
"function_default_cpu_type": types.StringType,
1005+
"function_default_timeout": types.Int64Type,
1006+
},
1007+
}
1008+
10291009
type ResourceConfig struct {
1030-
FunctionDefaultMemoryType types.String `tfsdk:"function_default_cpu_type"`
1031-
FunctionDefaultTimeout types.Int64 `tfsdk:"function_default_timeout"`
1010+
FunctionDefaultCPUType types.String `tfsdk:"function_default_cpu_type"`
1011+
FunctionDefaultTimeout types.Int64 `tfsdk:"function_default_timeout"`
1012+
}
1013+
1014+
func (p *Project) resourceConfig(ctx context.Context) (rc *ResourceConfig, diags diag.Diagnostics) {
1015+
diags = p.ResourceConfig.As(ctx, &rc, basetypes.ObjectAsOptions{
1016+
UnhandledNullAsEmpty: true,
1017+
UnhandledUnknownAsEmpty: true,
1018+
})
1019+
return rc, diags
10321020
}
10331021

1034-
func (r *ResourceConfig) toUpdateProjectRequest() *client.ResourceConfig {
1035-
resourceConfig := &client.ResourceConfig{}
1022+
func (r *ResourceConfig) toClientResourceConfig() *client.ResourceConfig {
10361023
if r == nil {
1037-
return resourceConfig
1024+
return nil
10381025
}
1026+
var resourceConfig = &client.ResourceConfig{}
10391027

1040-
if !r.FunctionDefaultMemoryType.IsNull() {
1041-
resourceConfig.FunctionDefaultMemoryType = r.FunctionDefaultMemoryType.ValueString()
1028+
if !r.FunctionDefaultCPUType.IsUnknown() {
1029+
resourceConfig.FunctionDefaultMemoryType = r.FunctionDefaultCPUType.ValueStringPointer()
10421030
}
1043-
if !r.FunctionDefaultTimeout.IsNull() {
1044-
resourceConfig.FunctionDefaultTimeout = r.FunctionDefaultTimeout.ValueInt64()
1031+
if !r.FunctionDefaultTimeout.IsUnknown() {
1032+
resourceConfig.FunctionDefaultTimeout = r.FunctionDefaultTimeout.ValueInt64Pointer()
10451033
}
10461034
return resourceConfig
10471035
}
@@ -1260,14 +1248,12 @@ func convertResponseToProject(ctx context.Context, response client.ProjectRespon
12601248
oidcTokenConfig.IssuerMode = types.StringValue(response.OIDCTokenConfig.IssuerMode)
12611249
}
12621250

1263-
resourceConfig := &ResourceConfig{}
1264-
if response.ResourceConfig != nil && plan.ResourceConfig != nil {
1265-
if !plan.ResourceConfig.FunctionDefaultMemoryType.IsNull() {
1266-
resourceConfig.FunctionDefaultMemoryType = types.StringValue(response.ResourceConfig.FunctionDefaultMemoryType)
1267-
}
1268-
if !plan.ResourceConfig.FunctionDefaultTimeout.IsNull() {
1269-
resourceConfig.FunctionDefaultTimeout = types.Int64Value(response.ResourceConfig.FunctionDefaultTimeout)
1270-
}
1251+
resourceConfig := types.ObjectNull(resourceConfigAttrType.AttrTypes)
1252+
if response.ResourceConfig != nil {
1253+
resourceConfig = types.ObjectValueMust(resourceConfigAttrType.AttrTypes, map[string]attr.Value{
1254+
"function_default_cpu_type": types.StringPointerValue(response.ResourceConfig.FunctionDefaultMemoryType),
1255+
"function_default_timeout": types.Int64PointerValue(response.ResourceConfig.FunctionDefaultTimeout),
1256+
})
12711257
}
12721258

12731259
var oal *OptionsAllowlist
@@ -1531,9 +1517,10 @@ func (r *projectResource) Create(ctx context.Context, req resource.CreateRequest
15311517
)
15321518
return
15331519
}
1534-
tflog.Info(ctx, "created project", map[string]any{
1520+
tflog.Error(ctx, "created project", map[string]any{
15351521
"team_id": result.TeamID.ValueString(),
15361522
"project_id": result.ID.ValueString(),
1523+
"project": result,
15371524
})
15381525
diags = resp.State.Set(ctx, result)
15391526
resp.Diagnostics.Append(diags...)

vercel/resource_project_test.go

+6
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ func TestAcc_Project(t *testing.T) {
8282
resource.TestCheckResourceAttr("vercel_project.test", "skew_protection", "7 days"),
8383
resource.TestCheckResourceAttr("vercel_project.test", "oidc_token_config.enabled", "true"),
8484
resource.TestCheckResourceAttr("vercel_project.test", "oidc_token_config.issuer_mode", "team"),
85+
resource.TestCheckResourceAttr("vercel_project.test", "resource_config.function_default_cpu_type", "standard"),
86+
resource.TestCheckResourceAttr("vercel_project.test", "resource_config.function_default_timeout", "60"),
8587
),
8688
},
8789
// Update testing
@@ -923,6 +925,10 @@ resource "vercel_project" "test" {
923925
enabled = true
924926
issuer_mode = "team"
925927
}
928+
resource_config = {
929+
function_default_cpu_type = "standard"
930+
function_default_timeout = 60
931+
}
926932
environment = [
927933
{
928934
key = "foo"

0 commit comments

Comments
 (0)