这是indexloc提供的服务,不要输入任何密码
Skip to content

Aliases: Add aliases deployment #32

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
terraform-provider-vercel
.task/
Dist/
.DS_Store
44 changes: 44 additions & 0 deletions client/alias_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package client

import (
"context"
"fmt"
"net/http"
"strings"

"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"`
}

// 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 teamID != "" {
url = fmt.Sprintf("%s?teamId=%s", url, teamID)
}
payload := string(mustMarshal(request))
req, err := http.NewRequestWithContext(
ctx,
"POST",
url,
strings.NewReader(payload),
)
if err != nil {
return r, err
}

tflog.Trace(ctx, "creating alias", map[string]interface{}{
"url": url,
"payload": payload,
})
err = c.doRequest(req, &r)
if err != nil {
return r, err
}

return r, nil
}
44 changes: 44 additions & 0 deletions client/alias_delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package client

import (
"context"
"fmt"
"net/http"

"github.com/hashicorp/terraform-plugin-log/tflog"
)

// DeleteAliasResponse defines the response the Vercel API returns when an alias is deleted.
type DeleteAliasResponse struct {
Status string `json:"status"`
}

// DeleteAlias deletes an alias within Vercel.
func (c *Client) DeleteAlias(ctx context.Context, aliasUID string, teamID string) (r DeleteAliasResponse, err error) {
url := fmt.Sprintf("%s/v2/aliases/%s", c.baseURL, aliasUID)
req, err := http.NewRequest(
"DELETE",
url,
nil,
)
if err != nil {
return r, err
}

// Add query parameters
q := req.URL.Query()
if teamID != "" {
q.Add("teamId", teamID)
}
req.URL.RawQuery = q.Encode()
Comment on lines +28 to +33
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's interesting that the delete endpoint does the teamId parameter differently to the create/get endpoints. 🤔

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi. I think this file was copied/modified from deployment_delete.go which also uses req.URL.Query to add the parameter. So we picked the 1 inconsistent file to use as our starting point. Every other file uses fmt.Sprintf to do the same thing.

terraform-provider-vercel % grep teamId client/* | grep fmt.Sprintf
client/alias_create.go:         url = fmt.Sprintf("%s?teamId=%s", url, teamID)
client/alias_get.go:            url = fmt.Sprintf("%s?teamId=%s", url, teamID)
client/deployment_create.go:            url = fmt.Sprintf("%s&teamId=%s", url, teamID)
client/deployment_get.go:               url = fmt.Sprintf("%s?teamId=%s", url, teamID)
client/environment_variable_upsert.go:          url = fmt.Sprintf("%s?teamId=%s", url, teamID)
client/environment_variables_delete.go:         url = fmt.Sprintf("%s?teamId=%s", url, teamID)
client/environment_variables_get.go:            url = fmt.Sprintf("%s&teamId=%s", url, teamID)
client/project_create.go:               url = fmt.Sprintf("%s?teamId=%s", url, teamID)
client/project_delete.go:               url = fmt.Sprintf("%s?teamId=%s", url, teamID)
client/project_domain_create.go:                url = fmt.Sprintf("%s?teamId=%s", url, teamID)
client/project_domain_delete.go:                url = fmt.Sprintf("%s?teamId=%s", url, teamID)
client/project_domain_get.go:           url = fmt.Sprintf("%s?teamId=%s", url, teamID)
client/project_domain_update.go:                url = fmt.Sprintf("%s?teamId=%s", url, teamID)
client/project_get.go:          url = fmt.Sprintf("%s?teamId=%s", url, teamID)
client/project_list.go:         url = fmt.Sprintf("%s&teamId=%s", url, teamID)
client/project_update.go:               url = fmt.Sprintf("%s?teamId=%s", url, teamID)
client/team_get.go:             url = fmt.Sprintf("%s?teamId=%s", url, teamID)
terraform-provider-vercel % grep teamId client/* | grep -v fmt.Sprintf
client/alias_delete.go:         q.Add("teamId", teamID)
client/deployment_delete.go:            q.Add("teamId", teamID)


tflog.Trace(ctx, "deleting alias", map[string]interface{}{
"url": url,
})
err = c.doRequest(req, &r)
if err != nil {
return r, fmt.Errorf("url: %s, err: %s", url, err)
}

return r, nil
}
41 changes: 41 additions & 0 deletions client/alias_get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package client

import (
"context"
"fmt"
"net/http"

"github.com/hashicorp/terraform-plugin-log/tflog"
)

// AliasResponse defines the response the Vercel API returns for an alias.
type AliasResponse struct {
UID string `json:"uid"`
Alias string `json:"alias"`
DeploymentId string `json:"deploymentId"`
}

// GetAlias retrieves information about an existing alias from vercel.
func (c *Client) GetAlias(ctx context.Context, aliasID, teamID string) (r AliasResponse, err error) {
url := fmt.Sprintf("%s/v3/aliases/%s", c.baseURL, aliasID)
if teamID != "" {
url = fmt.Sprintf("%s?teamId=%s", url, teamID)
}
req, err := http.NewRequestWithContext(
ctx,
"GET",
url,
nil,
)
if err != nil {
return r, fmt.Errorf("creating request: %s", err)
}
tflog.Trace(ctx, "getting alias", map[string]interface{}{
"url": url,
})
err = c.doRequest(req, &r)
if err != nil {
return r, err
}
return r, nil
}
1 change: 1 addition & 0 deletions client/deployment_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type gitSource struct {
Owner string `json:"owner,omitempty"`
Slug string `json:"slug,omitempty"`
Ref string `json:"ref"`
SHA string `json:"sha"`
}

// CreateDeploymentRequest defines the request the Vercel API expects in order to create a deployment.
Expand Down
5 changes: 5 additions & 0 deletions client/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package client

import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
Expand Down Expand Up @@ -57,6 +58,10 @@ func (c *Client) doRequest(req *http.Request, v interface{}) error {
errorResponse.RawMessage = responseBody
return errorResponse
}
if resp.StatusCode == 204 {
//204 means "no content", we are treating it as an error
return errors.New("no content")
}
Comment on lines +61 to +64
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we can do this as other API endpoints definitely return a 204 for certain success criteria. I'm currently looking into our API implementation for the GET alias endpoint though to see if it's something I can change though. It may block this PR in the meantime though, so apologies for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we were discussing this 204 thing internally, It feels quite odd (almost like an API bug) to respond 204 for a not found element. I did not have a proper look at the rest of the endpoints but totally agree with you, if 204 != 404 then we should put there a bit more logic.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe here, it behaves as a 404. I think it's a mistake in our API though. I think it's something I'd be able to correct in the API though, so we get a 404, as this makes much more sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That'd be ideal.


if v == nil {
return nil
Expand Down
34 changes: 34 additions & 0 deletions docs/resources/alias.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "vercel_alias Resource - terraform-provider-vercel"
subcategory: ""
description: |-
Provides an alias resource.
An alias allows a deployment to be accessed through a different URL.
---

# vercel_alias (Resource)

Provides an alias resource.

An alias allows a deployment to be accessed through a different URL.



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `alias` (String) The alias to be set on the deployment. It will become the subdomain of the Vercel project top level domain
- `deployment_id` (String) The deployment id to alias

### Optional

- `team_id` (String) The team or scope id

### Read-Only

- `uid` (String)


95 changes: 95 additions & 0 deletions vercel/data_source_alias.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package vercel

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
)

type dataSourceAliasType struct{}

// GetSchema returns the schema information for an alias data source
func (r dataSourceAliasType) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) {
return tfsdk.Schema{
Description: `
Provides information about an existing alias within Vercel.

An alias allows a deployment to be accessed through a different URL.
`,
Attributes: map[string]tfsdk.Attribute{
"team_id": {
Optional: true,
Type: types.StringType,
PlanModifiers: tfsdk.AttributePlanModifiers{tfsdk.RequiresReplace()},
Description: "The team ID the alias exists beneath.",
},
"alias": {
Required: true,
Type: types.StringType,
Description: "The alias or alias ID to be retrieved.",
},
"deployment_id": {
Computed: true,
Type: types.StringType,
Description: "The deployment ID.",
},
"uid": {
Computed: true,
Type: types.StringType,
Description: "The unique identifier of the alias.",
},
},
}, nil
}

// NewDataSource instantiates a new DataSource of this DataSourceType.
func (r dataSourceAliasType) NewDataSource(ctx context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
return dataSourceAlias{
p: *(p.(*provider)),
}, nil
}

type dataSourceAlias struct {
p provider
}

// Read will read the alias information by requesting it from the Vercel API, and will update terraform
// with this information.
// It is called by the provider whenever data source values should be read to update state.
func (r dataSourceAlias) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
var config Alias
diags := req.Config.Get(ctx, &config)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

out, err := r.p.client.GetAlias(ctx, config.Alias.Value, config.TeamID.Value)
if err != nil {
resp.Diagnostics.AddError(
"Error reading alias",
fmt.Sprintf("Could not read alias %s %s, unexpected error: %s",
config.TeamID.Value,
config.Alias.Value,
err,
),
)
return
}

result := convertResponseToAlias(out, config)
tflog.Trace(ctx, "read project", map[string]interface{}{
"team_id": result.TeamID.Value,
"alias": result.Alias.Value,
})

diags = resp.State.Set(ctx, result)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
2 changes: 2 additions & 0 deletions vercel/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Use the navigation to the left to read about the available resources.
// GetResources shows the available resources for the vercel provider
func (p *provider) GetResources(_ context.Context) (map[string]tfsdk.ResourceType, diag.Diagnostics) {
return map[string]tfsdk.ResourceType{
"vercel_alias": resourceAliasType{},
"vercel_deployment": resourceDeploymentType{},
"vercel_project": resourceProjectType{},
"vercel_project_domain": resourceProjectDomainType{},
Expand All @@ -56,6 +57,7 @@ func (p *provider) GetDataSources(_ context.Context) (map[string]tfsdk.DataSourc
"vercel_file": dataSourceFileType{},
"vercel_project": dataSourceProjectType{},
"vercel_project_directory": dataSourceProjectDirectoryType{},
"vercel_alias": dataSourceAliasType{},
}, nil
}

Expand Down
Loading