mailshake-ruby 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 173cfe60415683c55435fdd081c79a9824daa162b7dee69738ea74940326d36b
4
- data.tar.gz: 6e7324f62f055eb0d7482c4e93cec793a033d291d5a3ecc0a6ff2557cfb9a779
3
+ metadata.gz: 328a77f17bacfe02fc4a7a4a869a1aa78bde71a05096c95057e7a9586c380a75
4
+ data.tar.gz: 07323100c2bfc0a3743c60204d248860dd04b784c79cdba0e060a168b659e963
5
5
  SHA512:
6
- metadata.gz: 7531e6410d2417febd4142731dab363e37d8fe9986c2bc06866153ff589a057fc63b8539ce0b8d17372f9c59864e51a303875cfb05eddf48ccc6e48af2415e86
7
- data.tar.gz: ef2a7a8450785b67d28e1266135343418eca95af48f5674feed3e0779bc757a4ddb2f3506e188a95fafa752e597a665343949a6cf1a0c6a4a17bbb7eb5b3a260
6
+ metadata.gz: cb67630c178d246c30d3c10ab59f94b4e19c93a1f9e2423814bf9db3f641f5cc2eed12bdc8822b5c52f4d1e82c7a6b3d23d1de51cad5f6ab5e1c26a3590c0247
7
+ data.tar.gz: de3dc3ef281a458821178873fc05c6bb1e36d1ea252adaee6991869c8b5b86e8a6f9f5e67785b9ca23fa4cc36f1c06389ce1fa618443da674ebdccfbdfb4bd1f
@@ -64,7 +64,9 @@ module Mailshake
64
64
  def handle_response(response)
65
65
  case response.code
66
66
  when 200, 201, 202, 204
67
- response.parsed_response
67
+ parsed = response.parsed_response
68
+ check_for_limit_errors(parsed, response)
69
+ parsed
68
70
  when 401
69
71
  raise AuthenticationError, "Authentication failed: #{response.body}"
70
72
  when 404
@@ -74,6 +76,7 @@ module Mailshake
74
76
  raise RateLimitError.new("Rate limit exceeded", response.code, response.body, retry_after)
75
77
  when 400, 422
76
78
  parsed = response.parsed_response || {}
79
+ check_for_limit_errors(parsed, response)
77
80
  errors = parsed['errors'] || {}
78
81
  raise ValidationError.new(
79
82
  parsed['message'] || 'Validation failed',
@@ -87,5 +90,22 @@ module Mailshake
87
90
  raise APIError.new("Unexpected response", response.code, response.body)
88
91
  end
89
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
90
110
  end
91
111
  end
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mailshake
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/spec/examples.txt CHANGED
@@ -1,86 +1,89 @@
1
1
  example_id | status | run_time |
2
2
  ------------------------------------------------- | ------ | --------------- |
3
- ./spec/mailshake/activity_spec.rb[1:1:1] | passed | 0.0003 seconds |
4
- ./spec/mailshake/activity_spec.rb[1:1:2] | passed | 0.00075 seconds |
3
+ ./spec/mailshake/activity_spec.rb[1:1:1] | passed | 0.00034 seconds |
4
+ ./spec/mailshake/activity_spec.rb[1:1:2] | passed | 0.00045 seconds |
5
5
  ./spec/mailshake/activity_spec.rb[1:2:1] | passed | 0.00038 seconds |
6
- ./spec/mailshake/activity_spec.rb[1:3:1] | passed | 0.00048 seconds |
7
- ./spec/mailshake/activity_spec.rb[1:4:1] | passed | 0.00037 seconds |
8
- ./spec/mailshake/activity_spec.rb[1:5:1] | passed | 0.00037 seconds |
9
- ./spec/mailshake/activity_spec.rb[1:6:1] | passed | 0.0004 seconds |
10
- ./spec/mailshake/activity_spec.rb[1:7:1] | passed | 0.0004 seconds |
11
- ./spec/mailshake/campaigns_spec.rb[1:1:1] | passed | 0.00049 seconds |
12
- ./spec/mailshake/campaigns_spec.rb[1:1:2] | passed | 0.00082 seconds |
13
- ./spec/mailshake/campaigns_spec.rb[1:2:1] | passed | 0.01488 seconds |
14
- ./spec/mailshake/campaigns_spec.rb[1:3:1] | passed | 0.00045 seconds |
15
- ./spec/mailshake/campaigns_spec.rb[1:4:1] | passed | 0.00039 seconds |
16
- ./spec/mailshake/campaigns_spec.rb[1:5:1] | passed | 0.0005 seconds |
17
- ./spec/mailshake/campaigns_spec.rb[1:6:1] | passed | 0.00053 seconds |
18
- ./spec/mailshake/campaigns_spec.rb[1:7:1] | passed | 0.00066 seconds |
6
+ ./spec/mailshake/activity_spec.rb[1:3:1] | passed | 0.00043 seconds |
7
+ ./spec/mailshake/activity_spec.rb[1:4:1] | passed | 0.00041 seconds |
8
+ ./spec/mailshake/activity_spec.rb[1:5:1] | passed | 0.00043 seconds |
9
+ ./spec/mailshake/activity_spec.rb[1:6:1] | passed | 0.00043 seconds |
10
+ ./spec/mailshake/activity_spec.rb[1:7:1] | passed | 0.00044 seconds |
11
+ ./spec/mailshake/campaigns_spec.rb[1:1:1] | passed | 0.0004 seconds |
12
+ ./spec/mailshake/campaigns_spec.rb[1:1:2] | passed | 0.00053 seconds |
13
+ ./spec/mailshake/campaigns_spec.rb[1:2:1] | passed | 0.0006 seconds |
14
+ ./spec/mailshake/campaigns_spec.rb[1:3:1] | passed | 0.00033 seconds |
15
+ ./spec/mailshake/campaigns_spec.rb[1:4:1] | passed | 0.0006 seconds |
16
+ ./spec/mailshake/campaigns_spec.rb[1:5:1] | passed | 0.00037 seconds |
17
+ ./spec/mailshake/campaigns_spec.rb[1:6:1] | passed | 0.00035 seconds |
18
+ ./spec/mailshake/campaigns_spec.rb[1:7:1] | passed | 0.00059 seconds |
19
19
  ./spec/mailshake/client_spec.rb[1:1:1] | passed | 0.00004 seconds |
20
- ./spec/mailshake/client_spec.rb[1:1:2:1] | passed | 0.00058 seconds |
21
- ./spec/mailshake/client_spec.rb[1:2:1] | passed | 0.00021 seconds |
22
- ./spec/mailshake/client_spec.rb[1:2:2] | passed | 0.00042 seconds |
23
- ./spec/mailshake/client_spec.rb[1:2:3] | passed | 0.00025 seconds |
24
- ./spec/mailshake/client_spec.rb[1:2:4] | passed | 0.00038 seconds |
25
- ./spec/mailshake/client_spec.rb[1:3:1] | passed | 0.00028 seconds |
26
- ./spec/mailshake/client_spec.rb[1:4:1] | passed | 0.00036 seconds |
27
- ./spec/mailshake/client_spec.rb[1:5:1] | passed | 0.00029 seconds |
28
- ./spec/mailshake/client_spec.rb[1:5:2] | passed | 0.0003 seconds |
29
- ./spec/mailshake/client_spec.rb[1:5:3] | passed | 0.00039 seconds |
30
- ./spec/mailshake/client_spec.rb[1:5:4] | passed | 0.00032 seconds |
31
- ./spec/mailshake/client_spec.rb[1:5:5] | passed | 0.00036 seconds |
20
+ ./spec/mailshake/client_spec.rb[1:1:2:1] | passed | 0.00005 seconds |
21
+ ./spec/mailshake/client_spec.rb[1:2:1] | passed | 0.00028 seconds |
22
+ ./spec/mailshake/client_spec.rb[1:2:2] | passed | 0.00033 seconds |
23
+ ./spec/mailshake/client_spec.rb[1:2:3] | passed | 0.0003 seconds |
24
+ ./spec/mailshake/client_spec.rb[1:2:4] | passed | 0.00042 seconds |
25
+ ./spec/mailshake/client_spec.rb[1:3:1] | passed | 0.00049 seconds |
26
+ ./spec/mailshake/client_spec.rb[1:4:1] | passed | 0.00066 seconds |
27
+ ./spec/mailshake/client_spec.rb[1:5:1] | passed | 0.00031 seconds |
28
+ ./spec/mailshake/client_spec.rb[1:5:2] | passed | 0.00031 seconds |
29
+ ./spec/mailshake/client_spec.rb[1:5:3] | passed | 0.00213 seconds |
30
+ ./spec/mailshake/client_spec.rb[1:5:4] | passed | 0.00108 seconds |
31
+ ./spec/mailshake/client_spec.rb[1:5:5] | passed | 0.00031 seconds |
32
+ ./spec/mailshake/client_spec.rb[1:5:6] | passed | 0.00037 seconds |
33
+ ./spec/mailshake/client_spec.rb[1:5:7] | passed | 0.00028 seconds |
34
+ ./spec/mailshake/client_spec.rb[1:5:8] | passed | 0.00039 seconds |
32
35
  ./spec/mailshake/configuration_spec.rb[1:1:1] | passed | 0.00003 seconds |
33
- ./spec/mailshake/configuration_spec.rb[1:1:2] | passed | 0.00004 seconds |
36
+ ./spec/mailshake/configuration_spec.rb[1:1:2] | passed | 0.00003 seconds |
34
37
  ./spec/mailshake/configuration_spec.rb[1:2:1] | passed | 0.00003 seconds |
35
38
  ./spec/mailshake/configuration_spec.rb[1:2:2] | passed | 0.00003 seconds |
36
39
  ./spec/mailshake/configuration_spec.rb[1:2:3] | passed | 0.00003 seconds |
37
40
  ./spec/mailshake/configuration_spec.rb[1:3:1] | passed | 0.00003 seconds |
38
- ./spec/mailshake/configuration_spec.rb[1:3:2] | passed | 0.00004 seconds |
39
- ./spec/mailshake/leads_spec.rb[1:1:1] | passed | 0.00106 seconds |
40
- ./spec/mailshake/leads_spec.rb[1:1:2] | passed | 0.00046 seconds |
41
- ./spec/mailshake/leads_spec.rb[1:2:1] | passed | 0.00159 seconds |
42
- ./spec/mailshake/leads_spec.rb[1:3:1] | passed | 0.00112 seconds |
43
- ./spec/mailshake/leads_spec.rb[1:4:1] | passed | 0.00199 seconds |
44
- ./spec/mailshake/leads_spec.rb[1:5:1] | passed | 0.00145 seconds |
45
- ./spec/mailshake/leads_spec.rb[1:6:1] | passed | 0.0003 seconds |
46
- ./spec/mailshake/models/base_model_spec.rb[1:1:1] | passed | 0.00004 seconds |
41
+ ./spec/mailshake/configuration_spec.rb[1:3:2] | passed | 0.00003 seconds |
42
+ ./spec/mailshake/leads_spec.rb[1:1:1] | passed | 0.00041 seconds |
43
+ ./spec/mailshake/leads_spec.rb[1:1:2] | passed | 0.00043 seconds |
44
+ ./spec/mailshake/leads_spec.rb[1:2:1] | passed | 0.00209 seconds |
45
+ ./spec/mailshake/leads_spec.rb[1:3:1] | passed | 0.00039 seconds |
46
+ ./spec/mailshake/leads_spec.rb[1:4:1] | passed | 0.01393 seconds |
47
+ ./spec/mailshake/leads_spec.rb[1:5:1] | passed | 0.00032 seconds |
48
+ ./spec/mailshake/leads_spec.rb[1:6:1] | passed | 0.00031 seconds |
49
+ ./spec/mailshake/models/base_model_spec.rb[1:1:1] | passed | 0.00005 seconds |
47
50
  ./spec/mailshake/models/base_model_spec.rb[1:1:2] | passed | 0.00004 seconds |
48
51
  ./spec/mailshake/models/base_model_spec.rb[1:1:3] | passed | 0.00004 seconds |
49
- ./spec/mailshake/models/base_model_spec.rb[1:2:1] | passed | 0.00005 seconds |
50
- ./spec/mailshake/models/base_model_spec.rb[1:2:2] | passed | 0.00004 seconds |
51
- ./spec/mailshake/models/base_model_spec.rb[1:3:1] | passed | 0.00004 seconds |
52
+ ./spec/mailshake/models/base_model_spec.rb[1:2:1] | passed | 0.00004 seconds |
53
+ ./spec/mailshake/models/base_model_spec.rb[1:2:2] | passed | 0.00028 seconds |
54
+ ./spec/mailshake/models/base_model_spec.rb[1:3:1] | passed | 0.00045 seconds |
52
55
  ./spec/mailshake/models/base_model_spec.rb[1:3:2] | passed | 0.00006 seconds |
53
- ./spec/mailshake/models/base_model_spec.rb[1:3:3] | passed | 0.00003 seconds |
54
- ./spec/mailshake/models/base_model_spec.rb[1:3:4] | passed | 0.00005 seconds |
56
+ ./spec/mailshake/models/base_model_spec.rb[1:3:3] | passed | 0.00048 seconds |
57
+ ./spec/mailshake/models/base_model_spec.rb[1:3:4] | passed | 0.00007 seconds |
55
58
  ./spec/mailshake/models/base_model_spec.rb[1:4:1] | passed | 0.00004 seconds |
56
- ./spec/mailshake/models/base_model_spec.rb[1:4:2] | passed | 0.00004 seconds |
59
+ ./spec/mailshake/models/base_model_spec.rb[1:4:2] | passed | 0.00005 seconds |
57
60
  ./spec/mailshake/models/base_model_spec.rb[1:5:1] | passed | 0.00004 seconds |
58
- ./spec/mailshake/models/base_model_spec.rb[1:6:1] | passed | 0.0001 seconds |
61
+ ./spec/mailshake/models/base_model_spec.rb[1:6:1] | passed | 0.00003 seconds |
59
62
  ./spec/mailshake/models/base_model_spec.rb[1:6:2] | passed | 0.00003 seconds |
60
63
  ./spec/mailshake/models/list_spec.rb[1:1:1] | passed | 0.00006 seconds |
61
- ./spec/mailshake/models/list_spec.rb[1:1:2] | passed | 0.00003 seconds |
62
- ./spec/mailshake/models/list_spec.rb[1:1:3] | passed | 0.00003 seconds |
64
+ ./spec/mailshake/models/list_spec.rb[1:1:2] | passed | 0.00007 seconds |
65
+ ./spec/mailshake/models/list_spec.rb[1:1:3] | passed | 0.00004 seconds |
63
66
  ./spec/mailshake/models/list_spec.rb[1:2:1] | passed | 0.00004 seconds |
64
- ./spec/mailshake/models/list_spec.rb[1:2:2] | passed | 0.00134 seconds |
67
+ ./spec/mailshake/models/list_spec.rb[1:2:2] | passed | 0.00004 seconds |
65
68
  ./spec/mailshake/models/list_spec.rb[1:3:1] | passed | 0.00005 seconds |
66
- ./spec/mailshake/models/list_spec.rb[1:3:2] | passed | 0.00005 seconds |
67
- ./spec/mailshake/models/list_spec.rb[1:3:3] | passed | 0.00004 seconds |
68
- ./spec/mailshake/models/list_spec.rb[1:3:4] | passed | 0.00005 seconds |
69
- ./spec/mailshake/models/list_spec.rb[1:3:5] | passed | 0.00004 seconds |
70
- ./spec/mailshake/models/list_spec.rb[1:4:1] | passed | 0.00004 seconds |
71
- ./spec/mailshake/models/list_spec.rb[1:5:1] | passed | 0.00004 seconds |
72
- ./spec/mailshake/push_spec.rb[1:1:1] | passed | 0.00023 seconds |
73
- ./spec/mailshake/push_spec.rb[1:1:2] | passed | 0.00049 seconds |
74
- ./spec/mailshake/push_spec.rb[1:2:1] | passed | 0.0003 seconds |
75
- ./spec/mailshake/recipients_spec.rb[1:1:1] | passed | 0.00054 seconds |
69
+ ./spec/mailshake/models/list_spec.rb[1:3:2] | passed | 0.00006 seconds |
70
+ ./spec/mailshake/models/list_spec.rb[1:3:3] | passed | 0.00005 seconds |
71
+ ./spec/mailshake/models/list_spec.rb[1:3:4] | passed | 0.00007 seconds |
72
+ ./spec/mailshake/models/list_spec.rb[1:3:5] | passed | 0.00005 seconds |
73
+ ./spec/mailshake/models/list_spec.rb[1:4:1] | passed | 0.00005 seconds |
74
+ ./spec/mailshake/models/list_spec.rb[1:5:1] | passed | 0.00006 seconds |
75
+ ./spec/mailshake/push_spec.rb[1:1:1] | passed | 0.00036 seconds |
76
+ ./spec/mailshake/push_spec.rb[1:1:2] | passed | 0.00032 seconds |
77
+ ./spec/mailshake/push_spec.rb[1:2:1] | passed | 0.00138 seconds |
78
+ ./spec/mailshake/recipients_spec.rb[1:1:1] | passed | 0.00052 seconds |
76
79
  ./spec/mailshake/recipients_spec.rb[1:2:1] | passed | 0.00036 seconds |
77
- ./spec/mailshake/recipients_spec.rb[1:3:1] | passed | 0.0004 seconds |
78
- ./spec/mailshake/recipients_spec.rb[1:3:2] | passed | 0.00192 seconds |
79
- ./spec/mailshake/recipients_spec.rb[1:4:1] | passed | 0.00038 seconds |
80
- ./spec/mailshake/recipients_spec.rb[1:5:1] | passed | 0.00039 seconds |
81
- ./spec/mailshake/recipients_spec.rb[1:6:1] | passed | 0.00032 seconds |
82
- ./spec/mailshake/recipients_spec.rb[1:7:1] | passed | 0.00038 seconds |
83
- ./spec/mailshake/senders_spec.rb[1:1:1] | passed | 0.00031 seconds |
84
- ./spec/mailshake/senders_spec.rb[1:1:2] | passed | 0.0004 seconds |
85
- ./spec/mailshake/team_spec.rb[1:1:1] | passed | 0.00026 seconds |
86
- ./spec/mailshake/team_spec.rb[1:1:2] | passed | 0.00045 seconds |
80
+ ./spec/mailshake/recipients_spec.rb[1:3:1] | passed | 0.00127 seconds |
81
+ ./spec/mailshake/recipients_spec.rb[1:3:2] | passed | 0.00145 seconds |
82
+ ./spec/mailshake/recipients_spec.rb[1:4:1] | passed | 0.00167 seconds |
83
+ ./spec/mailshake/recipients_spec.rb[1:5:1] | passed | 0.00124 seconds |
84
+ ./spec/mailshake/recipients_spec.rb[1:6:1] | passed | 0.00138 seconds |
85
+ ./spec/mailshake/recipients_spec.rb[1:7:1] | passed | 0.00047 seconds |
86
+ ./spec/mailshake/senders_spec.rb[1:1:1] | passed | 0.00043 seconds |
87
+ ./spec/mailshake/senders_spec.rb[1:1:2] | passed | 0.00044 seconds |
88
+ ./spec/mailshake/team_spec.rb[1:1:1] | passed | 0.00031 seconds |
89
+ ./spec/mailshake/team_spec.rb[1:1:2] | passed | 0.00051 seconds |
@@ -129,5 +129,58 @@ RSpec.describe Mailshake::Client do
129
129
 
130
130
  expect { client.get("/error") }.to raise_error(Mailshake::APIError)
131
131
  end
132
+
133
+ it "raises LimitReachedError when response contains limit_reached error code" do
134
+ stub_request(:get, "#{base_url}/quota")
135
+ .to_return(
136
+ status: 200,
137
+ body: {
138
+ error: "limit_reached",
139
+ message: "Please wait and try again after: 2026-04-02T16:00:00Z",
140
+ retryAfter: "2026-04-02T16:00:00Z"
141
+ }.to_json,
142
+ headers: { "Content-Type" => "application/json" }
143
+ )
144
+
145
+ expect { client.get("/quota") }.to raise_error(Mailshake::LimitReachedError) do |error|
146
+ expect(error.retry_after).to eq("2026-04-02T16:00:00Z")
147
+ expect(error.message).to eq("Please wait and try again after: 2026-04-02T16:00:00Z")
148
+ expect(error.status_code).to eq(200)
149
+ end
150
+ end
151
+
152
+ it "raises LimitReachedError when errorCode field is used" do
153
+ stub_request(:post, "#{base_url}/quota")
154
+ .to_return(
155
+ status: 400,
156
+ body: {
157
+ errorCode: "limit_reached",
158
+ message: "Quota exceeded",
159
+ retryAfter: "2026-04-02T17:00:00Z"
160
+ }.to_json,
161
+ headers: { "Content-Type" => "application/json" }
162
+ )
163
+
164
+ expect { client.post("/quota", {}) }.to raise_error(Mailshake::LimitReachedError) do |error|
165
+ expect(error.retry_after).to eq("2026-04-02T17:00:00Z")
166
+ end
167
+ end
168
+
169
+ it "raises MonthlyRecipientLimitError when response contains exceeds_monthly_recipients" do
170
+ stub_request(:post, "#{base_url}/recipients")
171
+ .to_return(
172
+ status: 400,
173
+ body: {
174
+ error: "exceeds_monthly_recipients",
175
+ message: "You have exceeded your monthly recipient limit"
176
+ }.to_json,
177
+ headers: { "Content-Type" => "application/json" }
178
+ )
179
+
180
+ expect { client.post("/recipients", {}) }.to raise_error(Mailshake::MonthlyRecipientLimitError) do |error|
181
+ expect(error.message).to eq("You have exceeded your monthly recipient limit")
182
+ expect(error.status_code).to eq(400)
183
+ end
184
+ end
132
185
  end
133
186
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mailshake-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eduardo Souza