Repository: amp-labs/connectors
Pull request #998 (2024-08-27 13:49:36)

[ENG-1498] feat: Make ReadParams.Fields unique
# Behaviour Change
Read operation before:
![image](https://github.com/user-attachments/assets/8c69dc0f-bff3-4237-8021-df8a0e5ee649)
After the change:
![image](https://github.com/user-attachments/assets/b0d7ddaa-9351-4580-b2c6-046c409535a8)

# Changes
All read access to `common.ReadParams.Fields` is replaced with `common.ReadParams.GetUniqueFields()`.

# Considirations
This implementation does not affect `go` struct inits. Creating a dedicated constructor method felt very restrictive.
Changing HTTP Client was to late and much control is lost at this level (some connectors pass fields in special ways, syntax, so no assumptions can be made here). Calling a utility method makes it very disconnected and doesn't send a right message. Hence the member method.




Cobalt0s: * **#998** <a href="https://app.graphite.dev/github/pr/amp-labs/connectors/998?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: -->
Cobalt0s: @RajatPawar 

I like this idea. This aliasing will make easier usage for sure. I am not a fan of using handy outside as I mentioned. More of internal utility.
![image](https://github.com/user-attachments/assets/72b620d9-13ef-4272-878e-641dbffeeef8)

However, speaking about utils naming. One utils repo I removed by doing several PRs making such obsolete. It is usually suggested not to name packages with very general names. With growing codebase it may go out of hand when utils is everywhere. But I cannot come up with a better naming. We have multiple utils but that utility is for different context so it has a different flavour or color, the way I like to think about it. Some current utils I called `mockutils`, `testutils`, `testroutines`, `naming`, `jsonquery`, etc. All of them where utils originally but then evolved separately.
Mostly `handy` deals with data structures or universal golang utilities like pointer creation or struct cloning.

We could rename handy but maybe in a different PR. Maybe better split it. I would prefer some data structure package. What would be the name? 
* `dst`  - data structure
* `container`
* `housing` - there will be no naming collisions ahah. (utils doens't tell much, but housing too, but more memorable once you know). The house because data has a place to live: as a map or as a list, etc.
* `pile` - data pile, pile.DefaultMap
* `holder`
* other




laurenzlong: Hey @Cobalt0s what happens if the caller puts in duplicate fields?

```
result, err := conn.Read(ctx, connectors.ReadParams{
  ObjectName: "Contact",
  Fields: connectors.Fields("FirstName", "FirstName") // ideally this should not error out
})
```

Can you please start a test file inside `handy` and add a test case for this?

Can you also create a PR in the server repo to update how we are calling the Read method?


Cobalt0s: * **#998** <a href="https://app.graphite.dev/github/pr/amp-labs/connectors/998?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: -->
Cobalt0s: > Hey @Cobalt0s what happens if the caller puts in duplicate fields?
> 
> ```
> result, err := conn.Read(ctx, connectors.ReadParams{
>   ObjectName: "Contact",
>   Fields: connectors.Fields("FirstName", "FirstName") // ideally this should not error out
> })
> ```
> 
> Can you please start a test file inside `handy` and add a test case for this?
> 
> Can you also create a PR in the server repo to update how we are calling the Read method?

@laurenzlong 

Added test suite: https://github.com/amp-labs/connectors/blob/5ef9f57e75297b5c6525527590f3939d5c9df98f/common/handy/set_test.go.
Created draft PR which will be updated once this one is merged: https://github.com/amp-labs/server/pull/1708


Patch:
diff --git a/common/handy/maps.go b/common/handy/maps.go
index d2fbb177..f8dfe5a3 100644
--- a/common/handy/maps.go
+++ b/common/handy/maps.go
@@ -14,7 +14,7 @@ func (m Map[K, V]) Keys() []K {
 }
 
 func (m Map[K, V]) KeySet() Set[K] {
-	return NewSet(m.Keys())
+	return NewSetFromList(m.Keys())
 }
 
 func (m Map[K, V]) Has(key K) bool {
diff --git a/common/handy/set.go b/common/handy/set.go
index b1920bae..c891d60c 100644
--- a/common/handy/set.go
+++ b/common/handy/set.go
@@ -1,11 +1,23 @@
 package handy
 
+// NewStringSet and StringSet are Aliases.
+var NewStringSet = NewSet[string] // nolint:gochecknoglobals
+type StringSet = Set[string]
+
 // Set data structure that can hold any type of data.
 type Set[T comparable] map[T]struct{}
 
-// NewSet creates a set from slice.
-func NewSet[V comparable](values []V) Set[V] {
-	result := make(Set[V])
+// NewSet creates a set from multiple values.
+func NewSet[V comparable](values ...V) Set[V] {
+	result := make(Set[V], len(values))
+	result.Add(values)
+
+	return result
+}
+
+// NewSetFromList creates a set from slice.
+func NewSetFromList[V comparable](values []V) Set[V] {
+	result := make(Set[V], len(values))
 	result.Add(values)
 
 	return result
@@ -25,9 +37,12 @@ func (s Set[T]) AddOne(value T) {
 
 // List returns unique set in a shape of a slice.
 func (s Set[T]) List() []T {
-	list := make([]T, 0)
+	list := make([]T, len(s))
+	index := 0
+
 	for v := range s {
-		list = append(list, v)
+		list[index] = v
+		index += 1
 	}
 
 	return list
diff --git a/common/parse.go b/common/parse.go
index 603d443a..ed21752f 100644
--- a/common/parse.go
+++ b/common/parse.go
@@ -3,6 +3,7 @@ package common
 import (
 	"strings"
 
+	"github.com/amp-labs/connectors/common/handy"
 	"github.com/amp-labs/connectors/common/jsonquery"
 	"github.com/spyzhov/ajson"
 )
@@ -23,7 +24,7 @@ func ParseResult(
 	recordsFunc func(*ajson.Node) ([]map[string]any, error),
 	nextPageFunc func(*ajson.Node) (string, error),
 	marshalFunc func([]map[string]any, []string) ([]ReadResultRow, error),
-	fields []string,
+	fields handy.Set[string],
 ) (*ReadResult, error) {
 	body, ok := resp.Body()
 	if !ok {
@@ -40,7 +41,7 @@ func ParseResult(
 		return nil, err
 	}
 
-	marshaledData, err := marshalFunc(records, fields)
+	marshaledData, err := marshalFunc(records, fields.List())
 	if err != nil {
 		return nil, err
 	}
diff --git a/common/types.go b/common/types.go
index af66f3db..736acfc0 100644
--- a/common/types.go
+++ b/common/types.go
@@ -5,6 +5,8 @@ import (
 	"errors"
 	"fmt"
 	"time"
+
+	"github.com/amp-labs/connectors/common/handy"
 )
 
 var (
@@ -92,7 +94,7 @@ type ReadParams struct {
 	// The name of the object we are reading, e.g. "Account"
 	ObjectName string // required
 	// The fields we are reading from the object, e.g. ["Id", "Name", "BillingCity"]
-	Fields []string // required, at least one field needed
+	Fields handy.StringSet // required, at least one field needed
 	// NextPage is an opaque token that can be used to get the next page of results.
 	NextPage NextPageToken // optional, only set this if you want to read the next page of results
 	// Since is a timestamp that can be used to get only records that have changed since that time.
diff --git a/connectors.go b/connectors.go
index 13fc6ffc..fc8d8f7e 100644
--- a/connectors.go
+++ b/connectors.go
@@ -6,6 +6,7 @@ import (
 	"io"
 
 	"github.com/amp-labs/connectors/common"
+	"github.com/amp-labs/connectors/common/handy"
 	"github.com/amp-labs/connectors/providers"
 )
 
@@ -99,3 +100,5 @@ type (
 
 	ErrorWithStatus = common.HTTPStatusError
 )
+
+var Fields = handy.NewStringSet // nolint:gochecknoglobals
diff --git a/examples/deep_connectors/salesforce/salesforce.go b/examples/deep_connectors/salesforce/salesforce.go
index 4446c623..7cf842b1 100644
--- a/examples/deep_connectors/salesforce/salesforce.go
+++ b/examples/deep_connectors/salesforce/salesforce.go
@@ -46,7 +46,7 @@ func salesforceDeepExample(ctx context.Context) error {
 	// Make a read request to Salesforce
 	result, err := conn.Read(ctx, connectors.ReadParams{
 		ObjectName: "Contact",
-		Fields:     []string{"FirstName", "LastName", "Email"},
+		Fields:     connectors.Fields("FirstName", "LastName", "Email"),
 	})
 	if err != nil {
 		return err
@@ -59,7 +59,7 @@ func salesforceDeepExample(ctx context.Context) error {
 	for !result.Done {
 		result, err = conn.Read(ctx, connectors.ReadParams{
 			ObjectName: "Contact",
-			Fields:     []string{"FirstName", "LastName", "Email"},
+			Fields:     connectors.Fields("FirstName", "LastName", "Email"),
 			NextPage:   result.NextPage,
 		})
 		if err != nil {
diff --git a/providers/dynamicscrm/read.go b/providers/dynamicscrm/read.go
index 16675163..8a4bf726 100644
--- a/providers/dynamicscrm/read.go
+++ b/providers/dynamicscrm/read.go
@@ -55,8 +55,9 @@ func (c *Connector) buildReadURL(config common.ReadParams) (*urlbuilder.URL, err
 		return nil, err
 	}
 
-	if len(config.Fields) != 0 {
-		url.WithQueryParam("$select", strings.Join(config.Fields, ","))
+	fields := config.Fields.List()
+	if len(fields) != 0 {
+		url.WithQueryParam("$select", strings.Join(fields, ","))
 	}
 
 	return url, nil
diff --git a/providers/gong/objectNames.go b/providers/gong/objectNames.go
index 26499953..93628d99 100644
--- a/providers/gong/objectNames.go
+++ b/providers/gong/objectNames.go
@@ -6,6 +6,6 @@ import (
 )
 
 // Supported object names can be found under schemas.json.
-var supportedObjectsByRead = handy.NewSet( //nolint:gochecknoglobals
+var supportedObjectsByRead = handy.NewSetFromList( //nolint:gochecknoglobals
 	metadata.Schemas.GetObjectNames(),
 )
diff --git a/providers/hubspot/read.go b/providers/hubspot/read.go
index 36aa858f..69e4bc61 100644
--- a/providers/hubspot/read.go
+++ b/providers/hubspot/read.go
@@ -78,8 +78,9 @@ func (c *Connector) Read(ctx context.Context, config common.ReadParams) (*common
 func makeQueryValues(config common.ReadParams) string {
 	queryValues := url.Values{}
 
-	if len(config.Fields) != 0 {
-		queryValues.Add("properties", strings.Join(config.Fields, ","))
+	fields := config.Fields.List()
+	if len(fields) != 0 {
+		queryValues.Add("properties", strings.Join(fields, ","))
 	}
 
 	if config.Deleted {
diff --git a/providers/hubspot/search.go b/providers/hubspot/search.go
index 1b485095..99e044df 100644
--- a/providers/hubspot/search.go
+++ b/providers/hubspot/search.go
@@ -99,7 +99,7 @@ func makeFilterBody(config SearchParams) map[string]any {
 	}
 
 	if config.Fields != nil {
-		filterBody["properties"] = config.Fields
+		filterBody["properties"] = config.Fields.List()
 	}
 
 	return filterBody
diff --git a/providers/hubspot/types.go b/providers/hubspot/types.go
index 45553ac9..03b97795 100644
--- a/providers/hubspot/types.go
+++ b/providers/hubspot/types.go
@@ -1,6 +1,9 @@
 package hubspot
 
-import "github.com/amp-labs/connectors/common"
+import (
+	"github.com/amp-labs/connectors/common"
+	"github.com/amp-labs/connectors/common/handy"
+)
 
 type SearchParams struct {
 	// The name of the object we are reading, e.g. "Account"
@@ -12,7 +15,7 @@ type SearchParams struct {
 	// FilterBy is the filter to apply to the search
 	FilterGroups []FilterGroup // optional
 	// Fields is the list of fields to return in the result.
-	Fields []string // optional
+	Fields handy.Set[string] // optional
 }
 
 func (p SearchParams) ValidateParams() error {
diff --git a/providers/instantly/objectNames.go b/providers/instantly/objectNames.go
index 35bceb35..9da9e2e4 100644
--- a/providers/instantly/objectNames.go
+++ b/providers/instantly/objectNames.go
@@ -12,24 +12,24 @@ const (
 	objectNameUniboxReplies    = "unibox-replies"
 )
 
-var supportedObjectsByRead = handy.NewSet([]string{ //nolint:gochecknoglobals
+var supportedObjectsByRead = handy.NewSet( //nolint:gochecknoglobals
 	// Object Name	----------	API endpoint path
 	objectNameCampaigns, // campaign/list
 	objectNameAccounts,  // account/list
 	objectNameEmails,    // unibox/emails
 	objectNameTags,      // custom-tag
-})
+)
 
-var supportedObjectsByWrite = handy.NewSet([]string{ //nolint:gochecknoglobals
+var supportedObjectsByWrite = handy.NewSet( //nolint:gochecknoglobals
 	// Object Name	----------	API endpoint path
 	objectNameTags,             // custom-tag
 	objectNameLeads,            // lead/add
 	objectNameBlocklistEntries, // blocklist/add/entries
 	objectNameUniboxReplies,    // unibox/emails/reply
-})
+)
 
-var supportedObjectsByDelete = handy.NewSet([]string{ //nolint:gochecknoglobals
+var supportedObjectsByDelete = handy.NewSet( //nolint:gochecknoglobals
 	// Delete tag.
 	// https://developer.instantly.ai/tags/delete-a-tag
 	objectNameTags,
-})
+)
diff --git a/providers/intercom/objectNames.go b/providers/intercom/objectNames.go
index c016caee..3ae5c7ce 100644
--- a/providers/intercom/objectNames.go
+++ b/providers/intercom/objectNames.go
@@ -6,7 +6,7 @@ import (
 )
 
 // Supported object names can be found under schemas.json.
-var supportedObjectsByRead = handy.NewSet( //nolint:gochecknoglobals
+var supportedObjectsByRead = handy.NewSetFromList( //nolint:gochecknoglobals
 	metadata.Schemas.GetObjectNames(),
 )
 
diff --git a/providers/pipeliner/objectNames.go b/providers/pipeliner/objectNames.go
index b2df4148..c5b640cb 100644
--- a/providers/pipeliner/objectNames.go
+++ b/providers/pipeliner/objectNames.go
@@ -6,6 +6,6 @@ import (
 )
 
 // Supported object names can be found under schemas.json.
-var supportedObjectsByRead = handy.NewSet( //nolint:gochecknoglobals
+var supportedObjectsByRead = handy.NewSetFromList( //nolint:gochecknoglobals
 	metadata.Schemas.GetObjectNames(),
 )
diff --git a/providers/salesforce/bulk-info.go b/providers/salesforce/bulk-info.go
index 4174fe75..0ad1b0a6 100644
--- a/providers/salesforce/bulk-info.go
+++ b/providers/salesforce/bulk-info.go
@@ -36,7 +36,7 @@ func (c *Connector) ListIngestJobsInfo(ctx context.Context, jobIds ...string) ([
 	// If we have jobIds, we create a set to keep track of the matches we need to find. Each time we get
 	// a match, we remove it from the set. If the set is empty, we can break the loop to save time and unnecessary
 	// pagination.
-	pending := handy.NewSet[string](jobIds)
+	pending := handy.NewSetFromList(jobIds)
 
 	// To keep track of pages
 	location := url.String()
diff --git a/providers/salesforce/read.go b/providers/salesforce/read.go
index 5b04db17..6d4a5fbf 100644
--- a/providers/salesforce/read.go
+++ b/providers/salesforce/read.go
@@ -54,7 +54,7 @@ func (c *Connector) buildReadURL(config common.ReadParams) (*urlbuilder.URL, err
 
 // makeSOQL returns the SOQL query for the desired read operation.
 func makeSOQL(config common.ReadParams) *soqlBuilder {
-	soql := (&soqlBuilder{}).SelectFields(config.Fields).From(config.ObjectName)
+	soql := (&soqlBuilder{}).SelectFields(config.Fields.List()).From(config.ObjectName)
 
 	// If Since is not set, then we're doing a backfill. We read all rows (in pages)
 	if !config.Since.IsZero() {
diff --git a/providers/salesloft/objectNames.go b/providers/salesloft/objectNames.go
index 2647abe6..e2be6aad 100644
--- a/providers/salesloft/objectNames.go
+++ b/providers/salesloft/objectNames.go
@@ -6,6 +6,6 @@ import (
 )
 
 // Supported object names can be found under schemas.json.
-var supportedObjectsByRead = handy.NewSet( //nolint:gochecknoglobals
+var supportedObjectsByRead = handy.NewSetFromList( //nolint:gochecknoglobals
 	metadata.Schemas.GetObjectNames(),
 )
diff --git a/providers/smartlead/objectNames.go b/providers/smartlead/objectNames.go
index 34d2f656..55a9b55b 100644
--- a/providers/smartlead/objectNames.go
+++ b/providers/smartlead/objectNames.go
@@ -12,16 +12,16 @@ const (
 )
 
 // Supported object names can be found under schemas.json.
-var supportedObjectsByRead = handy.NewSet( //nolint:gochecknoglobals
+var supportedObjectsByRead = handy.NewSetFromList( //nolint:gochecknoglobals
 	metadata.Schemas.GetObjectNames(),
 )
 
-var supportedObjectsByWrite = handy.NewSet([]string{ //nolint:gochecknoglobals
+var supportedObjectsByWrite = handy.NewSet( //nolint:gochecknoglobals
 	objectNameCampaign,
 	objectNameEmailAccount,
 	objectNameClient,
-})
+)
 
-var supportedObjectsByDelete = handy.NewSet([]string{ //nolint:gochecknoglobals
+var supportedObjectsByDelete = handy.NewSet( //nolint:gochecknoglobals
 	objectNameCampaign,
-})
+)
diff --git a/providers/zendesksupport/objectNames.go b/providers/zendesksupport/objectNames.go
index 0d6cece4..8cadcb2c 100644
--- a/providers/zendesksupport/objectNames.go
+++ b/providers/zendesksupport/objectNames.go
@@ -6,7 +6,7 @@ import (
 )
 
 // Supported object names can be found under schemas.json.
-var supportedObjectsByRead = handy.NewSet( //nolint:gochecknoglobals
+var supportedObjectsByRead = handy.NewSetFromList( //nolint:gochecknoglobals
 	metadata.Schemas.GetObjectNames(),
 )
 
diff --git a/scripts/openapi/gong/metadata/main.go b/scripts/openapi/gong/metadata/main.go
index 0cce67b6..90377650 100644
--- a/scripts/openapi/gong/metadata/main.go
+++ b/scripts/openapi/gong/metadata/main.go
@@ -15,7 +15,7 @@ var (
 	// Even though OpenAPI docs and official documentation say that some query parameters are required
 	// in practice you still can make an API call without any specified.
 	// Must include "calls" object.
-	queryParamFilterExceptions = handy.NewSet([]string{"calls"}) // nolint:gochecknoglobals
+	queryParamFilterExceptions = handy.NewSet("calls") // nolint:gochecknoglobals
 
 	ignoreEndpoints = []string{ // nolint:gochecknoglobals
 		"/v2/settings/scorecards",
diff --git a/tools/fileconv/api3/strategy.go b/tools/fileconv/api3/strategy.go
index 6f84b019..0beffdcc 100644
--- a/tools/fileconv/api3/strategy.go
+++ b/tools/fileconv/api3/strategy.go
@@ -13,14 +13,14 @@ import (
 // Suffix:	*/batch		- ignores paths ending with batch
 // Prefix:	/v2/*		- ignores paths starting with v2.
 type ignorePathStrategy struct {
-	ignoreEndpoints handy.Set[string]
+	ignoreEndpoints handy.StringSet
 	prefixes        []string
 	suffixes        []string
 }
 
 func newIgnorePathStrategy(endpoints []string) *ignorePathStrategy {
 	result := &ignorePathStrategy{
-		ignoreEndpoints: handy.NewSet([]string{}),
+		ignoreEndpoints: handy.NewStringSet(),
 		prefixes:        make([]string, 0),
 		suffixes:        make([]string, 0),
 	}
diff --git a/tools/scrapper/models.go b/tools/scrapper/models.go
index fb1b4e45..35e2671a 100644
--- a/tools/scrapper/models.go
+++ b/tools/scrapper/models.go
@@ -130,7 +130,7 @@ type queryParamObjectStats struct {
 // CalculateQueryParamStats produces statistics on objects and their query parameters.
 // queryParamRegistry - holds query parameter name to the list of object names that use it.
 func CalculateQueryParamStats(queryParamRegistry handy.Lists[string]) *QueryParamStats {
-	objects := handy.NewSet([]string{})
+	objects := handy.NewStringSet()
 	for _, objectNames := range queryParamRegistry {
 		objects.Add(objectNames)
 	}


Test patch:
diff --git a/common/handy/set_test.go b/common/handy/set_test.go
new file mode 100644
index 00000000..21e72b73
--- /dev/null
+++ b/common/handy/set_test.go
@@ -0,0 +1,47 @@
+package handy
+
+import (
+	"sort"
+	"testing"
+
+	"github.com/amp-labs/connectors/test/utils/testutils"
+)
+
+func TestStringSet(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		name     string
+		input    []string
+		expected []string
+	}{
+		{
+			name:     "Empty set",
+			input:    []string{},
+			expected: []string{},
+		},
+		{
+			name:     "Multiple elements",
+			input:    []string{"apple", "pineapple"},
+			expected: []string{"apple", "pineapple"},
+		},
+		{
+			name:     "Item repetitions",
+			input:    []string{"apple", "kiwi", "kiwi", "orange", "pineapple", "kiwi"},
+			expected: []string{"apple", "kiwi", "orange", "pineapple"},
+		},
+	}
+
+	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()
+
+			set := NewStringSet(tt.input...)
+			output := set.List()
+			sort.Strings(output)
+			testutils.CheckOutputWithError(t, tt.name, tt.expected, nil, output, nil)
+		})
+	}
+}
diff --git a/providers/atlassian/read_test.go b/providers/atlassian/read_test.go
index 63bcc824..ac63896f 100644
--- a/providers/atlassian/read_test.go
+++ b/providers/atlassian/read_test.go
@@ -40,13 +40,13 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:         "Mime response header expected",
-			Input:        common.ReadParams{ObjectName: "issues", Fields: []string{"id"}},
+			Input:        common.ReadParams{ObjectName: "issues", Fields: connectors.Fields("id")},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{interpreter.ErrMissingContentType},
 		},
 		{
 			Name:  "Correct error message is understood from JSON response",
-			Input: common.ReadParams{ObjectName: "issues", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "issues", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusBadRequest)
@@ -59,7 +59,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Invalid path understood as not found error",
-			Input: common.ReadParams{ObjectName: "issues", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "issues", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusNotFound)
@@ -72,7 +72,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Incorrect key in payload",
-			Input: common.ReadParams{ObjectName: "issues", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "issues", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -84,7 +84,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Incorrect data type in payload",
-			Input: common.ReadParams{ObjectName: "issues", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "issues", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -96,7 +96,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Empty array produces no next page",
-			Input: common.ReadParams{ObjectName: "issues", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "issues", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -118,7 +118,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Issue must have fields property",
-			Input: common.ReadParams{ObjectName: "issues", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "issues", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -131,7 +131,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Issue must have id property",
-			Input: common.ReadParams{ObjectName: "issues", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "issues", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -144,7 +144,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Missing starting index produces no next page",
-			Input: common.ReadParams{ObjectName: "issues", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "issues", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -167,7 +167,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Next page is implied from start index and issues size",
-			Input: common.ReadParams{ObjectName: "issues", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "issues", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -193,7 +193,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Name: "Since rounds to minute time frame",
 			Input: common.ReadParams{
 				ObjectName: "issues",
-				Fields:     []string{"id"},
+				Fields:     connectors.Fields("id"),
 				Since:      time.Now().Add(-5 * time.Minute),
 			},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -222,7 +222,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Name: "Next page is propagated in query params",
 			Input: common.ReadParams{
 				ObjectName: "issues",
-				Fields:     []string{"id"},
+				Fields:     connectors.Fields("id"),
 				NextPage:   "17",
 			},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -253,7 +253,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Name: "Successful list of rows",
 			Input: common.ReadParams{
 				ObjectName: "issues",
-				Fields:     []string{"id", "summary"},
+				Fields:     connectors.Fields("id", "summary"),
 			},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
@@ -324,7 +324,10 @@ func TestReadWithoutMetadata(t *testing.T) {
 		t.Fatal("failed to create connector")
 	}
 
-	_, err = connector.Read(context.Background(), common.ReadParams{ObjectName: "issues", Fields: []string{"id"}})
+	_, err = connector.Read(context.Background(), common.ReadParams{
+		ObjectName: "issues",
+		Fields:     connectors.Fields("id"),
+	})
 	if !errors.Is(err, ErrMissingCloudId) {
 		t.Fatalf("expected Read method to complain about missing cloud id")
 	}
diff --git a/providers/dynamicscrm/read_test.go b/providers/dynamicscrm/read_test.go
index cf8c5125..dbacbd89 100644
--- a/providers/dynamicscrm/read_test.go
+++ b/providers/dynamicscrm/read_test.go
@@ -35,13 +35,13 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 		},
 		{
 			Name:         "Mime response header expected",
-			Input:        common.ReadParams{ObjectName: "contact", Fields: []string{"id"}},
+			Input:        common.ReadParams{ObjectName: "contact", Fields: connectors.Fields("id")},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{interpreter.ErrMissingContentType},
 		},
 		{
 			Name:  "Correct error message is understood from JSON response",
-			Input: common.ReadParams{ObjectName: "contact", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "contact", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusBadRequest)
@@ -58,7 +58,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 		},
 		{
 			Name:  "Incorrect key in payload",
-			Input: common.ReadParams{ObjectName: "contact", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "contact", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -70,7 +70,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 		},
 		{
 			Name:  "Incorrect data type in payload",
-			Input: common.ReadParams{ObjectName: "contact", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "contact", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -82,7 +82,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 		},
 		{
 			Name:  "Next page cursor may be missing in payload",
-			Input: common.ReadParams{ObjectName: "contact", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "contact", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -98,7 +98,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 		},
 		{
 			Name:  "Successful read with chosen fields",
-			Input: common.ReadParams{ObjectName: "contact", Fields: []string{"fullname", "fax"}},
+			Input: common.ReadParams{ObjectName: "contact", Fields: connectors.Fields("fullname", "fax")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
diff --git a/providers/gong/read_test.go b/providers/gong/read_test.go
index 6ce82be9..322cf97b 100644
--- a/providers/gong/read_test.go
+++ b/providers/gong/read_test.go
@@ -36,19 +36,19 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 		},
 		{
 			Name:         "Unsupported object name",
-			Input:        common.ReadParams{ObjectName: "butterflies", Fields: []string{"id"}},
+			Input:        common.ReadParams{ObjectName: "butterflies", Fields: connectors.Fields("id")},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{common.ErrOperationNotSupportedForObject},
 		},
 		{
 			Name:         "Mime response header expected",
-			Input:        common.ReadParams{ObjectName: "calls", Fields: []string{"id"}},
+			Input:        common.ReadParams{ObjectName: "calls", Fields: connectors.Fields("id")},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{interpreter.ErrMissingContentType},
 		},
 		{
 			Name:  "Incorrect key in payload",
-			Input: common.ReadParams{ObjectName: "calls", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "calls", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -60,7 +60,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 		},
 		{
 			Name:  "Bad request handling test",
-			Input: common.ReadParams{ObjectName: "calls", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "calls", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusBadRequest)
@@ -77,7 +77,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 		},
 		{
 			Name:  "Records section is missing in the payload",
-			Input: common.ReadParams{ObjectName: "calls", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "calls", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -89,7 +89,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 		},
 		{
 			Name:  "currentPageSize may be missing in payload",
-			Input: common.ReadParams{ObjectName: "calls", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "calls", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -112,7 +112,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 		},
 		{
 			Name:  "Successful read with 2 entries without cursor/next page",
-			Input: common.ReadParams{ObjectName: "calls", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "calls", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -150,7 +150,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 
 		{
 			Name:  "Successful read with 2 entries and cursor for next page",
-			Input: common.ReadParams{ObjectName: "calls", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "calls", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -188,7 +188,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 		},
 		{
 			Name:  "Incorrect data type in payload",
-			Input: common.ReadParams{ObjectName: "calls", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "calls", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
diff --git a/providers/instantly/read_test.go b/providers/instantly/read_test.go
index eb2fa1d0..68e9ad58 100644
--- a/providers/instantly/read_test.go
+++ b/providers/instantly/read_test.go
@@ -40,13 +40,13 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:         "Mime response header expected",
-			Input:        common.ReadParams{ObjectName: "campaigns", Fields: []string{"id"}},
+			Input:        common.ReadParams{ObjectName: "campaigns", Fields: connectors.Fields("id")},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{interpreter.ErrMissingContentType},
 		},
 		{
 			Name:     "Unknown object name is not supported",
-			Input:    common.ReadParams{ObjectName: "orders", Fields: []string{"id"}},
+			Input:    common.ReadParams{ObjectName: "orders", Fields: connectors.Fields("id")},
 			Server:   mockserver.Dummy(),
 			Expected: nil,
 			ExpectedErrs: []error{
@@ -55,7 +55,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Correct error message is understood from JSON response",
-			Input: common.ReadParams{ObjectName: "campaigns", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "campaigns", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusNotFound)
@@ -68,7 +68,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Incorrect key in payload",
-			Input: common.ReadParams{ObjectName: "emails", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "emails", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -80,7 +80,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Incorrect data type in payload",
-			Input: common.ReadParams{ObjectName: "emails", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "emails", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -95,7 +95,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Input: common.ReadParams{
 				ObjectName: "campaigns",
 				NextPage:   "test-placeholder?skip=700",
-				Fields:     []string{"id"},
+				Fields:     connectors.Fields("id"),
 			},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
@@ -122,7 +122,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Name: "Current empty page signifies no next page",
 			Input: common.ReadParams{
 				ObjectName: "campaigns",
-				Fields:     []string{"id"},
+				Fields:     connectors.Fields("id"),
 			},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
@@ -145,7 +145,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Name: "Read campaigns with chosen fields",
 			Input: common.ReadParams{
 				ObjectName: "campaigns",
-				Fields:     []string{"name"},
+				Fields:     connectors.Fields("name"),
 			},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
@@ -184,7 +184,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Name: "Read tags with chosen fields",
 			Input: common.ReadParams{
 				ObjectName: "tags",
-				Fields:     []string{"label", "description"},
+				Fields:     connectors.Fields("label", "description"),
 			},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
diff --git a/providers/intercom/read_test.go b/providers/intercom/read_test.go
index 7d4e3160..b6f947de 100644
--- a/providers/intercom/read_test.go
+++ b/providers/intercom/read_test.go
@@ -48,19 +48,19 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:         "Unsupported object name",
-			Input:        common.ReadParams{ObjectName: "butterflies", Fields: []string{"id"}},
+			Input:        common.ReadParams{ObjectName: "butterflies", Fields: connectors.Fields("id")},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{common.ErrOperationNotSupportedForObject},
 		},
 		{
 			Name:         "Mime response header expected",
-			Input:        common.ReadParams{ObjectName: "contacts", Fields: []string{"id"}},
+			Input:        common.ReadParams{ObjectName: "contacts", Fields: connectors.Fields("id")},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{interpreter.ErrMissingContentType},
 		},
 		{
 			Name:  "Correct error message is understood from JSON response",
-			Input: common.ReadParams{ObjectName: "contacts", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "contacts", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusNotFound)
@@ -72,7 +72,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Incorrect key in payload",
-			Input: common.ReadParams{ObjectName: "contacts", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "contacts", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -84,7 +84,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Incorrect data type in payload",
-			Input: common.ReadParams{ObjectName: "contacts", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "contacts", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -96,7 +96,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Next page cursor may be missing in payload",
-			Input: common.ReadParams{ObjectName: "contacts", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "contacts", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -114,7 +114,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "API version header is passed as server request",
-			Input: common.ReadParams{ObjectName: "articles", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "articles", Fields: connectors.Fields("id")},
 			// notes is not supported for now, but its payload is good for testing
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
@@ -132,7 +132,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Next page URL is resolved, when provided with a string",
-			Input: common.ReadParams{ObjectName: "articles", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "articles", Fields: connectors.Fields("id")},
 			// notes is not supported for now, but its payload is good for testing
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
@@ -149,7 +149,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Next page URL is inferred, when provided with an object",
-			Input: common.ReadParams{ObjectName: "contacts", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "contacts", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -167,7 +167,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Next page URL is empty, when provided with null object",
-			Input: common.ReadParams{ObjectName: "articles", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "articles", Fields: connectors.Fields("id")},
 			// notes is not supported for now, but its payload is good for testing
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
@@ -183,7 +183,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Next page URL is empty, when provided with missing object",
-			Input: common.ReadParams{ObjectName: "contacts", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "contacts", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -200,7 +200,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Name: "Successful read with chosen fields",
 			Input: common.ReadParams{
 				ObjectName: "contacts",
-				Fields:     []string{"email", "name"},
+				Fields:     connectors.Fields("email", "name"),
 			},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
@@ -242,7 +242,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Name: "Successful read of named list",
 			Input: common.ReadParams{
 				ObjectName: "conversations",
-				Fields:     []string{"state"},
+				Fields:     connectors.Fields("state"),
 			},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
diff --git a/providers/pipeliner/read_test.go b/providers/pipeliner/read_test.go
index c2c343c4..ade5c541 100644
--- a/providers/pipeliner/read_test.go
+++ b/providers/pipeliner/read_test.go
@@ -39,19 +39,19 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:         "Unsupported object name",
-			Input:        common.ReadParams{ObjectName: "butterflies", Fields: []string{"id"}},
+			Input:        common.ReadParams{ObjectName: "butterflies", Fields: connectors.Fields("id")},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{common.ErrOperationNotSupportedForObject},
 		},
 		{
 			Name:         "Mime response header expected",
-			Input:        common.ReadParams{ObjectName: "Profiles", Fields: []string{"id"}},
+			Input:        common.ReadParams{ObjectName: "Profiles", Fields: connectors.Fields("id")},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{interpreter.ErrMissingContentType},
 		},
 		{
 			Name:  "Correct error message is understood from JSON response",
-			Input: common.ReadParams{ObjectName: "Profiles", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "Profiles", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusNotFound)
@@ -64,7 +64,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Incorrect key in payload",
-			Input: common.ReadParams{ObjectName: "Profiles", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "Profiles", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -76,7 +76,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Incorrect data type in payload",
-			Input: common.ReadParams{ObjectName: "Profiles", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "Profiles", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -88,7 +88,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Next page cursor may be missing in payload",
-			Input: common.ReadParams{ObjectName: "Profiles", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "Profiles", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -107,7 +107,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Next page URL is inferred, when provided with an object",
-			Input: common.ReadParams{ObjectName: "Profiles", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "Profiles", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -123,7 +123,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Next page URL is empty, when provided with null object",
-			Input: common.ReadParams{ObjectName: "Profiles", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "Profiles", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -140,7 +140,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Name: "Successful read with chosen fields",
 			Input: common.ReadParams{
 				ObjectName: "Profiles",
-				Fields:     []string{"name", "owner_id"},
+				Fields:     connectors.Fields("name", "owner_id"),
 			},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
diff --git a/providers/salesforce/read_test.go b/providers/salesforce/read_test.go
index 24f85d92..87d6c858 100644
--- a/providers/salesforce/read_test.go
+++ b/providers/salesforce/read_test.go
@@ -37,13 +37,13 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:         "Mime response header expected",
-			Input:        common.ReadParams{ObjectName: "leads", Fields: []string{"Name"}},
+			Input:        common.ReadParams{ObjectName: "leads", Fields: connectors.Fields("Name")},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{interpreter.ErrMissingContentType},
 		},
 		{
 			Name:  "Correct error message is understood from JSON response",
-			Input: common.ReadParams{ObjectName: "leads", Fields: []string{"Name"}},
+			Input: common.ReadParams{ObjectName: "leads", Fields: connectors.Fields("Name")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusBadRequest)
@@ -55,7 +55,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Incorrect key in payload",
-			Input: common.ReadParams{ObjectName: "leads", Fields: []string{"Name"}},
+			Input: common.ReadParams{ObjectName: "leads", Fields: connectors.Fields("Name")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -67,7 +67,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Incorrect data type in payload",
-			Input: common.ReadParams{ObjectName: "leads", Fields: []string{"Name"}},
+			Input: common.ReadParams{ObjectName: "leads", Fields: connectors.Fields("Name")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -79,7 +79,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Next page cursor may be missing in payload",
-			Input: common.ReadParams{ObjectName: "leads", Fields: []string{"Name"}},
+			Input: common.ReadParams{ObjectName: "leads", Fields: connectors.Fields("Name")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -96,7 +96,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Next page URL is resolved, when provided with a string",
-			Input: common.ReadParams{ObjectName: "leads", Fields: []string{"City"}},
+			Input: common.ReadParams{ObjectName: "leads", Fields: connectors.Fields("City")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -114,7 +114,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Name: "Successful read with chosen fields",
 			Input: common.ReadParams{
 				ObjectName: "contacts",
-				Fields:     []string{"Department", "AssistantName"},
+				Fields:     connectors.Fields("Department", "AssistantName"),
 			},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
diff --git a/providers/salesloft/read_test.go b/providers/salesloft/read_test.go
index 12ce5be7..d0e2405d 100644
--- a/providers/salesloft/read_test.go
+++ b/providers/salesloft/read_test.go
@@ -44,19 +44,19 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:         "Unsupported object name",
-			Input:        common.ReadParams{ObjectName: "butterflies", Fields: []string{"id"}},
+			Input:        common.ReadParams{ObjectName: "butterflies", Fields: connectors.Fields("id")},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{common.ErrOperationNotSupportedForObject},
 		},
 		{
 			Name:         "Mime response header expected",
-			Input:        common.ReadParams{ObjectName: "users", Fields: []string{"id"}},
+			Input:        common.ReadParams{ObjectName: "users", Fields: connectors.Fields("id")},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{interpreter.ErrMissingContentType},
 		},
 		{
 			Name:  "Correct error message is understood from JSON response",
-			Input: common.ReadParams{ObjectName: "users", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "users", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusNotFound)
@@ -70,7 +70,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Incorrect key in payload",
-			Input: common.ReadParams{ObjectName: "users", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "users", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -82,7 +82,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Incorrect data type in payload",
-			Input: common.ReadParams{ObjectName: "users", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "users", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -94,7 +94,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Next page cursor may be missing in payload",
-			Input: common.ReadParams{ObjectName: "users", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "users", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -108,7 +108,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Next page URL is correctly inferred",
-			Input: common.ReadParams{ObjectName: "people", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "people", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -125,7 +125,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Successful read with 25 entries, checking one row",
-			Input: common.ReadParams{ObjectName: "people", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "people", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -156,7 +156,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Name: "Successful read with chosen fields",
 			Input: common.ReadParams{
 				ObjectName: "people",
-				Fields:     []string{"email_address", "person_company_website"},
+				Fields:     connectors.Fields("email_address", "person_company_website"),
 			},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
@@ -187,7 +187,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Name: "Listing Users without pagination payload",
 			Input: common.ReadParams{
 				ObjectName: "users",
-				Fields:     []string{"email", "guid"},
+				Fields:     connectors.Fields("email", "guid"),
 			},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
@@ -218,7 +218,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Successful read accounts without since query",
-			Input: common.ReadParams{ObjectName: "accounts", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "accounts", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				mockutils.RespondToMissingQueryParameters(w, r, []string{"updated_at[gte]"}, func() {
@@ -239,7 +239,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Input: common.ReadParams{
 				ObjectName: "accounts",
 				Since:      accountsSince,
-				Fields:     []string{"id"},
+				Fields:     connectors.Fields("id"),
 			},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
diff --git a/providers/smartlead/read_test.go b/providers/smartlead/read_test.go
index 3d68f21d..fa462376 100644
--- a/providers/smartlead/read_test.go
+++ b/providers/smartlead/read_test.go
@@ -38,19 +38,19 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 		},
 		{
 			Name:         "Unsupported object name",
-			Input:        common.ReadParams{ObjectName: "butterflies", Fields: []string{"id"}},
+			Input:        common.ReadParams{ObjectName: "butterflies", Fields: connectors.Fields("id")},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{common.ErrOperationNotSupportedForObject},
 		},
 		{
 			Name:         "Mime response header expected",
-			Input:        common.ReadParams{ObjectName: "email-accounts", Fields: []string{"id"}},
+			Input:        common.ReadParams{ObjectName: "email-accounts", Fields: connectors.Fields("id")},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{interpreter.ErrMissingContentType},
 		},
 		{
 			Name:  "Correct error message is understood from HTML response",
-			Input: common.ReadParams{ObjectName: "email-accounts", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "email-accounts", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "text/html")
 				w.WriteHeader(http.StatusBadRequest)
@@ -63,7 +63,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 		},
 		{
 			Name:  "Incorrect data type in payload",
-			Input: common.ReadParams{ObjectName: "email-accounts", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "email-accounts", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -73,7 +73,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 		},
 		{
 			Name:  "Empty read response",
-			Input: common.ReadParams{ObjectName: "campaigns", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "campaigns", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -89,7 +89,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop
 		},
 		{
 			Name:  "Successful read with chosen fields",
-			Input: common.ReadParams{ObjectName: "campaigns", Fields: []string{"name", "status"}},
+			Input: common.ReadParams{ObjectName: "campaigns", Fields: connectors.Fields("name", "status")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
diff --git a/providers/zendesksupport/read_test.go b/providers/zendesksupport/read_test.go
index 7587e748..dfdad557 100644
--- a/providers/zendesksupport/read_test.go
+++ b/providers/zendesksupport/read_test.go
@@ -39,13 +39,13 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:         "Mime response header expected",
-			Input:        common.ReadParams{ObjectName: "triggers", Fields: []string{"id"}},
+			Input:        common.ReadParams{ObjectName: "triggers", Fields: connectors.Fields("id")},
 			Server:       mockserver.Dummy(),
 			ExpectedErrs: []error{interpreter.ErrMissingContentType},
 		},
 		{
 			Name:  "Correct error message is understood from JSON response",
-			Input: common.ReadParams{ObjectName: "triggers", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "triggers", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusNotFound)
@@ -57,7 +57,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Forbidden error code and response",
-			Input: common.ReadParams{ObjectName: "triggers", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "triggers", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusForbidden)
@@ -69,7 +69,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Incorrect key in payload",
-			Input: common.ReadParams{ObjectName: "triggers", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "triggers", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -81,7 +81,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Incorrect data type in payload",
-			Input: common.ReadParams{ObjectName: "triggers", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "triggers", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -93,7 +93,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Next page cursor may be missing in payload",
-			Input: common.ReadParams{ObjectName: "users", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "users", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -107,7 +107,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 		},
 		{
 			Name:  "Next page URL is resolved, when provided with a string",
-			Input: common.ReadParams{ObjectName: "users", Fields: []string{"id"}},
+			Input: common.ReadParams{ObjectName: "users", Fields: connectors.Fields("id")},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
 				w.WriteHeader(http.StatusOK)
@@ -126,7 +126,7 @@ func TestRead(t *testing.T) { //nolint:funlen,gocognit,cyclop,maintidx
 			Name: "Successful read with chosen fields",
 			Input: common.ReadParams{
 				ObjectName: "tickets",
-				Fields:     []string{"type", "subject", "status"},
+				Fields:     connectors.Fields("type", "subject", "status"),
 			},
 			Server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Header().Set("Content-Type", "application/json")
diff --git a/test/apollo/read/read.go b/test/apollo/read/read.go
index 4d3b47ba..1792ca5f 100644
--- a/test/apollo/read/read.go
+++ b/test/apollo/read/read.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"github.com/amp-labs/connectors"
 	"log"
 	"os"
 	"time"
@@ -43,7 +44,7 @@ func MainFn() int {
 func testReadOpportunitiesSearch(ctx context.Context, conn *ap.Connector) error {
 	params := common.ReadParams{
 		ObjectName: "opportunities",
-		Fields:     []string{"id"},
+		Fields:     connectors.Fields("id"),
 	}
 
 	res, err := conn.Read(ctx, params)
@@ -66,7 +67,7 @@ func testReadOpportunitiesSearch(ctx context.Context, conn *ap.Connector) error
 func testReadEmailAccounts(ctx context.Context, conn *ap.Connector) error {
 	params := common.ReadParams{
 		ObjectName: "email_accounts",
-		Fields:     []string{"user_id", "id", "email"},
+		Fields:     connectors.Fields("user_id", "id", "email"),
 		Since:      time.Now().Add(-1800 * time.Hour),
 	}
 
@@ -90,7 +91,7 @@ func testReadEmailAccounts(ctx context.Context, conn *ap.Connector) error {
 func testReadCustomFields(ctx context.Context, conn *ap.Connector) error {
 	params := common.ReadParams{
 		ObjectName: "typed_custom_fields",
-		Fields:     []string{"type", "id", "modality"},
+		Fields:     connectors.Fields("type", "id", "modality"),
 		Since:      time.Now().Add(-1800 * time.Hour),
 	}
 
diff --git a/test/apollo/search/search.go b/test/apollo/search/search.go
index c32372be..5eb03c61 100644
--- a/test/apollo/search/search.go
+++ b/test/apollo/search/search.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"github.com/amp-labs/connectors"
 	"log"
 	"os"
 
@@ -42,7 +43,7 @@ func MainFn() int {
 func testReadContactsSearch(ctx context.Context, conn *ap.Connector) error {
 	params := common.ReadParams{
 		ObjectName: "contacts",
-		Fields:     []string{"id"},
+		Fields:     connectors.Fields("id"),
 		// NextPage:   "2",
 	}
 
@@ -66,7 +67,7 @@ func testReadContactsSearch(ctx context.Context, conn *ap.Connector) error {
 func testReadOpportunitiesSearch(ctx context.Context, conn *ap.Connector) error {
 	params := common.ReadParams{
 		ObjectName: "opportunities",
-		Fields:     []string{"id"},
+		Fields:     connectors.Fields("id"),
 	}
 
 	res, err := conn.Search(ctx, params)
@@ -89,7 +90,7 @@ func testReadOpportunitiesSearch(ctx context.Context, conn *ap.Connector) error
 func testReadPeopleSearch(ctx context.Context, conn *ap.Connector) error {
 	params := common.ReadParams{
 		ObjectName: "mixed_people",
-		Fields:     []string{"id"},
+		Fields:     connectors.Fields("id"),
 	}
 
 	res, err := conn.Search(ctx, params)
diff --git a/test/atlassian/read/main.go b/test/atlassian/read/main.go
index 71c286dd..39a31d76 100644
--- a/test/atlassian/read/main.go
+++ b/test/atlassian/read/main.go
@@ -7,6 +7,7 @@ import (
 	"os/signal"
 	"syscall"
 
+	"github.com/amp-labs/connectors"
 	"github.com/amp-labs/connectors/common"
 	connTest "github.com/amp-labs/connectors/test/atlassian"
 	"github.com/amp-labs/connectors/test/utils"
@@ -24,9 +25,7 @@ func main() {
 	defer utils.Close(conn)
 
 	res, err := conn.Read(ctx, common.ReadParams{
-		Fields: []string{
-			"id", "summary", "status",
-		},
+		Fields: connectors.Fields("id", "summary", "status"),
 		// Below is the example to get issues that were updated in the last 15 min.
 		// Since: time.Now().Add(-15 * time.Minute),
 	})
diff --git a/test/atlassian/write-delete/main.go b/test/atlassian/write-delete/main.go
index e4b86ab2..37a7088c 100644
--- a/test/atlassian/write-delete/main.go
+++ b/test/atlassian/write-delete/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/providers/atlassian"
 	connTest "github.com/amp-labs/connectors/test/atlassian"
@@ -108,9 +109,7 @@ func searchIssue(res *common.ReadResult, key, value string) map[string]any {
 
 func readIssue(ctx context.Context, conn *atlassian.Connector) *common.ReadResult {
 	res, err := conn.Read(ctx, common.ReadParams{
-		Fields: []string{
-			"id", "fields",
-		},
+		Fields: connectors.Fields("id", "fields"),
 	})
 	if err != nil {
 		utils.Fail("error reading from Atlassian", "error", err)
diff --git a/test/dynamicscrm/metadata/main.go b/test/dynamicscrm/metadata/main.go
index 555f37a9..f9c5b552 100644
--- a/test/dynamicscrm/metadata/main.go
+++ b/test/dynamicscrm/metadata/main.go
@@ -36,7 +36,7 @@ func main() {
 
 	fmt.Println("Read object using all fields from ListObjectMetadata")
 
-	requestFields := handy.Map[string, string](metadata.Result[objectName].FieldsMap).Keys()
+	requestFields := handy.Map[string, string](metadata.Result[objectName].FieldsMap).KeySet()
 
 	response, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: objectName,
@@ -49,9 +49,9 @@ func main() {
 			utils.Fail("expected to read at least one record", "error", err)
 		}
 
-		givenFields := handy.Map[string, any](response.Data[0].Fields).Keys()
+		givenFields := handy.Map[string, any](response.Data[0].Fields).KeySet()
 
-		difference := handy.NewSet(givenFields).Diff(handy.NewSet(requestFields))
+		difference := givenFields.Diff(requestFields)
 		if len(difference) != 0 {
 			utils.Fail("connector read didn't match requested fields", "difference", difference)
 		}
diff --git a/test/dynamicscrm/read/main.go b/test/dynamicscrm/read/main.go
index ae2f3df2..832b3ff0 100644
--- a/test/dynamicscrm/read/main.go
+++ b/test/dynamicscrm/read/main.go
@@ -7,6 +7,7 @@ import (
 	"os/signal"
 	"syscall"
 
+	"github.com/amp-labs/connectors"
 	"github.com/amp-labs/connectors/common"
 	"github.com/amp-labs/connectors/providers/dynamicscrm"
 	connTest "github.com/amp-labs/connectors/test/dynamicscrm"
@@ -28,9 +29,7 @@ func main() {
 
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: objectName,
-		Fields: []string{
-			"fullname", "emailaddress1", "fax",
-		},
+		Fields:     connectors.Fields("fullname", "emailaddress1", "fax"),
 	})
 	if err != nil {
 		utils.Fail("error reading from microsoft CRM", "error", err)
diff --git a/test/dynamicscrm/write-delete/main.go b/test/dynamicscrm/write-delete/main.go
index 7dcbf9a0..38b18746 100644
--- a/test/dynamicscrm/write-delete/main.go
+++ b/test/dynamicscrm/write-delete/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/common/handy"
 	"github.com/amp-labs/connectors/providers/dynamicscrm"
@@ -99,9 +100,9 @@ func searchLead(res *common.ReadResult, key, value string) map[string]any {
 func readLeads(ctx context.Context, conn *dynamicscrm.Connector) *common.ReadResult {
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: objectName,
-		Fields: []string{
+		Fields: connectors.Fields(
 			"leadid", "lastname", "firstname", "companyname", "subject",
-		},
+		),
 	})
 	if err != nil {
 		utils.Fail("error reading from microsoft CRM", "error", err)
diff --git a/test/gong/metadata/main.go b/test/gong/metadata/main.go
index ab8ec605..ad9a69a4 100644
--- a/test/gong/metadata/main.go
+++ b/test/gong/metadata/main.go
@@ -2,6 +2,7 @@ package main
 
 import (
 	"context"
+	"github.com/amp-labs/connectors"
 	"log/slog"
 	"os/signal"
 	"syscall"
@@ -29,7 +30,7 @@ func main() {
 
 	response, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: objectName,
-		Fields:     []string{"firstName", "lastName", "emailAddress"},
+		Fields:     connectors.Fields("firstName", "lastName", "emailAddress"),
 	})
 	if err != nil {
 		utils.Fail("error reading from Gong", "error", err)
diff --git a/test/gong/read/read.go b/test/gong/read/read.go
index bb633de3..605d14cb 100644
--- a/test/gong/read/read.go
+++ b/test/gong/read/read.go
@@ -7,6 +7,7 @@ import (
 	"os/signal"
 	"syscall"
 
+	"github.com/amp-labs/connectors"
 	"github.com/amp-labs/connectors/common"
 	connTest "github.com/amp-labs/connectors/test/gong"
 	"github.com/amp-labs/connectors/test/utils"
@@ -25,7 +26,7 @@ func main() {
 
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: "calls", // could be calls, users
-		Fields:     []string{"url"},
+		Fields:     connectors.Fields("url"),
 	})
 	if err != nil {
 		utils.Fail("error reading from Gong", "error", err)
diff --git a/test/hubspot/write/main.go b/test/hubspot/write/main.go
index 091e5003..783a9805 100644
--- a/test/hubspot/write/main.go
+++ b/test/hubspot/write/main.go
@@ -8,6 +8,7 @@ import (
 	"syscall"
 	"time"
 
+	"github.com/amp-labs/connectors"
 	"github.com/amp-labs/connectors/common"
 	connTest "github.com/amp-labs/connectors/test/hubspot"
 	"github.com/amp-labs/connectors/test/utils"
@@ -59,7 +60,7 @@ func main() {
 
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: "contacts",
-		Fields:     []string{"email", "phone", "company", "website", "lastname", "firstname"},
+		Fields:     connectors.Fields("email", "phone", "company", "website", "lastname", "firstname"),
 		NextPage:   "",
 		Since:      time.Now().Add(-5 * time.Minute),
 	})
diff --git a/test/instantly/read/main.go b/test/instantly/read/main.go
index 79735fd0..651e76da 100644
--- a/test/instantly/read/main.go
+++ b/test/instantly/read/main.go
@@ -3,6 +3,7 @@ package main
 import (
 	"context"
 	"fmt"
+	"github.com/amp-labs/connectors"
 	"log/slog"
 	"os"
 	"os/signal"
@@ -29,9 +30,9 @@ func main() {
 
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: objectName,
-		Fields: []string{
+		Fields: connectors.Fields(
 			"name",
-		},
+		),
 	})
 	if err != nil {
 		utils.Fail("error reading from Instantly", "error", err)
diff --git a/test/instantly/write-delete/main.go b/test/instantly/write-delete/main.go
index 759594da..45708cbe 100644
--- a/test/instantly/write-delete/main.go
+++ b/test/instantly/write-delete/main.go
@@ -2,6 +2,7 @@ package main
 
 import (
 	"context"
+	"github.com/amp-labs/connectors"
 	"log/slog"
 	"os/signal"
 	"syscall"
@@ -80,9 +81,9 @@ func searchTags(res *common.ReadResult, key, value string) map[string]any {
 func readTags(ctx context.Context, conn *instantly.Connector) *common.ReadResult {
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: objectName,
-		Fields: []string{
+		Fields: connectors.Fields(
 			"id", "view", "name",
-		},
+		),
 	})
 	if err != nil {
 		utils.Fail("error reading from Instantly", "error", err)
diff --git a/test/intercom/read/main.go b/test/intercom/read/main.go
index 7c86a6d7..4dd9399e 100644
--- a/test/intercom/read/main.go
+++ b/test/intercom/read/main.go
@@ -7,6 +7,7 @@ import (
 	"os/signal"
 	"syscall"
 
+	"github.com/amp-labs/connectors"
 	"github.com/amp-labs/connectors/common"
 	"github.com/amp-labs/connectors/providers/intercom"
 	msTest "github.com/amp-labs/connectors/test/intercom"
@@ -26,11 +27,11 @@ func main() {
 
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: "conversations",
-		Fields: []string{
+		Fields: connectors.Fields(
 			"id",
 			"state",
 			"type",
-		},
+		),
 	})
 	if err != nil {
 		utils.Fail("error reading from Intercom", "error", err)
diff --git a/test/intercom/write-delete/main.go b/test/intercom/write-delete/main.go
index 2b457665..12ebaaab 100644
--- a/test/intercom/write-delete/main.go
+++ b/test/intercom/write-delete/main.go
@@ -7,6 +7,7 @@ import (
 	"strconv"
 	"syscall"
 
+	"github.com/amp-labs/connectors"
 	"github.com/amp-labs/connectors/common"
 	"github.com/amp-labs/connectors/providers/intercom"
 	msTest "github.com/amp-labs/connectors/test/intercom"
@@ -73,9 +74,7 @@ func main() {
 func getAdminID(ctx context.Context, conn *intercom.Connector) string {
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: "admins",
-		Fields: []string{
-			"id",
-		},
+		Fields:     connectors.Fields("id"),
 	})
 	if err != nil {
 		utils.Fail("error reading from Intercom", "error", err)
@@ -113,9 +112,7 @@ func searchArticles(res *common.ReadResult, key, value string) map[string]any {
 func readArticles(ctx context.Context, conn *intercom.Connector) *common.ReadResult {
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: "articles",
-		Fields: []string{
-			"id", "title", "description", "author_id",
-		},
+		Fields:     connectors.Fields("id", "title", "description", "author_id"),
 	})
 	if err != nil {
 		utils.Fail("error reading from Intercom", "error", err)
diff --git a/test/marketo/read/read.go b/test/marketo/read/read.go
index 1da2df3b..92dfac86 100644
--- a/test/marketo/read/read.go
+++ b/test/marketo/read/read.go
@@ -8,6 +8,7 @@ import (
 	"os"
 	"time"
 
+	"github.com/amp-labs/connectors"
 	"github.com/amp-labs/connectors/common"
 	mk "github.com/amp-labs/connectors/test/marketo"
 )
@@ -40,7 +41,7 @@ func testReadChannels(ctx context.Context) error {
 
 	params := common.ReadParams{
 		ObjectName: "channels",
-		Fields:     []string{"applicableProgramType", "id", "name"},
+		Fields:     connectors.Fields("applicableProgramType", "id", "name"),
 	}
 
 	res, err := conn.Read(ctx, params)
@@ -65,7 +66,7 @@ func testReadSmartCampaigns(ctx context.Context) error {
 
 	params := common.ReadParams{
 		ObjectName: "smartCampaigns",
-		Fields:     []string{"description", "id", "name"},
+		Fields:     connectors.Fields("description", "id", "name"),
 		Since:      time.Now().Add(-1800 * time.Hour),
 	}
 
@@ -91,7 +92,7 @@ func testReadCampaigns(ctx context.Context) error {
 
 	params := common.ReadParams{
 		ObjectName: "campaigns",
-		Fields:     []string{"createdAt", "id", "name"},
+		Fields:     connectors.Fields("createdAt", "id", "name"),
 		Since:      time.Now().Add(-1800 * time.Hour),
 	}
 
diff --git a/test/outreach/read/read.go b/test/outreach/read/read.go
index 4ef2544c..c5f5ac0b 100644
--- a/test/outreach/read/read.go
+++ b/test/outreach/read/read.go
@@ -46,7 +46,7 @@ func testReadSequences(ctx context.Context, conn connectors.ReadConnector) error
 	config := connectors.ReadParams{
 		ObjectName: "sequences",
 		Since:      time.Now().Add(-720 * time.Hour),
-		Fields:     []string{"openCount", "description", "id"},
+		Fields:     connectors.Fields("openCount", "description", "id"),
 	}
 
 	result, err := conn.Read(ctx, config)
@@ -70,7 +70,7 @@ func testReadMailings(ctx context.Context, conn connectors.ReadConnector) error
 	config := connectors.ReadParams{
 		ObjectName: "mailings",
 		Since:      time.Now().Add(-720 * time.Hour),
-		Fields:     []string{"bodyHtml", "errorReason", "id"},
+		Fields:     connectors.Fields("bodyHtml", "errorReason", "id"),
 	}
 
 	result, err := conn.Read(ctx, config)
@@ -94,7 +94,7 @@ func testReadProspects(ctx context.Context, conn connectors.ReadConnector) error
 	config := connectors.ReadParams{
 		ObjectName: "prospects",
 		Since:      time.Now().Add(-720 * time.Hour),
-		Fields:     []string{"addressCountry", "campaignName", "id"},
+		Fields:     connectors.Fields("addressCountry", "campaignName", "id"),
 	}
 
 	result, err := conn.Read(ctx, config)
diff --git a/test/pipeliner/read/main.go b/test/pipeliner/read/main.go
index b8e9ece1..e68bc8d3 100644
--- a/test/pipeliner/read/main.go
+++ b/test/pipeliner/read/main.go
@@ -7,6 +7,7 @@ import (
 	"os/signal"
 	"syscall"
 
+	"github.com/amp-labs/connectors"
 	"github.com/amp-labs/connectors/common"
 	"github.com/amp-labs/connectors/providers/pipeliner"
 	connTest "github.com/amp-labs/connectors/test/pipeliner"
@@ -28,9 +29,7 @@ func main() {
 
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: objectName,
-		Fields: []string{
-			"id", "formatted_name", "account_position",
-		},
+		Fields:     connectors.Fields("id", "formatted_name", "account_position"),
 	})
 	if err != nil {
 		utils.Fail("error reading from Pipeliner", "error", err)
diff --git a/test/pipeliner/write-delete/main.go b/test/pipeliner/write-delete/main.go
index 547d1e12..2e72ca2f 100644
--- a/test/pipeliner/write-delete/main.go
+++ b/test/pipeliner/write-delete/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/providers/pipeliner"
 	connTest "github.com/amp-labs/connectors/test/pipeliner"
@@ -85,9 +86,7 @@ func searchNotes(res *common.ReadResult, key, value string) map[string]any {
 func readNotes(ctx context.Context, conn *pipeliner.Connector) *common.ReadResult {
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: objectName,
-		Fields: []string{
-			"id", "view", "name",
-		},
+		Fields:     connectors.Fields("id", "view", "name"),
 	})
 	if err != nil {
 		utils.Fail("error reading from Pipeliner", "error", err)
@@ -99,7 +98,7 @@ func readNotes(ctx context.Context, conn *pipeliner.Connector) *common.ReadResul
 func getFirstObjectID(ctx context.Context, conn *pipeliner.Connector, name string) string {
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: name,
-		Fields:     []string{"id"},
+		Fields:     connectors.Fields("id"),
 	})
 	if err != nil {
 		utils.Fail("error reading from Pipeliner", "error", err)
diff --git a/test/salesforce/bulk/read/main.go b/test/salesforce/bulk/read/main.go
index c4573e77..e7987487 100644
--- a/test/salesforce/bulk/read/main.go
+++ b/test/salesforce/bulk/read/main.go
@@ -2,6 +2,7 @@ package main
 
 import (
 	"context"
+	"github.com/amp-labs/connectors"
 	"log/slog"
 	"os"
 	"os/signal"
@@ -26,7 +27,7 @@ func main() {
 
 	res, err := conn.BulkRead(ctx, common.ReadParams{
 		ObjectName: "Account",
-		Fields:     []string{"Id", "Name"},
+		Fields:     connectors.Fields("Id", "Name"),
 	})
 	if err != nil {
 		utils.Fail("error bulk reading from Salesforce", "error", err)
diff --git a/test/salesforce/metadata-read/main.go b/test/salesforce/metadata-read/main.go
index 42bd2f77..0cbd426a 100644
--- a/test/salesforce/metadata-read/main.go
+++ b/test/salesforce/metadata-read/main.go
@@ -7,6 +7,7 @@ import (
 	"strings"
 	"syscall"
 
+	"github.com/amp-labs/connectors"
 	"github.com/amp-labs/connectors/common"
 	connTest "github.com/amp-labs/connectors/test/salesforce"
 	"github.com/amp-labs/connectors/test/utils"
@@ -30,7 +31,7 @@ func main() {
 
 	response, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: objectName,
-		Fields:     []string{"*"},
+		Fields:     connectors.Fields("*"),
 	})
 	if err != nil {
 		utils.Fail("error reading from Salesforce", "error", err)
diff --git a/test/salesforce/read-write/read-write.go b/test/salesforce/read-write/read-write.go
index b0383da2..6e13015d 100644
--- a/test/salesforce/read-write/read-write.go
+++ b/test/salesforce/read-write/read-write.go
@@ -55,7 +55,7 @@ func testReadConnector(ctx context.Context, conn connectors.ReadConnector) error
 	// Read some data from Salesforce
 	res, err := conn.Read(ctx, connectors.ReadParams{
 		ObjectName: "Account",
-		Fields:     []string{"Id", "Name", "BillingCity", "IsDeleted"},
+		Fields:     connectors.Fields("Id", "Name", "BillingCity", "IsDeleted"),
 	})
 	if err != nil {
 		return fmt.Errorf("error reading from Salesforce: %w", err)
diff --git a/test/salesloft/read/main.go b/test/salesloft/read/main.go
index 6509c872..e96ce4bb 100644
--- a/test/salesloft/read/main.go
+++ b/test/salesloft/read/main.go
@@ -7,6 +7,7 @@ import (
 	"os/signal"
 	"syscall"
 
+	"github.com/amp-labs/connectors"
 	"github.com/amp-labs/connectors/common"
 	"github.com/amp-labs/connectors/providers/salesloft"
 	msTest "github.com/amp-labs/connectors/test/salesloft"
@@ -26,10 +27,7 @@ func main() {
 
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: "people",
-		Fields: []string{
-			"display_name",
-			"email_address",
-		},
+		Fields:     connectors.Fields("display_name", "email_address"),
 	})
 	if err != nil {
 		utils.Fail("error reading from Salesloft", "error", err)
diff --git a/test/salesloft/write-delete/main.go b/test/salesloft/write-delete/main.go
index d7198d73..f668e378 100644
--- a/test/salesloft/write-delete/main.go
+++ b/test/salesloft/write-delete/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/providers/salesloft"
 	msTest "github.com/amp-labs/connectors/test/salesloft"
@@ -86,9 +87,7 @@ func searchListView(res *common.ReadResult, key, value string) map[string]any {
 func readListViews(ctx context.Context, conn *salesloft.Connector) *common.ReadResult {
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: "saved_list_views",
-		Fields: []string{
-			"id", "view", "name",
-		},
+		Fields:     connectors.Fields("id", "view", "name"),
 	})
 	if err != nil {
 		utils.Fail("error reading from Salesloft", "error", err)
diff --git a/test/smartlead/read/main.go b/test/smartlead/read/main.go
index e2930dba..fd0122e5 100644
--- a/test/smartlead/read/main.go
+++ b/test/smartlead/read/main.go
@@ -7,6 +7,7 @@ import (
 	"os/signal"
 	"syscall"
 
+	"github.com/amp-labs/connectors"
 	"github.com/amp-labs/connectors/common"
 	connTest "github.com/amp-labs/connectors/test/smartlead"
 	"github.com/amp-labs/connectors/test/utils"
@@ -27,9 +28,7 @@ func main() {
 
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: objectName,
-		Fields: []string{
-			"name", "status", "user_id",
-		},
+		Fields:     connectors.Fields("name", "status", "user_id"),
 	})
 	if err != nil {
 		utils.Fail("error reading from Smartlead", "error", err)
diff --git a/test/zendesksupport/read/main.go b/test/zendesksupport/read/main.go
index b23fa8ec..7813163c 100644
--- a/test/zendesksupport/read/main.go
+++ b/test/zendesksupport/read/main.go
@@ -7,6 +7,7 @@ import (
 	"os/signal"
 	"syscall"
 
+	"github.com/amp-labs/connectors"
 	"github.com/amp-labs/connectors/common"
 	"github.com/amp-labs/connectors/providers/zendesksupport"
 	"github.com/amp-labs/connectors/test/utils"
@@ -28,9 +29,7 @@ func main() {
 
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: objectName,
-		Fields: []string{
-			"name", "time_zone", "role",
-		},
+		Fields:     connectors.Fields("name", "time_zone", "role"),
 	})
 	if err != nil {
 		utils.Fail("error reading from Zendesk Support", "error", err)
diff --git a/test/zendesksupport/write-delete/main.go b/test/zendesksupport/write-delete/main.go
index 256c7c1c..29f0d5d6 100644
--- a/test/zendesksupport/write-delete/main.go
+++ b/test/zendesksupport/write-delete/main.go
@@ -7,6 +7,7 @@ import (
 	"strings"
 	"syscall"
 
+	"github.com/amp-labs/connectors"
 	"github.com/amp-labs/connectors/common"
 	"github.com/amp-labs/connectors/providers/zendesksupport"
 	"github.com/amp-labs/connectors/test/utils"
@@ -99,9 +100,7 @@ func searchBrands(res *common.ReadResult, key, value string) map[string]any {
 func readBrands(ctx context.Context, conn *zendesksupport.Connector) *common.ReadResult {
 	res, err := conn.Read(ctx, common.ReadParams{
 		ObjectName: objectName,
-		Fields: []string{
-			"id", "view", "name",
-		},
+		Fields:     connectors.Fields("id", "view", "name"),
 	})
 	if err != nil {
 		utils.Fail("error reading from ZendeskSupport", "error", err)