diff --git a/client/alias_create.go b/client/alias_create.go index 8998fe8a..0d6c55c2 100644 --- a/client/alias_create.go +++ b/client/alias_create.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -28,22 +26,18 @@ func (c *Client) CreateAlias(ctx context.Context, request CreateAliasRequest, de url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(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) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "POST", + url: url, + body: payload, + }, &aliasResponse) if err != nil { return r, err } diff --git a/client/alias_delete.go b/client/alias_delete.go index 19b2045c..b6012dcb 100644 --- a/client/alias_delete.go +++ b/client/alias_delete.go @@ -3,7 +3,6 @@ package client import ( "context" "fmt" - "net/http" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -19,18 +18,15 @@ func (c *Client) DeleteAlias(ctx context.Context, aliasUID string, teamID string if c.teamID(teamID) != "" { url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(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) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "DELETE", + url: url, + body: "", + }, &r) return r, err } diff --git a/client/alias_get.go b/client/alias_get.go index 2d269f0b..c61924b5 100644 --- a/client/alias_get.go +++ b/client/alias_get.go @@ -3,7 +3,6 @@ package client import ( "context" "fmt" - "net/http" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -22,19 +21,15 @@ func (c *Client) GetAlias(ctx context.Context, alias, teamID string) (r AliasRes if c.teamID(teamID) != "" { url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(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) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "GET", + url: url, + body: "", + }, &r) r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/deployment_create.go b/client/deployment_create.go index 68c53166..2fa83076 100644 --- a/client/deployment_create.go +++ b/client/deployment_create.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "log" - "net/http" "strings" "time" @@ -201,21 +200,17 @@ func (c *Client) CreateDeployment(ctx context.Context, request CreateDeploymentR url = fmt.Sprintf("%s&teamId=%s", url, c.teamID(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 deployment", map[string]interface{}{ "url": url, "payload": payload, }) - err = c.doRequest(req, &r) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "POST", + url: url, + body: payload, + }, &r) var apiErr APIError if errors.As(err, &apiErr) && apiErr.Code == "missing_files" { var missingFilesError MissingFilesError diff --git a/client/deployment_delete.go b/client/deployment_delete.go index adc0eb37..9367d280 100644 --- a/client/deployment_delete.go +++ b/client/deployment_delete.go @@ -3,7 +3,6 @@ package client import ( "context" "fmt" - "net/http" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -20,18 +19,15 @@ func (c *Client) DeleteDeployment(ctx context.Context, deploymentID string, team if c.teamID(teamID) != "" { url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequest( - "DELETE", - url, - nil, - ) - if err != nil { - return r, err - } tflog.Trace(ctx, "deleting deployment", map[string]interface{}{ "url": url, }) - err = c.doRequest(req, &r) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "DELETE", + url: url, + body: "", + }, &r) return r, err } diff --git a/client/deployment_get.go b/client/deployment_get.go index bca30b1d..034ed1ab 100644 --- a/client/deployment_get.go +++ b/client/deployment_get.go @@ -3,7 +3,6 @@ package client import ( "context" "fmt" - "net/http" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -14,20 +13,16 @@ func (c *Client) GetDeployment(ctx context.Context, deploymentID, teamID string) if c.teamID(teamID) != "" { url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequestWithContext( - ctx, - "GET", - url, - nil, - ) - if err != nil { - return r, err - } tflog.Trace(ctx, "getting deployment", map[string]interface{}{ "url": url, }) - err = c.doRequest(req, &r) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "GET", + url: url, + body: "", + }, &r) r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/dns_record_create.go b/client/dns_record_create.go index 88bfc9b1..a116e02f 100644 --- a/client/dns_record_create.go +++ b/client/dns_record_create.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" ) // SRV defines the metata required for creating an SRV type DNS Record. @@ -33,20 +31,15 @@ func (c *Client) CreateDNSRecord(ctx context.Context, teamID string, request Cre url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequestWithContext( - ctx, - "POST", - url, - strings.NewReader(string(mustMarshal(request))), - ) - if err != nil { - return r, err - } - var response struct { RecordID string `json:"uid"` } - err = c.doRequest(req, &response) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "POST", + url: url, + body: string(mustMarshal(request)), + }, &response) if err != nil { return r, err } diff --git a/client/dns_record_delete.go b/client/dns_record_delete.go index b68e726a..8b1dd24e 100644 --- a/client/dns_record_delete.go +++ b/client/dns_record_delete.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" ) // DeleteDNSRecord removes a DNS domain from Vercel. @@ -14,15 +12,10 @@ func (c *Client) DeleteDNSRecord(ctx context.Context, domain, recordID, teamID s url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequestWithContext( - ctx, - "DELETE", - url, - strings.NewReader(""), - ) - if err != nil { - return err - } - - return c.doRequest(req, nil) + return c.doRequest(clientRequest{ + ctx: ctx, + method: "DELETE", + url: url, + body: "", + }, nil) } diff --git a/client/dns_record_get.go b/client/dns_record_get.go index a232eac2..09372070 100644 --- a/client/dns_record_get.go +++ b/client/dns_record_get.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" ) // DNSRecord is the information Vercel surfaces about a DNS record associated with a particular domain. @@ -27,17 +25,12 @@ func (c *Client) GetDNSRecord(ctx context.Context, recordID, teamID string) (r D url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequestWithContext( - ctx, - "GET", - url, - strings.NewReader(""), - ) - if err != nil { - return r, err - } - - err = c.doRequest(req, &r) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "GET", + url: url, + body: "", + }, &r) r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/dns_record_list.go b/client/dns_record_list.go index 20dd8b16..ac94d61c 100644 --- a/client/dns_record_list.go +++ b/client/dns_record_list.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" ) // ListDNSRecords is a test helper for listing DNS records that exist for a given domain. @@ -17,20 +15,15 @@ func (c *Client) ListDNSRecords(ctx context.Context, domain, teamID string) (r [ url = fmt.Sprintf("%s&teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequestWithContext( - ctx, - "GET", - url, - strings.NewReader(""), - ) - if err != nil { - return r, err - } - dr := struct { Records []DNSRecord `json:"records"` }{} - err = c.doRequest(req, &dr) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "GET", + url: url, + body: "", + }, &dr) for _, record := range dr.Records { record.TeamID = c.teamID(teamID) } diff --git a/client/dns_record_update.go b/client/dns_record_update.go index 6619dbf2..401e5d63 100644 --- a/client/dns_record_update.go +++ b/client/dns_record_update.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -34,21 +32,16 @@ func (c *Client) UpdateDNSRecord(ctx context.Context, teamID, recordID string, r } payload := string(mustMarshal(request)) - req, err := http.NewRequestWithContext( - ctx, - "PATCH", - url, - strings.NewReader(payload), - ) - if err != nil { - return r, err - } - tflog.Trace(ctx, "updating DNS record", map[string]interface{}{ "url": url, "payload": payload, }) - err = c.doRequest(req, &r) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "PATCH", + url: url, + body: payload, + }, &r) r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/environment_variable_create.go b/client/environment_variable_create.go index 783dfa42..bd6d7d7c 100644 --- a/client/environment_variable_create.go +++ b/client/environment_variable_create.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -32,21 +30,17 @@ func (c *Client) CreateEnvironmentVariable(ctx context.Context, request CreateEn url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID)) } payload := string(mustMarshal(request.EnvironmentVariable)) - req, err := http.NewRequestWithContext( - ctx, - "POST", - url, - strings.NewReader(payload), - ) - if err != nil { - return e, err - } tflog.Trace(ctx, "creating environment variable", map[string]interface{}{ "url": url, "payload": payload, }) - err = c.doRequest(req, &e) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "POST", + url: url, + body: payload, + }, &e) // The API response returns an encrypted environment variable, but we want to return the decrypted version. e.Value = request.EnvironmentVariable.Value e.TeamID = c.teamID(request.TeamID) @@ -65,20 +59,14 @@ func (c *Client) CreateEnvironmentVariables(ctx context.Context, request CreateE url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID)) } payload := string(mustMarshal(request.EnvironmentVariables)) - req, err := http.NewRequestWithContext( - ctx, - "POST", - url, - strings.NewReader(payload), - ) - if err != nil { - return err - } - tflog.Trace(ctx, "creating environment variables", map[string]interface{}{ "url": url, "payload": payload, }) - err = c.doRequest(req, nil) - return err + return c.doRequest(clientRequest{ + ctx: ctx, + method: "POST", + url: url, + body: payload, + }, nil) } diff --git a/client/environment_variable_update.go b/client/environment_variable_update.go index bdff03a5..d1d6adea 100644 --- a/client/environment_variable_update.go +++ b/client/environment_variable_update.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -29,21 +27,16 @@ func (c *Client) UpdateEnvironmentVariable(ctx context.Context, request UpdateEn url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID)) } payload := string(mustMarshal(request)) - req, err := http.NewRequestWithContext( - ctx, - "PATCH", - url, - strings.NewReader(payload), - ) - if err != nil { - return e, err - } - tflog.Trace(ctx, "updating environment variable", map[string]interface{}{ "url": url, "payload": payload, }) - err = c.doRequest(req, &e) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "PATCH", + url: url, + body: payload, + }, &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) diff --git a/client/environment_variables_delete.go b/client/environment_variables_delete.go index 8dfc9d36..4824719a 100644 --- a/client/environment_variables_delete.go +++ b/client/environment_variables_delete.go @@ -3,7 +3,6 @@ package client import ( "context" "fmt" - "net/http" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -14,18 +13,13 @@ func (c *Client) DeleteEnvironmentVariable(ctx context.Context, projectID, teamI if c.teamID(teamID) != "" { url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequestWithContext( - ctx, - "DELETE", - url, - nil, - ) - if err != nil { - return err - } - tflog.Trace(ctx, "deleting environment variable", map[string]interface{}{ "url": url, }) - return c.doRequest(req, nil) + return c.doRequest(clientRequest{ + ctx: ctx, + method: "DELETE", + url: url, + body: "", + }, nil) } diff --git a/client/environment_variables_get.go b/client/environment_variables_get.go index 0e5891d8..2a206375 100644 --- a/client/environment_variables_get.go +++ b/client/environment_variables_get.go @@ -3,7 +3,6 @@ package client import ( "context" "fmt" - "net/http" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -13,15 +12,6 @@ func (c *Client) getEnvironmentVariables(ctx context.Context, projectID, teamID if c.teamID(teamID) != "" { url = fmt.Sprintf("%s&teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequestWithContext( - ctx, - "GET", - url, - nil, - ) - if err != nil { - return nil, err - } envResponse := struct { Env []EnvironmentVariable `json:"envs"` @@ -29,7 +19,12 @@ func (c *Client) getEnvironmentVariables(ctx context.Context, projectID, teamID tflog.Trace(ctx, "getting environment variables", map[string]interface{}{ "url": url, }) - err = c.doRequest(req, &envResponse) + err := c.doRequest(clientRequest{ + ctx: ctx, + method: "GET", + url: url, + body: "", + }, &envResponse) for _, env := range envResponse.Env { env.TeamID = c.teamID(teamID) } @@ -42,20 +37,16 @@ func (c *Client) GetEnvironmentVariable(ctx context.Context, projectID, teamID, if c.teamID(teamID) != "" { url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequestWithContext( - ctx, - "GET", - url, - nil, - ) - if err != nil { - return e, err - } tflog.Trace(ctx, "getting environment variable", map[string]interface{}{ "url": url, }) - err = c.doRequest(req, &e) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "GET", + url: url, + body: "", + }, &e) e.TeamID = c.teamID(teamID) return e, err } diff --git a/client/file_create.go b/client/file_create.go index 5ddc6b6b..e33e76a7 100644 --- a/client/file_create.go +++ b/client/file_create.go @@ -40,6 +40,6 @@ func (c *Client) CreateFile(ctx context.Context, request CreateFileRequest) erro "url": url, "sha": request.SHA, }) - err = c.doRequest(req, nil) + err = c._doRequest(req, nil) return err } diff --git a/client/project_create.go b/client/project_create.go index 7d35ef53..765c38f3 100644 --- a/client/project_create.go +++ b/client/project_create.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -50,21 +48,18 @@ func (c *Client) CreateProject(ctx context.Context, teamID string, request Creat if c.teamID(teamID) != "" { url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequestWithContext( - ctx, - "POST", - url, - strings.NewReader(string(mustMarshal(request))), - ) - if err != nil { - return r, err - } + payload := string(mustMarshal(request)) tflog.Trace(ctx, "creating project", map[string]interface{}{ "url": url, - "payload": string(mustMarshal(request)), + "payload": payload, }) - err = c.doRequest(req, &r) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "POST", + url: url, + body: payload, + }, &r) if err != nil { return r, err } diff --git a/client/project_delete.go b/client/project_delete.go index 8d7f0319..0bd772cb 100644 --- a/client/project_delete.go +++ b/client/project_delete.go @@ -3,7 +3,6 @@ package client import ( "context" "fmt" - "net/http" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -15,18 +14,13 @@ func (c *Client) DeleteProject(ctx context.Context, projectID, teamID string) er if c.teamID(teamID) != "" { url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequestWithContext( - ctx, - "DELETE", - url, - nil, - ) - if err != nil { - return err - } - tflog.Trace(ctx, "deleting project", map[string]interface{}{ "url": url, }) - return c.doRequest(req, nil) + return c.doRequest(clientRequest{ + ctx: ctx, + method: "DELETE", + url: url, + body: "", + }, nil) } diff --git a/client/project_domain_create.go b/client/project_domain_create.go index 6c289fb6..67e7ed10 100644 --- a/client/project_domain_create.go +++ b/client/project_domain_create.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -28,21 +26,16 @@ func (c *Client) CreateProjectDomain(ctx context.Context, projectID, teamID stri } payload := string(mustMarshal(request)) - req, err := http.NewRequestWithContext( - ctx, - "POST", - url, - strings.NewReader(payload), - ) - if err != nil { - return r, err - } - tflog.Trace(ctx, "creating project domain", map[string]interface{}{ "url": url, "payload": payload, }) - err = c.doRequest(req, &r) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "POST", + url: url, + body: payload, + }, &r) r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/project_domain_delete.go b/client/project_domain_delete.go index e3a6995a..4a328437 100644 --- a/client/project_domain_delete.go +++ b/client/project_domain_delete.go @@ -3,7 +3,6 @@ package client import ( "context" "fmt" - "net/http" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -15,18 +14,13 @@ func (c *Client) DeleteProjectDomain(ctx context.Context, projectID, domain, tea url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequestWithContext( - ctx, - "DELETE", - url, - nil, - ) - if err != nil { - return err - } - tflog.Trace(ctx, "deleting project domain", map[string]interface{}{ "url": url, }) - return c.doRequest(req, nil) + return c.doRequest(clientRequest{ + ctx: ctx, + method: "DELETE", + url: url, + body: "", + }, nil) } diff --git a/client/project_domain_get.go b/client/project_domain_get.go index ae9289cc..d0d8626a 100644 --- a/client/project_domain_get.go +++ b/client/project_domain_get.go @@ -3,7 +3,6 @@ package client import ( "context" "fmt" - "net/http" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -26,20 +25,15 @@ func (c *Client) GetProjectDomain(ctx context.Context, projectID, domain, teamID url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequestWithContext( - ctx, - "GET", - url, - nil, - ) - if err != nil { - return r, err - } - tflog.Trace(ctx, "getting project domain", map[string]interface{}{ "url": url, }) - err = c.doRequest(req, &r) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "GET", + url: url, + body: "", + }, &r) r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/project_domain_update.go b/client/project_domain_update.go index c46ede49..84203863 100644 --- a/client/project_domain_update.go +++ b/client/project_domain_update.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -24,21 +22,16 @@ func (c *Client) UpdateProjectDomain(ctx context.Context, projectID, domain, tea } payload := string(mustMarshal(request)) - req, err := http.NewRequestWithContext( - ctx, - "PATCH", - url, - strings.NewReader(payload), - ) - if err != nil { - return r, err - } - tflog.Trace(ctx, "updating project domain", map[string]interface{}{ "url": url, "payload": payload, }) - err = c.doRequest(req, &r) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "PATCH", + url: url, + body: payload, + }, &r) r.TeamID = c.teamID(teamID) return r, err } diff --git a/client/project_get.go b/client/project_get.go index e4e03be6..14ad9c23 100644 --- a/client/project_get.go +++ b/client/project_get.go @@ -3,7 +3,6 @@ package client import ( "context" "fmt" - "net/http" "strings" "github.com/hashicorp/terraform-plugin-log/tflog" @@ -99,20 +98,16 @@ func (c *Client) GetProject(ctx context.Context, projectID, teamID string, shoul if c.teamID(teamID) != "" { url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequestWithContext( - ctx, - "GET", - url, - nil, - ) - if err != nil { - return r, err - } tflog.Trace(ctx, "getting project", map[string]interface{}{ "url": url, "shouldFetchEnvironment": shouldFetchEnvironmentVariables, }) - err = c.doRequest(req, &r) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "GET", + url: url, + body: "", + }, &r) if err != nil { return r, fmt.Errorf("unable to get project: %w", err) } diff --git a/client/project_list.go b/client/project_list.go index e9db4bbc..dca8361f 100644 --- a/client/project_list.go +++ b/client/project_list.go @@ -3,7 +3,6 @@ package client import ( "context" "fmt" - "net/http" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -15,23 +14,18 @@ func (c *Client) ListProjects(ctx context.Context, teamID string) (r []ProjectRe url = fmt.Sprintf("%s&teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequestWithContext( - ctx, - "GET", - url, - nil, - ) - if err != nil { - return r, err - } - pr := struct { Projects []ProjectResponse `json:"projects"` }{} tflog.Trace(ctx, "listing projects", map[string]interface{}{ "url": url, }) - err = c.doRequest(req, &pr) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "GET", + url: url, + body: "", + }, &pr) for _, p := range pr.Projects { p.TeamID = c.teamID(teamID) } diff --git a/client/project_update.go b/client/project_update.go index 08e8ef42..07f60376 100644 --- a/client/project_update.go +++ b/client/project_update.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -42,22 +40,17 @@ func (c *Client) UpdateProject(ctx context.Context, projectID, teamID string, re url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } payload := string(mustMarshal(request)) - req, err := http.NewRequestWithContext( - ctx, - "PATCH", - url, - strings.NewReader(payload), - ) - if err != nil { - return r, err - } - tflog.Trace(ctx, "updating project", map[string]interface{}{ "url": url, "payload": payload, "shouldFetchEnvironmentVariables": shouldFetchEnvironmentVariables, }) - err = c.doRequest(req, &r) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "PATCH", + url: url, + body: payload, + }, &r) if err != nil { return r, err } diff --git a/client/project_update_production_branch.go b/client/project_update_production_branch.go index db063234..6e8d31ab 100644 --- a/client/project_update_production_branch.go +++ b/client/project_update_production_branch.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -20,21 +18,17 @@ func (c *Client) UpdateProductionBranch(ctx context.Context, request UpdateProdu if c.teamID(request.TeamID) != "" { url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID)) } - req, err := http.NewRequestWithContext( - ctx, - "PATCH", - url, - strings.NewReader(string(mustMarshal(request))), - ) - if err != nil { - return r, err - } - + payload := string(mustMarshal(request)) tflog.Trace(ctx, "updating project production branch", map[string]interface{}{ "url": url, - "payload": string(mustMarshal(request)), + "payload": payload, }) - err = c.doRequest(req, &r) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "PATCH", + url: url, + body: payload, + }, &r) if err != nil { return r, err } diff --git a/client/request.go b/client/request.go index 88dea93d..ed9e238a 100644 --- a/client/request.go +++ b/client/request.go @@ -1,10 +1,17 @@ package client import ( + "context" "encoding/json" + "errors" "fmt" "io" "net/http" + "strconv" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-log/tflog" ) // APIError is an error type that exposes additional information about why an API request failed. @@ -13,6 +20,7 @@ type APIError struct { Message string `json:"message"` StatusCode int RawMessage []byte + retryAfter int } // Error provides a user friendly error message. @@ -20,17 +28,72 @@ func (e APIError) Error() string { return fmt.Sprintf("%s - %s", e.Code, e.Message) } +type clientRequest struct { + ctx context.Context + method string + url string + body string +} + +func (cr *clientRequest) toHTTPRequest() (*http.Request, error) { + r, err := http.NewRequestWithContext( + cr.ctx, + cr.method, + cr.url, + strings.NewReader(cr.body), + ) + if err != nil { + return nil, err + } + if cr.body != "" { + r.Header.Set("Content-Type", "application/json") + } + return r, nil +} + // doRequest is a helper function for consistently requesting data from vercel. // This manages: // - Setting the default Content-Type for requests with a body // - Authorization via the Bearer token // - Converting error responses into an inspectable type // - Unmarshaling responses -func (c *Client) doRequest(req *http.Request, v interface{}) error { - if req.Body != nil && req.Header.Get("Content-Type") == "" { - req.Header.Set("Content-Type", "application/json") +// - Parsing a Retry-After header in the case of rate limits being hit +// - In the case of a rate-limit being hit, trying again aftera period of time +func (c *Client) doRequest(req clientRequest, v interface{}) error { + r, err := req.toHTTPRequest() + if err != nil { + return err + } + err = c._doRequest(r, v) + for retries := 0; retries < 3; retries++ { + var apiErr APIError + if errors.As(err, &apiErr) && // we received an api error + apiErr.StatusCode == 429 && // and it was a rate limit + apiErr.retryAfter > 0 && // and there was a retry time + apiErr.retryAfter < 5*60 { // and the retry time is less than 5 minutes + tflog.Error(req.ctx, "Rate limit was hit", map[string]interface{}{ + "error": apiErr, + "retryAfter": apiErr.retryAfter, + }) + time.Sleep(time.Duration(apiErr.retryAfter) * time.Second) + r, err = req.toHTTPRequest() + if err != nil { + return err + } + err = c._doRequest(r, v) + if err != nil { + continue + } + return nil + } else { + break + } } + return err +} + +func (c *Client) _doRequest(req *http.Request, v interface{}) error { req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.token)) resp, err := c.http().Do(req) if err != nil { @@ -59,6 +122,16 @@ func (c *Client) doRequest(req *http.Request, v interface{}) error { } errorResponse.StatusCode = resp.StatusCode errorResponse.RawMessage = responseBody + errorResponse.retryAfter = 1000 // set a sensible default for retrying. This is in milliseconds. + if resp.StatusCode == 429 { + retryAfterRaw := resp.Header.Get("Retry-After") + if retryAfterRaw != "" { + retryAfter, err := strconv.Atoi(retryAfterRaw) + if err == nil && retryAfter > 0 { + errorResponse.retryAfter = retryAfter + } + } + } return errorResponse } diff --git a/client/shared_environment_variable_create.go b/client/shared_environment_variable_create.go index 6919a47f..d480554e 100644 --- a/client/shared_environment_variable_create.go +++ b/client/shared_environment_variable_create.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -43,15 +41,6 @@ func (c *Client) CreateSharedEnvironmentVariable(ctx context.Context, request Cr url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID)) } payload := string(mustMarshal(request.EnvironmentVariable)) - req, err := http.NewRequestWithContext( - ctx, - "POST", - url, - strings.NewReader(payload), - ) - if err != nil { - return e, err - } tflog.Trace(ctx, "creating shared environment variable", map[string]interface{}{ "url": url, "payload": payload, @@ -59,7 +48,12 @@ func (c *Client) CreateSharedEnvironmentVariable(ctx context.Context, request Cr var response struct { Created []SharedEnvironmentVariableResponse `json:"created"` } - err = c.doRequest(req, &response) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "POST", + url: url, + body: payload, + }, &response) if err != nil { return e, err } diff --git a/client/shared_environment_variable_delete.go b/client/shared_environment_variable_delete.go index 57c9dba1..639af7bf 100644 --- a/client/shared_environment_variable_delete.go +++ b/client/shared_environment_variable_delete.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -22,18 +20,13 @@ func (c *Client) DeleteSharedEnvironmentVariable(ctx context.Context, teamID, va variableID, }, })) - req, err := http.NewRequestWithContext( - ctx, - "DELETE", - url, - strings.NewReader(payload), - ) - if err != nil { - return err - } - tflog.Trace(ctx, "deleting shared environment variable", map[string]interface{}{ "url": url, }) - return c.doRequest(req, nil) + return c.doRequest(clientRequest{ + ctx: ctx, + method: "DELETE", + url: url, + body: payload, + }, nil) } diff --git a/client/shared_environment_variable_get.go b/client/shared_environment_variable_get.go index 4029dce6..d50645e0 100644 --- a/client/shared_environment_variable_get.go +++ b/client/shared_environment_variable_get.go @@ -3,7 +3,6 @@ package client import ( "context" "fmt" - "net/http" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -13,19 +12,16 @@ func (c *Client) GetSharedEnvironmentVariable(ctx context.Context, teamID, envID if c.teamID(teamID) != "" { url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) } - req, err := http.NewRequestWithContext( - ctx, - "GET", - url, - nil, - ) - if err != nil { - return e, err - } + tflog.Trace(ctx, "getting shared environment variable", map[string]interface{}{ "url": url, }) - err = c.doRequest(req, &e) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "GET", + url: url, + body: "", + }, &e) e.TeamID = c.teamID(teamID) return e, err } diff --git a/client/shared_environment_variable_update.go b/client/shared_environment_variable_update.go index a26f4180..f6bb73ce 100644 --- a/client/shared_environment_variable_update.go +++ b/client/shared_environment_variable_update.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -31,15 +29,6 @@ func (c *Client) UpdateSharedEnvironmentVariable(ctx context.Context, request Up request.EnvID: request, }, })) - req, err := http.NewRequestWithContext( - ctx, - "PATCH", - url, - strings.NewReader(payload), - ) - if err != nil { - return e, err - } tflog.Trace(ctx, "updating shared environment variable", map[string]interface{}{ "url": url, @@ -48,7 +37,12 @@ func (c *Client) UpdateSharedEnvironmentVariable(ctx context.Context, request Up var response struct { Updated []SharedEnvironmentVariableResponse `json:"updated"` } - err = c.doRequest(req, &response) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "PATCH", + url: url, + body: payload, + }, &response) if err != nil { return e, err } diff --git a/client/team_create.go b/client/team_create.go index 6b6be677..82f77e3b 100644 --- a/client/team_create.go +++ b/client/team_create.go @@ -3,8 +3,6 @@ package client import ( "context" "fmt" - "net/http" - "strings" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -25,20 +23,15 @@ func (c *Client) CreateTeam(ctx context.Context, request TeamCreateRequest) (r T url := fmt.Sprintf("%s/v1/teams", c.baseURL) payload := string(mustMarshal(request)) - req, err := http.NewRequestWithContext( - ctx, - "POST", - url, - strings.NewReader(payload), - ) - if err != nil { - return r, err - } - tflog.Trace(ctx, "creating team", map[string]interface{}{ "url": url, "payload": payload, }) - err = c.doRequest(req, &r) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "POST", + url: url, + body: payload, + }, &r) return r, err } diff --git a/client/team_delete.go b/client/team_delete.go index 163a7cb0..871c81a8 100644 --- a/client/team_delete.go +++ b/client/team_delete.go @@ -3,7 +3,6 @@ package client import ( "context" "fmt" - "net/http" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -11,18 +10,13 @@ import ( // DeleteTeam deletes an existing team within vercel. func (c *Client) DeleteTeam(ctx context.Context, teamID string) error { url := fmt.Sprintf("%s/v1/teams/%s", c.baseURL, teamID) - req, err := http.NewRequestWithContext( - ctx, - "DELETE", - url, - nil, - ) - if err != nil { - return err - } - tflog.Trace(ctx, "deleting team", map[string]interface{}{ "url": url, }) - return c.doRequest(req, nil) + return c.doRequest(clientRequest{ + ctx: ctx, + method: "DELETE", + url: url, + body: "", + }, nil) } diff --git a/client/team_get.go b/client/team_get.go index 7ee5b667..bbf5b22c 100644 --- a/client/team_get.go +++ b/client/team_get.go @@ -3,7 +3,6 @@ package client import ( "context" "fmt" - "net/http" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -11,19 +10,14 @@ import ( // GetTeam returns information about an existing team within vercel. 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", - url, - nil, - ) - if err != nil { - return r, err - } - tflog.Trace(ctx, "getting team", map[string]interface{}{ "url": url, }) - err = c.doRequest(req, &r) + err = c.doRequest(clientRequest{ + ctx: ctx, + method: "GET", + url: url, + body: "", + }, &r) return r, err }