mailshake-ruby 0.1.0 → 0.3.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.
- checksums.yaml +4 -4
- data/README.md +80 -19
- data/lib/mailshake/activity.rb +14 -7
- data/lib/mailshake/campaigns.rb +10 -5
- data/lib/mailshake/client.rb +23 -2
- data/lib/mailshake/errors.rb +15 -0
- data/lib/mailshake/leads.rb +6 -3
- data/lib/mailshake/models/add_recipients_request.rb +8 -0
- data/lib/mailshake/models/added_recipients.rb +8 -0
- data/lib/mailshake/models/base_model.rb +79 -0
- data/lib/mailshake/models/campaign.rb +10 -0
- data/lib/mailshake/models/campaign_export.rb +8 -0
- data/lib/mailshake/models/campaign_export_request.rb +8 -0
- data/lib/mailshake/models/click.rb +11 -0
- data/lib/mailshake/models/created_leads.rb +8 -0
- data/lib/mailshake/models/lead.rb +11 -0
- data/lib/mailshake/models/lead_status.rb +8 -0
- data/lib/mailshake/models/list.rb +30 -0
- data/lib/mailshake/models/message.rb +8 -0
- data/lib/mailshake/models/open.rb +11 -0
- data/lib/mailshake/models/recipient.rb +8 -0
- data/lib/mailshake/models/reply.rb +11 -0
- data/lib/mailshake/models/sender.rb +8 -0
- data/lib/mailshake/models/sent_message.rb +11 -0
- data/lib/mailshake/models/user.rb +8 -0
- data/lib/mailshake/recipients.rb +8 -4
- data/lib/mailshake/senders.rb +2 -1
- data/lib/mailshake/team.rb +2 -1
- data/lib/mailshake/version.rb +1 -1
- data/lib/mailshake.rb +21 -0
- data/spec/examples.txt +89 -60
- data/spec/mailshake/activity_spec.rb +4 -2
- data/spec/mailshake/campaigns_spec.rb +10 -4
- data/spec/mailshake/client_spec.rb +54 -0
- data/spec/mailshake/leads_spec.rb +5 -2
- data/spec/mailshake/models/base_model_spec.rb +118 -0
- data/spec/mailshake/models/list_spec.rb +103 -0
- data/spec/mailshake/push_spec.rb +1 -0
- data/spec/mailshake/recipients_spec.rb +7 -3
- data/spec/mailshake/senders_spec.rb +2 -1
- data/spec/mailshake/team_spec.rb +2 -1
- metadata +24 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 328a77f17bacfe02fc4a7a4a869a1aa78bde71a05096c95057e7a9586c380a75
|
|
4
|
+
data.tar.gz: 07323100c2bfc0a3743c60204d248860dd04b784c79cdba0e060a168b659e963
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cb67630c178d246c30d3c10ab59f94b4e19c93a1f9e2423814bf9db3f641f5cc2eed12bdc8822b5c52f4d1e82c7a6b3d23d1de51cad5f6ab5e1c26a3590c0247
|
|
7
|
+
data.tar.gz: de3dc3ef281a458821178873fc05c6bb1e36d1ea252adaee6991869c8b5b86e8a6f9f5e67785b9ca23fa4cc36f1c06389ce1fa618443da674ebdccfbdfb4bd1f
|
data/README.md
CHANGED
|
@@ -32,18 +32,29 @@ end
|
|
|
32
32
|
|
|
33
33
|
## Usage
|
|
34
34
|
|
|
35
|
+
All API responses return model objects with snake_case accessors and nested model hydration. Hash-style access (`result["key"]`) is still supported for backwards compatibility.
|
|
36
|
+
|
|
35
37
|
### Campaigns
|
|
36
38
|
|
|
37
39
|
```ruby
|
|
38
40
|
campaigns = Mailshake::Campaigns.new
|
|
39
41
|
|
|
40
|
-
campaigns.list(search: "onboarding", per_page: 10)
|
|
41
|
-
|
|
42
|
+
list = campaigns.list(search: "onboarding", per_page: 10)
|
|
43
|
+
list.each { |c| puts c.title }
|
|
44
|
+
|
|
45
|
+
campaign = campaigns.get(campaign_id: 1)
|
|
46
|
+
campaign.title # => "Q2 Outreach"
|
|
47
|
+
campaign.is_paused # => false
|
|
48
|
+
campaign.sender.email_address # => "sales@company.com"
|
|
49
|
+
campaign.messages.first.subject # => "Hey {{first}}"
|
|
50
|
+
|
|
42
51
|
campaigns.create(title: "Q2 Outreach", sender_id: 5)
|
|
43
52
|
campaigns.pause(campaign_id: 1)
|
|
44
53
|
campaigns.unpause(campaign_id: 1)
|
|
45
|
-
|
|
46
|
-
campaigns.
|
|
54
|
+
|
|
55
|
+
export = campaigns.export(campaign_ids: [1, 2], export_type: "csv", timezone: "US/Pacific")
|
|
56
|
+
status = campaigns.export_status(status_id: export.check_status_id)
|
|
57
|
+
puts status.csv_download_url if status.is_finished
|
|
47
58
|
```
|
|
48
59
|
|
|
49
60
|
### Recipients
|
|
@@ -51,10 +62,16 @@ campaigns.export_status(status_id: 42)
|
|
|
51
62
|
```ruby
|
|
52
63
|
recipients = Mailshake::Recipients.new
|
|
53
64
|
|
|
54
|
-
recipients.add(campaign_id: 1, addresses: [{ emailAddress: "john@example.com" }])
|
|
55
|
-
recipients.add_status(status_id:
|
|
56
|
-
|
|
57
|
-
recipients.
|
|
65
|
+
result = recipients.add(campaign_id: 1, addresses: [{ emailAddress: "john@example.com" }])
|
|
66
|
+
status = recipients.add_status(status_id: result.check_status_id)
|
|
67
|
+
|
|
68
|
+
list = recipients.list(campaign_id: 1, filter: "active", per_page: 25)
|
|
69
|
+
list.each { |r| puts "#{r.full_name} <#{r.email_address}>" }
|
|
70
|
+
|
|
71
|
+
recipient = recipients.get(recipient_id: 100)
|
|
72
|
+
recipient.email_address # => "john@example.com"
|
|
73
|
+
recipient.is_paused # => false
|
|
74
|
+
|
|
58
75
|
recipients.pause(campaign_id: 1, email_address: "john@example.com")
|
|
59
76
|
recipients.unpause(campaign_id: 1, email_address: "john@example.com")
|
|
60
77
|
recipients.unsubscribe(email_addresses: ["john@example.com"])
|
|
@@ -65,10 +82,18 @@ recipients.unsubscribe(email_addresses: ["john@example.com"])
|
|
|
65
82
|
```ruby
|
|
66
83
|
activity = Mailshake::Activity.new
|
|
67
84
|
|
|
68
|
-
activity.sent(campaign_id: 1, per_page: 50)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
activity.
|
|
85
|
+
sent = activity.sent(campaign_id: 1, per_page: 50)
|
|
86
|
+
sent.each { |msg| puts "#{msg.subject} -> #{msg.recipient.email_address}" }
|
|
87
|
+
|
|
88
|
+
opens = activity.opens(campaign_id: 1, exclude_duplicates: true)
|
|
89
|
+
opens.each { |o| puts "#{o.recipient.email_address} opened at #{o.action_date}" }
|
|
90
|
+
|
|
91
|
+
clicks = activity.clicks(campaign_id: 1, match_url: "https://example.com")
|
|
92
|
+
clicks.each { |c| puts "#{c.recipient.email_address} clicked #{c.link}" }
|
|
93
|
+
|
|
94
|
+
replies = activity.replies(campaign_id: 1, reply_type: "reply")
|
|
95
|
+
replies.each { |r| puts "#{r.recipient.email_address}: #{r.plain_text_body}" }
|
|
96
|
+
|
|
72
97
|
activity.created_leads(campaign_id: 1, since: "2026-01-01")
|
|
73
98
|
activity.lead_assignments(campaign_id: 1)
|
|
74
99
|
activity.lead_status_changes(campaign_id: 1)
|
|
@@ -79,8 +104,17 @@ activity.lead_status_changes(campaign_id: 1)
|
|
|
79
104
|
```ruby
|
|
80
105
|
leads = Mailshake::Leads.new
|
|
81
106
|
|
|
82
|
-
leads.list(campaign_id: 1, status: "open")
|
|
83
|
-
|
|
107
|
+
list = leads.list(campaign_id: 1, status: "open")
|
|
108
|
+
list.each do |lead|
|
|
109
|
+
puts "#{lead.recipient.email_address} - #{lead.status}"
|
|
110
|
+
puts " assigned to: #{lead.assigned_to&.full_name}"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
lead = leads.get(lead_id: 42)
|
|
114
|
+
lead.status # => "open"
|
|
115
|
+
lead.recipient.email_address # => "john@example.com"
|
|
116
|
+
lead.campaign.title # => "Q2 Outreach"
|
|
117
|
+
|
|
84
118
|
leads.create(campaign_id: 1, email_addresses: ["john@example.com"])
|
|
85
119
|
leads.close(lead_id: 42, status: "won")
|
|
86
120
|
leads.ignore(lead_id: 42)
|
|
@@ -92,7 +126,8 @@ leads.reopen(lead_id: 42)
|
|
|
92
126
|
```ruby
|
|
93
127
|
team = Mailshake::Team.new
|
|
94
128
|
|
|
95
|
-
team.list_members(search: "john", per_page: 10)
|
|
129
|
+
members = team.list_members(search: "john", per_page: 10)
|
|
130
|
+
members.each { |u| puts "#{u.full_name} <#{u.email_address}>" }
|
|
96
131
|
```
|
|
97
132
|
|
|
98
133
|
### Senders
|
|
@@ -100,7 +135,8 @@ team.list_members(search: "john", per_page: 10)
|
|
|
100
135
|
```ruby
|
|
101
136
|
senders = Mailshake::Senders.new
|
|
102
137
|
|
|
103
|
-
senders.list(search: "sales", per_page: 10)
|
|
138
|
+
list = senders.list(search: "sales", per_page: 10)
|
|
139
|
+
list.each { |s| puts "#{s.from_name} <#{s.email_address}>" }
|
|
104
140
|
```
|
|
105
141
|
|
|
106
142
|
### Push/Webhooks
|
|
@@ -116,16 +152,41 @@ push.delete(target_url: "https://example.com/webhook")
|
|
|
116
152
|
|
|
117
153
|
```ruby
|
|
118
154
|
client = Mailshake.client
|
|
119
|
-
client.me
|
|
155
|
+
user = client.me
|
|
156
|
+
user.full_name # => "Eduardo Souza"
|
|
157
|
+
user.email_address # => "eduardo@example.com"
|
|
158
|
+
user.is_team_admin # => true
|
|
120
159
|
```
|
|
121
160
|
|
|
161
|
+
## Models
|
|
162
|
+
|
|
163
|
+
All responses are wrapped in model objects under `Mailshake::Models`. Models provide:
|
|
164
|
+
|
|
165
|
+
- **Snake_case accessors** - `campaign.is_paused` instead of `campaign["isPaused"]`
|
|
166
|
+
- **Nested model hydration** - `lead.recipient.email_address` returns a `Recipient` model
|
|
167
|
+
- **Backwards compatibility** - `campaign["title"]` still works
|
|
168
|
+
- **`to_h`** - convert back to the original hash
|
|
169
|
+
|
|
170
|
+
See the [full model reference](https://github.com/ESouza/mailshake-ruby/wiki/Models) in the wiki.
|
|
171
|
+
|
|
122
172
|
## Pagination
|
|
123
173
|
|
|
124
|
-
|
|
174
|
+
List endpoints return a `Models::List` object with `Enumerable` support:
|
|
125
175
|
|
|
126
176
|
```ruby
|
|
127
177
|
result = campaigns.list(per_page: 10)
|
|
128
|
-
|
|
178
|
+
result.each { |c| puts c.title }
|
|
179
|
+
result.map(&:title)
|
|
180
|
+
|
|
181
|
+
# Paginate through all results
|
|
182
|
+
all = []
|
|
183
|
+
result = campaigns.list(per_page: 25)
|
|
184
|
+
all.concat(result.results)
|
|
185
|
+
|
|
186
|
+
while result.next_token
|
|
187
|
+
result = campaigns.list(per_page: 25, next_token: result.next_token)
|
|
188
|
+
all.concat(result.results)
|
|
189
|
+
end
|
|
129
190
|
```
|
|
130
191
|
|
|
131
192
|
## Error Handling
|
data/lib/mailshake/activity.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Mailshake
|
|
4
4
|
class Activity < Base
|
|
5
5
|
def sent(message_type: nil, campaign_message_type: nil, campaign_id: nil, recipient_email_address: nil, exclude_body: nil, next_token: nil, per_page: nil)
|
|
6
|
-
client.get("/activity/sent", camelize_params(
|
|
6
|
+
response = client.get("/activity/sent", camelize_params(
|
|
7
7
|
message_type: message_type,
|
|
8
8
|
campaign_message_type: campaign_message_type,
|
|
9
9
|
campaign_id: campaign_id,
|
|
@@ -12,10 +12,11 @@ module Mailshake
|
|
|
12
12
|
next_token: next_token,
|
|
13
13
|
per_page: per_page
|
|
14
14
|
))
|
|
15
|
+
Models::List.new(response, Models::SentMessage)
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
def opens(campaign_id: nil, exclude_duplicates: nil, recipient_email_address: nil, next_token: nil, per_page: nil, since: nil)
|
|
18
|
-
client.get("/activity/opens", camelize_params(
|
|
19
|
+
response = client.get("/activity/opens", camelize_params(
|
|
19
20
|
campaign_id: campaign_id,
|
|
20
21
|
exclude_duplicates: exclude_duplicates,
|
|
21
22
|
recipient_email_address: recipient_email_address,
|
|
@@ -23,10 +24,11 @@ module Mailshake
|
|
|
23
24
|
per_page: per_page,
|
|
24
25
|
since: since
|
|
25
26
|
))
|
|
27
|
+
Models::List.new(response, Models::Open)
|
|
26
28
|
end
|
|
27
29
|
|
|
28
30
|
def clicks(campaign_id: nil, exclude_duplicates: nil, match_url: nil, recipient_email_address: nil, next_token: nil, per_page: nil, since: nil)
|
|
29
|
-
client.get("/activity/clicks", camelize_params(
|
|
31
|
+
response = client.get("/activity/clicks", camelize_params(
|
|
30
32
|
campaign_id: campaign_id,
|
|
31
33
|
exclude_duplicates: exclude_duplicates,
|
|
32
34
|
match_url: match_url,
|
|
@@ -35,20 +37,22 @@ module Mailshake
|
|
|
35
37
|
per_page: per_page,
|
|
36
38
|
since: since
|
|
37
39
|
))
|
|
40
|
+
Models::List.new(response, Models::Click)
|
|
38
41
|
end
|
|
39
42
|
|
|
40
43
|
def replies(reply_type: nil, campaign_id: nil, recipient_email_address: nil, next_token: nil, per_page: nil)
|
|
41
|
-
client.get("/activity/replies", camelize_params(
|
|
44
|
+
response = client.get("/activity/replies", camelize_params(
|
|
42
45
|
reply_type: reply_type,
|
|
43
46
|
campaign_id: campaign_id,
|
|
44
47
|
recipient_email_address: recipient_email_address,
|
|
45
48
|
next_token: next_token,
|
|
46
49
|
per_page: per_page
|
|
47
50
|
))
|
|
51
|
+
Models::List.new(response, Models::Reply)
|
|
48
52
|
end
|
|
49
53
|
|
|
50
54
|
def created_leads(campaign_id: nil, recipient_email_address: nil, assigned_to_email_address: nil, next_token: nil, per_page: nil, since: nil)
|
|
51
|
-
client.get("/activity/created-leads", camelize_params(
|
|
55
|
+
response = client.get("/activity/created-leads", camelize_params(
|
|
52
56
|
campaign_id: campaign_id,
|
|
53
57
|
recipient_email_address: recipient_email_address,
|
|
54
58
|
assigned_to_email_address: assigned_to_email_address,
|
|
@@ -56,19 +60,21 @@ module Mailshake
|
|
|
56
60
|
per_page: per_page,
|
|
57
61
|
since: since
|
|
58
62
|
))
|
|
63
|
+
Models::List.new(response, Models::Lead)
|
|
59
64
|
end
|
|
60
65
|
|
|
61
66
|
def lead_assignments(campaign_id: nil, next_token: nil, per_page: nil, since: nil)
|
|
62
|
-
client.get("/activity/lead-assignments", camelize_params(
|
|
67
|
+
response = client.get("/activity/lead-assignments", camelize_params(
|
|
63
68
|
campaign_id: campaign_id,
|
|
64
69
|
next_token: next_token,
|
|
65
70
|
per_page: per_page,
|
|
66
71
|
since: since
|
|
67
72
|
))
|
|
73
|
+
Models::List.new(response, Models::Lead)
|
|
68
74
|
end
|
|
69
75
|
|
|
70
76
|
def lead_status_changes(campaign_id: nil, recipient_email_address: nil, assigned_to_email_address: nil, next_token: nil, per_page: nil, since: nil)
|
|
71
|
-
client.get("/activity/lead-status-changes", camelize_params(
|
|
77
|
+
response = client.get("/activity/lead-status-changes", camelize_params(
|
|
72
78
|
campaign_id: campaign_id,
|
|
73
79
|
recipient_email_address: recipient_email_address,
|
|
74
80
|
assigned_to_email_address: assigned_to_email_address,
|
|
@@ -76,6 +82,7 @@ module Mailshake
|
|
|
76
82
|
per_page: per_page,
|
|
77
83
|
since: since
|
|
78
84
|
))
|
|
85
|
+
Models::List.new(response, Models::LeadStatus)
|
|
79
86
|
end
|
|
80
87
|
end
|
|
81
88
|
end
|
data/lib/mailshake/campaigns.rb
CHANGED
|
@@ -3,15 +3,18 @@
|
|
|
3
3
|
module Mailshake
|
|
4
4
|
class Campaigns < Base
|
|
5
5
|
def list(search: nil, next_token: nil, per_page: nil)
|
|
6
|
-
client.get("/campaigns/list", camelize_params(search: search, next_token: next_token, per_page: per_page))
|
|
6
|
+
response = client.get("/campaigns/list", camelize_params(search: search, next_token: next_token, per_page: per_page))
|
|
7
|
+
Models::List.new(response, Models::Campaign)
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
def get(campaign_id:)
|
|
10
|
-
client.get("/campaigns/get", camelize_params(campaign_id: campaign_id))
|
|
11
|
+
response = client.get("/campaigns/get", camelize_params(campaign_id: campaign_id))
|
|
12
|
+
Models::Campaign.new(response)
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
def create(title:, sender_id: nil)
|
|
14
|
-
client.post("/campaigns/create", camelize_params(title: title, sender_id: sender_id))
|
|
16
|
+
response = client.post("/campaigns/create", camelize_params(title: title, sender_id: sender_id))
|
|
17
|
+
Models::Campaign.new(response)
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
def pause(campaign_id:)
|
|
@@ -23,11 +26,13 @@ module Mailshake
|
|
|
23
26
|
end
|
|
24
27
|
|
|
25
28
|
def export(campaign_ids: nil, export_type: nil, timezone: nil)
|
|
26
|
-
client.post("/campaigns/export", camelize_params(campaign_ids: campaign_ids, export_type: export_type, timezone: timezone))
|
|
29
|
+
response = client.post("/campaigns/export", camelize_params(campaign_ids: campaign_ids, export_type: export_type, timezone: timezone))
|
|
30
|
+
Models::CampaignExportRequest.new(response)
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
def export_status(status_id:)
|
|
30
|
-
client.get("/campaigns/export-status", camelize_params(status_id: status_id))
|
|
34
|
+
response = client.get("/campaigns/export-status", camelize_params(status_id: status_id))
|
|
35
|
+
Models::CampaignExport.new(response)
|
|
31
36
|
end
|
|
32
37
|
end
|
|
33
38
|
end
|
data/lib/mailshake/client.rb
CHANGED
|
@@ -22,7 +22,8 @@ module Mailshake
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def me
|
|
25
|
-
get("/me")
|
|
25
|
+
response = get("/me")
|
|
26
|
+
Models::User.new(response)
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
private
|
|
@@ -63,7 +64,9 @@ module Mailshake
|
|
|
63
64
|
def handle_response(response)
|
|
64
65
|
case response.code
|
|
65
66
|
when 200, 201, 202, 204
|
|
66
|
-
response.parsed_response
|
|
67
|
+
parsed = response.parsed_response
|
|
68
|
+
check_for_limit_errors(parsed, response)
|
|
69
|
+
parsed
|
|
67
70
|
when 401
|
|
68
71
|
raise AuthenticationError, "Authentication failed: #{response.body}"
|
|
69
72
|
when 404
|
|
@@ -73,6 +76,7 @@ module Mailshake
|
|
|
73
76
|
raise RateLimitError.new("Rate limit exceeded", response.code, response.body, retry_after)
|
|
74
77
|
when 400, 422
|
|
75
78
|
parsed = response.parsed_response || {}
|
|
79
|
+
check_for_limit_errors(parsed, response)
|
|
76
80
|
errors = parsed['errors'] || {}
|
|
77
81
|
raise ValidationError.new(
|
|
78
82
|
parsed['message'] || 'Validation failed',
|
|
@@ -86,5 +90,22 @@ module Mailshake
|
|
|
86
90
|
raise APIError.new("Unexpected response", response.code, response.body)
|
|
87
91
|
end
|
|
88
92
|
end
|
|
93
|
+
|
|
94
|
+
def check_for_limit_errors(parsed, response)
|
|
95
|
+
return unless parsed.is_a?(Hash)
|
|
96
|
+
|
|
97
|
+
error_code = parsed['error'] || parsed['errorCode']
|
|
98
|
+
return unless error_code
|
|
99
|
+
|
|
100
|
+
case error_code
|
|
101
|
+
when 'limit_reached'
|
|
102
|
+
retry_after = parsed['retryAfter']
|
|
103
|
+
message = parsed['message'] || "Quota limit reached"
|
|
104
|
+
raise LimitReachedError.new(message, response.code, response.body, retry_after)
|
|
105
|
+
when 'exceeds_monthly_recipients'
|
|
106
|
+
message = parsed['message'] || "Monthly recipient limit exceeded"
|
|
107
|
+
raise MonthlyRecipientLimitError.new(message, response.code, response.body)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
89
110
|
end
|
|
90
111
|
end
|
data/lib/mailshake/errors.rb
CHANGED
|
@@ -48,4 +48,19 @@ module Mailshake
|
|
|
48
48
|
@retry_after = retry_after
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
|
+
|
|
52
|
+
class LimitReachedError < APIError
|
|
53
|
+
attr_reader :retry_after
|
|
54
|
+
|
|
55
|
+
def initialize(message = "Quota limit reached", status_code = nil, response_body = nil, retry_after = nil)
|
|
56
|
+
super(message, status_code, response_body)
|
|
57
|
+
@retry_after = retry_after
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class MonthlyRecipientLimitError < APIError
|
|
62
|
+
def initialize(message = "Monthly recipient limit exceeded", status_code = nil, response_body = nil)
|
|
63
|
+
super(message, status_code, response_body)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
51
66
|
end
|
data/lib/mailshake/leads.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Mailshake
|
|
4
4
|
class Leads < Base
|
|
5
5
|
def list(campaign_id: nil, status: nil, assigned_to_email_address: nil, search: nil, next_token: nil, per_page: nil)
|
|
6
|
-
client.get("/leads/list", camelize_params(
|
|
6
|
+
response = client.get("/leads/list", camelize_params(
|
|
7
7
|
campaign_id: campaign_id,
|
|
8
8
|
status: status,
|
|
9
9
|
assigned_to_email_address: assigned_to_email_address,
|
|
@@ -11,23 +11,26 @@ module Mailshake
|
|
|
11
11
|
next_token: next_token,
|
|
12
12
|
per_page: per_page
|
|
13
13
|
))
|
|
14
|
+
Models::List.new(response, Models::Lead)
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def get(lead_id: nil, recipient_id: nil, campaign_id: nil, email_address: nil)
|
|
17
|
-
client.get("/leads/get", camelize_params(
|
|
18
|
+
response = client.get("/leads/get", camelize_params(
|
|
18
19
|
lead_id: lead_id,
|
|
19
20
|
recipient_id: recipient_id,
|
|
20
21
|
campaign_id: campaign_id,
|
|
21
22
|
email_address: email_address
|
|
22
23
|
))
|
|
24
|
+
Models::Lead.new(response)
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
def create(campaign_id:, email_addresses: nil, recipient_ids: nil)
|
|
26
|
-
client.post("/leads/create", camelize_params(
|
|
28
|
+
response = client.post("/leads/create", camelize_params(
|
|
27
29
|
campaign_id: campaign_id,
|
|
28
30
|
email_addresses: email_addresses,
|
|
29
31
|
recipient_ids: recipient_ids
|
|
30
32
|
))
|
|
33
|
+
Models::CreatedLeads.new(response)
|
|
31
34
|
end
|
|
32
35
|
|
|
33
36
|
def close(lead_id: nil, campaign_id: nil, email_address: nil, recipient_id: nil, status: nil)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailshake
|
|
4
|
+
module Models
|
|
5
|
+
class BaseModel
|
|
6
|
+
class << self
|
|
7
|
+
def has_one(field, klass)
|
|
8
|
+
nested_one[field.to_s] = klass
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def has_many(field, klass)
|
|
12
|
+
nested_many[field.to_s] = klass
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def nested_one
|
|
16
|
+
@nested_one ||= {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def nested_many
|
|
20
|
+
@nested_many ||= {}
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
attr_reader :data
|
|
25
|
+
|
|
26
|
+
def initialize(data = {})
|
|
27
|
+
@data = data || {}
|
|
28
|
+
define_accessors
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def [](key)
|
|
32
|
+
@data[key]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def to_h
|
|
36
|
+
@data
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
40
|
+
@accessors&.key?(method_name.to_s) || super
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def define_accessors
|
|
46
|
+
@accessors = {}
|
|
47
|
+
@data.each do |key, value|
|
|
48
|
+
snake_key = underscore(key.to_s)
|
|
49
|
+
hydrated = hydrate(snake_key, value)
|
|
50
|
+
@accessors[snake_key] = hydrated
|
|
51
|
+
|
|
52
|
+
define_singleton_method(snake_key) { @accessors[snake_key] }
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def hydrate(snake_key, value)
|
|
57
|
+
one_klass = self.class.nested_one[snake_key]
|
|
58
|
+
many_klass = self.class.nested_many[snake_key]
|
|
59
|
+
|
|
60
|
+
if one_klass && value.is_a?(Hash)
|
|
61
|
+
one_klass.new(value)
|
|
62
|
+
elsif many_klass && value.is_a?(Array)
|
|
63
|
+
value.map { |item| item.is_a?(Hash) ? many_klass.new(item) : item }
|
|
64
|
+
else
|
|
65
|
+
value
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def underscore(camel_cased_word)
|
|
70
|
+
word = camel_cased_word.to_s.dup
|
|
71
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
|
|
72
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
|
73
|
+
word.tr!("-", "_")
|
|
74
|
+
word.downcase!
|
|
75
|
+
word
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailshake
|
|
4
|
+
module Models
|
|
5
|
+
class List
|
|
6
|
+
include Enumerable
|
|
7
|
+
|
|
8
|
+
attr_reader :results, :next_token, :data
|
|
9
|
+
|
|
10
|
+
def initialize(data, model_class)
|
|
11
|
+
@data = data || {}
|
|
12
|
+
raw_results = @data["results"] || []
|
|
13
|
+
@results = raw_results.map { |item| item.is_a?(Hash) ? model_class.new(item) : item }
|
|
14
|
+
@next_token = @data["nextToken"]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def each(&block)
|
|
18
|
+
@results.each(&block)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def [](key)
|
|
22
|
+
@data[key]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_h
|
|
26
|
+
@data
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|