mailgun-ruby 1.2.15 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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