Repository: amp-labs/connectors
Pull request #1041 (2024-09-13 17:44:11)

feat: Zendesk modules use URLs from schema.json
# Background

As of now the only supported resources have short URLs. There is no systematic approach for other cases.

# Changes

* The static schema file holds URL paths for every object, allowing nested REST API resources to be requested via Read/Write/Delete methods.
Some connectors have few endpoints with hardcoded mapping between ObjectName and URL. However, in this case `schema.json` was generated from the OpenAPI file, so it naturally holds the mapping. As a result, creating URLs is straightforward.
```go
path, err := metadata.Schemas.LookupURLPath(c.ModuleID, objectName)
url, err := urlbuilder.New(c.BaseURL, path)
```
* I noticed that pagination response format differs between ticketing and help center modules. I updated the parsing so that both pagination formats are handled in a fallback sequence, starting with the more likely format.
* ZendeskSupport BaseURL is modified :exclamation: :exclamation: :exclamation: 

# Test Results
Running Zendesk test scripts for backwards compatability.
![image](https://github.com/user-attachments/assets/9c8b1b02-16fe-4c39-9c95-293cb895b345)
![image](https://github.com/user-attachments/assets/2c31e41b-10d2-482e-ad4c-eb9729dc3285)
![image](https://github.com/user-attachments/assets/99a78981-2c49-4dd5-af4c-4c4995c8ce72)


# Review
[First commit](https://github.com/amp-labs/connectors/pull/1041/commits/c44521ce0216efbeb7a2e8f4b6dca45609da146c) has main code changes. [Second](https://github.com/amp-labs/connectors/pull/1041/commits/9bc54f7fff8075934648693e3283ec080d11d69c) is mock tests.

Cobalt0s: > [!WARNING]
> <b>This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack <a href="https://app.graphite.dev/github/pr/amp-labs/connectors/1041?utm_source=stack-comment-downstack-mergeability-warning" >on Graphite</a>.</b>
> <a href="https://graphite.dev/docs/merge-pull-requests">Learn more</a>

* **#1041** <a href="https://app.graphite.dev/github/pr/amp-labs/connectors/1041?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a> 👈
* **#1001** <a href="https://app.graphite.dev/github/pr/amp-labs/connectors/1001?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>: 1 other dependent PR ([#995](https://github.com/amp-labs/connectors/pull/995) <a href="https://app.graphite.dev/github/pr/amp-labs/connectors/995?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>)
* **#1000** <a href="https://app.graphite.dev/github/pr/amp-labs/connectors/1000?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>: 1 other dependent PR ([#1007](https://github.com/amp-labs/connectors/pull/1007) <a href="https://app.graphite.dev/github/pr/amp-labs/connectors/1007?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>)
* **#1015** <a href="https://app.graphite.dev/github/pr/amp-labs/connectors/1015?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#1014** <a href="https://app.graphite.dev/github/pr/amp-labs/connectors/1014?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#1013** <a href="https://app.graphite.dev/github/pr/amp-labs/connectors/1013?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* `main`

This stack of pull requests is managed by Graphite. <a href="https://stacking.dev/?utm_source=stack-comment">Learn more about stacking.</a>
<h2></h2>

 Join @Cobalt0s and the rest of your teammates on <a href="https://graphite.dev?utm-source=stack-comment"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="11px" height="11px"/> <b>Graphite</b></a>
  <!-- Current dependencies on/for this PR: -->
RajatPawar: Please add screenshots of the test results before merging this. Thanks!

Patch:
diff --git a/providers/zendesk.go b/providers/zendesk.go
index 085f2e42..733ebdc0 100644
--- a/providers/zendesk.go
+++ b/providers/zendesk.go
@@ -10,7 +10,7 @@ func init() { // nolint:funlen
 	SetInfo(ZendeskSupport, ProviderInfo{
 		DisplayName: "Zendesk Support",
 		AuthType:    Oauth2,
-		BaseURL:     "https://{{.workspace}}.zendesk.com/api",
+		BaseURL:     "https://{{.workspace}}.zendesk.com",
 		Oauth2Opts: &Oauth2Opts{
 			GrantType:                 AuthorizationCode,
 			AuthURL:                   "https://{{.workspace}}.zendesk.com/oauth/authorizations/new",
diff --git a/providers/zendesksupport/connector.go b/providers/zendesksupport/connector.go
index 4a775df8..0bb63b2c 100644
--- a/providers/zendesksupport/connector.go
+++ b/providers/zendesksupport/connector.go
@@ -6,10 +6,9 @@ import (
 	"github.com/amp-labs/connectors/common/paramsbuilder"
 	"github.com/amp-labs/connectors/common/urlbuilder"
 	"github.com/amp-labs/connectors/providers"
+	"github.com/amp-labs/connectors/providers/zendesksupport/metadata"
 )
 
-const apiVersion = "v2"
-
 type Connector struct {
 	BaseURL string
 	Client  *common.JSONHTTPClient
@@ -59,8 +58,13 @@ func (c *Connector) String() string {
 	return c.Provider() + ".Connector"
 }
 
-func (c *Connector) getURL(arg string) (*urlbuilder.URL, error) {
-	return urlbuilder.New(c.BaseURL, apiVersion, arg)
+func (c *Connector) getURL(objectName string) (*urlbuilder.URL, error) {
+	path, err := metadata.Schemas.LookupURLPath(c.Module.ID, objectName)
+	if err != nil {
+		return nil, err
+	}
+
+	return urlbuilder.New(c.BaseURL, path)
 }
 
 func (c *Connector) setBaseURL(newURL string) {
diff --git a/providers/zendesksupport/parse.go b/providers/zendesksupport/parse.go
index c5ccd53c..b7c84798 100644
--- a/providers/zendesksupport/parse.go
+++ b/providers/zendesksupport/parse.go
@@ -6,5 +6,16 @@ import (
 )
 
 func getNextRecordsURL(node *ajson.Node) (string, error) {
-	return jsonquery.New(node, "links").StrWithDefault("next", "")
+	nextPage, err := jsonquery.New(node, "links").StrWithDefault("next", "")
+	if err != nil {
+		return "", err
+	}
+
+	if len(nextPage) != 0 {
+		return nextPage, nil
+	}
+
+	// Next page can be found under different location.
+	// This format was noticed via Zendesk HelpCenter module.
+	return jsonquery.New(node).StrWithDefault("next_page", "")
 }


Test patch:
diff --git a/providers/zendesksupport/delete_test.go b/providers/zendesksupport/delete_test.go
index 2eb93d0f..61cb9a5f 100644
--- a/providers/zendesksupport/delete_test.go
+++ b/providers/zendesksupport/delete_test.go
@@ -47,8 +47,11 @@ func TestDelete(t *testing.T) { // nolint:funlen,cyclop
 			Input: common.DeleteParams{ObjectName: "brands", RecordId: "31207417638931"},
 			Server: mockserver.Conditional{
 				Setup: mockserver.ContentJSON(),
-				If:    mockcond.MethodDELETE(),
-				Then:  mockserver.Response(http.StatusNoContent),
+				If: mockcond.And{
+					mockcond.PathSuffix("/v2/brands/31207417638931"),
+					mockcond.MethodDELETE(),
+				},
+				Then: mockserver.Response(http.StatusNoContent),
 			}.Server(),
 			Expected:     &common.DeleteResult{Success: true},
 			ExpectedErrs: nil,
diff --git a/providers/zendesksupport/metadata_test.go b/providers/zendesksupport/metadata_test.go
index 2b530c1a..875325bb 100644
--- a/providers/zendesksupport/metadata_test.go
+++ b/providers/zendesksupport/metadata_test.go
@@ -12,7 +12,7 @@ import (
 	"github.com/amp-labs/connectors/test/utils/testroutines"
 )
 
-func TestListObjectMetadata(t *testing.T) { // nolint:funlen,gocognit,cyclop
+func TestListObjectMetadataZendeskSupportModule(t *testing.T) { // nolint:funlen,gocognit,cyclop
 	t.Parallel()
 
 	tests := []testroutines.Metadata{
@@ -23,8 +23,8 @@ func TestListObjectMetadata(t *testing.T) { // nolint:funlen,gocognit,cyclop
 			ExpectedErrs: []error{common.ErrMissingObjects},
 		},
 		{
-			Name:         "Unknown object requested",
-			Input:        []string{"butterflies"},
+			Name:         "Object coming from different module is unknown",
+			Input:        []string{"articles"},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{staticschema.ErrObjectNotFound},
 		},
@@ -107,6 +107,72 @@ func TestListObjectMetadata(t *testing.T) { // nolint:funlen,gocognit,cyclop
 	}
 }
 
+func TestListObjectMetadataHelpCenterModule(t *testing.T) { // nolint:funlen,gocognit,cyclop
+	t.Parallel()
+
+	tests := []testroutines.Metadata{
+		{
+			Name:         "Object coming from different module is unknown",
+			Input:        []string{"brands"},
+			Server:       mockserver.Dummy(),
+			ExpectedErrs: []error{staticschema.ErrObjectNotFound},
+		},
+		{
+			Name:   "Successfully describe one object with metadata",
+			Input:  []string{"articles"},
+			Server: mockserver.Dummy(),
+			Expected: &common.ListObjectMetadataResult{
+				Result: map[string]common.ObjectMetadata{
+					"articles": {
+						DisplayName: "Articles",
+						FieldsMap: map[string]string{
+							"author_id":           "author_id",
+							"body":                "body",
+							"comments_disabled":   "comments_disabled",
+							"content_tag_ids":     "content_tag_ids",
+							"created_at":          "created_at",
+							"draft":               "draft",
+							"edited_at":           "edited_at",
+							"html_url":            "html_url",
+							"id":                  "id",
+							"label_names":         "label_names",
+							"locale":              "locale",
+							"outdated":            "outdated",
+							"outdated_locales":    "outdated_locales",
+							"permission_group_id": "permission_group_id",
+							"position":            "position",
+							"promoted":            "promoted",
+							"section_id":          "section_id",
+							"source_locale":       "source_locale",
+							"title":               "title",
+							"updated_at":          "updated_at",
+							"url":                 "url",
+							"user_segment_id":     "user_segment_id",
+							"user_segment_ids":    "user_segment_ids",
+							"vote_count":          "vote_count",
+							"vote_sum":            "vote_sum",
+						},
+					},
+				},
+				Errors: nil,
+			},
+			ExpectedErrs: nil,
+		},
+	}
+
+	for _, tt := range tests {
+		// nolint:varnamelen
+		tt := tt // rebind, omit loop side effects for parallel goroutine
+		t.Run(tt.Name, func(t *testing.T) {
+			t.Parallel()
+
+			tt.Run(t, func() (connectors.ObjectMetadataConnector, error) {
+				return constructTestConnector(tt.Server.URL, ModuleHelpCenter)
+			})
+		})
+	}
+}
+
 func BenchmarkListObjectMetadata(b *testing.B) {
 	connector, err := NewConnector(
 		WithAuthenticatedClient(http.DefaultClient),
diff --git a/providers/zendesksupport/read_test.go b/providers/zendesksupport/read_test.go
index 828ee745..b97d111f 100644
--- a/providers/zendesksupport/read_test.go
+++ b/providers/zendesksupport/read_test.go
@@ -9,12 +9,13 @@ import (
 	"github.com/amp-labs/connectors/common"
 	"github.com/amp-labs/connectors/common/jsonquery"
 	"github.com/amp-labs/connectors/test/utils/mockutils"
+	"github.com/amp-labs/connectors/test/utils/mockutils/mockcond"
 	"github.com/amp-labs/connectors/test/utils/mockutils/mockserver"
 	"github.com/amp-labs/connectors/test/utils/testroutines"
 	"github.com/amp-labs/connectors/test/utils/testutils"
 )
 
-func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
+func TestReadZendeskSupportModule(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 	t.Parallel()
 
 	responseErrorFormat := testutils.DataFromFile(t, "resource-not-found.json")
@@ -35,6 +36,12 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{common.ErrMissingFields},
 		},
+		{
+			Name:         "Object coming from different module is unknown",
+			Input:        common.ReadParams{ObjectName: "user_segments", Fields: connectors.Fields("id")},
+			Server:       mockserver.Dummy(),
+			ExpectedErrs: []error{common.ErrOperationNotSupportedForObject},
+		},
 		{
 			Name:  "Correct error message is understood from JSON response",
 			Input: common.ReadParams{ObjectName: "triggers", Fields: connectors.Fields("id")},
@@ -114,9 +121,10 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 				ObjectName: "tickets",
 				Fields:     connectors.Fields("type", "subject", "status"),
 			},
-			Server: mockserver.Fixed{
-				Setup:  mockserver.ContentJSON(),
-				Always: mockserver.Response(http.StatusOK, responseReadTickets),
+			Server: mockserver.Conditional{
+				Setup: mockserver.ContentJSON(),
+				If:    mockcond.PathSuffix("/v2/tickets"),
+				Then:  mockserver.Response(http.StatusOK, responseReadTickets),
 			}.Server(),
 			Comparator: func(baseURL string, actual, expected *common.ReadResult) bool {
 				// custom comparison focuses on subset of fields to keep the test short
@@ -160,9 +168,71 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 	}
 }
 
-func constructTestConnector(
-	serverURL string, moduleID common.ModuleID, // nolint:unparam
-) (*Connector, error) {
+func TestReadHelpCenterModule(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
+	t.Parallel()
+
+	responseReadPosts := testutils.DataFromFile(t, "read-posts.json")
+
+	tests := []testroutines.Read{
+		{
+			Name:         "Object coming from different module is unknown",
+			Input:        common.ReadParams{ObjectName: "triggers", Fields: connectors.Fields("id")},
+			Server:       mockserver.Dummy(),
+			ExpectedErrs: []error{common.ErrOperationNotSupportedForObject},
+		},
+		{
+			Name: "Successful read with chosen fields",
+			Input: common.ReadParams{
+				ObjectName: "posts",
+				Fields:     connectors.Fields("title", "topic_id", "status"),
+			},
+			Server: mockserver.Conditional{
+				Setup: mockserver.ContentJSON(),
+				If:    mockcond.PathSuffix("/v2/community/posts"),
+				Then:  mockserver.Response(http.StatusOK, responseReadPosts),
+			}.Server(),
+			Comparator: func(baseURL string, actual, expected *common.ReadResult) bool {
+				// custom comparison focuses on subset of fields to keep the test short
+				return mockutils.ReadResultComparator.SubsetFields(actual, expected) &&
+					mockutils.ReadResultComparator.SubsetRaw(actual, expected) &&
+					actual.NextPage.String() == expected.NextPage.String() &&
+					actual.Done == expected.Done
+			},
+			Expected: &common.ReadResult{
+				Data: []common.ReadResultRow{{
+					Fields: map[string]any{
+						"title":    "How do I get around the community?",
+						"topic_id": float64(27980065395091),
+						"status":   "none",
+					},
+					Raw: map[string]any{
+						"id":         float64(27980065413139),
+						"created_at": "2024-04-01T13:01:11Z",
+						"updated_at": "2024-04-01T13:01:11Z",
+					},
+				}},
+				NextPage: "https://d3v-ampersand.zendesk.com" +
+					"/api/v2/help_center/community/posts.json?page=2&per_page=30",
+				Done: false,
+			},
+			ExpectedErrs: nil,
+		},
+	}
+
+	for _, tt := range tests {
+		// nolint:varnamelen
+		tt := tt // rebind, omit loop side effects for parallel goroutine
+		t.Run(tt.Name, func(t *testing.T) {
+			t.Parallel()
+
+			tt.Run(t, func() (connectors.ReadConnector, error) {
+				return constructTestConnector(tt.Server.URL, ModuleHelpCenter)
+			})
+		})
+	}
+}
+
+func constructTestConnector(serverURL string, moduleID common.ModuleID) (*Connector, error) {
 	connector, err := NewConnector(
 		WithAuthenticatedClient(http.DefaultClient),
 		WithWorkspace("test-workspace"),
diff --git a/providers/zendesksupport/test/read-posts.json b/providers/zendesksupport/test/read-posts.json
new file mode 100644
index 00000000..e5ec5f04
--- /dev/null
+++ b/providers/zendesksupport/test/read-posts.json
@@ -0,0 +1,33 @@
+{
+  "posts": [
+    {
+      "id": 27980065413139,
+      "title": "How do I get around the community?",
+      "details": "<p>You can use search to find answers. You can also browse topics and posts using views and filters. See <a href=\"https://support.zendesk.com/hc/en-us/articles/203664386#topic_dqx_znw_3k\" target=\"_blank\" rel=\"nofollow noreferrer\">Getting around the community</a>.</p>",
+      "author_id": 26363624373267,
+      "vote_sum": 0,
+      "vote_count": 0,
+      "comment_count": 0,
+      "follower_count": 0,
+      "topic_id": 27980065395091,
+      "html_url": "https://d3v-ampersand.zendesk.com/hc/en-us/community/posts/27980065413139-How-do-I-get-around-the-community",
+      "created_at": "2024-04-01T13:01:11Z",
+      "updated_at": "2024-04-01T13:01:11Z",
+      "url": "https://d3v-ampersand.zendesk.com/api/v2/help_center/community/posts/27980065413139-How-do-I-get-around-the-community.json",
+      "featured": false,
+      "pinned": false,
+      "closed": false,
+      "frozen": false,
+      "status": "none",
+      "non_author_editor_id": null,
+      "non_author_updated_at": null,
+      "content_tag_ids": []
+    }
+  ],
+  "page": 1,
+  "previous_page": null,
+  "next_page": "https://d3v-ampersand.zendesk.com/api/v2/help_center/community/posts.json?page=2&per_page=30",
+  "per_page": 30,
+  "page_count": 2,
+  "count": 32
+}
diff --git a/providers/zendesksupport/test/write-post.json b/providers/zendesksupport/test/write-post.json
new file mode 100644
index 00000000..ad6f24be
--- /dev/null
+++ b/providers/zendesksupport/test/write-post.json
@@ -0,0 +1,25 @@
+{
+  "post": {
+    "id": 33507191590803,
+    "title": "Help!",
+    "details": "My printer is on fire!",
+    "author_id": 26363624373267,
+    "vote_sum": 0,
+    "vote_count": 0,
+    "comment_count": 0,
+    "follower_count": 0,
+    "topic_id": 27980065395091,
+    "html_url": "https://d3v-ampersand.zendesk.com/hc/en-us/community/posts/33507191590803-Help",
+    "created_at": "2024-09-17T20:15:16Z",
+    "updated_at": "2024-09-17T20:15:16Z",
+    "url": "https://d3v-ampersand.zendesk.com/api/v2/help_center/community/posts/33507191590803-Help.json",
+    "featured": false,
+    "pinned": false,
+    "closed": false,
+    "frozen": false,
+    "status": "none",
+    "non_author_editor_id": null,
+    "non_author_updated_at": null,
+    "content_tag_ids": []
+  }
+}
diff --git a/providers/zendesksupport/write_test.go b/providers/zendesksupport/write_test.go
index a633f143..3da60b4d 100644
--- a/providers/zendesksupport/write_test.go
+++ b/providers/zendesksupport/write_test.go
@@ -14,7 +14,7 @@ import (
 	"github.com/amp-labs/connectors/test/utils/testutils"
 )
 
-func TestWrite(t *testing.T) { // nolint:funlen,cyclop
+func TestWriteZendeskSupportModule(t *testing.T) { // nolint:funlen,cyclop
 	t.Parallel()
 
 	// server-error.json occurs when trying to Create object without payload name.
@@ -37,6 +37,12 @@ func TestWrite(t *testing.T) { // nolint:funlen,cyclop
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{common.ErrMissingRecordData},
 		},
+		{
+			Name:         "Unsupported object name",
+			Input:        common.WriteParams{ObjectName: "signals", RecordData: "dummy"},
+			Server:       mockserver.Dummy(),
+			ExpectedErrs: []error{common.ErrResolvingURLPathForObject},
+		},
 		{
 			Name:  "Missing write parameter",
 			Input: common.WriteParams{ObjectName: "brands", RecordData: "dummy"},
@@ -108,8 +114,11 @@ func TestWrite(t *testing.T) { // nolint:funlen,cyclop
 			Input: common.WriteParams{ObjectName: "brands", RecordData: "dummy"},
 			Server: mockserver.Conditional{
 				Setup: mockserver.ContentJSON(),
-				If:    mockcond.MethodPOST(),
-				Then:  mockserver.Response(http.StatusOK, createBrand),
+				If: mockcond.And{
+					mockcond.PathSuffix("/v2/brands"),
+					mockcond.MethodPOST(),
+				},
+				Then: mockserver.Response(http.StatusOK, createBrand),
 			}.Server(),
 			Comparator: func(serverURL string, actual, expected *common.WriteResult) bool {
 				return mockutils.WriteResultComparator.SubsetData(actual, expected)
@@ -143,3 +152,50 @@ func TestWrite(t *testing.T) { // nolint:funlen,cyclop
 		})
 	}
 }
+
+func TestWriteHelpCenterModule(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
+	t.Parallel()
+
+	responseCreatePost := testutils.DataFromFile(t, "write-post.json")
+
+	tests := []testroutines.Write{
+		{
+			Name:  "Creating a help center post invokes correct endpoint",
+			Input: common.WriteParams{ObjectName: "posts", RecordData: "dummy"},
+			Server: mockserver.Conditional{
+				Setup: mockserver.ContentJSON(),
+				If: mockcond.And{
+					mockcond.PathSuffix("/v2/community/posts"),
+					mockcond.MethodPOST(),
+				},
+				Then: mockserver.Response(http.StatusOK, responseCreatePost),
+			}.Server(),
+			Comparator: func(serverURL string, actual, expected *common.WriteResult) bool {
+				return mockutils.WriteResultComparator.SubsetData(actual, expected)
+			},
+			Expected: &common.WriteResult{
+				Success:  true,
+				RecordId: "33507191590803",
+				Errors:   nil,
+				Data: map[string]any{
+					"id":      float64(33507191590803),
+					"title":   "Help!",
+					"details": "My printer is on fire!",
+				},
+			},
+			ExpectedErrs: nil,
+		},
+	}
+
+	for _, tt := range tests {
+		// nolint:varnamelen
+		tt := tt // rebind, omit loop side effects for parallel goroutine
+		t.Run(tt.Name, func(t *testing.T) {
+			t.Parallel()
+
+			tt.Run(t, func() (connectors.WriteConnector, error) {
+				return constructTestConnector(tt.Server.URL, ModuleHelpCenter)
+			})
+		})
+	}
+}
diff --git a/test/zendesksupport/metadata/main.go b/test/zendesksupport/metadata/main.go
index 65041b94..a42db013 100644
--- a/test/zendesksupport/metadata/main.go
+++ b/test/zendesksupport/metadata/main.go
@@ -6,6 +6,7 @@ import (
 	"os/signal"
 	"syscall"
 
+	"github.com/amp-labs/connectors"
 	"github.com/amp-labs/connectors/common"
 	"github.com/amp-labs/connectors/test/utils"
 	"github.com/amp-labs/connectors/test/utils/mockutils"
@@ -29,6 +30,7 @@ func main() {
 
 	response, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: objectName,
+		Fields:     connectors.Fields("name", "time_zone", "role"),
 	})
 	if err != nil {
 		utils.Fail("error reading from ZendeskSupport", "error", err)