Repository: amp-labs/connectors
Pull request #1413 (2025-02-27 09:37:43)

[CON-1077] feat: Add Gorgias Metadata Connector
Tests:
<img width="1226" alt="Screenshot 2025-02-27 at 12 24 08" src="https://github.com/user-attachments/assets/3f1c2545-5257-4dbc-9846-f8d7dfa104ff" />



Patch:
diff --git a/go.mod b/go.mod
index 80dccfc5..2488b5fe 100644
--- a/go.mod
+++ b/go.mod
@@ -20,7 +20,7 @@ require (
 	github.com/stretchr/testify v1.10.0
 	gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a
 	golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329
-	golang.org/x/oauth2 v0.26.0
+	golang.org/x/oauth2 v0.27.0
 	golang.org/x/text v0.22.0
 )
 
diff --git a/go.sum b/go.sum
index 52fa5317..6bfeeb07 100644
--- a/go.sum
+++ b/go.sum
@@ -143,8 +143,8 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
 golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
 golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
 golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
-golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
-golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
+golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
diff --git a/providers/gorgias/connector.go b/providers/gorgias/connector.go
new file mode 100644
index 00000000..2128d911
--- /dev/null
+++ b/providers/gorgias/connector.go
@@ -0,0 +1,45 @@
+package gorgias
+
+import (
+	"github.com/amp-labs/connectors/common"
+	"github.com/amp-labs/connectors/internal/components"
+	"github.com/amp-labs/connectors/internal/components/operations"
+	"github.com/amp-labs/connectors/internal/components/schema"
+	"github.com/amp-labs/connectors/providers"
+)
+
+const restAPIPrefix = "api"
+
+type Connector struct {
+	// Basic connector
+	*components.Connector
+
+	// Require authenticated client
+	common.RequireAuthenticatedClient
+	// Require workspace
+	common.RequireWorkspace
+
+	// Supported operations
+	components.SchemaProvider
+}
+
+func NewConnector(params common.Parameters) (*Connector, error) {
+	// Create base connector with provider info
+	return components.Initialize(providers.Gorgias, params, constructor)
+}
+
+func constructor(base *components.Connector) (*Connector, error) {
+	connector := &Connector{Connector: base}
+
+	// Set the metadata provider for the connector
+	connector.SchemaProvider = schema.NewObjectSchemaProvider(
+		connector.HTTPClient().Client,
+		schema.FetchModeParallel,
+		operations.SingleObjectMetadataHandlers{
+			BuildRequest:  connector.buildSingleObjectMetadataRequest,
+			ParseResponse: connector.parseSingleObjectMetadataResponse,
+		},
+	)
+
+	return connector, nil
+}
diff --git a/providers/gorgias/handlers.go b/providers/gorgias/handlers.go
new file mode 100644
index 00000000..63242757
--- /dev/null
+++ b/providers/gorgias/handlers.go
@@ -0,0 +1,73 @@
+package gorgias
+
+import (
+	"context"
+	"net/http"
+
+	"github.com/amp-labs/connectors/common"
+	"github.com/amp-labs/connectors/common/naming"
+	"github.com/amp-labs/connectors/common/urlbuilder"
+)
+
+const (
+	account          = "account"
+	limitQuery       = "limit"
+	metadataPageSize = "1"
+)
+
+type dataResponse struct {
+	Data []map[string]any `json:"data"`
+	// Request Metadata Fields
+}
+
+func (c *Connector) buildSingleObjectMetadataRequest(ctx context.Context, objectName string) (*http.Request, error) {
+	url, err := urlbuilder.New(c.ProviderInfo().BaseURL, restAPIPrefix, objectName)
+	if err != nil {
+		return nil, err
+	}
+
+	// Limit response to 1 record data.
+	url.WithQueryParam(limitQuery, metadataPageSize)
+
+	return http.NewRequestWithContext(ctx, http.MethodGet, url.String(), nil)
+}
+
+func (c *Connector) parseSingleObjectMetadataResponse(
+	ctx context.Context,
+	objectName string,
+	response *common.JSONHTTPResponse,
+) (*common.ObjectMetadata, error) {
+	objectMetadata := common.ObjectMetadata{
+		FieldsMap:   make(map[string]string),
+		DisplayName: naming.CapitalizeFirstLetterEveryWord(objectName),
+	}
+
+	// All supported objects return a response following the `dataResponse` schema,
+	// with the exception of the `account` object.
+	switch objectName {
+	case account:
+		record, err := common.UnmarshalJSON[map[string]any](response)
+		if err != nil {
+			return nil, common.ErrFailedToUnmarshalBody
+		}
+
+		for fld := range *record {
+			objectMetadata.FieldsMap[fld] = fld
+		}
+	default:
+		records, err := common.UnmarshalJSON[dataResponse](response)
+		if err != nil {
+			return nil, common.ErrFailedToUnmarshalBody
+		}
+
+		if len(records.Data) == 0 {
+			return nil, common.ErrMissingExpectedValues
+		}
+
+		for fld := range records.Data[0] {
+			objectMetadata.FieldsMap[fld] = fld
+		}
+	}
+
+	return &objectMetadata, nil
+}


Test patch:
diff --git a/test/gorgias/connector.go b/test/gorgias/connector.go
new file mode 100644
index 00000000..d64bcf1d
--- /dev/null
+++ b/test/gorgias/connector.go
@@ -0,0 +1,55 @@
+package gorgias
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+
+	"github.com/amp-labs/connectors/common"
+	"github.com/amp-labs/connectors/common/scanning/credscanning"
+	"github.com/amp-labs/connectors/providers"
+	"github.com/amp-labs/connectors/providers/gorgias"
+	"github.com/amp-labs/connectors/test/utils"
+	"golang.org/x/oauth2"
+)
+
+func GetConnector(ctx context.Context) *gorgias.Connector {
+	filePath := credscanning.LoadPath(providers.Gorgias)
+	reader := utils.MustCreateProvCredJSON(filePath, true, true)
+
+	client, err := common.NewOAuthHTTPClient(ctx,
+		common.WithOAuthClient(http.DefaultClient),
+		common.WithOAuthConfig(getConfig(reader)),
+		common.WithOAuthToken(reader.GetOauthToken()),
+	)
+	if err != nil {
+		utils.Fail(err.Error())
+	}
+
+	conn, err := gorgias.NewConnector(common.Parameters{
+		AuthenticatedClient: client,
+		Workspace:           "ampersand",
+	})
+	if err != nil {
+		utils.Fail("error creating connector", "error", err)
+	}
+
+	return conn
+}
+
+func getConfig(reader *credscanning.ProviderCredentials) *oauth2.Config {
+	cfg := oauth2.Config{
+		ClientID:     reader.Get(credscanning.Fields.ClientId),
+		ClientSecret: reader.Get(credscanning.Fields.ClientSecret),
+		Endpoint: oauth2.Endpoint{
+			AuthURL:   fmt.Sprintf("https://%s.gorgias.com/oauth/authorize", "ampersand"),
+			TokenURL:  fmt.Sprintf("https://%s.gorgias.com/oauth/token", "ampersand"),
+			AuthStyle: oauth2.AuthStyleAutoDetect,
+		},
+		Scopes: []string{
+			"offline", "write:all",
+		},
+	}
+
+	return &cfg
+}
diff --git a/test/gorgias/metadata/main.go b/test/gorgias/metadata/main.go
new file mode 100644
index 00000000..b9240d8c
--- /dev/null
+++ b/test/gorgias/metadata/main.go
@@ -0,0 +1,31 @@
+package main
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/amp-labs/connectors/test/gorgias"
+	"github.com/amp-labs/connectors/test/utils"
+)
+
+func main() {
+	if err := run(); err != nil {
+		utils.Fail(err.Error())
+	}
+}
+
+func run() error {
+	ctx := context.Background()
+	connector := gorgias.GetConnector(ctx)
+
+	m, err := connector.ListObjectMetadata(ctx, []string{"account", "customers", "events"})
+	if err != nil {
+		return err
+	}
+
+	// Print the results
+	fmt.Println("Results: ", m.Result)
+	fmt.Println("Errors: ", m.Errors)
+
+	return nil
+}