diff --git a/client/alias_create.go b/client/alias_create.go index 75044b04..8998fe8a 100644 --- a/client/alias_create.go +++ b/client/alias_create.go @@ -16,15 +16,16 @@ type CreateAliasRequest struct { // 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"` + UID string `json:"uid"` + Alias string `json:"alias"` + TeamID string `json:"-"` } // 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) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } payload := string(mustMarshal(request)) req, err := http.NewRequestWithContext( @@ -51,5 +52,6 @@ func (c *Client) CreateAlias(ctx context.Context, request CreateAliasRequest, de UID: aliasResponse.UID, Alias: aliasResponse.Alias, DeploymentID: deploymentID, + TeamID: c.teamID(teamID), }, nil } diff --git a/client/alias_delete.go b/client/alias_delete.go index a0dbfecb..19b2045c 100644 --- a/client/alias_delete.go +++ b/client/alias_delete.go @@ -16,8 +16,8 @@ type DeleteAliasResponse struct { // 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) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequest( "DELETE", diff --git a/client/alias_get.go b/client/alias_get.go index 80a57bdf..2d269f0b 100644 --- a/client/alias_get.go +++ b/client/alias_get.go @@ -13,13 +13,14 @@ type AliasResponse struct { UID string `json:"uid"` Alias string `json:"alias"` DeploymentID string `json:"deploymentId"` + TeamID string `json:"-"` } // 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) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequestWithContext( ctx, @@ -34,5 +35,6 @@ func (c *Client) GetAlias(ctx context.Context, alias, teamID string) (r AliasRes "url": url, }) err = c.doRequest(req, &r) + r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/client.go b/client/client.go index 710d3915..47036b34 100644 --- a/client/client.go +++ b/client/client.go @@ -9,6 +9,7 @@ import ( type Client struct { token string client *http.Client + _teamID string baseURL string } @@ -31,3 +32,18 @@ func New(token string) *Client { baseURL: "https://api.vercel.com", } } + +func (c *Client) WithTeamID(teamID string) *Client { + c._teamID = teamID + return c +} + +// teamID is a helper method to return one of two values based on specificity. +// It will return an explicitly passed teamID if it is defined. If not defined, +// it will fall back to the teamID configured on the client. +func (c *Client) teamID(teamID string) string { + if teamID != "" { + return teamID + } + return c._teamID +} diff --git a/client/deployment_create.go b/client/deployment_create.go index ad749e56..68c53166 100644 --- a/client/deployment_create.go +++ b/client/deployment_create.go @@ -77,6 +77,7 @@ type DeploymentResponse struct { ErrorMessage string `json:"errorMessage"` ID string `json:"id"` ProjectID string `json:"projectId"` + TeamID string `json:"-"` ReadyState string `json:"readyState"` Target *string `json:"target"` URL string `json:"url"` @@ -196,8 +197,8 @@ func (c *Client) CreateDeployment(ctx context.Context, request CreateDeploymentR request.GitSource = &gitSource } url := fmt.Sprintf("%s/v12/now/deployments?skipAutoDetectionConfirmation=1", c.baseURL) - if teamID != "" { - url = fmt.Sprintf("%s&teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s&teamId=%s", url, c.teamID(teamID)) } payload := string(mustMarshal(request)) req, err := http.NewRequestWithContext( diff --git a/client/deployment_delete.go b/client/deployment_delete.go index f947257a..adc0eb37 100644 --- a/client/deployment_delete.go +++ b/client/deployment_delete.go @@ -17,8 +17,8 @@ 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) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequest( "DELETE", diff --git a/client/deployment_get.go b/client/deployment_get.go index eb63e79c..bca30b1d 100644 --- a/client/deployment_get.go +++ b/client/deployment_get.go @@ -11,8 +11,8 @@ import ( // GetDeployment retrieves information from Vercel about an existing Deployment. func (c *Client) GetDeployment(ctx context.Context, deploymentID, teamID string) (r DeploymentResponse, err error) { url := fmt.Sprintf("%s/v13/deployments/%s", c.baseURL, deploymentID) - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequestWithContext( ctx, @@ -28,5 +28,6 @@ func (c *Client) GetDeployment(ctx context.Context, deploymentID, teamID string) "url": url, }) err = c.doRequest(req, &r) + r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/dns_record_create.go b/client/dns_record_create.go index e7c7635c..88bfc9b1 100644 --- a/client/dns_record_create.go +++ b/client/dns_record_create.go @@ -29,8 +29,8 @@ type CreateDNSRecordRequest struct { // CreateDNSRecord creates a DNS record for a specified domain name within Vercel. func (c *Client) CreateDNSRecord(ctx context.Context, teamID string, request CreateDNSRecordRequest) (r DNSRecord, err error) { url := fmt.Sprintf("%s/v4/domains/%s/records", c.baseURL, request.Domain) - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequestWithContext( diff --git a/client/dns_record_delete.go b/client/dns_record_delete.go index fee3d655..b68e726a 100644 --- a/client/dns_record_delete.go +++ b/client/dns_record_delete.go @@ -10,8 +10,8 @@ import ( // DeleteDNSRecord removes a DNS domain from Vercel. func (c *Client) DeleteDNSRecord(ctx context.Context, domain, recordID, teamID string) error { url := fmt.Sprintf("%s/v2/domains/%s/records/%s", c.baseURL, domain, recordID) - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequestWithContext( diff --git a/client/dns_record_get.go b/client/dns_record_get.go index 2a723a64..a232eac2 100644 --- a/client/dns_record_get.go +++ b/client/dns_record_get.go @@ -12,6 +12,7 @@ type DNSRecord struct { Creator string `json:"creator"` Domain string `json:"domain"` ID string `json:"id"` + TeamID string `json:"-"` Name string `json:"name"` TTL int64 `json:"ttl"` Value string `json:"value"` @@ -22,8 +23,8 @@ type DNSRecord struct { // GetDNSRecord retrieves information about a DNS domain from Vercel. func (c *Client) GetDNSRecord(ctx context.Context, recordID, teamID string) (r DNSRecord, err error) { url := fmt.Sprintf("%s/domains/records/%s", c.baseURL, recordID) - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequestWithContext( @@ -37,5 +38,6 @@ func (c *Client) GetDNSRecord(ctx context.Context, recordID, teamID string) (r D } err = c.doRequest(req, &r) + r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/dns_record_list.go b/client/dns_record_list.go index 27664490..20dd8b16 100644 --- a/client/dns_record_list.go +++ b/client/dns_record_list.go @@ -13,8 +13,8 @@ import ( // into a production ready function would require some refactoring. func (c *Client) ListDNSRecords(ctx context.Context, domain, teamID string) (r []DNSRecord, err error) { url := fmt.Sprintf("%s/v4/domains/%s/records?limit=100", c.baseURL, domain) - if teamID != "" { - url = fmt.Sprintf("%s&teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s&teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequestWithContext( @@ -31,5 +31,8 @@ func (c *Client) ListDNSRecords(ctx context.Context, domain, teamID string) (r [ Records []DNSRecord `json:"records"` }{} err = c.doRequest(req, &dr) + for _, record := range dr.Records { + record.TeamID = c.teamID(teamID) + } return dr.Records, err } diff --git a/client/dns_record_update.go b/client/dns_record_update.go index be368587..6619dbf2 100644 --- a/client/dns_record_update.go +++ b/client/dns_record_update.go @@ -29,8 +29,8 @@ type UpdateDNSRecordRequest struct { // UpdateDNSRecord updates a DNS record for a specified domain name within Vercel. func (c *Client) UpdateDNSRecord(ctx context.Context, teamID, recordID string, request UpdateDNSRecordRequest) (r DNSRecord, err error) { url := fmt.Sprintf("%s/v4/domains/records/%s", c.baseURL, recordID) - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } payload := string(mustMarshal(request)) @@ -49,5 +49,6 @@ func (c *Client) UpdateDNSRecord(ctx context.Context, teamID, recordID string, r "payload": payload, }) err = c.doRequest(req, &r) + r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/environment_variable_create.go b/client/environment_variable_create.go index 944c28d1..c01e23cb 100644 --- a/client/environment_variable_create.go +++ b/client/environment_variable_create.go @@ -24,8 +24,8 @@ type CreateEnvironmentVariableRequest struct { // CreateEnvironmentVariable will create a brand new environment variable if one does not exist. func (c *Client) CreateEnvironmentVariable(ctx context.Context, request CreateEnvironmentVariableRequest) (e EnvironmentVariable, err error) { url := fmt.Sprintf("%s/v9/projects/%s/env", c.baseURL, request.ProjectID) - if request.TeamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, request.TeamID) + if c.teamID(request.TeamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID)) } payload := string(mustMarshal(request)) req, err := http.NewRequestWithContext( @@ -45,5 +45,6 @@ func (c *Client) CreateEnvironmentVariable(ctx context.Context, request CreateEn err = c.doRequest(req, &e) // The API response returns an encrypted environment variable, but we want to return the decrypted version. e.Value = request.Value + e.TeamID = c.teamID(request.TeamID) return e, err } diff --git a/client/environment_variable_update.go b/client/environment_variable_update.go index e40a5603..bdff03a5 100644 --- a/client/environment_variable_update.go +++ b/client/environment_variable_update.go @@ -25,8 +25,8 @@ type UpdateEnvironmentVariableRequest struct { // UpdateEnvironmentVariable will update an existing environment variable to the latest information. func (c *Client) UpdateEnvironmentVariable(ctx context.Context, request UpdateEnvironmentVariableRequest) (e EnvironmentVariable, err error) { url := fmt.Sprintf("%s/v9/projects/%s/env/%s", c.baseURL, request.ProjectID, request.EnvID) - if request.TeamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, request.TeamID) + if c.teamID(request.TeamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID)) } payload := string(mustMarshal(request)) req, err := http.NewRequestWithContext( @@ -46,5 +46,6 @@ func (c *Client) UpdateEnvironmentVariable(ctx context.Context, request UpdateEn err = c.doRequest(req, &e) // The API response returns an encrypted environment variable, but we want to return the decrypted version. e.Value = request.Value + e.TeamID = c.teamID(request.TeamID) return e, err } diff --git a/client/environment_variables_delete.go b/client/environment_variables_delete.go index 8966f6d2..8dfc9d36 100644 --- a/client/environment_variables_delete.go +++ b/client/environment_variables_delete.go @@ -11,8 +11,8 @@ import ( // DeleteEnvironmentVariable will remove an environment variable from Vercel. func (c *Client) DeleteEnvironmentVariable(ctx context.Context, projectID, teamID, variableID string) error { url := fmt.Sprintf("%s/v8/projects/%s/env/%s", c.baseURL, projectID, variableID) - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequestWithContext( ctx, diff --git a/client/environment_variables_get.go b/client/environment_variables_get.go index bbee76f9..0e5891d8 100644 --- a/client/environment_variables_get.go +++ b/client/environment_variables_get.go @@ -10,8 +10,8 @@ import ( func (c *Client) getEnvironmentVariables(ctx context.Context, projectID, teamID string) ([]EnvironmentVariable, error) { url := fmt.Sprintf("%s/v8/projects/%s/env?decrypt=true", c.baseURL, projectID) - if teamID != "" { - url = fmt.Sprintf("%s&teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s&teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequestWithContext( ctx, @@ -30,14 +30,17 @@ func (c *Client) getEnvironmentVariables(ctx context.Context, projectID, teamID "url": url, }) err = c.doRequest(req, &envResponse) + for _, env := range envResponse.Env { + env.TeamID = c.teamID(teamID) + } return envResponse.Env, err } // GetEnvironmentVariable gets a singluar environment variable from Vercel based on its ID. func (c *Client) GetEnvironmentVariable(ctx context.Context, projectID, teamID, envID string) (e EnvironmentVariable, err error) { url := fmt.Sprintf("%s/v1/projects/%s/env/%s", c.baseURL, projectID, envID) - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequestWithContext( ctx, @@ -53,5 +56,6 @@ func (c *Client) GetEnvironmentVariable(ctx context.Context, projectID, teamID, "url": url, }) err = c.doRequest(req, &e) + e.TeamID = c.teamID(teamID) return e, err } diff --git a/client/file_create.go b/client/file_create.go index c4dd8f6b..5ddc6b6b 100644 --- a/client/file_create.go +++ b/client/file_create.go @@ -21,7 +21,7 @@ type CreateFileRequest struct { func (c *Client) CreateFile(ctx context.Context, request CreateFileRequest) error { url := fmt.Sprintf("%s/v2/now/files", c.baseURL) if request.TeamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, request.TeamID) + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID)) } req, err := http.NewRequestWithContext( ctx, diff --git a/client/project_create.go b/client/project_create.go index d48aecaa..7d35ef53 100644 --- a/client/project_create.go +++ b/client/project_create.go @@ -25,6 +25,7 @@ type EnvironmentVariable struct { GitBranch *string `json:"gitBranch,omitempty"` Type string `json:"type"` ID string `json:"id,omitempty"` + TeamID string `json:"-"` } // CreateProjectRequest defines the information necessary to create a project. @@ -46,8 +47,8 @@ type CreateProjectRequest struct { // CreateProject will create a project within Vercel. func (c *Client) CreateProject(ctx context.Context, teamID string, request CreateProjectRequest) (r ProjectResponse, err error) { url := fmt.Sprintf("%s/v8/projects", c.baseURL) - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequestWithContext( ctx, @@ -72,5 +73,6 @@ func (c *Client) CreateProject(ctx context.Context, teamID string, request Creat return r, fmt.Errorf("error getting environment variables: %w", err) } r.EnvironmentVariables = env + r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/project_delete.go b/client/project_delete.go index a1f3b94c..8d7f0319 100644 --- a/client/project_delete.go +++ b/client/project_delete.go @@ -12,8 +12,8 @@ import ( // remove every environment variable, as these cease to exist when a project is removed. func (c *Client) DeleteProject(ctx context.Context, projectID, teamID string) error { url := fmt.Sprintf("%s/v8/projects/%s", c.baseURL, projectID) - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequestWithContext( ctx, diff --git a/client/project_domain_create.go b/client/project_domain_create.go index 148e42e4..6c517ff6 100644 --- a/client/project_domain_create.go +++ b/client/project_domain_create.go @@ -23,8 +23,8 @@ type CreateProjectDomainRequest struct { // CreateProjectDomain creates a project domain within Vercel. func (c *Client) CreateProjectDomain(ctx context.Context, projectID, teamID string, request CreateProjectDomainRequest) (r ProjectDomainResponse, err error) { url := fmt.Sprintf("%s/v8/projects/%s/domains", c.baseURL, projectID) - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } payload := string(mustMarshal(request)) @@ -43,5 +43,6 @@ func (c *Client) CreateProjectDomain(ctx context.Context, projectID, teamID stri "payload": payload, }) err = c.doRequest(req, &r) + r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/project_domain_delete.go b/client/project_domain_delete.go index cf89ba03..e3a6995a 100644 --- a/client/project_domain_delete.go +++ b/client/project_domain_delete.go @@ -11,8 +11,8 @@ import ( // DeleteProjectDomain removes any association of a domain name with a Vercel project. func (c *Client) DeleteProjectDomain(ctx context.Context, projectID, domain, teamID string) error { url := fmt.Sprintf("%s/v8/projects/%s/domains/%s", c.baseURL, projectID, domain) - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequestWithContext( diff --git a/client/project_domain_get.go b/client/project_domain_get.go index 3cb2d863..ae9289cc 100644 --- a/client/project_domain_get.go +++ b/client/project_domain_get.go @@ -13,6 +13,7 @@ import ( type ProjectDomainResponse struct { Name string `json:"name"` ProjectID string `json:"projectId"` + TeamID string `json:"-"` Redirect *string `json:"redirect"` RedirectStatusCode *int64 `json:"redirectStatusCode"` GitBranch *string `json:"gitBranch"` @@ -21,8 +22,8 @@ type ProjectDomainResponse struct { // GetProjectDomain retrieves information about a project domain from Vercel. func (c *Client) GetProjectDomain(ctx context.Context, projectID, domain, teamID string) (r ProjectDomainResponse, err error) { url := fmt.Sprintf("%s/v8/projects/%s/domains/%s", c.baseURL, projectID, domain) - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequestWithContext( @@ -39,5 +40,6 @@ func (c *Client) GetProjectDomain(ctx context.Context, projectID, domain, teamID "url": url, }) err = c.doRequest(req, &r) + r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/project_domain_update.go b/client/project_domain_update.go index b0e634fd..c46ede49 100644 --- a/client/project_domain_update.go +++ b/client/project_domain_update.go @@ -19,8 +19,8 @@ type UpdateProjectDomainRequest struct { // UpdateProjectDomain updates an existing project domain within Vercel. func (c *Client) UpdateProjectDomain(ctx context.Context, projectID, domain, teamID string, request UpdateProjectDomainRequest) (r ProjectDomainResponse, err error) { url := fmt.Sprintf("%s/v8/projects/%s/domains/%s", c.baseURL, projectID, domain) - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } payload := string(mustMarshal(request)) @@ -39,5 +39,6 @@ func (c *Client) UpdateProjectDomain(ctx context.Context, projectID, domain, tea "payload": payload, }) err = c.doRequest(req, &r) + r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/project_get.go b/client/project_get.go index bb909912..33c534ef 100644 --- a/client/project_get.go +++ b/client/project_get.go @@ -48,6 +48,7 @@ type ProjectResponse struct { EnvironmentVariables []EnvironmentVariable `json:"env"` Framework *string `json:"framework"` ID string `json:"id"` + TeamID string `json:"-"` InstallCommand *string `json:"installCommand"` Link *struct { Type string `json:"type"` @@ -72,8 +73,8 @@ type ProjectResponse struct { // GetProject retrieves information about an existing project from Vercel. func (c *Client) GetProject(ctx context.Context, projectID, teamID string, shouldFetchEnvironmentVariables bool) (r ProjectResponse, err error) { url := fmt.Sprintf("%s/v8/projects/%s", c.baseURL, projectID) - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequestWithContext( ctx, @@ -103,5 +104,6 @@ func (c *Client) GetProject(ctx context.Context, projectID, teamID string, shoul // encrypted. This isn't useful, so we just remove them. r.EnvironmentVariables = nil } + r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/project_list.go b/client/project_list.go index fd7aab48..e9db4bbc 100644 --- a/client/project_list.go +++ b/client/project_list.go @@ -11,8 +11,8 @@ import ( // ListProjects lists the top 100 projects (no pagination) from within Vercel. func (c *Client) ListProjects(ctx context.Context, teamID string) (r []ProjectResponse, err error) { url := fmt.Sprintf("%s/v8/projects?limit=100", c.baseURL) - if teamID != "" { - url = fmt.Sprintf("%s&teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s&teamId=%s", url, c.teamID(teamID)) } req, err := http.NewRequestWithContext( @@ -32,5 +32,8 @@ func (c *Client) ListProjects(ctx context.Context, teamID string) (r []ProjectRe "url": url, }) err = c.doRequest(req, &pr) + for _, p := range pr.Projects { + p.TeamID = c.teamID(teamID) + } return pr.Projects, err } diff --git a/client/project_update.go b/client/project_update.go index a0da6a4c..27e9bb12 100644 --- a/client/project_update.go +++ b/client/project_update.go @@ -29,10 +29,10 @@ type UpdateProjectRequest struct { } // UpdateProject updates an existing projects configuration within Vercel. -func (c *Client) UpdateProject(ctx context.Context, projectID, teamID string, request UpdateProjectRequest) (r ProjectResponse, err error) { +func (c *Client) UpdateProject(ctx context.Context, projectID, teamID string, request UpdateProjectRequest, shouldFetchEnvironmentVariables bool) (r ProjectResponse, err error) { url := fmt.Sprintf("%s/v8/projects/%s", c.baseURL, projectID) - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } payload := string(mustMarshal(request)) req, err := http.NewRequestWithContext( @@ -46,17 +46,23 @@ func (c *Client) UpdateProject(ctx context.Context, projectID, teamID string, re } tflog.Trace(ctx, "updating project", map[string]interface{}{ - "url": url, - "payload": payload, + "url": url, + "payload": payload, + "shouldFetchEnvironmentVariables": shouldFetchEnvironmentVariables, }) err = c.doRequest(req, &r) if err != nil { return r, err } - env, err := c.getEnvironmentVariables(ctx, r.ID, teamID) - if err != nil { - return r, fmt.Errorf("error getting environment variables for project: %w", err) + if shouldFetchEnvironmentVariables { + r.EnvironmentVariables, err = c.getEnvironmentVariables(ctx, r.ID, teamID) + if err != nil { + return r, fmt.Errorf("error getting environment variables for project: %w", err) + } + } else { + r.EnvironmentVariables = nil } - r.EnvironmentVariables = env + + r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/team_get.go b/client/team_get.go index 1503f29d..7ee5b667 100644 --- a/client/team_get.go +++ b/client/team_get.go @@ -9,13 +9,8 @@ import ( ) // GetTeam returns information about an existing team within vercel. -func (c *Client) GetTeam(ctx context.Context, teamID, slug string) (r TeamResponse, err error) { - url := c.baseURL + "/v1/teams" - if teamID != "" { - url = fmt.Sprintf("%s?teamId=%s", url, teamID) - } else if slug != "" { - url = fmt.Sprintf("%s?slug=%s", url, slug) - } +func (c *Client) GetTeam(ctx context.Context, idOrSlug string) (r TeamResponse, err error) { + url := fmt.Sprintf("%s/v2/teams/%s", c.baseURL, idOrSlug) req, err := http.NewRequestWithContext( ctx, "GET", diff --git a/docs/index.md b/docs/index.md index bc84c5cc..cd4aa80a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -43,3 +43,4 @@ provider "vercel" { ### Optional - `api_token` (String, Sensitive) The Vercel API Token to use. This can also be specified with the `VERCEL_API_TOKEN` shell environment variable. Tokens can be created from your [Vercel settings](https://vercel.com/account/tokens). +- `team` (String) The default Vercel Team to use when creating resources. This can be provided as either a team slug, or team ID. The slug and ID are both available from the Team Settings page in the Vercel dashboard. diff --git a/docs/resources/dns_record.md b/docs/resources/dns_record.md index 6e9b5e52..73e95412 100644 --- a/docs/resources/dns_record.md +++ b/docs/resources/dns_record.md @@ -136,10 +136,13 @@ Optional: Import is supported using the following syntax: ```shell -# Import via the team_id and record ID. -# Record ID can be taken from the network tab on the domains page. +# If importing into a personal account, or with a team configured on +# the provider, simply use the record id. +# - record_id can be taken from the network tab on the domains page. +terraform import vercel_dns_record.example rec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx + +# Alternatively, you can import via the team_id and record_id. +# - team_id can be found in the team `settings` tab in the Vercel UI. +# - record_id can be taken from the network tab on the domains page. terraform import vercel_dns_record.example team_xxxxxxxxxxxxxxxxxxxxxxxx/rec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx - -# If importing without a team, simply use the record ID. -terraform import vercel_dns_record.personal_example rec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` diff --git a/docs/resources/project.md b/docs/resources/project.md index f04a624e..3c84ad00 100644 --- a/docs/resources/project.md +++ b/docs/resources/project.md @@ -97,11 +97,13 @@ Optional: Import is supported using the following syntax: ```shell -# Import via the team_id and project_id. -# team_id can be found in the team `settings` tab in the Vercel UI. -# project_id can be found in the project `settings` tab in the Vercel UI. +# If importing into a personal account, or with a team configured on +# the provider, simply use the project ID. +# - project_id can be found in the project `settings` tab in the Vercel UI. +terraform import vercel_project.example prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx + +# Alternatively, you can import via the team_id and project_id. +# - team_id can be found in the team `settings` tab in the Vercel UI. +# - project_id can be found in the project `settings` tab in the Vercel UI. terraform import vercel_project.example team_xxxxxxxxxxxxxxxxxxxxxxxx/prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx - -# If importing without a team, simply use the project ID. -terraform import vercel_project.personal_example prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` diff --git a/docs/resources/project_domain.md b/docs/resources/project_domain.md index 57a83a67..466c7fef 100644 --- a/docs/resources/project_domain.md +++ b/docs/resources/project_domain.md @@ -65,11 +65,13 @@ resource "vercel_project_domain" "example_redirect" { Import is supported using the following syntax: ```shell -# Import via the team_id, project_id and domain name. -# team_id can be found in the team `settings` tab in the Vercel UI. -# project_id can be found in the project `settings` tab in the Vercel UI. +# If importing into a personal account, or with a team configured on +# the provider, simply use the project ID and domain. +# - project_id can be found in the project `settings` tab in the Vercel UI. +terraform import vercel_project_domain.example prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/example.com + +# Alternatively, you can import via the team_id, project_id and domain name. +# - team_id can be found in the team `settings` tab in the Vercel UI. +# - project_id can be found in the project `settings` tab in the Vercel UI. terraform import vercel_project_domain.example team_xxxxxxxxxxxxxxxxxxxxxxxx/prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/example.com - -# If importing without a team, simply use the project ID and domain. -terraform import vercel_project_domain.personal_example prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/example.com ``` diff --git a/docs/resources/project_environment_variable.md b/docs/resources/project_environment_variable.md index f18ee8e0..234c94f1 100644 --- a/docs/resources/project_environment_variable.md +++ b/docs/resources/project_environment_variable.md @@ -61,7 +61,7 @@ resource "vercel_project_environment_variable" "example_git_branch" { - `key` (String) The name of the Environment Variable. - `project_id` (String) The ID of the Vercel project. - `target` (Set of String) The environments that the Environment Variable should be present on. Valid targets are either `production`, `preview`, or `development`. -- `value` (String) The value of the Environment Variable. +- `value` (String, Sensitive) The value of the Environment Variable. ### Optional @@ -77,11 +77,16 @@ resource "vercel_project_environment_variable" "example_git_branch" { Import is supported using the following syntax: ```shell -# Import via the team_id, project_id and environment variable id. -# team_id can be found in the team `settings` tab in the Vercel UI. -# environment variable id can be taken from the network tab on the project page. +# If importing into a personal account, or with a team configured on +# the provider, simply use the project_id and environment variable id. +# - project_id can be found in the project `settings` tab in the Vercel UI. +# - environment variable id can be taken from the network tab on the project page. +terraform import vercel_project_environment_variable.example prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/FdT2e1E5Of6Cihmt + +# Alternatively, you can import via the team_id, project_id and +# environment variable id. +# - team_id can be found in the team `settings` tab in the Vercel UI. +# - project_id can be found in the project `settings` tab in the Vercel UI. +# - environment variable id can be taken from the network tab on the project page. terraform import vercel_project_environment_variable.example team_xxxxxxxxxxxxxxxxxxxxxxxx/prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/FdT2e1E5Of6Cihmt - -# If importing without a team, simply use the project_id and environment variable id. -terraform import vercel_project_environment_variable.example_git_branch prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/FdT2e1E5Of6Cihmt ``` diff --git a/examples/resources/vercel_dns_record/import.sh b/examples/resources/vercel_dns_record/import.sh index 4a2257a3..90f2a2a7 100644 --- a/examples/resources/vercel_dns_record/import.sh +++ b/examples/resources/vercel_dns_record/import.sh @@ -1,6 +1,10 @@ -# Import via the team_id and record ID. -# Record ID can be taken from the network tab on the domains page. +# If importing into a personal account, or with a team configured on +# the provider, simply use the record id. +# - record_id can be taken from the network tab on the domains page. +terraform import vercel_dns_record.example rec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx + +# Alternatively, you can import via the team_id and record_id. +# - team_id can be found in the team `settings` tab in the Vercel UI. +# - record_id can be taken from the network tab on the domains page. terraform import vercel_dns_record.example team_xxxxxxxxxxxxxxxxxxxxxxxx/rec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx -# If importing without a team, simply use the record ID. -terraform import vercel_dns_record.personal_example rec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/examples/resources/vercel_project/import.sh b/examples/resources/vercel_project/import.sh index 20f80678..b38a9b2a 100644 --- a/examples/resources/vercel_project/import.sh +++ b/examples/resources/vercel_project/import.sh @@ -1,7 +1,9 @@ -# Import via the team_id and project_id. -# team_id can be found in the team `settings` tab in the Vercel UI. -# project_id can be found in the project `settings` tab in the Vercel UI. -terraform import vercel_project.example team_xxxxxxxxxxxxxxxxxxxxxxxx/prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx +# If importing into a personal account, or with a team configured on +# the provider, simply use the project ID. +# - project_id can be found in the project `settings` tab in the Vercel UI. +terraform import vercel_project.example prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx -# If importing without a team, simply use the project ID. -terraform import vercel_project.personal_example prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx +# Alternatively, you can import via the team_id and project_id. +# - team_id can be found in the team `settings` tab in the Vercel UI. +# - project_id can be found in the project `settings` tab in the Vercel UI. +terraform import vercel_project.example team_xxxxxxxxxxxxxxxxxxxxxxxx/prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/examples/resources/vercel_project_domain/import.sh b/examples/resources/vercel_project_domain/import.sh index 452ae18d..c799d360 100644 --- a/examples/resources/vercel_project_domain/import.sh +++ b/examples/resources/vercel_project_domain/import.sh @@ -1,7 +1,9 @@ -# Import via the team_id, project_id and domain name. -# team_id can be found in the team `settings` tab in the Vercel UI. -# project_id can be found in the project `settings` tab in the Vercel UI. -terraform import vercel_project_domain.example team_xxxxxxxxxxxxxxxxxxxxxxxx/prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/example.com +# If importing into a personal account, or with a team configured on +# the provider, simply use the project ID and domain. +# - project_id can be found in the project `settings` tab in the Vercel UI. +terraform import vercel_project_domain.example prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/example.com -# If importing without a team, simply use the project ID and domain. -terraform import vercel_project_domain.personal_example prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/example.com +# Alternatively, you can import via the team_id, project_id and domain name. +# - team_id can be found in the team `settings` tab in the Vercel UI. +# - project_id can be found in the project `settings` tab in the Vercel UI. +terraform import vercel_project_domain.example team_xxxxxxxxxxxxxxxxxxxxxxxx/prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/example.com diff --git a/examples/resources/vercel_project_environment_variable/import.sh b/examples/resources/vercel_project_environment_variable/import.sh index cc619c73..b2f3884c 100644 --- a/examples/resources/vercel_project_environment_variable/import.sh +++ b/examples/resources/vercel_project_environment_variable/import.sh @@ -1,7 +1,12 @@ -# Import via the team_id, project_id and environment variable id. -# team_id can be found in the team `settings` tab in the Vercel UI. -# environment variable id can be taken from the network tab on the project page. -terraform import vercel_project_environment_variable.example team_xxxxxxxxxxxxxxxxxxxxxxxx/prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/FdT2e1E5Of6Cihmt +# If importing into a personal account, or with a team configured on +# the provider, simply use the project_id and environment variable id. +# - project_id can be found in the project `settings` tab in the Vercel UI. +# - environment variable id can be taken from the network tab on the project page. +terraform import vercel_project_environment_variable.example prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/FdT2e1E5Of6Cihmt -# If importing without a team, simply use the project_id and environment variable id. -terraform import vercel_project_environment_variable.example_git_branch prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/FdT2e1E5Of6Cihmt +# Alternatively, you can import via the team_id, project_id and +# environment variable id. +# - team_id can be found in the team `settings` tab in the Vercel UI. +# - project_id can be found in the project `settings` tab in the Vercel UI. +# - environment variable id can be taken from the network tab on the project page. +terraform import vercel_project_environment_variable.example team_xxxxxxxxxxxxxxxxxxxxxxxx/prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/FdT2e1E5Of6Cihmt diff --git a/vercel/data_source_alias.go b/vercel/data_source_alias.go index 64e3d772..abcaea56 100644 --- a/vercel/data_source_alias.go +++ b/vercel/data_source_alias.go @@ -24,6 +24,7 @@ An Alias allows a ` + "`vercel_deployment` to be accessed through a different UR Attributes: map[string]tfsdk.Attribute{ "team_id": { Optional: true, + Computed: true, Type: types.StringType, Description: "The ID of the team the Alias and Deployment exist under.", }, diff --git a/vercel/data_source_project.go b/vercel/data_source_project.go index 42831f9f..96cb2d22 100644 --- a/vercel/data_source_project.go +++ b/vercel/data_source_project.go @@ -28,6 +28,7 @@ For more detailed information, please see the [Vercel documentation](https://ver Attributes: map[string]tfsdk.Attribute{ "team_id": { Optional: true, + Computed: true, Type: types.StringType, Description: "The team ID the project exists beneath.", }, diff --git a/vercel/provider.go b/vercel/provider.go index f61193ed..1c9272f2 100644 --- a/vercel/provider.go +++ b/vercel/provider.go @@ -2,6 +2,7 @@ package vercel import ( "context" + "fmt" "os" "regexp" @@ -38,6 +39,11 @@ Use the navigation to the left to read about the available resources. Description: "The Vercel API Token to use. This can also be specified with the `VERCEL_API_TOKEN` shell environment variable. Tokens can be created from your [Vercel settings](https://vercel.com/account/tokens).", Sensitive: true, }, + "team": { + Type: types.StringType, + Optional: true, + Description: "The default Vercel Team to use when creating resources. This can be provided as either a team slug, or team ID. The slug and ID are both available from the Team Settings page in the Vercel dashboard.", + }, }, }, nil } @@ -67,6 +73,7 @@ func (p *vercelProvider) GetDataSources(_ context.Context) (map[string]provider. type providerData struct { APIToken types.String `tfsdk:"api_token"` + Team types.String `tfsdk:"team"` } // apiTokenRe is a regex for an API access token. We use this to validate that the @@ -116,5 +123,23 @@ func (p *vercelProvider) Configure(ctx context.Context, req provider.ConfigureRe } p.client = client.New(apiToken) + if config.Team.Value != "" { + res, err := p.client.GetTeam(ctx, config.Team.Value) + if client.NotFound(err) { + resp.Diagnostics.AddError( + "Vercel Team not found", + "You provided a `team` field on the Vercel provider, but the team could not be found. Please check the team slug or ID is correct and that your api_token has access to the team.", + ) + return + } + if err != nil { + resp.Diagnostics.AddError( + "Unexpected error reading Vercel Team", + fmt.Sprintf("Could not read Vercel Team %s, unexpected error: %s", config.Team.Value, err), + ) + return + } + p.client = p.client.WithTeamID(res.ID) + } p.configured = true } diff --git a/vercel/resource_alias.go b/vercel/resource_alias.go index 482d23c1..2c62821c 100644 --- a/vercel/resource_alias.go +++ b/vercel/resource_alias.go @@ -37,8 +37,9 @@ An Alias allows a ` + "`vercel_deployment` to be accessed through a different UR }, "team_id": { Optional: true, + Computed: true, Description: "The ID of the team the Alias and Deployment exist under.", - PlanModifiers: tfsdk.AttributePlanModifiers{resource.RequiresReplace()}, + PlanModifiers: tfsdk.AttributePlanModifiers{resource.RequiresReplace(), resource.UseStateForUnknown()}, Type: types.StringType, }, "id": { diff --git a/vercel/resource_alias_model.go b/vercel/resource_alias_model.go index b69c78f1..e26c075d 100644 --- a/vercel/resource_alias_model.go +++ b/vercel/resource_alias_model.go @@ -21,6 +21,6 @@ func convertResponseToAlias(response client.AliasResponse, plan Alias) Alias { Alias: plan.Alias, ID: types.String{Value: response.UID}, DeploymentID: types.String{Value: response.DeploymentID}, - TeamID: plan.TeamID, + TeamID: toTeamID(response.TeamID), } } diff --git a/vercel/resource_deployment.go b/vercel/resource_deployment.go index d83a5faf..0a2bc93d 100644 --- a/vercel/resource_deployment.go +++ b/vercel/resource_deployment.go @@ -58,7 +58,8 @@ terraform to your Deployment. "team_id": { Description: "The team ID to add the deployment to.", Optional: true, - PlanModifiers: tfsdk.AttributePlanModifiers{resource.RequiresReplace()}, + Computed: true, + PlanModifiers: tfsdk.AttributePlanModifiers{resource.RequiresReplace(), resource.UseStateForUnknown()}, Type: types.StringType, }, "project_id": { @@ -306,8 +307,7 @@ func (r resourceDeployment) Create(ctx context.Context, req resource.CreateReque } _, err = r.p.client.GetProject(ctx, plan.ProjectID.Value, plan.TeamID.Value, false) - var apiErr client.APIError - if err != nil && errors.As(err, &apiErr) && apiErr.StatusCode == 404 { + if client.NotFound(err) { resp.Diagnostics.AddError( "Error creating deployment", "Could not find project, please make sure both the project_id and team_id match the project and team you wish to deploy to.", diff --git a/vercel/resource_deployment_model.go b/vercel/resource_deployment_model.go index b61723f2..e5805678 100644 --- a/vercel/resource_deployment_model.go +++ b/vercel/resource_deployment_model.go @@ -203,7 +203,7 @@ func convertResponseToDeployment(response client.DeploymentResponse, plan Deploy ElemType: types.StringType, Elems: domains, }, - TeamID: plan.TeamID, + TeamID: toTeamID(response.TeamID), Environment: plan.Environment, ProjectID: types.String{Value: response.ProjectID}, ID: types.String{Value: response.ID}, diff --git a/vercel/resource_dns_record.go b/vercel/resource_dns_record.go index e9f0d194..5d06ae4d 100644 --- a/vercel/resource_dns_record.go +++ b/vercel/resource_dns_record.go @@ -2,7 +2,6 @@ package vercel import ( "context" - "errors" "fmt" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -34,8 +33,9 @@ For more detailed information, please see the [Vercel documentation](https://ver }, "team_id": { Optional: true, + Computed: true, Description: "The team ID that the domain and DNS records belong to.", - PlanModifiers: tfsdk.AttributePlanModifiers{resource.RequiresReplace()}, + PlanModifiers: tfsdk.AttributePlanModifiers{resource.RequiresReplace(), resource.UseStateForUnknown()}, Type: types.StringType, }, "domain": { @@ -215,7 +215,7 @@ func (r resourceDNSRecord) Create(ctx context.Context, req resource.CreateReques return } - result, err := convertResponseToDNSRecord(out, plan.TeamID, plan.Value, plan.SRV) + result, err := convertResponseToDNSRecord(out, plan.Value, plan.SRV) if err != nil { resp.Diagnostics.AddError( "Error parsing DNS Record response", @@ -247,8 +247,7 @@ func (r resourceDNSRecord) Read(ctx context.Context, req resource.ReadRequest, r } out, err := r.p.client.GetDNSRecord(ctx, state.ID.Value, state.TeamID.Value) - var apiErr client.APIError - if err != nil && errors.As(err, &apiErr) && apiErr.StatusCode == 404 { + if client.NotFound(err) { resp.State.RemoveResource(ctx) return } @@ -264,7 +263,7 @@ func (r resourceDNSRecord) Read(ctx context.Context, req resource.ReadRequest, r return } - result, err := convertResponseToDNSRecord(out, state.TeamID, state.Value, state.SRV) + result, err := convertResponseToDNSRecord(out, state.Value, state.SRV) if err != nil { resp.Diagnostics.AddError( "Error parsing DNS Record response", @@ -320,7 +319,7 @@ func (r resourceDNSRecord) Update(ctx context.Context, req resource.UpdateReques return } - result, err := convertResponseToDNSRecord(out, plan.TeamID, plan.Value, plan.SRV) + result, err := convertResponseToDNSRecord(out, plan.Value, plan.SRV) if err != nil { resp.Diagnostics.AddError( "Error parsing DNS Record response", @@ -351,8 +350,7 @@ func (r resourceDNSRecord) Delete(ctx context.Context, req resource.DeleteReques } err := r.p.client.DeleteDNSRecord(ctx, state.Domain.Value, state.ID.Value, state.TeamID.Value) - var apiErr client.APIError - if err != nil && errors.As(err, &apiErr) && apiErr.StatusCode == 404 { + if client.NotFound(err) { // The DNS Record is already gone - do nothing. return } @@ -398,12 +396,8 @@ func (r resourceDNSRecord) ImportState(ctx context.Context, req resource.ImportS ) return } - stringTypeTeamID := types.String{Value: teamID} - if teamID == "" { - stringTypeTeamID.Null = true - } - result, err := convertResponseToDNSRecord(out, stringTypeTeamID, types.String{}, nil) + result, err := convertResponseToDNSRecord(out, types.String{}, nil) if err != nil { resp.Diagnostics.AddError( "Error processing DNS Record response", diff --git a/vercel/resource_dns_record_model.go b/vercel/resource_dns_record_model.go index f89d7ff6..b1816009 100644 --- a/vercel/resource_dns_record_model.go +++ b/vercel/resource_dns_record_model.go @@ -71,19 +71,14 @@ func (d DNSRecord) toUpdateRequest() client.UpdateDNSRecordRequest { } } -func convertResponseToDNSRecord(r client.DNSRecord, tid types.String, value types.String, srv *SRV) (record DNSRecord, err error) { - teamID := types.String{Value: tid.Value} - if tid.Unknown || tid.Null { - teamID.Null = true - } - +func convertResponseToDNSRecord(r client.DNSRecord, value types.String, srv *SRV) (record DNSRecord, err error) { record = DNSRecord{ Domain: types.String{Value: r.Domain}, ID: types.String{Value: r.ID}, MXPriority: types.Int64{Null: true}, Name: types.String{Value: r.Name}, TTL: types.Int64{Value: r.TTL}, - TeamID: teamID, + TeamID: toTeamID(r.TeamID), Type: types.String{Value: r.RecordType}, } diff --git a/vercel/resource_dns_record_test.go b/vercel/resource_dns_record_test.go index 35e89058..04e9a1d7 100644 --- a/vercel/resource_dns_record_test.go +++ b/vercel/resource_dns_record_test.go @@ -2,7 +2,6 @@ package vercel_test import ( "context" - "errors" "fmt" "testing" @@ -25,15 +24,11 @@ func testAccDNSRecordDestroy(n, teamID string) resource.TestCheckFunc { _, err := testClient().GetDNSRecord(context.TODO(), rs.Primary.ID, teamID) - var apiErr client.APIError if err == nil { return fmt.Errorf("Found project but expected it to have been deleted") } - if err != nil && errors.As(err, &apiErr) { - if apiErr.StatusCode == 404 { - return nil - } - return fmt.Errorf("Unexpected error checking for deleted project: %s", apiErr) + if client.NotFound(err) { + return nil } return err diff --git a/vercel/resource_project.go b/vercel/resource_project.go index fef436b1..f3be972c 100644 --- a/vercel/resource_project.go +++ b/vercel/resource_project.go @@ -2,7 +2,6 @@ package vercel import ( "context" - "errors" "fmt" "regexp" "strings" @@ -34,8 +33,9 @@ At this time you cannot use a Vercel Project resource with in-line ` + "`environ Attributes: map[string]tfsdk.Attribute{ "team_id": { Optional: true, + Computed: true, Type: types.StringType, - PlanModifiers: tfsdk.AttributePlanModifiers{resource.RequiresReplace()}, + PlanModifiers: tfsdk.AttributePlanModifiers{resource.RequiresReplace(), resource.UseStateForUnknown()}, Description: "The team ID to add the project to.", }, "name": { @@ -393,7 +393,7 @@ func (r resourceProject) Update(ctx context.Context, req resource.UpdateRequest, }) } - out, err := r.p.client.UpdateProject(ctx, state.ID.Value, state.TeamID.Value, plan.toUpdateProjectRequest(state.Name.Value)) + out, err := r.p.client.UpdateProject(ctx, state.ID.Value, state.TeamID.Value, plan.toUpdateProjectRequest(state.Name.Value), !plan.Environment.Null) if err != nil { resp.Diagnostics.AddError( "Error updating project", @@ -431,8 +431,7 @@ func (r resourceProject) Delete(ctx context.Context, req resource.DeleteRequest, } err := r.p.client.DeleteProject(ctx, state.ID.Value, state.TeamID.Value) - var apiErr client.APIError - if err != nil && errors.As(err, &apiErr) && apiErr.StatusCode == 404 { + if client.NotFound(err) { return } if err != nil { @@ -498,7 +497,6 @@ func (r resourceProject) ImportState(ctx context.Context, req resource.ImportSta InstallCommand: types.String{Null: true}, OutputDirectory: types.String{Null: true}, PublicSource: types.Bool{Null: true}, - TeamID: types.String{Value: teamID, Null: teamID == ""}, }, types.Set{Null: true}) tflog.Trace(ctx, "imported project", map[string]interface{}{ "team_id": result.TeamID.Value, diff --git a/vercel/resource_project_domain.go b/vercel/resource_project_domain.go index 042d14b4..bf30c35c 100644 --- a/vercel/resource_project_domain.go +++ b/vercel/resource_project_domain.go @@ -2,7 +2,6 @@ package vercel import ( "context" - "errors" "fmt" "strings" @@ -35,7 +34,8 @@ By default, Project Domains will be automatically applied to any ` + "`productio }, "team_id": { Optional: true, - PlanModifiers: tfsdk.AttributePlanModifiers{resource.RequiresReplace()}, + Computed: true, + PlanModifiers: tfsdk.AttributePlanModifiers{resource.RequiresReplace(), resource.UseStateForUnknown()}, Type: types.StringType, Description: "The ID of the team the project exists under.", }, @@ -102,8 +102,7 @@ func (r resourceProjectDomain) Create(ctx context.Context, req resource.CreateRe } _, err := r.p.client.GetProject(ctx, plan.ProjectID.Value, plan.TeamID.Value, false) - var apiErr client.APIError - if err != nil && errors.As(err, &apiErr) && apiErr.StatusCode == 404 { + if client.NotFound(err) { resp.Diagnostics.AddError( "Error creating project domain", "Could not find project, please make sure both the project_id and team_id match the project and team you wish to deploy to.", @@ -125,7 +124,7 @@ func (r resourceProjectDomain) Create(ctx context.Context, req resource.CreateRe return } - result := convertResponseToProjectDomain(out, plan.TeamID) + result := convertResponseToProjectDomain(out) tflog.Trace(ctx, "added domain to project", map[string]interface{}{ "project_id": result.ProjectID.Value, "domain": result.Domain.Value, @@ -166,7 +165,7 @@ func (r resourceProjectDomain) Read(ctx context.Context, req resource.ReadReques return } - result := convertResponseToProjectDomain(out, state.TeamID) + result := convertResponseToProjectDomain(out) tflog.Trace(ctx, "read project domain", map[string]interface{}{ "project_id": result.ProjectID.Value, "domain": result.Domain.Value, @@ -208,7 +207,7 @@ func (r resourceProjectDomain) Update(ctx context.Context, req resource.UpdateRe return } - result := convertResponseToProjectDomain(out, plan.TeamID) + result := convertResponseToProjectDomain(out) tflog.Trace(ctx, "update project domain", map[string]interface{}{ "project_id": result.ProjectID.Value, "domain": result.Domain.Value, @@ -232,8 +231,7 @@ func (r resourceProjectDomain) Delete(ctx context.Context, req resource.DeleteRe } err := r.p.client.DeleteProjectDomain(ctx, state.ProjectID.Value, state.Domain.Value, state.TeamID.Value) - var apiErr client.APIError - if err != nil && errors.As(err, &apiErr) && apiErr.StatusCode == 404 { + if client.NotFound(err) { // The domain is already gone - do nothing. resp.State.RemoveResource(ctx) return @@ -297,11 +295,7 @@ func (r resourceProjectDomain) ImportState(ctx context.Context, req resource.Imp return } - stringTypeTeamID := types.String{Value: teamID} - if teamID == "" { - stringTypeTeamID.Null = true - } - result := convertResponseToProjectDomain(out, stringTypeTeamID) + result := convertResponseToProjectDomain(out) tflog.Trace(ctx, "imported project domain", map[string]interface{}{ "project_id": result.ProjectID.Value, "domain": result.Domain.Value, diff --git a/vercel/resource_project_domain_model.go b/vercel/resource_project_domain_model.go index 7a626bd1..cb6bccaa 100644 --- a/vercel/resource_project_domain_model.go +++ b/vercel/resource_project_domain_model.go @@ -16,11 +16,7 @@ type ProjectDomain struct { TeamID types.String `tfsdk:"team_id"` } -func convertResponseToProjectDomain(response client.ProjectDomainResponse, tid types.String) ProjectDomain { - teamID := types.String{Value: tid.Value} - if tid.Unknown || tid.Null { - teamID.Null = true - } +func convertResponseToProjectDomain(response client.ProjectDomainResponse) ProjectDomain { return ProjectDomain{ Domain: types.String{Value: response.Name}, GitBranch: fromStringPointer(response.GitBranch), @@ -28,7 +24,7 @@ func convertResponseToProjectDomain(response client.ProjectDomainResponse, tid t ProjectID: types.String{Value: response.ProjectID}, Redirect: fromStringPointer(response.Redirect), RedirectStatusCode: fromInt64Pointer(response.RedirectStatusCode), - TeamID: teamID, + TeamID: toTeamID(response.TeamID), } } diff --git a/vercel/resource_project_environment_variable.go b/vercel/resource_project_environment_variable.go index 7c5426ff..812b93c2 100644 --- a/vercel/resource_project_environment_variable.go +++ b/vercel/resource_project_environment_variable.go @@ -2,7 +2,6 @@ package vercel import ( "context" - "errors" "fmt" "strings" @@ -63,8 +62,9 @@ At this time you cannot use a Vercel Project resource with in-line ` + "`environ }, "team_id": { Optional: true, + Computed: true, Description: "The ID of the Vercel team.", - PlanModifiers: tfsdk.AttributePlanModifiers{resource.RequiresReplace()}, + PlanModifiers: tfsdk.AttributePlanModifiers{resource.RequiresReplace(), resource.UseStateForUnknown()}, Type: types.StringType, }, "id": { @@ -107,8 +107,7 @@ func (r resourceProjectEnvironmentVariable) Create(ctx context.Context, req reso } _, err := r.p.client.GetProject(ctx, plan.ProjectID.Value, plan.TeamID.Value, false) - var apiErr client.APIError - if err != nil && errors.As(err, &apiErr) && apiErr.StatusCode == 404 { + if client.NotFound(err) { resp.Diagnostics.AddError( "Error creating project environment variable", "Could not find project, please make sure both the project_id and team_id match the project and team you wish to deploy to.", @@ -125,7 +124,7 @@ func (r resourceProjectEnvironmentVariable) Create(ctx context.Context, req reso return } - result := convertResponseToProjectEnvironmentVariable(response, plan.TeamID, plan.ProjectID) + result := convertResponseToProjectEnvironmentVariable(response, plan.ProjectID) tflog.Trace(ctx, "created project environment variable", map[string]interface{}{ "id": result.ID.Value, @@ -168,7 +167,7 @@ func (r resourceProjectEnvironmentVariable) Read(ctx context.Context, req resour return } - result := convertResponseToProjectEnvironmentVariable(out, state.TeamID, state.ProjectID) + result := convertResponseToProjectEnvironmentVariable(out, state.ProjectID) tflog.Trace(ctx, "read project environment variable", map[string]interface{}{ "id": result.ID.Value, "team_id": result.TeamID.Value, @@ -200,7 +199,7 @@ func (r resourceProjectEnvironmentVariable) Update(ctx context.Context, req reso return } - result := convertResponseToProjectEnvironmentVariable(response, plan.TeamID, plan.ProjectID) + result := convertResponseToProjectEnvironmentVariable(response, plan.ProjectID) tflog.Trace(ctx, "updated project environment variable", map[string]interface{}{ "id": result.ID.Value, @@ -286,7 +285,7 @@ func (r resourceProjectEnvironmentVariable) ImportState(ctx context.Context, req return } - result := convertResponseToProjectEnvironmentVariable(out, types.String{Value: teamID, Null: teamID == ""}, types.String{Value: projectID}) + result := convertResponseToProjectEnvironmentVariable(out, types.String{Value: projectID}) tflog.Trace(ctx, "imported project environment variable", map[string]interface{}{ "team_id": result.TeamID.Value, "project_id": result.ProjectID.Value, diff --git a/vercel/resource_project_environment_variable_model.go b/vercel/resource_project_environment_variable_model.go index 853522b8..a8538a84 100644 --- a/vercel/resource_project_environment_variable_model.go +++ b/vercel/resource_project_environment_variable_model.go @@ -52,7 +52,7 @@ func (e *ProjectEnvironmentVariable) toUpdateEnvironmentVariableRequest() client // convertResponseToProjectEnvironmentVariable is used to populate terraform state based on an API response. // Where possible, values from the API response are used to populate state. If not possible, // values from plan are used. -func convertResponseToProjectEnvironmentVariable(response client.EnvironmentVariable, teamID, projectID types.String) ProjectEnvironmentVariable { +func convertResponseToProjectEnvironmentVariable(response client.EnvironmentVariable, projectID types.String) ProjectEnvironmentVariable { target := []types.String{} for _, t := range response.Target { target = append(target, types.String{Value: t}) @@ -63,7 +63,7 @@ func convertResponseToProjectEnvironmentVariable(response client.EnvironmentVari GitBranch: fromStringPointer(response.GitBranch), Key: types.String{Value: response.Key}, Value: types.String{Value: response.Value}, - TeamID: teamID, + TeamID: toTeamID(response.TeamID), ProjectID: projectID, ID: types.String{Value: response.ID}, } diff --git a/vercel/resource_project_model.go b/vercel/resource_project_model.go index 815e1d92..b34c2407 100644 --- a/vercel/resource_project_model.go +++ b/vercel/resource_project_model.go @@ -158,7 +158,6 @@ type projectCoercedFields struct { InstallCommand types.String OutputDirectory types.String PublicSource types.Bool - TeamID types.String } func (p *Project) coercedFields() projectCoercedFields { @@ -168,7 +167,6 @@ func (p *Project) coercedFields() projectCoercedFields { InstallCommand: p.InstallCommand, OutputDirectory: p.OutputDirectory, PublicSource: p.PublicSource, - TeamID: p.TeamID, } } @@ -251,6 +249,6 @@ func convertResponseToProject(response client.ProjectResponse, fields projectCoe PublicSource: uncoerceBool(fields.PublicSource, fromBoolPointer(response.PublicSource)), RootDirectory: fromStringPointer(response.RootDirectory), ServerlessFunctionRegion: fromStringPointer(response.ServerlessFunctionRegion), - TeamID: types.String{Value: fields.TeamID.Value, Null: fields.TeamID.Null || fields.TeamID.Unknown}, + TeamID: toTeamID(response.TeamID), } } diff --git a/vercel/types_conversions.go b/vercel/types_conversions.go index cd2002bf..4c2c8003 100644 --- a/vercel/types_conversions.go +++ b/vercel/types_conversions.go @@ -43,3 +43,7 @@ func fromInt64Pointer(v *int64) types.Int64 { } return types.Int64{Value: *v} } + +func toTeamID(v string) types.String { + return types.String{Value: v, Null: v == ""} +}