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
38 changes: 20 additions & 18 deletions workflows.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ const (
type WorkflowTimerUnit string

const (
WorkflowTimerUnitSeconds WorkflowTimerUnit = "s"
WorkflowTimerUnitMinutes WorkflowTimerUnit = "m"
WorkflowTimerUnitHours WorkflowTimerUnit = "h"
WorkflowTimerUnitDays WorkflowTimerUnit = "d"
Expand Down Expand Up @@ -197,9 +196,9 @@ type SimplifiedContactPropertyTriggerWorkflowNode struct {
// SimplifiedAddToListTriggerWorkflowNode is the AddToListTrigger variant of
// [SimplifiedWorkflowNode].
type SimplifiedAddToListTriggerWorkflowNode struct {
NextNodeIDs []string `json:"nextNodeIds"`
MailingList string `json:"mailingList,omitempty"`
ReEligible *bool `json:"reEligible,omitempty"`
NextNodeIDs []string `json:"nextNodeIds"`
MailingListID *string `json:"mailingListId"`
ReEligible *bool `json:"reEligible,omitempty"`
}

// SimplifiedBlankTriggerWorkflowNode is the BlankTrigger variant of
Expand Down Expand Up @@ -439,10 +438,11 @@ type ContactPropertyTriggerWorkflowNode struct {
// AddToListTriggerWorkflowNode is the AddToListTrigger variant of
// [WorkflowNode].
type AddToListTriggerWorkflowNode struct {
ID string `json:"id"`
WorkflowID string `json:"workflowId"`
NextNodeIDs []string `json:"nextNodeIds"`
ReEligible bool `json:"reEligible"`
ID string `json:"id"`
WorkflowID string `json:"workflowId"`
NextNodeIDs []string `json:"nextNodeIds"`
MailingListID *string `json:"mailingListId"`
ReEligible bool `json:"reEligible"`
}

// BlankTriggerWorkflowNode is the BlankTrigger variant of [WorkflowNode].
Expand Down Expand Up @@ -471,14 +471,13 @@ type TimerActionWorkflowNode struct {
Unit WorkflowTimerUnit `json:"unit"`
}

// SendEmailActionWorkflowNode is the SendEmailAction variant of
// [WorkflowNode]. The full variant does not include emailMessageId (only the
// simplified variant does).
// SendEmailActionWorkflowNode is the SendEmailAction variant of [WorkflowNode].
type SendEmailActionWorkflowNode struct {
ID string `json:"id"`
WorkflowID string `json:"workflowId"`
NextNodeIDs []string `json:"nextNodeIds"`
Subject string `json:"subject,omitempty"`
ID string `json:"id"`
WorkflowID string `json:"workflowId"`
NextNodeIDs []string `json:"nextNodeIds"`
EmailMessageID string `json:"emailMessageId"`
Subject string `json:"subject"`
}

// ExitActionWorkflowNode is the ExitAction variant of [WorkflowNode].
Expand Down Expand Up @@ -649,13 +648,16 @@ func marshalDiscriminated(typeName string, inner any) ([]byte, error) {
if typeName == "" {
return nil, fmt.Errorf("workflow node: typeName is empty")
}
if inner == nil {
return nil, fmt.Errorf("workflow node: %s variant is nil", typeName)
}
raw, err := json.Marshal(inner)
if err != nil {
return nil, err
}
// A typed-nil variant pointer is a non-nil interface but marshals to
// "null"; treat that (and an untyped nil) as a missing variant instead of
// panicking on the nil map below.
if string(raw) == "null" {
return nil, fmt.Errorf("workflow node: %s variant is nil", typeName)
}
var fields map[string]json.RawMessage
if err := json.Unmarshal(raw, &fields); err != nil {
return nil, err
Expand Down
79 changes: 79 additions & 0 deletions workflows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1230,3 +1230,82 @@ func TestCreatedWorkflowNode_MarshalKeepsRevisionAndChildren(t *testing.T) {
t.Errorf("marshal dropped createdChildNodes: %s", raw)
}
}

func TestGetWorkflowNode_AddToListTrigger(t *testing.T) {
body := `{
"id": "node_a",
"workflowId": "wf_1",
"typeName": "AddToListTrigger",
"nextNodeIds": ["node_next"],
"mailingListId": "ml_1",
"reEligible": true
}`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(body))
}))
defer server.Close()

client := NewClient("test-key", WithBaseURL(server.URL))
node, err := client.GetWorkflowNode("wf_1", "node_a")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if node.AddToListTrigger == nil {
t.Fatal("AddToListTrigger is nil")
}
if node.AddToListTrigger.MailingListID == nil || *node.AddToListTrigger.MailingListID != "ml_1" {
t.Errorf("MailingListID = %v, want ml_1", node.AddToListTrigger.MailingListID)
}
}

func TestGetWorkflowNode_SendEmailAction(t *testing.T) {
body := `{
"id": "node_s",
"workflowId": "wf_1",
"typeName": "SendEmailAction",
"nextNodeIds": ["node_next"],
"emailMessageId": "em_1",
"subject": "Welcome"
}`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(body))
}))
defer server.Close()

client := NewClient("test-key", WithBaseURL(server.URL))
node, err := client.GetWorkflowNode("wf_1", "node_s")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if node.SendEmailAction == nil {
t.Fatal("SendEmailAction is nil")
}
if node.SendEmailAction.EmailMessageID != "em_1" {
t.Errorf("EmailMessageID = %q, want em_1", node.SendEmailAction.EmailMessageID)
}
if node.SendEmailAction.Subject != "Welcome" {
t.Errorf("Subject = %q, want Welcome", node.SendEmailAction.Subject)
}
}

func TestSimplifiedAddToListNode_DecodesMailingListID(t *testing.T) {
const in = `{"typeName":"AddToListTrigger","nextNodeIds":[],"mailingListId":"ml_1","reEligible":true}`
var n SimplifiedWorkflowNode
if err := json.Unmarshal([]byte(in), &n); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if n.AddToListTrigger == nil {
t.Fatal("AddToListTrigger is nil")
}
if n.AddToListTrigger.MailingListID == nil || *n.AddToListTrigger.MailingListID != "ml_1" {
t.Errorf("MailingListID = %v, want ml_1", n.AddToListTrigger.MailingListID)
}
}

func TestMarshalDiscriminated_TypedNilVariantErrors(t *testing.T) {
// A node with the discriminator set but the variant pointer nil must return
// an error, not panic on a nil-map write.
if _, err := json.Marshal(WorkflowNode{TypeName: WorkflowNodeTypeTimerAction}); err == nil {
t.Fatal("expected error marshaling a node with a nil variant, got none")
}
}