Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions api/automation.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,120 @@ func (c *Client) GetWorkflow(workflowID string) (*Workflow, error) {

return &result, nil
}

// CreateWorkflow creates a new workflow
func (c *Client) CreateWorkflow(data map[string]interface{}) (*Workflow, error) {
url := fmt.Sprintf("%s/automation/v4/flows", c.BaseURL)

body, err := c.post(url, data)
if err != nil {
return nil, err
}

var result Workflow
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("failed to parse workflow response: %w", err)
}

return &result, nil
}

// UpdateWorkflow updates an existing workflow
func (c *Client) UpdateWorkflow(workflowID string, data map[string]interface{}) (*Workflow, error) {
if workflowID == "" {
return nil, fmt.Errorf("workflow ID is required")
}

url := fmt.Sprintf("%s/automation/v4/flows/%s", c.BaseURL, workflowID)

body, err := c.patch(url, data)
if err != nil {
return nil, err
}

var result Workflow
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("failed to parse workflow response: %w", err)
}

return &result, nil
}

// DeleteWorkflow deletes a workflow by ID
func (c *Client) DeleteWorkflow(workflowID string) error {
if workflowID == "" {
return fmt.Errorf("workflow ID is required")
}

url := fmt.Sprintf("%s/automation/v4/flows/%s", c.BaseURL, workflowID)

_, err := c.delete(url)
return err
}

// WorkflowEnrollment represents an enrollment in a workflow
type WorkflowEnrollment struct {
ObjectID string `json:"objectId"`
ObjectType string `json:"objectType,omitempty"`
EnrolledAt string `json:"enrolledAt,omitempty"`
Status string `json:"status,omitempty"`
EnrollmentID string `json:"enrollmentId,omitempty"`
}

// WorkflowEnrollmentList represents a paginated list of enrollments
type WorkflowEnrollmentList struct {
Results []WorkflowEnrollment `json:"results"`
Paging *Paging `json:"paging,omitempty"`
}

// EnrollInWorkflow enrolls an object in a workflow
func (c *Client) EnrollInWorkflow(workflowID string, objectID string) error {
if workflowID == "" {
return fmt.Errorf("workflow ID is required")
}
if objectID == "" {
return fmt.Errorf("object ID is required")
}

url := fmt.Sprintf("%s/automation/v4/flows/%s/enrollments/start", c.BaseURL, workflowID)

data := map[string]interface{}{
"objectId": objectID,
}

_, err := c.post(url, data)
return err
}

// ListWorkflowEnrollments lists enrollments for a workflow
func (c *Client) ListWorkflowEnrollments(workflowID string, opts ListOptions) (*WorkflowEnrollmentList, error) {
if workflowID == "" {
return nil, fmt.Errorf("workflow ID is required")
}

url := fmt.Sprintf("%s/automation/v4/flows/%s/enrollments", c.BaseURL, workflowID)

params := make(map[string]string)
if opts.Limit > 0 {
params["limit"] = strconv.Itoa(opts.Limit)
}
if opts.After != "" {
params["after"] = opts.After
}

if len(params) > 0 {
url = buildURL(url, params)
}

body, err := c.get(url)
if err != nil {
return nil, err
}

var result WorkflowEnrollmentList
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("failed to parse enrollments response: %w", err)
}

return &result, nil
}
206 changes: 206 additions & 0 deletions api/automation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,209 @@ func TestClient_GetWorkflow(t *testing.T) {
assert.Nil(t, workflow)
})
}

func TestClient_CreateWorkflow(t *testing.T) {
t.Run("success", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/automation/v4/flows", r.URL.Path)
assert.Equal(t, http.MethodPost, r.Method)

w.WriteHeader(http.StatusCreated)
w.Write([]byte(`{
"id": "workflow-456",
"name": "New Workflow",
"type": "CONTACT_FLOW",
"isEnabled": false,
"createdAt": "2024-01-20T10:00:00Z"
}`))
}))
defer server.Close()

client := &Client{
BaseURL: server.URL,
AccessToken: "test-token",
HTTPClient: server.Client(),
}

data := map[string]interface{}{
"name": "New Workflow",
"type": "CONTACT_FLOW",
}
workflow, err := client.CreateWorkflow(data)
require.NoError(t, err)
assert.Equal(t, "workflow-456", workflow.ID)
assert.Equal(t, "New Workflow", workflow.Name)
})
}

func TestClient_UpdateWorkflow(t *testing.T) {
t.Run("success", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/automation/v4/flows/workflow-123", r.URL.Path)
assert.Equal(t, http.MethodPatch, r.Method)

w.WriteHeader(http.StatusOK)
w.Write([]byte(`{
"id": "workflow-123",
"name": "Updated Workflow",
"type": "CONTACT_FLOW",
"isEnabled": true,
"updatedAt": "2024-01-21T10:00:00Z"
}`))
}))
defer server.Close()

client := &Client{
BaseURL: server.URL,
AccessToken: "test-token",
HTTPClient: server.Client(),
}

updates := map[string]interface{}{
"name": "Updated Workflow",
}
workflow, err := client.UpdateWorkflow("workflow-123", updates)
require.NoError(t, err)
assert.Equal(t, "workflow-123", workflow.ID)
assert.Equal(t, "Updated Workflow", workflow.Name)
})

t.Run("empty ID", func(t *testing.T) {
client := &Client{BaseURL: "https://api.hubapi.com"}
workflow, err := client.UpdateWorkflow("", map[string]interface{}{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "workflow ID is required")
assert.Nil(t, workflow)
})
}

func TestClient_DeleteWorkflow(t *testing.T) {
t.Run("success", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/automation/v4/flows/workflow-123", r.URL.Path)
assert.Equal(t, http.MethodDelete, r.Method)

w.WriteHeader(http.StatusNoContent)
}))
defer server.Close()

client := &Client{
BaseURL: server.URL,
AccessToken: "test-token",
HTTPClient: server.Client(),
}

err := client.DeleteWorkflow("workflow-123")
require.NoError(t, err)
})

t.Run("empty ID", func(t *testing.T) {
client := &Client{BaseURL: "https://api.hubapi.com"}
err := client.DeleteWorkflow("")
assert.Error(t, err)
assert.Contains(t, err.Error(), "workflow ID is required")
})
}

func TestClient_EnrollInWorkflow(t *testing.T) {
t.Run("success", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/automation/v4/flows/workflow-123/enrollments/start", r.URL.Path)
assert.Equal(t, http.MethodPost, r.Method)

w.WriteHeader(http.StatusOK)
w.Write([]byte(`{}`))
}))
defer server.Close()

client := &Client{
BaseURL: server.URL,
AccessToken: "test-token",
HTTPClient: server.Client(),
}

err := client.EnrollInWorkflow("workflow-123", "contact-456")
require.NoError(t, err)
})

t.Run("empty workflow ID", func(t *testing.T) {
client := &Client{BaseURL: "https://api.hubapi.com"}
err := client.EnrollInWorkflow("", "contact-456")
assert.Error(t, err)
assert.Contains(t, err.Error(), "workflow ID is required")
})

t.Run("empty object ID", func(t *testing.T) {
client := &Client{BaseURL: "https://api.hubapi.com"}
err := client.EnrollInWorkflow("workflow-123", "")
assert.Error(t, err)
assert.Contains(t, err.Error(), "object ID is required")
})
}

func TestClient_ListWorkflowEnrollments(t *testing.T) {
t.Run("success", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/automation/v4/flows/workflow-123/enrollments", r.URL.Path)
assert.Equal(t, http.MethodGet, r.Method)

w.WriteHeader(http.StatusOK)
w.Write([]byte(`{
"results": [
{
"objectId": "contact-456",
"objectType": "CONTACT",
"status": "ACTIVE",
"enrolledAt": "2024-01-20T10:00:00Z"
}
],
"paging": {
"next": {
"after": "abc123"
}
}
}`))
}))
defer server.Close()

client := &Client{
BaseURL: server.URL,
AccessToken: "test-token",
HTTPClient: server.Client(),
}

result, err := client.ListWorkflowEnrollments("workflow-123", ListOptions{Limit: 10})
require.NoError(t, err)
assert.Len(t, result.Results, 1)
assert.Equal(t, "contact-456", result.Results[0].ObjectID)
assert.Equal(t, "CONTACT", result.Results[0].ObjectType)
assert.Equal(t, "ACTIVE", result.Results[0].Status)
assert.Equal(t, "abc123", result.Paging.Next.After)
})

t.Run("empty workflow ID", func(t *testing.T) {
client := &Client{BaseURL: "https://api.hubapi.com"}
result, err := client.ListWorkflowEnrollments("", ListOptions{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "workflow ID is required")
assert.Nil(t, result)
})

t.Run("empty results", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"results": []}`))
}))
defer server.Close()

client := &Client{
BaseURL: server.URL,
AccessToken: "test-token",
HTTPClient: server.Client(),
}

result, err := client.ListWorkflowEnrollments("workflow-123", ListOptions{})
require.NoError(t, err)
assert.Empty(t, result.Results)
})
}
Loading