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

Add Alias resource and Data Source #41

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

Merged
merged 2 commits into from
Jun 7, 2022
Merged
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
55 changes: 55 additions & 0 deletions client/alias_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
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"`
}

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

// 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,
})
var aliasResponse createAliasResponse
err = c.doRequest(req, &aliasResponse)
if err != nil {
return r, err
}

return AliasResponse{
UID: aliasResponse.UID,
Alias: aliasResponse.Alias,
DeploymentID: deploymentID,
}, nil
}
36 changes: 36 additions & 0 deletions client/alias_delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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)
if teamID != "" {
url = fmt.Sprintf("%s?teamId=%s", url, teamID)
}
req, err := http.NewRequest(
"DELETE",
url,
nil,
)
if err != nil {
return r, err
}

tflog.Trace(ctx, "deleting alias", map[string]interface{}{
"url": url,
})
err = c.doRequest(req, &r)
return r, err
}
38 changes: 38 additions & 0 deletions client/alias_get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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, alias, teamID string) (r AliasResponse, err error) {
url := fmt.Sprintf("%s/v4/aliases/%s", c.baseURL, alias)
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)
return r, err
}
16 changes: 4 additions & 12 deletions client/deployment_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ type DeleteDeploymentResponse struct {
// DeleteDeployment deletes a deployment within Vercel.
func (c *Client) DeleteDeployment(ctx context.Context, deploymentID string, teamID string) (r DeleteDeploymentResponse, err error) {
url := fmt.Sprintf("%s/v13/deployments/%s", c.baseURL, deploymentID)
if teamID != "" {
url = fmt.Sprintf("%s?teamId=%s", url, teamID)
}
req, err := http.NewRequest(
"DELETE",
url,
Expand All @@ -26,20 +29,9 @@ func (c *Client) DeleteDeployment(ctx context.Context, deploymentID string, team
return r, err
}

// Add query parameters
q := req.URL.Query()
if teamID != "" {
q.Add("teamId", teamID)
}
req.URL.RawQuery = q.Encode()

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

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

import "errors"

// NotFound detects if an error returned by the Vercel API was the result of an entity not existing.
func NotFound(err error) bool {
var apiErr APIError
return err != nil && errors.As(err, &apiErr) && apiErr.StatusCode == 404
}
6 changes: 5 additions & 1 deletion client/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,17 @@ func (c *Client) doRequest(req *http.Request, v interface{}) error {

if resp.StatusCode >= 300 {
var errorResponse APIError
if string(responseBody) == "" {
errorResponse.StatusCode = resp.StatusCode
return errorResponse
}
err = json.Unmarshal(responseBody, &struct {
Error *APIError `json:"error"`
}{
Error: &errorResponse,
})
if err != nil {
return fmt.Errorf("error unmarshaling response: %w", err)
return fmt.Errorf("error unmarshaling response for status code %d: %w", resp.StatusCode, err)
}
errorResponse.StatusCode = resp.StatusCode
errorResponse.RawMessage = responseBody
Expand Down
34 changes: 34 additions & 0 deletions docs/data-sources/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 Data Source - terraform-provider-vercel"
subcategory: ""
description: |-
Provides information about an existing Alias resource.
An Alias allows a vercel_deployment to be accessed through a different URL.
---

# vercel_alias (Data Source)

Provides information about an existing Alias resource.

An Alias allows a `vercel_deployment` to be accessed through a different URL.



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

### Required

- `alias` (String) The Alias or Alias ID to be retrieved.

### Optional

- `team_id` (String) The ID of the team the Alias and Deployment exist under.

### Read-Only

- `deployment_id` (String) The ID of the Deployment the Alias is associated with.
- `id` (String) The ID of this resource.


2 changes: 2 additions & 0 deletions docs/data-sources/project.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ output "project_id" {
- `framework` (String) The framework that is being used for this project. If omitted, no framework is selected.
- `git_repository` (Attributes) The Git Repository that will be connected to the project. When this is defined, any pushes to the specified connected Git Repository will be automatically deployed. This requires the corresponding Vercel for [Github](https://vercel.com/docs/concepts/git/vercel-for-github), [Gitlab](https://vercel.com/docs/concepts/git/vercel-for-gitlab) or [Bitbucket](https://vercel.com/docs/concepts/git/vercel-for-bitbucket) plugins to be installed. (see [below for nested schema](#nestedatt--git_repository))
- `id` (String) The ID of this resource.
- `ignore_command` (String) When a commit is pushed to the Git repository that is connected with your Project, its SHA will determine if a new Build has to be issued. If the SHA was deployed before, no new Build will be issued. You can customize this behavior with a command that exits with code 1 (new Build needed) or code 0.
- `install_command` (String) The install command for this project. If omitted, this value will be automatically detected.
- `output_directory` (String) The output directory of the project. When null is used this value will be automatically detected.
- `public_source` (Boolean) Specifies whether the source code and logs of the deployments for this project should be public or not.
- `root_directory` (String) The name of a directory or relative path to the source code of your project. When null is used it will default to the project root.
- `serverless_function_region` (String) The region on Vercel's network to which your Serverless Functions are deployed. It should be close to any data source your Serverless Function might depend on. A new Deployment is required for your changes to take effect. Please see [Vercel's documentation](https://vercel.com/docs/concepts/edge-network/regions) for a full list of regions.

<a id="nestedatt--environment"></a>
### Nested Schema for `environment`
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 vercel_deployment to be accessed through a different URL.
---

# vercel_alias (Resource)

Provides an Alias resource.

An Alias allows a `vercel_deployment` to be accessed through a different URL.



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

### Required

- `alias` (String) The Alias we want to assign to the deployment defined in the URL.
- `deployment_id` (String) The id of the Deployment the Alias should be associated with.

### Optional

- `team_id` (String) The ID of the team the Alias and Deployment exist under.

### Read-Only

- `id` (String) The ID of this resource.


6 changes: 4 additions & 2 deletions docs/resources/project.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,12 @@ resource "vercel_project" "example" {
- `environment` (Attributes Set) A set of environment variables that should be configured for the project. (see [below for nested schema](#nestedatt--environment))
- `framework` (String) The framework that is being used for this project. If omitted, no framework is selected.
- `git_repository` (Attributes) The Git Repository that will be connected to the project. When this is defined, any pushes to the specified connected Git Repository will be automatically deployed. This requires the corresponding Vercel for [Github](https://vercel.com/docs/concepts/git/vercel-for-github), [Gitlab](https://vercel.com/docs/concepts/git/vercel-for-gitlab) or [Bitbucket](https://vercel.com/docs/concepts/git/vercel-for-bitbucket) plugins to be installed. (see [below for nested schema](#nestedatt--git_repository))
- `ignore_command` (String) When a commit is pushed to the Git repository that is connected with your Project, its SHA will determine if a new Build has to be issued. If the SHA was deployed before, no new Build will be issued. You can customize this behavior with a command that exits with code 1 (new Build needed) or code 0.
- `install_command` (String) The install command for this project. If omitted, this value will be automatically detected.
- `output_directory` (String) The output directory of the project. When null is used this value will be automatically detected.
- `output_directory` (String) The output directory of the project. If omitted, this value will be automatically detected.
- `public_source` (Boolean) Specifies whether the source code and logs of the deployments for this project should be public or not.
- `root_directory` (String) The name of a directory or relative path to the source code of your project. When null is used it will default to the project root.
- `root_directory` (String) The name of a directory or relative path to the source code of your project. If omitted, it will default to the project root.
- `serverless_function_region` (String) The region on Vercel's network to which your Serverless Functions are deployed. It should be close to any data source your Serverless Function might depend on. A new Deployment is required for your changes to take effect. Please see [Vercel's documentation](https://vercel.com/docs/concepts/edge-network/regions) for a full list of regions.
- `team_id` (String) The team ID to add the project to.

### Read-Only
Expand Down
93 changes: 93 additions & 0 deletions vercel/data_source_alias.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
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 resource.

An Alias allows a ` + "`vercel_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 ID of the team the Alias and Deployment exist under.",
},
"alias": {
Required: true,
Type: types.StringType,
Description: "The Alias or Alias ID to be retrieved.",
},
"deployment_id": {
Computed: true,
Type: types.StringType,
Description: "The ID of the Deployment the Alias is associated with.",
},
"id": {
Computed: true,
Type: types.StringType,
},
},
}, 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 alias", 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
}
}
Loading