From 981668ee4555ef7f47059a1ef3b80fa46a7a6d6d Mon Sep 17 00:00:00 2001 From: Douglas Harcourt Parsons Date: Mon, 28 Apr 2025 14:04:59 +0100 Subject: [PATCH] Support updating of existing alias resources Closes #249 --- client/alias.go | 27 +++++++++-------- vercel/resource_alias.go | 57 +++++++++++++++++++++++++++-------- vercel/resource_alias_test.go | 34 +++++++++++++++++++++ 3 files changed, 92 insertions(+), 26 deletions(-) diff --git a/client/alias.go b/client/alias.go index 97705f0c..5f5c133a 100644 --- a/client/alias.go +++ b/client/alias.go @@ -7,23 +7,24 @@ import ( "github.com/hashicorp/terraform-plugin-log/tflog" ) -// CreateAliasRequest defines the request the Vercel API expects in order to create an alias. -type CreateAliasRequest struct { - Alias string `json:"alias"` +// UpsertAliasRequest defines the request the Vercel API expects in order to create an alias. +type UpsertAliasRequest struct { + Alias string `json:"alias"` + DeploymentID string `json:"-"` + TeamID string `json:"-"` } // The create Alias endpoint does not return the full AliasResponse, only the UID and Alias. type createAliasResponse struct { - UID string `json:"uid"` - Alias string `json:"alias"` - TeamID string `json:"-"` + UID string `json:"uid"` + Alias string `json:"alias"` } -// CreateAlias creates an alias within Vercel. -func (c *Client) CreateAlias(ctx context.Context, request CreateAliasRequest, deploymentID string, teamID string) (r AliasResponse, err error) { - url := fmt.Sprintf("%s/v2/deployments/%s/aliases", c.baseURL, deploymentID) - if c.teamID(teamID) != "" { - url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) +// UpsertAlias creates an alias within Vercel. +func (c *Client) UpsertAlias(ctx context.Context, request UpsertAliasRequest) (r AliasResponse, err error) { + url := fmt.Sprintf("%s/v2/deployments/%s/aliases", c.baseURL, request.DeploymentID) + if c.teamID(request.TeamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID)) } payload := string(mustMarshal(request)) @@ -45,8 +46,8 @@ func (c *Client) CreateAlias(ctx context.Context, request CreateAliasRequest, de return AliasResponse{ UID: aliasResponse.UID, Alias: aliasResponse.Alias, - DeploymentID: deploymentID, - TeamID: c.teamID(teamID), + DeploymentID: request.DeploymentID, + TeamID: c.teamID(request.TeamID), }, nil } diff --git a/vercel/resource_alias.go b/vercel/resource_alias.go index fae24098..6293254d 100644 --- a/vercel/resource_alias.go +++ b/vercel/resource_alias.go @@ -58,14 +58,12 @@ Provides an Alias resource. An Alias allows a ` + "`vercel_deployment` to be accessed through a different URL.", Attributes: map[string]schema.Attribute{ "alias": schema.StringAttribute{ - Description: "The Alias we want to assign to the deployment defined in the URL.", - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + Description: "The Alias we want to assign to the deployment defined in the URL.", + Required: true, }, "deployment_id": schema.StringAttribute{ - Description: "The id of the Deployment the Alias should be associated with.", - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + Description: "The id of the Deployment the Alias should be associated with.", + Required: true, }, "team_id": schema.StringAttribute{ Optional: true, @@ -110,9 +108,12 @@ func (r *aliasResource) Create(ctx context.Context, req resource.CreateRequest, return } - out, err := r.client.CreateAlias(ctx, client.CreateAliasRequest{ - Alias: plan.Alias.ValueString(), - }, plan.DeploymentID.ValueString(), plan.TeamID.ValueString()) + // Use the update function to create and update aliases, as the API is an upsert + out, err := r.client.UpsertAlias(ctx, client.UpsertAliasRequest{ + Alias: plan.Alias.ValueString(), + DeploymentID: plan.DeploymentID.ValueString(), + TeamID: plan.TeamID.ValueString(), + }) if err != nil { resp.Diagnostics.AddError( "Error creating alias", @@ -176,11 +177,41 @@ func (r *aliasResource) Read(ctx context.Context, req resource.ReadRequest, resp } // Update updates the Alias state. +// The Vercel API for creating an alias is an upsert. We can simply call the create method again to update. func (r *aliasResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - resp.Diagnostics.AddError( - "Updating an Alias is not supported", - "Updating an Alias is not supported", - ) + var plan Alias + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Use the update function to create and update aliases, as the API is an upsert + out, err := r.client.UpsertAlias(ctx, client.UpsertAliasRequest{ + Alias: plan.Alias.ValueString(), + DeploymentID: plan.DeploymentID.ValueString(), + TeamID: plan.TeamID.ValueString(), + }) + if err != nil { + resp.Diagnostics.AddError( + "Error updating alias", + fmt.Sprintf("Could not update alias %s, unexpected error: %s", plan.Alias.ValueString(), err.Error()), + ) + return + } + + result := convertResponseToAlias(out, plan) + tflog.Info(ctx, "updated alias", map[string]any{ + "team_id": plan.TeamID.ValueString(), + "deployment_id": plan.DeploymentID.ValueString(), + "alias_id": result.ID.ValueString(), + }) + + diags = resp.State.Set(ctx, result) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } } // Delete deletes an Alias. diff --git a/vercel/resource_alias_test.go b/vercel/resource_alias_test.go index b6bd159f..332032ba 100644 --- a/vercel/resource_alias_test.go +++ b/vercel/resource_alias_test.go @@ -57,6 +57,15 @@ func TestAcc_AliasResource(t *testing.T) { resource.TestCheckResourceAttrSet("vercel_alias.test", "deployment_id"), ), }, + { + Config: testAccAliasResourceConfigUpdated(name, teamIDConfig()), + Check: resource.ComposeAggregateTestCheckFunc( + testCheckAliasExists(testTeam(), fmt.Sprintf("test-acc-%s.vercel.app", name)), + resource.TestCheckResourceAttr("vercel_alias.test", "alias", fmt.Sprintf("test-acc-%s.vercel.app", name)), + resource.TestCheckResourceAttrSet("vercel_alias.test", "id"), + resource.TestCheckResourceAttrSet("vercel_alias.test", "deployment_id"), + ), + }, }, }) } @@ -85,3 +94,28 @@ resource "vercel_alias" "test" { } `, name, team, testGithubRepo()) } + +func testAccAliasResourceConfigUpdated(name, team string) string { + return fmt.Sprintf(` +resource "vercel_project" "test" { + name = "test-acc-%[1]s" + %[2]s + git_repository = { + type = "github" + repo = "%[3]s" + } +} + +resource "vercel_deployment" "test_two" { + project_id = vercel_project.test.id + ref = "main" + %[2]s +} + +resource "vercel_alias" "test" { + alias = "test-acc-%[1]s.vercel.app" + deployment_id = vercel_deployment.test_two.id + %[2]s +} +`, name, team, testGithubRepo()) +}