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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ func main() {
EventProperties: map[string]any{
"plan": "pro",
},
ContactProperties: map[string]any{
"planName": "Pro",
},
})
if err != nil {
log.Fatal(err)
Expand Down
3 changes: 3 additions & 0 deletions campaigns.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ type LmxWarning struct {
// EmailMessageFields holds the editable fields of an email message. It is
// embedded in [UpdateEmailMessageRequest]; the request's Set map determines
// which fields are actually written.
//
// CCEmail and BCCEmail require CC/BCC to be enabled for the team and are not
// supported for campaign emails.
type EmailMessageFields struct {
Subject string `json:"subject,omitempty"`
PreviewText string `json:"previewText,omitempty"`
Expand Down
5 changes: 3 additions & 2 deletions contacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,9 @@ func (c *Client) UpdateContact(req UpdateContactRequest) error {
return nil
}

// DeleteContact deletes a contact identified by email or user ID. One of the
// two must be non-empty.
// DeleteContact deletes a contact identified by email or user ID. Set exactly
// one of the two. Only the non-empty identifier is sent; the API rejects a
// request that provides both or neither.
func (c *Client) DeleteContact(email, userID string) error {
body := make(map[string]any)
if email != "" {
Expand Down
3 changes: 3 additions & 0 deletions contacts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ func TestDeleteContact(t *testing.T) {
t.Errorf("body[%q] = %v, want %v", k, gotBody[k], v)
}
}
if len(gotBody) != len(tt.wantBody) {
t.Errorf("body = %v, want only %v", gotBody, tt.wantBody)
}
}

if tt.wantAPIErr != nil {
Expand Down
3 changes: 2 additions & 1 deletion transactional.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ func (c *Client) SendTransactional(req SendTransactionalRequest) error {
}

// ListTransactional returns a single page of transactional email templates
// along with pagination information. To iterate every page, use [Paginate]:
// from the legacy list endpoint, along with pagination information. To
// iterate every page, use [Paginate]:
//
// all, err := loops.Paginate(func(cursor string) ([]loops.TransactionalEmail, *loops.Pagination, error) {
// return client.ListTransactional(loops.PaginationParams{Cursor: cursor})
Expand Down
2 changes: 1 addition & 1 deletion version.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import "runtime/debug"
var Version = readVersion()

// SpecVersion is the Loops OpenAPI spec version this SDK is built against.
const SpecVersion = "1.21.9"
const SpecVersion = "1.21.14"

const modulePath = "github.com/loops-so/loops-go"

Expand Down
63 changes: 42 additions & 21 deletions workflows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1325,32 +1325,53 @@ func TestDeleteWorkflow_Confirmed(t *testing.T) {
}

func TestDeleteWorkflow_ConfirmationRequired(t *testing.T) {
const message = "This workflow is currently sending and has 3 queued contacts. Deleting it will stop sending it and cancel those queued contacts. Confirm deletion by sending a second request with confirmDelete: true."
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusConflict)
w.Write([]byte(`{"message":` + strconv.Quote(message) + `}`))
}))
defer server.Close()

client := NewClient("test-key", WithBaseURL(server.URL))
err := client.DeleteWorkflow("wf_1", DeleteWorkflowRequest{ExpectedRevisionID: ptr("rev_1")})
if !errors.Is(err, ErrWorkflowDeleteConfirmationRequired) {
t.Fatalf("expected ErrWorkflowDeleteConfirmationRequired, got %v", err)
}
var apiErr *APIError
if !errors.As(err, &apiErr) {
t.Fatalf("expected a wrapped *APIError, got %T: %v", err, err)
}
if apiErr.StatusCode != http.StatusConflict {
t.Errorf("StatusCode = %d, want 409", apiErr.StatusCode)
tests := []struct {
name string
message string
}{
{
name: "sending",
message: "This workflow is currently sending. Deleting it will stop sending it. Confirm deletion by sending a second request with confirmDelete: true.",
},
{
name: "queued contacts",
message: "This workflow has 3 queued contacts. Deleting it will cancel those queued contacts. Confirm deletion by sending a second request with confirmDelete: true.",
},
{
name: "sending and queued contacts",
message: "This workflow is currently sending and has 3 queued contacts. Deleting it will stop sending it and cancel those queued contacts. Confirm deletion by sending a second request with confirmDelete: true.",
},
}
if apiErr.Message != message {
t.Errorf("Message = %q", apiErr.Message)

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusConflict)
w.Write([]byte(`{"message":` + strconv.Quote(tt.message) + `}`))
}))
defer server.Close()

client := NewClient("test-key", WithBaseURL(server.URL))
err := client.DeleteWorkflow("wf_1", DeleteWorkflowRequest{ExpectedRevisionID: ptr("rev_1")})
if !errors.Is(err, ErrWorkflowDeleteConfirmationRequired) {
t.Fatalf("expected ErrWorkflowDeleteConfirmationRequired, got %v", err)
}
var apiErr *APIError
if !errors.As(err, &apiErr) {
t.Fatalf("expected a wrapped *APIError, got %T: %v", err, err)
}
if apiErr.StatusCode != http.StatusConflict {
t.Errorf("StatusCode = %d, want 409", apiErr.StatusCode)
}
if apiErr.Message != tt.message {
t.Errorf("Message = %q", apiErr.Message)
}
})
}
}

func TestDeleteWorkflow_StaleRevision409(t *testing.T) {
const message = "workflowRevisionId mismatch. Latest workflowRevisionId is rev_2. Refetch and retry."
const message = "workflowRevisionId mismatch. Latest workflowRevisionId is clx7a3b5c7d9e1f3g5h7i9j1. Refetch and retry."
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusConflict)
w.Write([]byte(`{"message":` + strconv.Quote(message) + `}`))
Expand Down
Loading