mailgun-ruby 1.2.15 → 1.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: 88449bde0909456b09f66ea57e253fbfe06f2de6fddb2640f9b5571479d9cedc
4
- data.tar.gz: 3ed8f2ace6300271251bedab59ffc118acd3fb570a7a361b5c26d6c2a98352e8
3
+ metadata.gz: f18ece3e95078672f58a188ff310d572559ac05aa442080a8a76ec357fa563b9
4
+ data.tar.gz: 30a957419c41fa13342db90ca3ee386d1c312da20daa41c06229801e6c07ec95
5
5
  SHA512:
6
- metadata.gz: 2159184a986dae450bec7069c8420671a860c7e06abe9ab660cf3a9af594ce62e67428c85782a25c242e99fc2bb250e172caa0e616e296332bf7f0f9fe7f8dc1
7
- data.tar.gz: 2cf4aa439edfaf850e0151b43edbce7722701db5ec55ab087dbaffed70a64a1f8d99975cda71bc1e303284b5f6ce6865706a1414299fe554bbfdbc6f7e77669e
6
+ metadata.gz: '0309b13c1dda7a95224776c9bb1e0a04a3bfef7945f9a92c24ea566742f183ff544294d9a8dd6af006856fa7c97d2175cf01fe84c72e85712c1da34ec08bc723'
7
+ data.tar.gz: 90db979ec789797886fa5210ea3b2ec9bac048dbb440facd9f356324093cc98ce657ecc589f719efe4b4c2b51251153071b936db0cd34877370debe02912b66b
data/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.2.16] - 2024-11-29
8
+
9
+ ### Added
10
+
11
+ - Metrics API support (https://github.com/mailgun/mailgun-ruby/pull/326)
12
+
7
13
  ## [1.2.15] - 2024-02-13
8
14
 
9
15
  ### Fixed
@@ -19,7 +25,6 @@ All notable changes to this project will be documented in this file.
19
25
  - Additional Domain Endpoints (https://github.com/mailgun/mailgun-ruby/pull/314)
20
26
  - Webhooks Update Method (https://github.com/mailgun/mailgun-ruby/pull/305)
21
27
  - Add support for AMP HTML (https://github.com/mailgun/mailgun-ruby/pull/304)
22
- - Add support for AMP HTML (https://github.com/mailgun/mailgun-ruby/pull/304)
23
28
 
24
29
  ### Fixed
25
30
 
@@ -40,4 +45,4 @@ All notable changes to this project will be documented in this file.
40
45
  ### Fixed
41
46
 
42
47
  - transform_for_mailgun block iteration issue (https://github.com/mailgun/mailgun-ruby/pull/298).
43
- - Typos in several files (https://github.com/mailgun/mailgun-ruby/pull/297).
48
+ - Typos in several files (https://github.com/mailgun/mailgun-ruby/pull/297).
data/Gemfile CHANGED
@@ -3,4 +3,5 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in mailgun.gemspec
4
4
  gemspec
5
5
 
6
+ gem 'mime-types'
6
7
  gem 'json', '~> 2.1', platform: :mri_19
data/README.md CHANGED
@@ -19,7 +19,7 @@ gem install mailgun-ruby
19
19
  Gemfile:
20
20
 
21
21
  ```ruby
22
- gem 'mailgun-ruby', '~>1.2.15'
22
+ gem 'mailgun-ruby', '~>1.2.16'
23
23
  ```
24
24
 
25
25
  Usage
@@ -193,6 +193,7 @@ This SDK includes the following components:
193
193
  - [Suppressions](docs/Suppressions.md)
194
194
  - [Templates](docs/Templates.md)
195
195
  - [EmailValidation](docs/EmailValidation.md)
196
+ - [Metrics](docs/Metrics.md)
196
197
 
197
198
  Message Builder allows you to quickly create the array of parameters, required
198
199
  to send a message, by calling a methods for each parameter.
data/docs/Metrics.md ADDED
@@ -0,0 +1,108 @@
1
+ Mailgun - Metrics
2
+ ====================
3
+
4
+ This is the Mailgun Ruby *Metrics* utilities.
5
+
6
+ The below assumes you've already installed the Mailgun Ruby SDK in to your
7
+ project. If not, go back to the master README for instructions. It currently supports
8
+ all calls except credentials.
9
+
10
+ ---
11
+
12
+ Mailgun collects many different events and generates event metrics which are available
13
+ in your Control Panel. This data is also available via our analytics metrics API endpoint.
14
+
15
+ You can view additional samples in the [metrics_spec.rb](/spec/integration/metrics_spec.rb)
16
+ or the Metrics client API in [metrics.rb](/lib/metrics/metrics.rb).
17
+
18
+ Usage
19
+ -----
20
+
21
+ To get an instance of the Metrics client:
22
+
23
+ ```ruby
24
+ require 'mailgun'
25
+
26
+ mg_client = Mailgun::Client.new('your-api-key', 'mailgun-api-host', 'v1')
27
+ metrics = Mailgun::Metrics.new(mg_client)
28
+ ````
29
+ ---
30
+ Get filtered metrics for an account:
31
+ ```ruby
32
+ options = {
33
+ {
34
+ resolution: 'hour',
35
+ metrics: [
36
+ 'accepted_count',
37
+ 'delivered_count',
38
+ 'clicked_rate',
39
+ 'opened_rate'
40
+ ],
41
+ include_aggregates: true,
42
+ start: 'Tue, 26 Nov 2024 20:56:50 -0500',
43
+ duration: '1m',
44
+ filter: {
45
+ AND: [
46
+ {
47
+ attribute: 'domain',
48
+ comparator: '!=',
49
+ values: [
50
+ {
51
+ label: 'example.com',
52
+ value: 'example.com'
53
+ }
54
+ ]
55
+ }
56
+ ]
57
+ },
58
+ dimensions: ['time'],
59
+ end: 'Tue, 30 Nov 2024 20:56:50 -0500',
60
+ include_subaccounts: true
61
+ }
62
+ }
63
+
64
+ metrics.account_metrics(options)
65
+ ```
66
+ ---
67
+
68
+ Get filtered usage metrics for an account:
69
+ ```ruby
70
+ options = {
71
+ resolution: 'hour',
72
+ metrics: [
73
+ 'accepted_count',
74
+ 'delivered_count',
75
+ 'clicked_rate',
76
+ 'opened_rate'
77
+ ],
78
+ include_aggregates: true,
79
+ start: 'Tue, 26 Nov 2024 20:56:50 -0500',
80
+ duration: '1m',
81
+ filter: {
82
+ AND: [
83
+ {
84
+ attribute: 'domain',
85
+ comparator: '!=',
86
+ values: [
87
+ {
88
+ label: 'example.com',
89
+ value: 'example.com'
90
+ }
91
+ ]
92
+ }
93
+ ]
94
+ },
95
+ dimensions: ['time'],
96
+ end: 'Tue, 30 Nov 2024 20:56:50 -0500',
97
+ include_subaccounts: true
98
+ }
99
+
100
+ metrics.account_usage_metrics(options)
101
+ ```
102
+
103
+ ---
104
+
105
+ More Documentation
106
+ ------------------
107
+ See the official [Mailgun Domain Docs](https://documentation.mailgun.com/docs/mailgun/api-reference/openapi-final/tag/Metrics/)
108
+ for more information
data/docs/Webhooks.md CHANGED
@@ -4,8 +4,7 @@ Mailgun - Webhooks
4
4
  This is the Mailgun Ruby *Webhook* utilities.
5
5
 
6
6
  The below assumes you've already installed the Mailgun Ruby SDK in to your
7
- project. If not, go back to the master README for instructions. It currently supports
8
- all calls except updating webhooks.
7
+ project. If not, go back to the master README for instructions.
9
8
 
10
9
  Usage - Webhooks
11
10
  -----------------------
@@ -27,6 +26,9 @@ hook.create_all 'my.perfect.domain', 'https://the.webhook.url/'
27
26
  # Add a url for a specific webhook
28
27
  hook.create 'my.perfect.domain', 'deliver', 'https://the.webhook.url/'
29
28
 
29
+ # Update the url for a specific webhook
30
+ hook.update 'my.perfect.domain', 'deliver', 'https://the.webhook.url/'
31
+
30
32
  # Remove a url for a specific webhook
31
33
  hook.remove 'my.perfect.domain', 'deliver'
32
34
 
@@ -19,16 +19,26 @@ module Mailgun
19
19
  timeout = nil,
20
20
  proxy_url = Mailgun.proxy_url)
21
21
 
22
- rest_client_params = {
23
- user: 'api',
24
- password: api_key,
25
- user_agent: "mailgun-sdk-ruby/#{Mailgun::VERSION}"
22
+ endpoint = endpoint_generator(api_host, api_version, ssl)
23
+
24
+ request_options = {
25
+ url: endpoint,
26
+ proxy: Mailgun.proxy_url,
27
+ ssl: {verify: ssl},
28
+ headers: {
29
+ 'User-Agent' => "mailgun-sdk-ruby/#{Mailgun::VERSION}",
30
+ 'Accept' =>'*/*'
31
+ }
26
32
  }
27
- rest_client_params[:timeout] = timeout if timeout
33
+ request_options.merge!(request: {timeout: timeout}) if timeout
34
+
35
+ @http_client = Faraday.new(request_options) do |conn|
36
+ conn.request :authorization, :basic, 'api', api_key
37
+ conn.request :url_encoded
38
+ conn.response :raise_error, include_request: true
39
+ conn.adapter Faraday.default_adapter
40
+ end
28
41
 
29
- endpoint = endpoint_generator(api_host, api_version, ssl)
30
- RestClient.proxy = proxy_url
31
- @http_client = RestClient::Resource.new(endpoint, rest_client_params)
32
42
  @test_mode = test_mode
33
43
  @api_version = api_version
34
44
  end
@@ -49,17 +59,17 @@ module Mailgun
49
59
 
50
60
  # Change API key
51
61
  def set_api_key(api_key)
52
- @http_client.options[:password] = api_key
62
+ @http_client.set_basic_auth('api', api_key)
53
63
  end
54
64
 
55
65
  # Add subaccount id to headers
56
66
  def set_subaccount(subaccount_id)
57
- @http_client.options[:headers] = { SUBACCOUNT_HEADER => subaccount_id }
67
+ @http_client.headers = @http_client.headers.merge!({ SUBACCOUNT_HEADER => subaccount_id })
58
68
  end
59
69
 
60
70
  # Reset subaccount for primary usage
61
71
  def reset_subaccount
62
- @http_client.options[:headers].delete(SUBACCOUNT_HEADER)
72
+ @http_client.headers.delete(SUBACCOUNT_HEADER)
63
73
  end
64
74
 
65
75
  # Client is in test mode?
@@ -95,7 +105,7 @@ module Mailgun
95
105
  return Response.from_hash(
96
106
  {
97
107
  :body => "{\"id\": \"test-mode-mail-#{SecureRandom.uuid}@localhost\", \"message\": \"Queued. Thank you.\"}",
98
- :code => 200,
108
+ :status => 200,
99
109
  }
100
110
  )
101
111
  end
@@ -130,7 +140,7 @@ module Mailgun
130
140
  # @param [Hash] headers Additional headers to pass to the resource.
131
141
  # @return [Mailgun::Response] A Mailgun::Response object.
132
142
  def post(resource_path, data, headers = {})
133
- response = @http_client[resource_path].post(data, headers)
143
+ response = @http_client.post(resource_path, data, headers)
134
144
  Response.new(response)
135
145
  rescue => err
136
146
  raise communication_error err
@@ -138,21 +148,19 @@ module Mailgun
138
148
 
139
149
  # Generic Mailgun GET Handler
140
150
  #
141
- # @param [String] resource_path This is the API resource you wish to interact
142
- # with. Be sure to include your domain, where necessary.
143
- # @param [Hash] params This should be a standard Hash
144
- # containing required parameters for the requested resource.
145
- # @param [String] accept Acceptable Content-Type of the response body.
146
- # @return [Mailgun::Response] A Mailgun::Response object.
147
- def get(resource_path, params = nil, accept = '*/*')
148
- if params
149
- response = @http_client[resource_path].get(params: params, accept: accept)
150
- else
151
- response = @http_client[resource_path].get(accept: accept)
152
- end
151
+ # @param [String] resource_path The API resource path to request, including the domain if required.
152
+ # @param [Hash] params Optional request parameters, including query parameters and headers.
153
+ # - `:headers` [Hash] (optional) Custom headers for the request.
154
+ # @param [String] accept The expected Content-Type of the response. Defaults to '*/*'.
155
+ # @return [Mailgun::Response] A response object containing the API response data.
156
+ # @raise [CommunicationError] If the request fails, raises a communication error.
157
+ def get(resource_path, params = {}, accept = '*/*')
158
+ headers = (params[:headers] || {}).merge(accept: accept)
159
+ response = @http_client.get(resource_path, params, headers)
160
+
153
161
  Response.new(response)
154
162
  rescue => err
155
- raise communication_error err
163
+ raise communication_error(err)
156
164
  end
157
165
 
158
166
  # Generic Mailgun PUT Handler
@@ -163,7 +171,7 @@ module Mailgun
163
171
  # containing required parameters for the requested resource.
164
172
  # @return [Mailgun::Response] A Mailgun::Response object.
165
173
  def put(resource_path, data)
166
- response = @http_client[resource_path].put(data)
174
+ response = @http_client.put(resource_path, data)
167
175
  Response.new(response)
168
176
  rescue => err
169
177
  raise communication_error err
@@ -176,9 +184,9 @@ module Mailgun
176
184
  # @return [Mailgun::Response] A Mailgun::Response object.
177
185
  def delete(resource_path, params = nil)
178
186
  if params
179
- response = @http_client[resource_path].delete(params: params)
187
+ response = @http_client.delete(resource_path, params: params)
180
188
  else
181
- response = @http_client[resource_path].delete
189
+ response = @http_client.delete(resource_path)
182
190
  end
183
191
  Response.new(response)
184
192
  rescue => err
@@ -227,7 +235,7 @@ module Mailgun
227
235
  # @param [StandardException] e upstream exception object
228
236
  def communication_error(e)
229
237
  if e.respond_to?(:response) && e.response
230
- return case e.response.code
238
+ return case e.response_status
231
239
  when Unauthorized::CODE
232
240
  Unauthorized.new(e.message, e.response)
233
241
  when BadRequest::CODE
@@ -29,10 +29,10 @@ module Mailgun
29
29
  # Public: Class for managing communications (eg http) response errors
30
30
  # Inherits from Mailgun::Error
31
31
  class CommunicationError < Error
32
- # Public: gets HTTP status code
33
- attr_reader :code
32
+ # Public: gets HTTP status status
33
+ attr_reader :status
34
34
 
35
- # Public: fallback if there is no response code on the object
35
+ # Public: fallback if there is no response status on the object
36
36
  NOCODE = 000
37
37
  FORBIDDEN = 'Forbidden'
38
38
 
@@ -43,17 +43,17 @@ module Mailgun
43
43
  #
44
44
  def initialize(message = nil, response = nil)
45
45
  @response = response
46
- @code = if response.nil?
46
+ @status = if response.nil?
47
47
  NOCODE
48
48
  else
49
- response.code
49
+ response.status
50
50
  end
51
51
 
52
52
  begin
53
53
  json = JSON.parse(response.body)
54
54
  api_message = json['message'] || json['Error'] || json['error']
55
55
  rescue JSON::ParserError
56
- api_message = response.body
56
+ api_message = response.response_body
57
57
  rescue NoMethodError
58
58
  api_message = "Unknown API error"
59
59
  rescue
@@ -65,7 +65,7 @@ module Mailgun
65
65
 
66
66
  super(message, response)
67
67
  rescue NoMethodError, JSON::ParserError
68
- @code = NOCODE
68
+ @status = NOCODE
69
69
  super(message, response)
70
70
  end
71
71
  end
@@ -0,0 +1,61 @@
1
+ require 'mailgun/exceptions/exceptions'
2
+
3
+ module Mailgun
4
+ # A Mailgun::Metrics object is a simple interface to Mailgun Metrics.
5
+ # Uses Mailgun
6
+ class Metrics
7
+ # Public: creates a new Mailgun::Metrics instance.
8
+ # Defaults to Mailgun::Client
9
+ def initialize(client = Mailgun::Client.new(Mailgun.api_key, Mailgun.api_host || 'api.mailgun.net', 'v1'))
10
+ @client = client
11
+ end
12
+
13
+ # Public: Post query to get account metrics
14
+ #
15
+ # options - [Hash] of
16
+ # start - [String] A start date (default: 7 days before current time). Must be in RFC 2822 format.
17
+ # end - [String] An end date (default: current time). Must be in RFC 2822 format.
18
+ # resolution - [String] A resolution in the format of 'day' 'hour' 'month'. Default is day.
19
+ # duration - [String] A duration in the format of '1d' '2h' '2m'. If duration is provided then it is calculated from the end date and overwrites the start date.
20
+ # dimensions - [Array] Attributes of the metric data such as 'subaccount'.
21
+ # metrics - [Array] Name of the metrics to receive the stats for such as 'processed_count'
22
+ # filter - [Object]
23
+ # AND: - [Array] of objects
24
+ # attribute - [String]
25
+ # comparator - [String]
26
+ # values - [Array] of objects
27
+ # label - [String]
28
+ # value - [String]
29
+ # include_subaccounts - [Boolean] Include stats from all subaccounts.
30
+ # include_aggregates - [Boolean] Include top-level aggregate metrics.
31
+ #
32
+ # Returns [Hash] Metrics
33
+ def account_metrics(options={})
34
+ @client.post('analytics/metrics', options.to_json, { "Content-Type" => "application/json" }).to_h!
35
+ end
36
+
37
+ # Public: Post query to get account usage metrics
38
+ #
39
+ # options - [Hash] of
40
+ # start - [String] A start date (default: 7 days before current time). Must be in RFC 2822 format.
41
+ # end - [String] An end date (default: current time). Must be in RFC 2822 format.
42
+ # resolution - [String] A resolution in the format of 'day' 'hour' 'month'. Default is day.
43
+ # duration - [String] A duration in the format of '1d' '2h' '2m'. If duration is provided then it is calculated from the end date and overwrites the start date.
44
+ # dimensions - [Array] Attributes of the metric data such as 'subaccount'.
45
+ # metrics - [Array] Name of the metrics to receive the stats for such as 'processed_count'
46
+ # filter - [Object]
47
+ # AND: - [Array] of objects
48
+ # attribute - [String]
49
+ # comparator - [String]
50
+ # values - [Array] of objects
51
+ # label - [String]
52
+ # value - [String]
53
+ # include_subaccounts - [Boolean] Include stats from all subaccounts.
54
+ # include_aggregates - [Boolean] Include top-level aggregate metrics.
55
+ #
56
+ # Returns [Hash] Metrics
57
+ def account_usage_metrics(options={})
58
+ @client.post('analytics/usage/metrics', options.to_json, { "Content-Type" => "application/json" }).to_h!
59
+ end
60
+ end
61
+ end
@@ -6,19 +6,20 @@ module Mailgun
6
6
  #
7
7
  # See the Github documentation for full examples.
8
8
  class Response
9
- # All responses have a payload and a code corresponding to http, though
9
+ # All responses have a payload and a status corresponding to http, though
10
10
  # slightly different
11
- attr_accessor :body, :code
11
+ attr_accessor :body, :status, :code
12
12
 
13
- ResponseHash = Struct.new(:body, :code)
13
+ ResponseHash = Struct.new(:body, :status)
14
14
  def self.from_hash(h)
15
15
  # Create a "fake" response object with the data passed from h
16
- self.new ResponseHash.new(h[:body], h[:code])
16
+ self.new ResponseHash.new(h[:body], h[:status])
17
17
  end
18
18
 
19
19
  def initialize(response)
20
20
  @body = response.body
21
- @code = response.code
21
+ @status = response.status
22
+ @code = response.status
22
23
  end
23
24
 
24
25
  # Return response as Ruby Hash
@@ -57,12 +58,12 @@ module Mailgun
57
58
  rescue => err
58
59
  raise ParseError.new(err), err
59
60
  end
60
-
61
- # Returns true if response code is 2xx
62
- #
63
- # @return [Boolean] A boolean that binarizes the response code result.
61
+
62
+ # Returns true if response status is 2xx
63
+ #
64
+ # @return [Boolean] A boolean that binarizes the response status result.
64
65
  def success?
65
- (200..299).include?(code)
66
+ (200..299).include?(status)
66
67
  end
67
68
  end
68
69
  end
@@ -1,4 +1,4 @@
1
1
  # It's the version. Yeay!
2
2
  module Mailgun
3
- VERSION = '1.2.15'
3
+ VERSION = '1.3.0'
4
4
  end
data/lib/mailgun.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'tempfile'
2
- require 'rest_client'
2
+ require 'faraday'
3
3
  require 'yaml'
4
4
  require 'json'
5
5
 
@@ -18,6 +18,7 @@ require 'mailgun/webhooks/webhooks'
18
18
  require 'mailgun/templates/templates'
19
19
  require 'mailgun/subaccounts/subaccounts'
20
20
  require 'mailgun/tags/tags'
21
+ require 'mailgun/metrics/metrics'
21
22
 
22
23
  # Module for interacting with the sweet Mailgun API.
23
24
  #
data/mailgun.gemspec CHANGED
@@ -30,11 +30,12 @@ Gem::Specification.new do |spec|
30
30
  spec.add_development_dependency 'bundler', '>= 1.16.2'
31
31
  spec.add_development_dependency 'rspec', '~> 3.8.0'
32
32
  spec.add_development_dependency 'rake', '~> 12.3.2'
33
+ spec.add_development_dependency 'mime-types'
33
34
  spec.add_development_dependency 'webmock', '~> 3.7'
34
35
  spec.add_development_dependency 'pry', '~> 0.11.3'
35
36
  spec.add_development_dependency 'vcr', '~> 3.0.3'
36
37
  spec.add_development_dependency 'simplecov', '~> 0.16.1'
37
38
  spec.add_development_dependency 'rails'
38
- spec.add_dependency 'rest-client', '>= 2.0.2'
39
+ spec.add_dependency 'faraday', "~> 2.1"
39
40
 
40
41
  end
@@ -51,7 +51,7 @@ describe 'Client exceptions', vcr: vcr_opts do
51
51
  :text => 'INTEGRATION TESTING'
52
52
  })
53
53
  rescue Mailgun::Unauthorized => err
54
- expect(err.message).to eq('401 Unauthorized - Invalid Domain or API key: Forbidden')
54
+ expect(err.message).to eq('the server responded with status 401 - Invalid Domain or API key')
55
55
  else
56
56
  fail
57
57
  end
@@ -75,7 +75,7 @@ describe 'Client exceptions', vcr: vcr_opts do
75
75
  :text => 'INTEGRATION TESTING'
76
76
  })
77
77
  rescue Mailgun::BadRequest => err
78
- expect(err.message).to eq('400 Bad Request: to parameter is not a valid address. please check documentation')
78
+ expect(err.message).to eq('the server responded with status 400')
79
79
  else
80
80
  fail
81
81
  end
@@ -99,7 +99,7 @@ describe 'Client exceptions', vcr: vcr_opts do
99
99
  :text => 'INTEGRATION TESTING'
100
100
  })
101
101
  rescue Mailgun::CommunicationError => err
102
- expect(err.message).to include('403 Forbidden')
102
+ expect(err.message).to include('403')
103
103
  else
104
104
  fail
105
105
  end
@@ -192,7 +192,7 @@ Testing some Mailgun awesomness!'
192
192
  expect(result.body).to include("message")
193
193
  expect(result.body).to include("id")
194
194
  end
195
-
195
+
196
196
  it 'receives success response code' do
197
197
  @mg_obj.enable_test_mode!
198
198
 
@@ -205,7 +205,7 @@ Testing some Mailgun awesomness!'
205
205
 
206
206
  result = @mg_obj.send_message(@domain, data)
207
207
  result.to_h!
208
-
208
+
209
209
  expect(result.success?).to be(true)
210
210
  end
211
211
  end
@@ -0,0 +1,218 @@
1
+ require 'spec_helper'
2
+ require 'mailgun'
3
+
4
+ vcr_opts = { cassette_name: 'metrics' }
5
+
6
+ describe Mailgun::Metrics, vcr: vcr_opts do
7
+ let(:metrics) { Mailgun::Metrics.new(Mailgun::Client.new(APIKEY, APIHOST, 'v1')) }
8
+
9
+ describe '#account_metrics' do
10
+ let(:options) do
11
+ {
12
+ resolution: 'hour',
13
+ metrics: [
14
+ 'accepted_count',
15
+ 'delivered_count',
16
+ 'clicked_rate',
17
+ 'opened_rate'
18
+ ],
19
+ include_aggregates: true,
20
+ start: 'Tue, 26 Nov 2024 20:56:50 -0500',
21
+ duration: '1m',
22
+ filter: {
23
+ AND: [
24
+ {
25
+ attribute: 'domain',
26
+ comparator: '!=',
27
+ values: [
28
+ {
29
+ label: 'example.com',
30
+ value: 'example.com'
31
+ }
32
+ ]
33
+ }
34
+ ]
35
+ },
36
+ dimensions: ['time'],
37
+ end: 'Tue, 30 Nov 2024 20:56:50 -0500',
38
+ include_subaccounts: true
39
+ }
40
+ end
41
+
42
+ it 'responds with account metrics' do
43
+ expect(metrics.account_metrics(options)).to eq(
44
+ {
45
+ "start" => "Fri, 01 Nov 2024 01:00:00 +0000",
46
+ "end" => "Sun, 01 Dec 2024 01:00:00 +0000",
47
+ "resolution" => "hour",
48
+ "duration" => "1m",
49
+ "dimensions" => ["time"],
50
+ "pagination" => {
51
+ "sort" => "", "skip" => 0, "limit" => 1500, "total" => 3
52
+ },
53
+ "items" => [{
54
+ "dimensions" => [{
55
+ "dimension" => "time",
56
+ "value" => "Wed, 27 Nov 2024 12:00:00 +0000",
57
+ "display_value" => "Wed, 27 Nov 2024 12:00:00 +0000"
58
+ }],
59
+ "metrics" => {
60
+ "accepted_count" => 1, "delivered_count" => 1, "opened_rate" => "0.0000", "clicked_rate" => "0.0000"
61
+ }
62
+ },
63
+ {
64
+ "dimensions" => [{
65
+ "dimension" => "time",
66
+ "value" => "Wed, 27 Nov 2024 13:00:00 +0000",
67
+ "display_value" => "Wed, 27 Nov 2024 13:00:00 +0000"
68
+ }],
69
+ "metrics" => {
70
+ "accepted_count" => 1, "delivered_count" => 1, "opened_rate" => "0.0000", "clicked_rate" => "0.0000"
71
+ }
72
+ },
73
+ {
74
+ "dimensions" => [{
75
+ "dimension" => "time",
76
+ "value" => "Thu, 28 Nov 2024 15:00:00 +0000",
77
+ "display_value" => "Thu, 28 Nov 2024 15:00:00 +0000"
78
+ }],
79
+ "metrics" => {
80
+ "accepted_count" => 1, "delivered_count" => 1, "opened_rate" => "0.0000", "clicked_rate" => "0.0000"
81
+ }
82
+ }
83
+ ],
84
+ "aggregates" => {
85
+ "metrics" => {
86
+ "accepted_count" => 3, "delivered_count" => 3, "opened_rate" => "0.0000", "clicked_rate" => "0.0000"
87
+ }
88
+ }
89
+ }
90
+ )
91
+ end
92
+ end
93
+
94
+ describe '#account_usage_metrics' do
95
+ let(:options) do
96
+ {
97
+ resolution: 'hour',
98
+ metrics: [
99
+ 'email_preview_count',
100
+ 'email_preview_failed_count',
101
+ 'email_validation_bulk_count',
102
+ 'email_validation_count',
103
+ 'email_validation_list_count',
104
+ 'email_validation_mailgun_count',
105
+ 'email_validation_mailjet_count',
106
+ 'email_validation_public_count',
107
+ 'email_validation_single_count',
108
+ 'email_validation_valid_count',
109
+ 'link_validation_count',
110
+ 'link_validation_failed_count',
111
+ 'processed_count',
112
+ 'seed_test_count'
113
+ ],
114
+ include_aggregates: true,
115
+ start: 'Tue, 26 Nov 2024 20:56:50 -0500',
116
+ duration: '1m',
117
+ filter: {
118
+ AND: [
119
+ {
120
+ attribute: 'subaccount',
121
+ comparator: '!=',
122
+ values: [
123
+ {
124
+ label: '12345',
125
+ value: '12345'
126
+ }
127
+ ]
128
+ }
129
+ ]
130
+ },
131
+ dimensions: ['time'],
132
+ end: 'Tue, 28 Nov 2024 20:56:50 -0500',
133
+ include_subaccounts: true
134
+ }
135
+ end
136
+
137
+ it 'responds with account usage metrics' do
138
+ expect(metrics.account_usage_metrics(options)).to eq(
139
+ {
140
+ "start" => "Tue, 29 Oct 2024 01:00:00 +0000",
141
+ "end" => "Fri, 29 Nov 2024 01:00:00 +0000",
142
+ "resolution" => "hour",
143
+ "duration" => "1m",
144
+ "dimensions" => ["time"],
145
+ "pagination" => {
146
+ "sort" => "", "skip" => 0, "limit" => 1500, "total" => 2
147
+ },
148
+ "items" => [{
149
+ "dimensions" => [{
150
+ "dimension" => "time",
151
+ "value" => "Wed, 27 Nov 2024 00:00:00 +0000",
152
+ "display_value" => "Wed, 27 Nov 2024 00:00:00 +0000"
153
+ }],
154
+ "metrics" => {
155
+ "processed_count" => 2,
156
+ "email_validation_count" => 0,
157
+ "email_validation_public_count" => 0,
158
+ "email_validation_valid_count" => 0,
159
+ "email_validation_single_count" => 0,
160
+ "email_validation_bulk_count" => 0,
161
+ "email_validation_list_count" => 0,
162
+ "email_validation_mailgun_count" => 0,
163
+ "email_validation_mailjet_count" => 0,
164
+ "email_preview_count" => 0,
165
+ "email_preview_failed_count" => 0,
166
+ "link_validation_count" => 0,
167
+ "link_validation_failed_count" => 0,
168
+ "seed_test_count" => 0
169
+ }
170
+ },
171
+ {
172
+ "dimensions" => [{
173
+ "dimension" => "time",
174
+ "value" => "Thu, 28 Nov 2024 00:00:00 +0000",
175
+ "display_value" => "Thu, 28 Nov 2024 00:00:00 +0000"
176
+ }],
177
+ "metrics" => {
178
+ "processed_count" => 1,
179
+ "email_validation_count" => 0,
180
+ "email_validation_public_count" => 0,
181
+ "email_validation_valid_count" => 0,
182
+ "email_validation_single_count" => 0,
183
+ "email_validation_bulk_count" => 0,
184
+ "email_validation_list_count" => 0,
185
+ "email_validation_mailgun_count" => 0,
186
+ "email_validation_mailjet_count" => 0,
187
+ "email_preview_count" => 0,
188
+ "email_preview_failed_count" => 0,
189
+ "link_validation_count" => 0,
190
+ "link_validation_failed_count" => 0,
191
+ "seed_test_count" => 0
192
+ }
193
+ }
194
+ ],
195
+ "aggregates" => {
196
+ "metrics" => {
197
+ "permanent_failed_count" => 0,
198
+ "processed_count" => 3,
199
+ "email_validation_count" => 0,
200
+ "email_validation_public_count" => 0,
201
+ "email_validation_valid_count" => 0,
202
+ "email_validation_single_count" => 0,
203
+ "email_validation_bulk_count" => 0,
204
+ "email_validation_list_count" => 0,
205
+ "email_validation_mailgun_count" => 0,
206
+ "email_validation_mailjet_count" => 0,
207
+ "email_preview_count" => 0,
208
+ "email_preview_failed_count" => 0,
209
+ "link_validation_count" => 0,
210
+ "link_validation_failed_count" => 0,
211
+ "seed_test_count" => 0
212
+ }
213
+ }
214
+ }
215
+ )
216
+ end
217
+ end
218
+ end
@@ -99,7 +99,7 @@ module Mailgun
99
99
  return Response.from_hash({ body: JSON.generate({"message" => "Queued. Thank you.", "id" => id}) })
100
100
  end
101
101
  if resource_endpoint == "bounces"
102
- return Response.from_hash({ body: JSON.generate({"total_count" => 1, "items" => {"created_at" => "Fri, 21 Oct 2011 11:02:55 GMT", "code" => 550, "address" => "baz@example.com", "error" => "Message was not accepted -- invalid mailbox. Local mailbox baz@example.com is unavailable: user not found"}}) })
102
+ return Response.from_hash({ body: JSON.generate({"total_count" => 1, "items" => {"created_at" => "Fri, 21 Oct 2011 11:02:55 GMT", "status" => 550, "address" => "baz@example.com", "error" => "Message was not accepted -- invalid mailbox. Local mailbox baz@example.com is unavailable: user not found"}}) })
103
103
  end
104
104
  if resource_endpoint == "lists"
105
105
  return Response.from_hash({ body: JSON.generate({"member" => {"vars" => {"age" => 26}, "name" => "Foo Bar", "subscribed" => false, "address" => "bar@example.com"}, "message" => "Mailing list member has been updated"}) })
@@ -5,13 +5,13 @@ RSpec.describe Mailgun::CommunicationError do
5
5
  context "when the Response body doesn't have a `message` property" do
6
6
  it "doesn't raise an error" do
7
7
  expect do
8
- described_class.new('Boom!', Mailgun::Response.from_hash({ code: 401, body: '{}' }))
8
+ described_class.new('Boom!', Mailgun::Response.from_hash({ status: 401, body: '{}' }))
9
9
  end.not_to raise_error
10
10
  end
11
11
 
12
12
  context "when the Response body has an `Error` property" do
13
13
  it "uses the `Error` property as the API message" do
14
- subject = described_class.new('Boom!', Mailgun::Response.from_hash({ code: 401, body: '{"Error":"unauthorized"}' }))
14
+ subject = described_class.new('Boom!', Mailgun::Response.from_hash({ status: 401, body: '{"Error":"unauthorized"}' }))
15
15
 
16
16
  expect(subject.message).to eq("Boom!: unauthorized")
17
17
  end
@@ -19,7 +19,7 @@ RSpec.describe Mailgun::CommunicationError do
19
19
 
20
20
  context "when the Response body has an `error` property" do
21
21
  it "uses the `Error` property as the API message" do
22
- subject = described_class.new('Boom!', Mailgun::Response.from_hash({ code: 401, body: '{"error":"not found"}' }))
22
+ subject = described_class.new('Boom!', Mailgun::Response.from_hash({ status: 401, body: '{"error":"not found"}' }))
23
23
 
24
24
  expect(subject.message).to eq("Boom!: not found")
25
25
  end
@@ -0,0 +1,116 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://api.mailgun.net/v1/analytics/metrics
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"resolution":"hour","metrics":["accepted_count","delivered_count","clicked_rate","opened_rate"],"include_aggregates":true,"start":"Tue,
9
+ 26 Nov 2024 20:56:50 -0500","duration":"1m","filter":{"AND":[{"attribute":"domain","comparator":"!=","values":[{"label":"example.com","value":"example.com"}]}]},"dimensions":["time"],"end":"Tue,
10
+ 30 Nov 2024 20:56:50 -0500","include_subaccounts":true}'
11
+ headers:
12
+ Accept:
13
+ - "*/*"
14
+ User-Agent:
15
+ - rest-client/2.1.0 (darwin23 x86_64) ruby/3.1.4p223
16
+ Content-Type:
17
+ - application/json
18
+ Content-Length:
19
+ - '387'
20
+ Accept-Encoding:
21
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
22
+ Host:
23
+ - api.mailgun.net
24
+ Authorization:
25
+ - Basic xxx
26
+ response:
27
+ status:
28
+ code: 200
29
+ message: OK
30
+ headers:
31
+ Access-Control-Allow-Credentials:
32
+ - 'true'
33
+ Access-Control-Allow-Origin:
34
+ - "*"
35
+ Cache-Control:
36
+ - no-store
37
+ Content-Length:
38
+ - '1006'
39
+ Content-Type:
40
+ - application/json; charset=utf-8
41
+ Date:
42
+ - Thu, 28 Nov 2024 18:20:22 GMT
43
+ Strict-Transport-Security:
44
+ - max-age=63072000; includeSubDomains
45
+ X-Mailgun-Key-Id:
46
+ - c02fd0ba-d8dbad66
47
+ X-Xss-Protection:
48
+ - 1; mode=block
49
+ body:
50
+ encoding: UTF-8
51
+ string: '{"start":"Fri, 01 Nov 2024 01:00:00 +0000","end":"Sun, 01 Dec 2024
52
+ 01:00:00 +0000","resolution":"hour","duration":"1m","dimensions":["time"],"pagination":{"sort":"","skip":0,"limit":1500,"total":3},"items":[{"dimensions":[{"dimension":"time","value":"Wed,
53
+ 27 Nov 2024 12:00:00 +0000","display_value":"Wed, 27 Nov 2024 12:00:00 +0000"}],"metrics":{"accepted_count":1,"delivered_count":1,"opened_rate":"0.0000","clicked_rate":"0.0000"}},{"dimensions":[{"dimension":"time","value":"Wed,
54
+ 27 Nov 2024 13:00:00 +0000","display_value":"Wed, 27 Nov 2024 13:00:00 +0000"}],"metrics":{"accepted_count":1,"delivered_count":1,"opened_rate":"0.0000","clicked_rate":"0.0000"}},{"dimensions":[{"dimension":"time","value":"Thu,
55
+ 28 Nov 2024 15:00:00 +0000","display_value":"Thu, 28 Nov 2024 15:00:00 +0000"}],"metrics":{"accepted_count":1,"delivered_count":1,"opened_rate":"0.0000","clicked_rate":"0.0000"}}],"aggregates":{"metrics":{"accepted_count":3,"delivered_count":3,"opened_rate":"0.0000","clicked_rate":"0.0000"}}}
56
+
57
+ '
58
+ http_version:
59
+ recorded_at: Thu, 28 Nov 2024 18:20:22 GMT
60
+ - request:
61
+ method: post
62
+ uri: https://api.mailgun.net/v1/analytics/usage/metrics
63
+ body:
64
+ encoding: UTF-8
65
+ string: '{"resolution":"hour","metrics":["email_preview_count","email_preview_failed_count","email_validation_bulk_count","email_validation_count","email_validation_list_count","email_validation_mailgun_count","email_validation_mailjet_count","email_validation_public_count","email_validation_single_count","email_validation_valid_count","link_validation_count","link_validation_failed_count","processed_count","seed_test_count"],"include_aggregates":true,"start":"Tue,
66
+ 26 Nov 2024 20:56:50 -0500","duration":"1m","filter":{"AND":[{"attribute":"subaccount","comparator":"!=","values":[{"label":"12345","value":"12345"}]}]},"dimensions":["time"],"end":"Tue,
67
+ 28 Nov 2024 20:56:50 -0500","include_subaccounts":true}'
68
+ headers:
69
+ Accept:
70
+ - "*/*"
71
+ User-Agent:
72
+ - rest-client/2.1.0 (darwin23 x86_64) ruby/3.1.4p223
73
+ Content-Type:
74
+ - application/json
75
+ Content-Length:
76
+ - '703'
77
+ Accept-Encoding:
78
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
79
+ Host:
80
+ - api.mailgun.net
81
+ Authorization:
82
+ - Basic xxx
83
+ response:
84
+ status:
85
+ code: 200
86
+ message: OK
87
+ headers:
88
+ Access-Control-Allow-Credentials:
89
+ - 'true'
90
+ Access-Control-Allow-Origin:
91
+ - "*"
92
+ Cache-Control:
93
+ - no-store
94
+ Content-Length:
95
+ - '1795'
96
+ Content-Type:
97
+ - application/json; charset=utf-8
98
+ Date:
99
+ - Thu, 28 Nov 2024 18:20:23 GMT
100
+ Strict-Transport-Security:
101
+ - max-age=63072000; includeSubDomains
102
+ X-Mailgun-Key-Id:
103
+ - c02fd0ba-d8dbad66
104
+ X-Xss-Protection:
105
+ - 1; mode=block
106
+ body:
107
+ encoding: UTF-8
108
+ string: '{"start":"Tue, 29 Oct 2024 01:00:00 +0000","end":"Fri, 29 Nov 2024
109
+ 01:00:00 +0000","resolution":"hour","duration":"1m","dimensions":["time"],"pagination":{"sort":"","skip":0,"limit":1500,"total":2},"items":[{"dimensions":[{"dimension":"time","value":"Wed,
110
+ 27 Nov 2024 00:00:00 +0000","display_value":"Wed, 27 Nov 2024 00:00:00 +0000"}],"metrics":{"processed_count":2,"email_validation_count":0,"email_validation_public_count":0,"email_validation_valid_count":0,"email_validation_single_count":0,"email_validation_bulk_count":0,"email_validation_list_count":0,"email_validation_mailgun_count":0,"email_validation_mailjet_count":0,"email_preview_count":0,"email_preview_failed_count":0,"link_validation_count":0,"link_validation_failed_count":0,"seed_test_count":0}},{"dimensions":[{"dimension":"time","value":"Thu,
111
+ 28 Nov 2024 00:00:00 +0000","display_value":"Thu, 28 Nov 2024 00:00:00 +0000"}],"metrics":{"processed_count":1,"email_validation_count":0,"email_validation_public_count":0,"email_validation_valid_count":0,"email_validation_single_count":0,"email_validation_bulk_count":0,"email_validation_list_count":0,"email_validation_mailgun_count":0,"email_validation_mailjet_count":0,"email_preview_count":0,"email_preview_failed_count":0,"link_validation_count":0,"link_validation_failed_count":0,"seed_test_count":0}}],"aggregates":{"metrics":{"permanent_failed_count":0,"processed_count":3,"email_validation_count":0,"email_validation_public_count":0,"email_validation_valid_count":0,"email_validation_single_count":0,"email_validation_bulk_count":0,"email_validation_list_count":0,"email_validation_mailgun_count":0,"email_validation_mailjet_count":0,"email_preview_count":0,"email_preview_failed_count":0,"link_validation_count":0,"link_validation_failed_count":0,"seed_test_count":0}}}
112
+
113
+ '
114
+ http_version:
115
+ recorded_at: Thu, 28 Nov 2024 18:20:23 GMT
116
+ recorded_with: VCR 3.0.3
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mailgun-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.15
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mailgun
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-10-07 00:00:00.000000000 Z
12
+ date: 2025-01-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -53,6 +53,20 @@ dependencies:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
55
  version: 12.3.2
56
+ - !ruby/object:Gem::Dependency
57
+ name: mime-types
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
56
70
  - !ruby/object:Gem::Dependency
57
71
  name: webmock
58
72
  requirement: !ruby/object:Gem::Requirement
@@ -124,19 +138,19 @@ dependencies:
124
138
  - !ruby/object:Gem::Version
125
139
  version: '0'
126
140
  - !ruby/object:Gem::Dependency
127
- name: rest-client
141
+ name: faraday
128
142
  requirement: !ruby/object:Gem::Requirement
129
143
  requirements:
130
- - - ">="
144
+ - - "~>"
131
145
  - !ruby/object:Gem::Version
132
- version: 2.0.2
146
+ version: '2.1'
133
147
  type: :runtime
134
148
  prerelease: false
135
149
  version_requirements: !ruby/object:Gem::Requirement
136
150
  requirements:
137
- - - ">="
151
+ - - "~>"
138
152
  - !ruby/object:Gem::Version
139
- version: 2.0.2
153
+ version: '2.1'
140
154
  description: Mailgun's Official Ruby SDK for interacting with the Mailgun API.
141
155
  email: support@mailgunhq.com
142
156
  executables: []
@@ -158,15 +172,16 @@ files:
158
172
  - docs/Events.md
159
173
  - docs/MessageBuilder.md
160
174
  - docs/Messages.md
175
+ - docs/Metrics.md
161
176
  - docs/OptInHandler.md
162
177
  - docs/Snippets.md
163
178
  - docs/Subaccounts.md
164
179
  - docs/Suppressions.md
180
+ - docs/Templates.md
165
181
  - docs/Webhooks.md
166
182
  - docs/railgun/EmailValidation.md
167
183
  - docs/railgun/Overview.md
168
184
  - docs/railgun/Parameters.md
169
- - docs/railgun/Templates.md
170
185
  - lib/mailgun-ruby.rb
171
186
  - lib/mailgun.rb
172
187
  - lib/mailgun/address.rb
@@ -178,6 +193,7 @@ files:
178
193
  - lib/mailgun/lists/opt_in_handler.rb
179
194
  - lib/mailgun/messages/batch_message.rb
180
195
  - lib/mailgun/messages/message_builder.rb
196
+ - lib/mailgun/metrics/metrics.rb
181
197
  - lib/mailgun/response.rb
182
198
  - lib/mailgun/subaccounts/subaccounts.rb
183
199
  - lib/mailgun/suppressions.rb
@@ -203,6 +219,7 @@ files:
203
219
  - spec/integration/mailer_spec.rb
204
220
  - spec/integration/mailgun_spec.rb
205
221
  - spec/integration/messages/sample_data/mime.txt
222
+ - spec/integration/metrics_spec.rb
206
223
  - spec/integration/routes_spec.rb
207
224
  - spec/integration/stats_spec.rb
208
225
  - spec/integration/subaccounts_spec.rb
@@ -240,6 +257,7 @@ files:
240
257
  - vcr_cassettes/mailing_list.todo.yml
241
258
  - vcr_cassettes/mailing_list.yml
242
259
  - vcr_cassettes/message_deliver.yml
260
+ - vcr_cassettes/metrics.yml
243
261
  - vcr_cassettes/routes.yml
244
262
  - vcr_cassettes/send_message.yml
245
263
  - vcr_cassettes/stats.yml
@@ -270,7 +288,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
270
288
  - !ruby/object:Gem::Version
271
289
  version: '0'
272
290
  requirements: []
273
- rubygems_version: 3.4.19
291
+ rubygems_version: 3.3.27
274
292
  signing_key:
275
293
  specification_version: 4
276
294
  summary: Mailgun's Official Ruby SDK
@@ -286,6 +304,7 @@ test_files:
286
304
  - spec/integration/mailer_spec.rb
287
305
  - spec/integration/mailgun_spec.rb
288
306
  - spec/integration/messages/sample_data/mime.txt
307
+ - spec/integration/metrics_spec.rb
289
308
  - spec/integration/routes_spec.rb
290
309
  - spec/integration/stats_spec.rb
291
310
  - spec/integration/subaccounts_spec.rb
File without changes