From 99c40263c9fdaefefa0ef9524bcbd17be66e6a0b Mon Sep 17 00:00:00 2001 From: Douglas Harcourt Parsons Date: Mon, 15 Apr 2024 15:17:49 +0100 Subject: [PATCH] Add support for updating Edge Config Fix some update-churn issues due to not using state for unknown IDs --- client/edge_config.go | 26 ++++++++++++++ docs/resources/edge_config.md | 2 +- vercel/resource_alias.go | 24 +++---------- vercel/resource_dns_record.go | 3 -- vercel/resource_edge_config.go | 54 +++++++++++++++++++++++----- vercel/resource_edge_config_test.go | 17 +++++++++ vercel/resource_edge_config_token.go | 5 +-- 7 files changed, 97 insertions(+), 34 deletions(-) diff --git a/client/edge_config.go b/client/edge_config.go index 743d1b94..4e61f2c6 100644 --- a/client/edge_config.go +++ b/client/edge_config.go @@ -53,6 +53,32 @@ func (c *Client) GetEdgeConfig(ctx context.Context, id, teamID string) (e EdgeCo return e, err } +type UpdateEdgeConfigRequest struct { + Slug string `json:"slug"` + TeamID string `json:"-"` + ID string `json:"-"` +} + +func (c *Client) UpdateEdgeConfig(ctx context.Context, request UpdateEdgeConfigRequest) (e EdgeConfig, err error) { + url := fmt.Sprintf("%s/v1/edge-config/%s", c.baseURL, request.ID) + if c.teamID(request.TeamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID)) + } + + payload := string(mustMarshal(request)) + tflog.Trace(ctx, "updating edge config", map[string]interface{}{ + "url": url, + "payload": payload, + }) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "PUT", + url: url, + body: payload, + }, &e) + return e, err +} + func (c *Client) DeleteEdgeConfig(ctx context.Context, id, teamID string) error { url := fmt.Sprintf("%s/v1/edge-config/%s", c.baseURL, id) if c.teamID(teamID) != "" { diff --git a/docs/resources/edge_config.md b/docs/resources/edge_config.md index e5fdf6a6..388b2501 100644 --- a/docs/resources/edge_config.md +++ b/docs/resources/edge_config.md @@ -50,7 +50,7 @@ resource "vercel_project_environment_variable" "example" { ### Read-Only -- `id` (String) The ID of this resource. +- `id` (String) The ID of the Edge Config. ## Import diff --git a/vercel/resource_alias.go b/vercel/resource_alias.go index 76594fee..ec1e95a6 100644 --- a/vercel/resource_alias.go +++ b/vercel/resource_alias.go @@ -70,7 +70,7 @@ An Alias allows a ` + "`vercel_deployment` to be accessed through a different UR Optional: true, Computed: true, Description: "The ID of the team the Alias and Deployment exist under. Required when configuring a team resource if a default team has not been set in the provider.", - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplaceIfConfigured()}, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplaceIfConfigured(), stringplanmodifier.UseStateForUnknown()}, }, "id": schema.StringAttribute{ Computed: true, @@ -156,24 +156,10 @@ func (r *aliasResource) Read(ctx context.Context, req resource.ReadRequest, resp // Update updates the Alias state. func (r *aliasResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan Alias - diags := req.Plan.Get(ctx, &plan) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - resp.Diagnostics.AddError( - "Error getting alias plan", - "Error getting alias plan", - ) - return - } - - var state Alias - diags = req.State.Get(ctx, &state) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - + resp.Diagnostics.AddError( + "Updating an Alias is not supported", + "Updating an Alias is not supported", + ) } // Delete deletes an Alias. diff --git a/vercel/resource_dns_record.go b/vercel/resource_dns_record.go index 03954500..fba01dad 100644 --- a/vercel/resource_dns_record.go +++ b/vercel/resource_dns_record.go @@ -2,7 +2,6 @@ package vercel import ( "context" - "encoding/json" "fmt" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -314,8 +313,6 @@ func (r *dnsRecordResource) Update(ctx context.Context, req resource.UpdateReque return } - thing, _ := json.Marshal(plan.toUpdateRequest()) - tflog.Error(ctx, "UPDATE REQUEST", map[string]interface{}{"request": string(thing)}) out, err := r.client.UpdateDNSRecord( ctx, plan.TeamID.ValueString(), diff --git a/vercel/resource_edge_config.go b/vercel/resource_edge_config.go index e0b9a906..e77fe5e7 100644 --- a/vercel/resource_edge_config.go +++ b/vercel/resource_edge_config.go @@ -61,9 +61,8 @@ Provides an Edge Config resource. An Edge Config is a global data store that enables experimentation with feature flags, A/B testing, critical redirects, and more.`, Attributes: map[string]schema.Attribute{ "name": schema.StringAttribute{ - Description: "The name/slug of the Edge Config.", - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + Description: "The name/slug of the Edge Config.", + Required: true, Validators: []validator.String{ stringRegex( regexp.MustCompile(`^[a-z0-9\_\-]{0,32}$`), @@ -75,10 +74,12 @@ An Edge Config is a global data store that enables experimentation with feature Optional: true, Computed: true, Description: "The ID of the team the Edge Config should exist under. Required when configuring a team resource if a default team has not been set in the provider.", - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplaceIfConfigured()}, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplaceIfConfigured(), stringplanmodifier.UseStateForUnknown()}, }, "id": schema.StringAttribute{ - Computed: true, + Description: "The ID of the Edge Config.", + Computed: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, }, }, } @@ -175,10 +176,45 @@ func (r *edgeConfigResource) Read(ctx context.Context, req resource.ReadRequest, // Update does nothing. func (r *edgeConfigResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - resp.Diagnostics.AddError( - "Updating an Edge Config is not supported", - "Updating an Edge Config is not supported", - ) + var plan EdgeConfig + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + out, err := r.client.UpdateEdgeConfig(ctx, client.UpdateEdgeConfigRequest{ + Slug: plan.Name.ValueString(), + TeamID: plan.TeamID.ValueString(), + ID: plan.ID.ValueString(), + }) + if client.NotFound(err) { + resp.State.RemoveResource(ctx) + return + } + if err != nil { + resp.Diagnostics.AddError( + "Error updating Edge Config", + fmt.Sprintf("Could not update Edge Config %s %s, unexpected error: %s", + plan.TeamID.ValueString(), + plan.ID.ValueString(), + err, + ), + ) + return + } + + result := responseToEdgeConfig(out) + tflog.Trace(ctx, "update edge config", map[string]interface{}{ + "team_id": result.TeamID.ValueString(), + "edge_config_id": result.ID.ValueString(), + }) + + diags = resp.State.Set(ctx, result) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } } // Delete deletes an Edge Config. diff --git a/vercel/resource_edge_config_test.go b/vercel/resource_edge_config_test.go index 861b2941..eeaf2bb1 100644 --- a/vercel/resource_edge_config_test.go +++ b/vercel/resource_edge_config_test.go @@ -65,6 +65,14 @@ func TestAcc_EdgeConfigResource(t *testing.T) { resource.TestCheckResourceAttrSet("vercel_edge_config.test", "id"), ), }, + { + Config: testAccResourceEdgeConfigUpdated(name, teamIDConfig()), + Check: resource.ComposeAggregateTestCheckFunc( + testCheckEdgeConfigExists(testTeam(), "vercel_edge_config.test"), + resource.TestCheckResourceAttr("vercel_edge_config.test", "name", fmt.Sprintf("%s-updated", name)), + resource.TestCheckResourceAttrSet("vercel_edge_config.test", "id"), + ), + }, }, }) } @@ -77,3 +85,12 @@ resource "vercel_edge_config" "test" { } `, name, team) } + +func testAccResourceEdgeConfigUpdated(name, team string) string { + return fmt.Sprintf(` +resource "vercel_edge_config" "test" { + name = "%[1]s-updated" + %[2]s +} +`, name, team) +} diff --git a/vercel/resource_edge_config_token.go b/vercel/resource_edge_config_token.go index 904e8e61..01f89a11 100644 --- a/vercel/resource_edge_config_token.go +++ b/vercel/resource_edge_config_token.go @@ -79,10 +79,11 @@ An Edge Config token is used to authenticate against an Edge Config's endpoint. Optional: true, Computed: true, Description: "The ID of the team the Edge Config should exist under. Required when configuring a team resource if a default team has not been set in the provider.", - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplaceIfConfigured()}, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplaceIfConfigured(), stringplanmodifier.UseStateForUnknown()}, }, "id": schema.StringAttribute{ - Computed: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplaceIfConfigured(), stringplanmodifier.UseStateForUnknown()}, + Computed: true, }, "token": schema.StringAttribute{ Description: "A read access token used for authenticating against the Edge Config's endpoint for high volume, low-latency requests.",