loops_sdk 2.3.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,6 +10,11 @@ module LoopsSdk
10
10
  def get(audience_segment_id:)
11
11
  make_request(method: :get, path: "v1/audience-segments/#{audience_segment_id}")
12
12
  end
13
+
14
+ def create(name:, filter:, description: nil)
15
+ body = { name: name, filter: filter, description: description }.compact
16
+ make_request(method: :post, path: "v1/audience-segments", body: body)
17
+ end
13
18
  end
14
19
  end
15
20
  end
@@ -9,11 +9,13 @@ module LoopsSdk
9
9
  case response.status
10
10
  when 200, 201
11
11
  JSON.parse(response.body)
12
+ when 204
13
+ nil
12
14
  when 429
13
15
  limit = response.headers["x-ratelimit-limit"]
14
16
  remaining = response.headers["x-ratelimit-remaining"]
15
17
  raise RateLimitError.new(limit, remaining)
16
- when 400, 401, 404, 405, 409, 413, 422, 500
18
+ when 400, 401, 404, 405, 409, 413, 422, 500, 501
17
19
  raise APIError.new(response.status, response.body)
18
20
  else
19
21
  raise APIError.new(response.status, "Unexpected error occurred")
@@ -34,6 +34,10 @@ module LoopsSdk
34
34
  }.compact
35
35
  make_request(method: :post, path: "v1/campaigns/#{campaign_id}", body: body)
36
36
  end
37
+
38
+ def metrics(campaign_id:)
39
+ make_request(method: :get, path: "v1/campaigns/#{campaign_id}/metrics")
40
+ end
37
41
  end
38
42
  end
39
43
  end
@@ -10,6 +10,15 @@ module LoopsSdk
10
10
  def get(component_id:)
11
11
  make_request(method: :get, path: "v1/components/#{component_id}")
12
12
  end
13
+
14
+ def create(name:, lmx:)
15
+ make_request(method: :post, path: "v1/components", body: { name: name, lmx: lmx })
16
+ end
17
+
18
+ def update(component_id:, name: nil, lmx: nil)
19
+ body = { name: name, lmx: lmx }.compact
20
+ make_request(method: :post, path: "v1/components/#{component_id}", body: body)
21
+ end
13
22
  end
14
23
  end
15
24
  end
@@ -52,6 +52,11 @@ module LoopsSdk
52
52
  }.compact
53
53
  make_request(method: :post, path: "v1/email-messages/#{email_message_id}/preview", body: body)
54
54
  end
55
+
56
+ def run_guardian(email_message_id:)
57
+ make_request(method: :get, path: "v1/email-messages/#{email_message_id}/guardian")
58
+ end
55
59
  end
56
60
  end
57
61
  end
62
+
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cgi"
4
+
5
+ module LoopsSdk
6
+ class EventPatterns < Base
7
+ class << self
8
+ def list(perPage: 20, cursor: nil)
9
+ make_request(method: :get, path: "v1/event-patterns", params: { perPage: perPage, cursor: cursor })
10
+ end
11
+
12
+ def get(event_pattern_id:)
13
+ make_request(method: :get, path: "v1/event-patterns/#{event_pattern_id}")
14
+ end
15
+
16
+ def get_by_name(event_name:)
17
+ encoded_name = CGI.escapeURIComponent(event_name)
18
+ make_request(method: :get, path: "v1/event-patterns/by-name/#{encoded_name}")
19
+ end
20
+ end
21
+ end
22
+ end
@@ -10,6 +10,16 @@ module LoopsSdk
10
10
  def get(theme_id:)
11
11
  make_request(method: :get, path: "v1/themes/#{theme_id}")
12
12
  end
13
+
14
+ def create(name:, styles: nil)
15
+ body = { name: name, styles: styles }.compact
16
+ make_request(method: :post, path: "v1/themes", body: body)
17
+ end
18
+
19
+ def update(theme_id:, name: nil, styles: nil)
20
+ body = { name: name, styles: styles }.compact
21
+ make_request(method: :post, path: "v1/themes/#{theme_id}", body: body)
22
+ end
13
23
  end
14
24
  end
15
25
  end
@@ -29,6 +29,10 @@ module LoopsSdk
29
29
  make_request(method: :post, path: "v1/transactional-emails/#{transactional_id}/publish")
30
30
  end
31
31
 
32
+ def metrics(transactional_id:)
33
+ make_request(method: :get, path: "v1/transactional-emails/#{transactional_id}/metrics")
34
+ end
35
+
32
36
  def send(transactional_id:, email:, add_to_audience: false, data_variables: {}, attachments: [], headers: {})
33
37
  attachments = attachments.map do |attachment|
34
38
  attachment.transform_keys { |key| key == :content_type ? :contentType : key }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LoopsSdk
4
- VERSION = "2.3.0"
4
+ VERSION = "2.6.0"
5
5
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LoopsSdk
4
+ class WorkflowGroups < Base
5
+ class << self
6
+ def list(perPage: 20, cursor: nil)
7
+ make_request(method: :get, path: "v1/workflow-groups", params: { perPage: perPage, cursor: cursor })
8
+ end
9
+
10
+ def create(name:, description: nil)
11
+ body = { name: name, description: description }.compact
12
+ make_request(method: :post, path: "v1/workflow-groups", body: body)
13
+ end
14
+
15
+ def get(workflow_group_id:)
16
+ make_request(method: :get, path: "v1/workflow-groups/#{workflow_group_id}")
17
+ end
18
+
19
+ def update(workflow_group_id:, name: nil, description: nil)
20
+ body = { name: name, description: description }.compact
21
+ make_request(method: :post, path: "v1/workflow-groups/#{workflow_group_id}", body: body)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -7,13 +7,148 @@ module LoopsSdk
7
7
  make_request(method: :get, path: "v1/workflows", params: { perPage: perPage, cursor: cursor })
8
8
  end
9
9
 
10
+ def create(name:, description: nil, mailing_list_id: nil, workflow_group_id: nil)
11
+ body = {
12
+ name: name,
13
+ description: description,
14
+ mailingListId: mailing_list_id,
15
+ workflowGroupId: workflow_group_id
16
+ }.compact
17
+ make_request(method: :post, path: "v1/workflows", body: body)
18
+ end
19
+
10
20
  def get(workflow_id:)
11
21
  make_request(method: :get, path: "v1/workflows/#{workflow_id}")
12
22
  end
13
23
 
24
+ def update(workflow_id:, expected_revision_id:, name: nil, description: nil, workflow_group_id: nil)
25
+ body = {
26
+ expectedRevisionId: expected_revision_id,
27
+ name: name,
28
+ description: description,
29
+ workflowGroupId: workflow_group_id
30
+ }.compact
31
+ # expectedRevisionId may be null for older workflows; always include it
32
+ body[:expectedRevisionId] = expected_revision_id
33
+ make_request(method: :post, path: "v1/workflows/#{workflow_id}", body: body)
34
+ end
35
+
36
+ def delete(workflow_id:, expected_revision_id:, confirm_delete: nil)
37
+ body = {
38
+ expectedRevisionId: expected_revision_id,
39
+ confirmDelete: confirm_delete
40
+ }.compact
41
+ # expectedRevisionId may be null for older workflows; always include it
42
+ body[:expectedRevisionId] = expected_revision_id
43
+ make_request(method: :delete, path: "v1/workflows/#{workflow_id}", body: body)
44
+ end
45
+
46
+ def change_mailing_list(workflow_id:, expected_revision_id:, mailing_list_id:, dry_run: nil, queued_contact_policy: nil)
47
+ body = {
48
+ expectedRevisionId: expected_revision_id,
49
+ mailingListId: mailing_list_id,
50
+ dryRun: dry_run,
51
+ queuedContactPolicy: queued_contact_policy
52
+ }.compact
53
+ # expectedRevisionId and mailingListId may be null; always include them
54
+ body[:expectedRevisionId] = expected_revision_id
55
+ body[:mailingListId] = mailing_list_id
56
+ make_request(method: :post, path: "v1/workflows/#{workflow_id}/mailing-list", body: body)
57
+ end
58
+
14
59
  def get_node(workflow_id:, node_id:)
15
60
  make_request(method: :get, path: "v1/workflows/#{workflow_id}/nodes/#{node_id}")
16
61
  end
62
+
63
+ def get_node_metrics(workflow_id:, node_id:)
64
+ make_request(method: :get, path: "v1/workflows/#{workflow_id}/nodes/#{node_id}/metrics")
65
+ end
66
+
67
+ def create_node(
68
+ workflow_id:,
69
+ expected_revision_id:,
70
+ insert_mode:,
71
+ node_type_name:,
72
+ from_node_id: nil,
73
+ to_node_id: nil,
74
+ before_node_id: nil
75
+ )
76
+ body = {
77
+ expectedRevisionId: expected_revision_id,
78
+ insertMode: insert_mode,
79
+ nodeTypeName: node_type_name
80
+ }
81
+
82
+ case insert_mode
83
+ when "between"
84
+ raise ArgumentError, "from_node_id and to_node_id are required when insert_mode is \"between\"." if from_node_id.nil? || to_node_id.nil?
85
+ raise ArgumentError, "before_node_id is not permitted when insert_mode is \"between\"." unless before_node_id.nil?
86
+
87
+ body[:fromNodeId] = from_node_id
88
+ body[:toNodeId] = to_node_id
89
+ when "before"
90
+ raise ArgumentError, "from_node_id is not permitted when insert_mode is \"before\"." unless from_node_id.nil?
91
+ raise ArgumentError, "Provide either to_node_id or before_node_id when insert_mode is \"before\", not both." if !to_node_id.nil? && !before_node_id.nil?
92
+ raise ArgumentError, "to_node_id or before_node_id is required when insert_mode is \"before\"." if to_node_id.nil? && before_node_id.nil?
93
+
94
+ if !to_node_id.nil?
95
+ body[:toNodeId] = to_node_id
96
+ else
97
+ body[:beforeNodeId] = before_node_id
98
+ end
99
+ when "after"
100
+ raise ArgumentError, "from_node_id is required when insert_mode is \"after\"." if from_node_id.nil?
101
+ raise ArgumentError, "to_node_id is not permitted when insert_mode is \"after\"." unless to_node_id.nil?
102
+ raise ArgumentError, "before_node_id is not permitted when insert_mode is \"after\"." unless before_node_id.nil?
103
+
104
+ body[:fromNodeId] = from_node_id
105
+ else
106
+ raise ArgumentError, "insert_mode must be \"between\", \"before\", or \"after\"."
107
+ end
108
+
109
+ make_request(method: :post, path: "v1/workflows/#{workflow_id}/nodes", body: body)
110
+ end
111
+
112
+ def update_node(workflow_id:, node_id:, expected_revision_id:, payload:)
113
+ body = {
114
+ expectedRevisionId: expected_revision_id,
115
+ payload: payload
116
+ }
117
+ make_request(method: :post, path: "v1/workflows/#{workflow_id}/nodes/#{node_id}", body: body)
118
+ end
119
+
120
+ def delete_node(workflow_id:, node_id:, expected_revision_id:, dry_run: nil, queued_contact_policy: nil)
121
+ body = {
122
+ expectedRevisionId: expected_revision_id,
123
+ dryRun: dry_run,
124
+ queuedContactPolicy: queued_contact_policy
125
+ }.compact
126
+ body[:expectedRevisionId] = expected_revision_id
127
+ make_request(method: :delete, path: "v1/workflows/#{workflow_id}/nodes/#{node_id}", body: body)
128
+ end
129
+
130
+ def add_branch(workflow_id:, node_id:, expected_revision_id:)
131
+ body = { expectedRevisionId: expected_revision_id }
132
+ make_request(method: :post, path: "v1/workflows/#{workflow_id}/nodes/#{node_id}/add-branch", body: body)
133
+ end
134
+
135
+ def reroute_node(workflow_id:, node_id:, expected_revision_id:, new_target_node_id:)
136
+ body = {
137
+ expectedRevisionId: expected_revision_id,
138
+ newTargetNodeId: new_target_node_id
139
+ }
140
+ make_request(method: :post, path: "v1/workflows/#{workflow_id}/nodes/#{node_id}/reroute", body: body)
141
+ end
142
+
143
+ def delete_node_recursive(workflow_id:, node_id:, expected_revision_id:, dry_run: nil, queued_contact_policy: nil)
144
+ body = {
145
+ expectedRevisionId: expected_revision_id,
146
+ dryRun: dry_run,
147
+ queuedContactPolicy: queued_contact_policy
148
+ }.compact
149
+ body[:expectedRevisionId] = expected_revision_id
150
+ make_request(method: :delete, path: "v1/workflows/#{workflow_id}/nodes/#{node_id}/recursive", body: body)
151
+ end
17
152
  end
18
153
  end
19
154
  end
data/lib/loops_sdk.rb CHANGED
@@ -20,6 +20,8 @@ require_relative "loops_sdk/email_messages"
20
20
  require_relative "loops_sdk/uploads"
21
21
  require_relative "loops_sdk/audience_segments"
22
22
  require_relative "loops_sdk/workflows"
23
+ require_relative "loops_sdk/workflow_groups"
24
+ require_relative "loops_sdk/event_patterns"
23
25
  require_relative "loops_sdk/transactional_groups"
24
26
 
25
27
  module LoopsSdk
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loops_sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Rowden
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-06-24 00:00:00.000000000 Z
11
+ date: 2026-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -45,6 +45,7 @@ files:
45
45
  - lib/loops_sdk/contacts.rb
46
46
  - lib/loops_sdk/dedicated_sending_ips.rb
47
47
  - lib/loops_sdk/email_messages.rb
48
+ - lib/loops_sdk/event_patterns.rb
48
49
  - lib/loops_sdk/events.rb
49
50
  - lib/loops_sdk/mailing_lists.rb
50
51
  - lib/loops_sdk/themes.rb
@@ -52,6 +53,7 @@ files:
52
53
  - lib/loops_sdk/transactional_groups.rb
53
54
  - lib/loops_sdk/uploads.rb
54
55
  - lib/loops_sdk/version.rb
56
+ - lib/loops_sdk/workflow_groups.rb
55
57
  - lib/loops_sdk/workflows.rb
56
58
  homepage: https://loops.so/docs/api-reference
57
59
  licenses: