Repository: newrelic/newrelic-client-go
Pull request #137 (2020-02-19 00:45:27)

chore(tests): improve http client tests
This PR adds some additional test coverage to the internal http client implementation including:

* RawPost type detection (to prevent panic)
* RawPost unit tests
* additional header tests (all set headers)
* Allows consuming app to set the service name header


Patch:
diff --git a/internal/http/client.go b/internal/http/client.go
index fc77e23..c6fae79 100644
--- a/internal/http/client.go
+++ b/internal/http/client.go
@@ -20,12 +20,14 @@ import (
 )
 
 const (
-	defaultTimeout  = time.Second * 30
-	defaultRetryMax = 3
+	defaultNewRelicRequestingServiceHeader = "NewRelic-Requesting-Services"
+	defaultServiceName                     = "newrelic-client-go"
+	defaultTimeout                         = time.Second * 30
+	defaultRetryMax                        = 3
 )
 
 var (
-	defaultUserAgent = fmt.Sprintf("newrelic/newrelic-client-go/%s (https://github.com/newrelic/newrelic-client-go)", version.Version)
+	defaultUserAgent = fmt.Sprintf("newrelic/%s/%s (https://github.com/newrelic/%s)", defaultServiceName, version.Version, defaultServiceName)
 )
 
 // NewRelicClient represents a client for communicating with the New Relic APIs.
@@ -61,6 +63,13 @@ func NewClient(cfg config.Config) NewRelicClient {
 		cfg.UserAgent = defaultUserAgent
 	}
 
+	// Either set or append the library name
+	if cfg.ServiceName == "" {
+		cfg.ServiceName = defaultServiceName
+	} else {
+		cfg.ServiceName = fmt.Sprintf("%s|%s", cfg.ServiceName, defaultServiceName)
+	}
+
 	r := retryablehttp.NewClient()
 	r.HTTPClient = &c
 	r.RetryMax = defaultRetryMax
@@ -122,9 +131,18 @@ func (c *NewRelicClient) RawPost(
 	respBody interface{},
 ) (*http.Response, error) {
 
-	requestBody := []byte(reqBody.(string))
+	switch val := reqBody.(type) {
+	case []byte:
+		return c.do(http.MethodPost, url, queryParams, reqBody, respBody)
+
+	case string:
+		requestBody := []byte(val)
+		return c.do(http.MethodPost, url, queryParams, requestBody, respBody)
+
+	default:
+		return nil, errors.New("invalid request body")
+	}
 
-	return c.do(http.MethodPost, url, queryParams, requestBody, respBody)
 }
 
 // Put represents an HTTP PUT request to a New Relic API.
@@ -174,6 +192,7 @@ func makeRequestBody(reqBody interface{}) (*bytes.Buffer, error) {
 }
 
 func (c *NewRelicClient) setHeaders(req *retryablehttp.Request) {
+	req.Header.Set(defaultNewRelicRequestingServiceHeader, defaultServiceName)
 	req.Header.Set("Content-Type", "application/json")
 	req.Header.Set("User-Agent", c.Config.UserAgent)
 
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 7fdcc6d..7089b4a 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -28,6 +28,7 @@ type Config struct {
 	SyntheticsBaseURL     string
 	InfrastructureBaseURL string
 	NerdGraphBaseURL      string
+	ServiceName           string
 
 	// LogLevel can be one of the following values:
 	// "panic", "fatal", "error", "warn", "info", "debug", "trace"
diff --git a/pkg/synthetics/monitor_labels.go b/pkg/synthetics/monitor_labels.go
index c3a8563..69e02aa 100644
--- a/pkg/synthetics/monitor_labels.go
+++ b/pkg/synthetics/monitor_labels.go
@@ -7,7 +7,7 @@ import (
 
 // GetMonitorLabels is used to retrieve all labels for a given Synthetics monitor.
 func (s *Synthetics) GetMonitorLabels(monitorID string) ([]*MonitorLabel, error) {
-	url := fmt.Sprintf("/monitors/%s/labels", monitorID)
+	url := fmt.Sprintf("/v4/monitors/%s/labels", monitorID)
 
 	resp := getMonitorLabelsResponse{}
 
@@ -21,7 +21,7 @@ func (s *Synthetics) GetMonitorLabels(monitorID string) ([]*MonitorLabel, error)
 
 // AddMonitorLabel is used to add a label to a given monitor.
 func (s *Synthetics) AddMonitorLabel(monitorID, labelKey, labelValue string) error {
-	url := fmt.Sprintf("/monitors/%s/labels", monitorID)
+	url := fmt.Sprintf("/v4/monitors/%s/labels", monitorID)
 
 	data := fmt.Sprintf("%s:%s", strings.Title(labelKey), strings.Title(labelValue))
 
@@ -37,7 +37,7 @@ func (s *Synthetics) AddMonitorLabel(monitorID, labelKey, labelValue string) err
 
 // DeleteMonitorLabel deletes a key:value label from the given Syntheics monitor.
 func (s *Synthetics) DeleteMonitorLabel(monitorID, labelKey, labelValue string) error {
-	url := fmt.Sprintf("/monitors/%s/labels/%s:%s", monitorID, strings.Title(labelKey), strings.Title(labelValue))
+	url := fmt.Sprintf("/v4/monitors/%s/labels/%s:%s", monitorID, strings.Title(labelKey), strings.Title(labelValue))
 
 	_, err := s.client.Delete(url, nil, nil)
 	if err != nil {
diff --git a/pkg/synthetics/monitor_scripts.go b/pkg/synthetics/monitor_scripts.go
index d203d26..cea6e64 100644
--- a/pkg/synthetics/monitor_scripts.go
+++ b/pkg/synthetics/monitor_scripts.go
@@ -9,7 +9,7 @@ import (
 // to a New Relic Synthetics scripted monitor.
 func (s *Synthetics) GetMonitorScript(monitorID string) (*MonitorScript, error) {
 	resp := MonitorScript{}
-	url := fmt.Sprintf("/monitors/%s/script", monitorID)
+	url := fmt.Sprintf("/v4/monitors/%s/script", monitorID)
 	_, err := s.client.Get(url, nil, &resp)
 
 	if err != nil {
@@ -31,7 +31,7 @@ func (s *Synthetics) GetMonitorScript(monitorID string) (*MonitorScript, error)
 func (s *Synthetics) UpdateMonitorScript(monitorID string, script MonitorScript) (*MonitorScript, error) {
 	script.Text = base64.StdEncoding.EncodeToString([]byte(script.Text))
 
-	url := fmt.Sprintf("/monitors/%s/script", monitorID)
+	url := fmt.Sprintf("/v4/monitors/%s/script", monitorID)
 	_, err := s.client.Put(url, nil, &script, nil)
 
 	if err != nil {
diff --git a/pkg/synthetics/monitors.go b/pkg/synthetics/monitors.go
index b1d95fc..3a1d2af 100644
--- a/pkg/synthetics/monitors.go
+++ b/pkg/synthetics/monitors.go
@@ -16,7 +16,7 @@ func (s *Synthetics) ListMonitors() ([]*Monitor, error) {
 		Limit: listMonitorsLimit,
 	}
 
-	_, err := s.client.Get("/monitors", &queryParams, &resp)
+	_, err := s.client.Get("/v4/monitors", &queryParams, &resp)
 
 	if err != nil {
 		return nil, err
@@ -28,7 +28,7 @@ func (s *Synthetics) ListMonitors() ([]*Monitor, error) {
 // GetMonitor is used to retrieve a specific New Relic Synthetics monitor.
 func (s *Synthetics) GetMonitor(monitorID string) (*Monitor, error) {
 	resp := Monitor{}
-	url := fmt.Sprintf("/monitors/%s", monitorID)
+	url := fmt.Sprintf("/v4/monitors/%s", monitorID)
 	_, err := s.client.Get(url, nil, &resp)
 
 	if err != nil {
@@ -40,7 +40,7 @@ func (s *Synthetics) GetMonitor(monitorID string) (*Monitor, error) {
 
 // CreateMonitor is used to create a New Relic Synthetics monitor.
 func (s *Synthetics) CreateMonitor(monitor Monitor) (*Monitor, error) {
-	resp, err := s.client.Post("/monitors", nil, &monitor, nil)
+	resp, err := s.client.Post("/v4/monitors", nil, &monitor, nil)
 
 	if err != nil {
 		return nil, err
@@ -56,7 +56,7 @@ func (s *Synthetics) CreateMonitor(monitor Monitor) (*Monitor, error) {
 
 // UpdateMonitor is used to update a New Relic Synthetics monitor.
 func (s *Synthetics) UpdateMonitor(monitor Monitor) (*Monitor, error) {
-	url := fmt.Sprintf("/monitors/%s", monitor.ID)
+	url := fmt.Sprintf("/v4/monitors/%s", monitor.ID)
 	_, err := s.client.Put(url, nil, &monitor, nil)
 
 	if err != nil {
@@ -68,7 +68,7 @@ func (s *Synthetics) UpdateMonitor(monitor Monitor) (*Monitor, error) {
 
 // DeleteMonitor is used to delete a New Relic Synthetics monitor.
 func (s *Synthetics) DeleteMonitor(monitorID string) error {
-	url := fmt.Sprintf("/monitors/%s", monitorID)
+	url := fmt.Sprintf("/v4/monitors/%s", monitorID)
 	_, err := s.client.Delete(url, nil, nil)
 
 	if err != nil {
diff --git a/pkg/synthetics/secure_credentials.go b/pkg/synthetics/secure_credentials.go
new file mode 100644
index 0000000..505fb1c
--- /dev/null
+++ b/pkg/synthetics/secure_credentials.go
@@ -0,0 +1,83 @@
+package synthetics
+
+import (
+	"fmt"
+)
+
+// GetSecureCredentials is used to retrieve all secure credentials from your New Relic account.
+func (s *Synthetics) GetSecureCredentials() ([]*SecureCredential, error) {
+	url := "/v1/secure-credentials"
+	resp := getSecureCredentialsResponse{}
+
+	_, err := s.client.Get(url, nil, &resp)
+	if err != nil {
+		return nil, err
+	}
+
+	return resp.SecureCredentials, nil
+}
+
+// GetSecureCredential is used to retrieve a specific secure credential from your New Relic account.
+func (s *Synthetics) GetSecureCredential(key string) (*SecureCredential, error) {
+	url := fmt.Sprintf("/v1/secure-credentials/%s", key)
+	var sc SecureCredential
+
+	_, err := s.client.Get(url, nil, &sc)
+	if err != nil {
+		return nil, err
+	}
+
+	return &sc, nil
+}
+
+// AddSecureCredential is used to add a secure credential to your New Relic account.
+func (s *Synthetics) AddSecureCredential(key, value, description string) (*SecureCredential, error) {
+	url := "/v1/secure-credentials"
+	sc := &SecureCredential{
+		Key:         key,
+		Value:       value,
+		Description: description,
+	}
+
+	_, err := s.client.Post(url, nil, sc, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return sc, nil
+}
+
+// UpdateSecureCredential is used to update a secure credential in your New Relic account.
+func (s *Synthetics) UpdateSecureCredential(key, value, description string) (*SecureCredential, error) {
+	url := fmt.Sprintf("/v1/secure-credentials/%s", key)
+	sc := &SecureCredential{
+		Key:         key,
+		Value:       value,
+		Description: description,
+	}
+
+	_, err := s.client.Put(url, nil, sc, nil)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return sc, nil
+}
+
+// DeleteSecureCredential deletes a secure credential from your New Relic account.
+func (s *Synthetics) DeleteSecureCredential(key string) error {
+	url := fmt.Sprintf("/v1/secure-credentials/%s", key)
+
+	_, err := s.client.Delete(url, nil, nil)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+type getSecureCredentialsResponse struct {
+	SecureCredentials []*SecureCredential `json:"secureCredentials"`
+	Count             int                 `json:"count"`
+}
diff --git a/pkg/synthetics/synthetics.go b/pkg/synthetics/synthetics.go
index af99b69..00197f1 100644
--- a/pkg/synthetics/synthetics.go
+++ b/pkg/synthetics/synthetics.go
@@ -11,9 +11,9 @@ import (
 
 // BaseURLs represents the base API URLs for the different environments of the Synthetics API.
 var BaseURLs = map[region.Region]string{
-	region.US:      "https://synthetics.newrelic.com/synthetics/api/v4",
-	region.EU:      "https://synthetics.eu.newrelic.com/synthetics/api/v4",
-	region.Staging: "https://staging-synthetics.newrelic.com/synthetics/api/v4",
+	region.US:      "https://synthetics.newrelic.com/synthetics/api",
+	region.EU:      "https://synthetics.eu.newrelic.com/synthetics/api",
+	region.Staging: "https://staging-synthetics.newrelic.com/synthetics/api",
 }
 
 // Synthetics is used to communicate with the New Relic Synthetics product.
diff --git a/pkg/synthetics/synthetics_types.go b/pkg/synthetics/synthetics_types.go
index ff50bdb..cc923a4 100644
--- a/pkg/synthetics/synthetics_types.go
+++ b/pkg/synthetics/synthetics_types.go
@@ -75,3 +75,12 @@ type MonitorLabel struct {
 	Value string `json:"value"`
 	Href  string `json:"href"`
 }
+
+// SecureCredential represents a Synthetics secure credential.
+type SecureCredential struct {
+	Key         string `json:"key"`
+	Description string `json:"description"`
+	Value       string `json:"value"`
+	CreatedAt   *Time  `json:"createdAt"`
+	LastUpdated *Time  `json:"lastUpdated"`
+}


Test patch:
diff --git a/internal/http/client_test.go b/internal/http/client_test.go
index 5bef5fe..0c250c7 100644
--- a/internal/http/client_test.go
+++ b/internal/http/client_test.go
@@ -15,6 +15,10 @@ import (
 	"github.com/newrelic/newrelic-client-go/pkg/errors"
 )
 
+const (
+	testServiceName = "serviceName"
+)
+
 func TestConfig(t *testing.T) {
 	t.Parallel()
 	testBaseURL := "https://www.mocky.io"
@@ -24,13 +28,17 @@ func TestConfig(t *testing.T) {
 	c := NewClient(config.Config{
 		APIKey:        testAPIKey,
 		BaseURL:       testBaseURL,
-		UserAgent:     testUserAgent,
-		Timeout:       &testTimeout,
 		HTTPTransport: testTransport,
+		ServiceName:   testServiceName,
+		Timeout:       &testTimeout,
+		UserAgent:     testUserAgent,
 	})
 
 	assert.Equal(t, &testTimeout, c.Config.Timeout)
 	assert.Equal(t, testBaseURL, c.Config.BaseURL)
+	assert.Equal(t, testUserAgent, c.Config.UserAgent)
+	assert.Equal(t, c.Config.ServiceName, testServiceName+"|newrelic-client-go")
+
 	assert.Same(t, testTransport, c.Config.HTTPTransport)
 }
 
@@ -42,6 +50,7 @@ func TestConfigDefaults(t *testing.T) {
 
 	assert.Equal(t, region.DefaultBaseURLs[region.Parse(c.Config.Region)], c.Config.BaseURL)
 	assert.Contains(t, c.Config.UserAgent, "newrelic/newrelic-client-go/")
+	assert.Equal(t, c.Config.ServiceName, "newrelic-client-go")
 }
 
 func TestDefaultErrorValue(t *testing.T) {
@@ -223,7 +232,9 @@ func TestHeaders(t *testing.T) {
 		w.WriteHeader(http.StatusOK)
 
 		assert.Equal(t, testAPIKey, r.Header.Get("x-api-key"))
+		assert.Equal(t, testPersonalAPIKey, r.Header.Get("api-key"))
 		assert.Equal(t, testUserAgent, r.Header.Get("user-agent"))
+		assert.Equal(t, "newrelic-client-go", r.Header.Get("newrelic-requesting-services"))
 	}))
 
 	_, err := c.Get("/path", nil, nil)
@@ -265,6 +276,26 @@ func TestPost(t *testing.T) {
 	assert.NoError(t, err)
 }
 
+func TestRawPost(t *testing.T) {
+	t.Parallel()
+	c := NewTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write([]byte(`{}`))
+	}))
+
+	// string
+	_, err := c.RawPost("/path", &struct{}{}, "test string payload", &struct{}{})
+	assert.NoError(t, err)
+
+	// []byte
+	_, err = c.RawPost("/path", &struct{}{}, []byte(`bytes`), &struct{}{})
+	assert.NoError(t, err)
+
+	// invalid
+	_, err = c.RawPost("/path", &struct{}{}, &struct{}{}, &struct{}{})
+	assert.Error(t, err)
+}
+
 func TestPut(t *testing.T) {
 	t.Parallel()
 	c := NewTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
diff --git a/internal/http/test_helpers.go b/internal/http/test_helpers.go
index 8e6e6a1..2f85556 100644
--- a/internal/http/test_helpers.go
+++ b/internal/http/test_helpers.go
@@ -8,8 +8,9 @@ import (
 )
 
 var (
-	testAPIKey    = "apiKey"
-	testUserAgent = "userAgent"
+	testAPIKey         = "apiKey"
+	testPersonalAPIKey = "personalAPIKey"
+	testUserAgent      = "userAgent"
 )
 
 // NewTestAPIClient returns a test NewRelicClient instance that is configured to communicate with a mock server.
@@ -17,9 +18,10 @@ func NewTestAPIClient(handler http.Handler) NewRelicClient {
 	ts := httptest.NewServer(handler)
 
 	c := NewClient(config.Config{
-		APIKey:    testAPIKey,
-		BaseURL:   ts.URL,
-		UserAgent: testUserAgent,
+		APIKey:         testAPIKey,
+		BaseURL:        ts.URL,
+		PersonalAPIKey: testPersonalAPIKey,
+		UserAgent:      testUserAgent,
 	})
 
 	return c
diff --git a/pkg/synthetics/monitor_labels_test.go b/pkg/synthetics/monitor_labels_test.go
index 29d286c..80de334 100644
--- a/pkg/synthetics/monitor_labels_test.go
+++ b/pkg/synthetics/monitor_labels_test.go
@@ -28,7 +28,7 @@ func TestGetMonitorLabel(t *testing.T) {
 
 	r, err := synthetics.GetMonitorLabels(testMonitorID)
 	assert.NoError(t, err)
-	assert.Equal(t, len(r), 1)
+	assert.Equal(t, 1, len(r))
 	assert.Equal(t, r[0].Type, "Testing")
 	assert.Equal(t, r[0].Value, "Mbnhl")
 }
diff --git a/pkg/synthetics/secure_credentials_integration_test.go b/pkg/synthetics/secure_credentials_integration_test.go
new file mode 100644
index 0000000..4e898a4
--- /dev/null
+++ b/pkg/synthetics/secure_credentials_integration_test.go
@@ -0,0 +1,76 @@
+// +build integration
+
+package synthetics
+
+import (
+	"fmt"
+	"os"
+	"strings"
+	"testing"
+
+	nr "github.com/newrelic/newrelic-client-go/internal/testing"
+	"github.com/newrelic/newrelic-client-go/pkg/config"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+var (
+	testIntegrationSecureCredentialValue       = "test value"
+	testIntegrationSecureCredentialDescription = "test description"
+	testIntegrationSecureCredential            = &SecureCredential{
+		Value:       testIntegrationSecureCredentialValue,
+		Description: testIntegrationSecureCredentialDescription,
+	}
+)
+
+func TestIntegrationSecureCredentials(t *testing.T) {
+	t.Parallel()
+
+	apiKey := os.Getenv("NEWRELIC_API_KEY")
+
+	if apiKey == "" {
+		t.Skipf("acceptance testing requires an API key")
+	}
+
+	synthetics := New(config.Config{
+		APIKey:   apiKey,
+		LogLevel: "debug",
+	})
+
+	// Setup
+	rand := strings.ToUpper(nr.RandSeq(5))
+	key := fmt.Sprintf("TEST_SYNTHETICS_SECURE_CREDENTIAL_%s", rand)
+	testIntegrationSecureCredential.Key = key
+	creds, err := synthetics.GetSecureCredentials()
+	require.NoError(t, err)
+
+	originalCount := len(creds)
+
+	// Test: Add
+	c, err := synthetics.AddSecureCredential(key, "asdf", testIntegrationSecureCredentialDescription)
+	require.NoError(t, err)
+
+	// Test: Get
+	c, err = synthetics.GetSecureCredential(key)
+	require.NoError(t, err)
+	assert.Equal(t, (*c).Key, key)
+	assert.Equal(t, (*c).Description, testIntegrationSecureCredentialDescription)
+
+	// Test: Get (Multiple)
+	creds, err = synthetics.GetSecureCredentials()
+	require.NoError(t, err)
+	assert.Equal(t, originalCount+1, len(creds))
+
+	// Test: Update
+	c, err = synthetics.UpdateSecureCredential(c.Key, testIntegrationSecureCredentialValue, "new test value")
+	require.NoError(t, err)
+	assert.Equal(t, "new test value", (*c).Description)
+
+	// Test: Delete
+	err = synthetics.DeleteSecureCredential(key)
+	require.NoError(t, err)
+
+	// Verify Delete
+	_, err = synthetics.GetSecureCredential(key)
+	require.Error(t, err)
+}
diff --git a/pkg/synthetics/secure_credentials_test.go b/pkg/synthetics/secure_credentials_test.go
new file mode 100644
index 0000000..d7deb6a
--- /dev/null
+++ b/pkg/synthetics/secure_credentials_test.go
@@ -0,0 +1,66 @@
+// +build unit
+
+package synthetics
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+var (
+	testSecureCredentialKey         = "TEST"
+	testSecureCredentialDescription = "Test Description"
+	testSecureCredential            = &SecureCredential{
+		Key:         testSecureCredentialKey,
+		Description: testSecureCredentialDescription,
+		CreatedAt:   &testTimestamp,
+		LastUpdated: &testTimestamp,
+	}
+	testGetSecureCredentialsJson = fmt.Sprintf(`
+	{
+		"secureCredentials": [
+			{
+				"key": "%[1]s",
+				"description": "%[2]s",
+				"createdAt": "2019-11-27T19:11:05.076+0000",
+				"lastUpdated": "2019-11-27T19:11:05.076+0000"
+			}, {
+				"key": "myKey2",
+				"description": "Description of myKey2",
+				"createdAt": "2019-11-27T19:11:05.076+0000",
+				"lastUpdated": "2019-11-27T19:11:05.076+0000"
+			}
+		],
+		"count": 2
+	}
+	`, testSecureCredentialKey, testSecureCredentialDescription, testTimestamp)
+)
+
+func TestGetSecureCredentials(t *testing.T) {
+	t.Parallel()
+	synthetics := newMockResponse(t, testGetSecureCredentialsJson, http.StatusOK)
+
+	r, err := synthetics.GetSecureCredentials()
+	assert.NoError(t, err)
+	assert.Equal(t, 2, len(r))
+	assert.Equal(t, r[0], testSecureCredential)
+}
+
+func TestAddSecureCredential(t *testing.T) {
+	t.Parallel()
+	synthetics := newMockResponse(t, "", http.StatusOK)
+
+	err := synthetics.AddMonitorLabel(testSecureCredentialKey, "test", "test")
+	assert.NoError(t, err)
+}
+
+func TestDeleteSecureCredenti(t *testing.T) {
+	t.Parallel()
+	synthetics := newMockResponse(t, "", http.StatusOK)
+
+	err := synthetics.DeleteMonitorLabel(testSecureCredentialKey, "test", "test")
+	assert.NoError(t, err)
+}
diff --git a/pkg/synthetics/synthetics_test.go b/pkg/synthetics/synthetics_test.go
index 7365057..1c28af8 100644
--- a/pkg/synthetics/synthetics_test.go
+++ b/pkg/synthetics/synthetics_test.go
@@ -18,9 +18,9 @@ func TestBaseURLs(t *testing.T) {
 	t.Parallel()
 
 	pairs := map[region.Region]string{
-		region.US:      "https://synthetics.newrelic.com/synthetics/api/v4",
-		region.EU:      "https://synthetics.eu.newrelic.com/synthetics/api/v4",
-		region.Staging: "https://staging-synthetics.newrelic.com/synthetics/api/v4",
+		region.US:      "https://synthetics.newrelic.com/synthetics/api",
+		region.EU:      "https://synthetics.eu.newrelic.com/synthetics/api",
+		region.Staging: "https://staging-synthetics.newrelic.com/synthetics/api",
 	}
 
 	// Default should be region.US