mailerlite-ruby 1.0.6 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.envrc +2 -0
- data/.github/workflows/main.yml +4 -4
- data/.github/workflows/publish_gem.yml +5 -5
- data/.gitignore +2 -1
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +21 -0
- data/README.md +583 -1
- data/flake.lock +27 -0
- data/flake.nix +25 -0
- data/lefthook.yml +14 -0
- data/lib/mailerlite/api_response.rb +24 -0
- data/lib/mailerlite/automations/automation_emails.rb +57 -0
- data/lib/mailerlite/automations/automation_reports.rb +114 -0
- data/lib/mailerlite/automations/automation_steps.rb +105 -0
- data/lib/mailerlite/automations/automation_templates.rb +34 -0
- data/lib/mailerlite/automations/automation_triggers.rb +62 -0
- data/lib/mailerlite/automations/automations.rb +177 -8
- data/lib/mailerlite/brand_styles/brand_styles.rb +24 -0
- data/lib/mailerlite/campaigns/campaigns.rb +33 -1
- data/lib/mailerlite/client.rb +15 -5
- data/lib/mailerlite/ecommerce/ecommerce.rb +316 -0
- data/lib/mailerlite/emails/emails.rb +306 -0
- data/lib/mailerlite/errors.rb +88 -0
- data/lib/mailerlite/files/files.rb +45 -0
- data/lib/mailerlite/groups/groups.rb +16 -8
- data/lib/mailerlite/params.rb +58 -0
- data/lib/mailerlite/query_params.rb +33 -0
- data/lib/mailerlite/request.rb +24 -0
- data/lib/mailerlite/segments/segments.rb +10 -10
- data/lib/mailerlite/subscribers/subscribers.rb +63 -8
- data/lib/mailerlite/version.rb +1 -1
- data/lib/mailerlite.rb +14 -0
- data/mailerlite-ruby.gemspec +8 -8
- data/spec/api_response_rspec.rb +140 -0
- data/spec/automation_emails_rspec.rb +40 -0
- data/spec/automation_reports_rspec.rb +92 -0
- data/spec/automation_steps_rspec.rb +76 -0
- data/spec/automation_templates_rspec.rb +24 -0
- data/spec/automation_triggers_rspec.rb +48 -0
- data/spec/automations_rspec.rb +143 -0
- data/spec/brand_styles_rspec.rb +49 -0
- data/spec/campaigns_rspec.rb +42 -0
- data/spec/ecommerce_rspec.rb +141 -0
- data/spec/emails_rspec.rb +692 -0
- data/spec/files_rspec.rb +90 -0
- data/spec/fixtures/brand_styles.json +50 -0
- data/spec/fixtures/campaign_simple_editor.json +24 -0
- data/spec/fixtures/email.json +81 -0
- data/spec/fixtures/email_block.json +12 -0
- data/spec/fixtures/email_block_created.json +12 -0
- data/spec/fixtures/email_block_created_rejected.json +15 -0
- data/spec/fixtures/email_block_moved.json +12 -0
- data/spec/fixtures/email_block_updated.json +9 -0
- data/spec/fixtures/email_block_updated_rejected.json +13 -0
- data/spec/fixtures/email_block_validation_invalid.json +5 -0
- data/spec/fixtures/email_etag.json +1 -0
- data/spec/fixtures/email_html.json +1 -0
- data/spec/fixtures/email_settings.json +111 -0
- data/spec/fixtures/email_settings_validation.json +9 -0
- data/spec/fixtures/email_template.json +1 -0
- data/spec/fixtures/email_version.json +1 -0
- data/spec/fixtures/email_versions.json +26 -0
- data/spec/fixtures/files.json +44 -0
- data/spec/groups_rspec.rb +21 -0
- data/spec/params_rspec.rb +126 -0
- data/spec/request_rspec.rb +139 -0
- data/spec/segments_rspec.rb +39 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/subscribers_rspec.rb +57 -0
- data/spec/support/json_fixture.rb +27 -0
- data/spec/support/request_stub.rb +25 -0
- metadata +69 -22
- /data/{renovate.json → .github/renovate.json} +0 -0
|
@@ -14,18 +14,33 @@ module MailerLite
|
|
|
14
14
|
|
|
15
15
|
# Returns a list of subscribers that match the specified filter criteria.
|
|
16
16
|
#
|
|
17
|
-
# @param filter[:status] [
|
|
17
|
+
# @param filter[:status] [String] the status of the subscribers to include in the results
|
|
18
|
+
# @param filter[:group] [String] the ID of a group, or several IDs separated with commas
|
|
19
|
+
# @param filter[:automation] [String] the ID of an automation, or several IDs separated with commas
|
|
20
|
+
# @param filter[:form] [String] the ID of a form, or several IDs separated with commas
|
|
21
|
+
# @param filter[:site] [String] the ID of a site, or several IDs separated with commas
|
|
22
|
+
# @param filter[:page] [String] the ID of a landing page, or several IDs separated with commas
|
|
23
|
+
# @param filter[:import] [String] the ID of an import, or several IDs separated with commas
|
|
24
|
+
# @param filter[:post] [String] the ID of a blog post, or several IDs separated with commas
|
|
18
25
|
# @param limit [Integer] the maximum number of subscribers to return
|
|
19
|
-
# @param
|
|
26
|
+
# @param cursor [String] the cursor for the page of results to return
|
|
27
|
+
# @param sort [String] the field to sort by, prefix with - for descending order
|
|
28
|
+
# @param include [String] the relations to embed, only groups is supported
|
|
29
|
+
# @param start_date [String] only subscribers who signed up on or after this date
|
|
30
|
+
# @param end_date [String] only subscribers who signed up on or before this date
|
|
31
|
+
# @param with_fields [Boolean] return each custom field with its name, value and type
|
|
20
32
|
# @return [HTTP::Response] the response from the API
|
|
21
|
-
def fetch(filter:, limit: nil, cursor: nil)
|
|
22
|
-
params =
|
|
33
|
+
def fetch(filter:, limit: nil, cursor: nil, sort: nil, include: nil, start_date: nil, end_date: nil, with_fields: nil)
|
|
34
|
+
params = QueryParams.filter(filter, %i[status group automation form site page import post])
|
|
23
35
|
|
|
24
36
|
params['limit'] = limit if limit
|
|
25
37
|
params['cursor'] = cursor if cursor
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
38
|
+
params['sort'] = sort if sort
|
|
39
|
+
params['include'] = include if include
|
|
40
|
+
params['start_date'] = start_date if start_date
|
|
41
|
+
params['end_date'] = end_date if end_date
|
|
42
|
+
params['with_fields'] = QueryParams.boolean(with_fields)
|
|
43
|
+
client.http.get(QueryParams.uri("#{MAILERLITE_API_URL}/subscribers", params))
|
|
29
44
|
end
|
|
30
45
|
|
|
31
46
|
# Creates a new subscriber with the specified details.
|
|
@@ -39,8 +54,11 @@ module MailerLite
|
|
|
39
54
|
# @param opted_in_at [DateTime] the date and time when the subscriber confirmed their subscription
|
|
40
55
|
# @param optin_ip [String] the IP address of the subscriber when they confirmed their subscription
|
|
41
56
|
# @param unsubscribed_at [DateTime] the date and time when the subscriber was unsubscribed
|
|
57
|
+
# @param resubscribe [Boolean] reactivate the subscriber if they are unsubscribed, bounced or junk
|
|
58
|
+
# @param double_optin [Boolean] send a confirmation email to the new subscriber
|
|
59
|
+
# @param notes [Array] notes to add to the subscriber
|
|
42
60
|
# @return [HTTP::Response] the response from the API
|
|
43
|
-
def create(email:, fields: nil, groups: nil, status: nil, subscribed_at: nil, ip_address: nil, opted_in_at: nil, optin_ip: nil, unsubscribed_at: nil)
|
|
61
|
+
def create(email:, fields: nil, groups: nil, status: nil, subscribed_at: nil, ip_address: nil, opted_in_at: nil, optin_ip: nil, unsubscribed_at: nil, resubscribe: nil, double_optin: nil, notes: nil)
|
|
44
62
|
params = { 'email' => email }
|
|
45
63
|
|
|
46
64
|
params['fields'] = fields if fields
|
|
@@ -51,6 +69,9 @@ module MailerLite
|
|
|
51
69
|
params['opted_in_at'] = opted_in_at if opted_in_at
|
|
52
70
|
params['optin_ip'] = optin_ip if optin_ip
|
|
53
71
|
params['unsubscribed_at'] = unsubscribed_at if unsubscribed_at
|
|
72
|
+
params['resubscribe'] = resubscribe
|
|
73
|
+
params['double_optin'] = double_optin
|
|
74
|
+
params['notes'] = notes if notes
|
|
54
75
|
|
|
55
76
|
client.http.post("#{MAILERLITE_API_URL}/subscribers", json: params.compact)
|
|
56
77
|
end
|
|
@@ -92,6 +113,18 @@ module MailerLite
|
|
|
92
113
|
client.http.get("#{MAILERLITE_API_URL}/subscribers/#{subscriber_id}")
|
|
93
114
|
end
|
|
94
115
|
|
|
116
|
+
# Returns the activity log of the specified subscriber
|
|
117
|
+
#
|
|
118
|
+
# @param subscriber_id [String] the ID of the subscriber
|
|
119
|
+
# @param filter[:log_name] [String] the type of activity to include in the results
|
|
120
|
+
# @param limit [Integer] the maximum number of entries to return
|
|
121
|
+
# @param page [Integer] the page number of the results to return
|
|
122
|
+
# @return [HTTP::Response] the response from the API
|
|
123
|
+
def activity_log(subscriber_id:, filter: {}, limit: nil, page: nil)
|
|
124
|
+
params = QueryParams.filter(filter, %i[log_name]).merge('limit' => limit, 'page' => page)
|
|
125
|
+
client.http.get(QueryParams.uri("#{MAILERLITE_API_URL}/subscribers/#{subscriber_id}/activity-log", params))
|
|
126
|
+
end
|
|
127
|
+
|
|
95
128
|
# Returns the details of the specified subscribers
|
|
96
129
|
#
|
|
97
130
|
# @param import_id [String] the ID of the import to fetch report
|
|
@@ -122,5 +155,27 @@ module MailerLite
|
|
|
122
155
|
def forget(subscriber_id)
|
|
123
156
|
client.http.post("#{MAILERLITE_API_URL}/subscribers/#{subscriber_id}/forget")
|
|
124
157
|
end
|
|
158
|
+
|
|
159
|
+
# Returns a subscriber's lifetime orders and revenue in one shop
|
|
160
|
+
#
|
|
161
|
+
# @param subscriber_id [String] the ID of the subscriber
|
|
162
|
+
# @param shop_id [String] the ID of the shop
|
|
163
|
+
# @return [HTTP::Response] the response from the API
|
|
164
|
+
def ecommerce_stats(subscriber_id:, shop_id:)
|
|
165
|
+
uri = URI("#{MAILERLITE_API_URL}/subscribers/#{subscriber_id}/reports/ecommerce")
|
|
166
|
+
uri.query = URI.encode_www_form('shop_id' => shop_id)
|
|
167
|
+
client.http.get(uri)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Returns a subscriber's orders in one shop
|
|
171
|
+
#
|
|
172
|
+
# @param subscriber_id [String] the ID of the subscriber
|
|
173
|
+
# @param shop_id [String] the ID of the shop
|
|
174
|
+
# @return [HTTP::Response] the response from the API
|
|
175
|
+
def ecommerce_orders(subscriber_id:, shop_id:, page: nil, limit: nil)
|
|
176
|
+
uri = URI("#{MAILERLITE_API_URL}/subscribers/#{subscriber_id}/reports/ecommerce/orders")
|
|
177
|
+
uri.query = URI.encode_www_form({ 'shop_id' => shop_id, 'page' => page, 'limit' => limit }.compact)
|
|
178
|
+
client.http.get(uri)
|
|
179
|
+
end
|
|
125
180
|
end
|
|
126
181
|
end
|
data/lib/mailerlite/version.rb
CHANGED
data/lib/mailerlite.rb
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'mailerlite/client'
|
|
4
4
|
require_relative 'mailerlite/version'
|
|
5
|
+
require_relative 'mailerlite/query_params'
|
|
6
|
+
require_relative 'mailerlite/errors'
|
|
7
|
+
require_relative 'mailerlite/params'
|
|
8
|
+
require_relative 'mailerlite/api_response'
|
|
9
|
+
require_relative 'mailerlite/request'
|
|
5
10
|
require_relative 'mailerlite/subscribers/subscribers'
|
|
6
11
|
require_relative 'mailerlite/campaigns/campaigns'
|
|
7
12
|
require_relative 'mailerlite/forms/forms'
|
|
@@ -9,6 +14,15 @@ require_relative 'mailerlite/groups/groups'
|
|
|
9
14
|
require_relative 'mailerlite/segments/segments'
|
|
10
15
|
require_relative 'mailerlite/fields/fields'
|
|
11
16
|
require_relative 'mailerlite/automations/automations'
|
|
17
|
+
require_relative 'mailerlite/automations/automation_triggers'
|
|
18
|
+
require_relative 'mailerlite/automations/automation_steps'
|
|
19
|
+
require_relative 'mailerlite/automations/automation_emails'
|
|
20
|
+
require_relative 'mailerlite/automations/automation_templates'
|
|
21
|
+
require_relative 'mailerlite/automations/automation_reports'
|
|
12
22
|
require_relative 'mailerlite/webhooks/webhooks'
|
|
13
23
|
require_relative 'mailerlite/batch/batch'
|
|
14
24
|
require_relative 'mailerlite/timezones/timezones'
|
|
25
|
+
require_relative 'mailerlite/ecommerce/ecommerce'
|
|
26
|
+
require_relative 'mailerlite/emails/emails'
|
|
27
|
+
require_relative 'mailerlite/brand_styles/brand_styles'
|
|
28
|
+
require_relative 'mailerlite/files/files'
|
data/mailerlite-ruby.gemspec
CHANGED
|
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
|
|
|
14
14
|
spec.description = "MailerLite's official Ruby SDK. Interacts with all endpoints at MailerLite API."
|
|
15
15
|
spec.homepage = 'https://www.MailerLite.com'
|
|
16
16
|
spec.license = 'MIT'
|
|
17
|
-
spec.required_ruby_version = '>=
|
|
17
|
+
spec.required_ruby_version = '>= 3.1'
|
|
18
18
|
|
|
19
19
|
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
|
20
20
|
|
|
@@ -27,13 +27,13 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
28
28
|
spec.require_paths = ['lib']
|
|
29
29
|
|
|
30
|
-
spec.add_development_dependency 'bundler', '~> 2.
|
|
31
|
-
spec.add_development_dependency 'rake', '~> 13.
|
|
32
|
-
spec.add_development_dependency 'rubocop', '~> 1.
|
|
33
|
-
spec.add_dependency 'dotenv', '~>
|
|
34
|
-
spec.add_dependency 'http', '~> 5.
|
|
35
|
-
spec.add_dependency 'json', '~> 2.
|
|
36
|
-
spec.add_dependency 'uri', '~>
|
|
30
|
+
spec.add_development_dependency 'bundler', '~> 2.5'
|
|
31
|
+
spec.add_development_dependency 'rake', '~> 13.2'
|
|
32
|
+
spec.add_development_dependency 'rubocop', '~> 1.69'
|
|
33
|
+
spec.add_dependency 'dotenv', '~> 3.1'
|
|
34
|
+
spec.add_dependency 'http', '~> 5.2'
|
|
35
|
+
spec.add_dependency 'json', '~> 2.9'
|
|
36
|
+
spec.add_dependency 'uri', '~> 1.0'
|
|
37
37
|
spec.add_development_dependency 'rspec'
|
|
38
38
|
spec.add_development_dependency 'simplecov'
|
|
39
39
|
spec.add_development_dependency 'vcr'
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'webmock/rspec'
|
|
5
|
+
require 'json'
|
|
6
|
+
require_relative 'support/json_fixture'
|
|
7
|
+
|
|
8
|
+
RSpec.describe MailerLite::ApiResponse do
|
|
9
|
+
include_context 'with the dummy API token'
|
|
10
|
+
|
|
11
|
+
let(:url) { "#{MAILERLITE_API_URL}/emails/123" }
|
|
12
|
+
|
|
13
|
+
def response_for(status:, body: '', headers: {})
|
|
14
|
+
stub_request(:get, url).to_return(status: status, body: body, headers: headers)
|
|
15
|
+
MailerLite::Client.new.http.get(url).extend(described_class)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe '#idempotent_replay?' do
|
|
19
|
+
it 'is true for the value true' do
|
|
20
|
+
expect(response_for(status: 201, headers: { 'Idempotent-Replayed' => 'true' }).idempotent_replay?).to be(true)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'reads the value without case and without spaces' do
|
|
24
|
+
expect(response_for(status: 201, headers: { 'Idempotent-Replayed' => 'True' }).idempotent_replay?).to be(true)
|
|
25
|
+
expect(response_for(status: 201, headers: { 'Idempotent-Replayed' => ' TRUE ' }).idempotent_replay?).to be(true)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'is false when the header is not there' do
|
|
29
|
+
expect(response_for(status: 201).idempotent_replay?).to be(false)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'is false for other values' do
|
|
33
|
+
expect(response_for(status: 201, headers: { 'Idempotent-Replayed' => 'false' }).idempotent_replay?).to be(false)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'reads a header that comes two times' do
|
|
37
|
+
expect(response_for(status: 201, headers: { 'Idempotent-Replayed' => %w[true true] }).idempotent_replay?).to be(true)
|
|
38
|
+
expect(response_for(status: 201, headers: { 'Idempotent-Replayed' => %w[false false] }).idempotent_replay?).to be(false)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe '#api_error' do
|
|
43
|
+
it 'is nil for a 2xx status' do
|
|
44
|
+
response = response_for(status: 200, body: load_fixture('email').to_json)
|
|
45
|
+
expect(response.api_error).to be_nil
|
|
46
|
+
expect(response).to be_a(HTTP::Response)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'has the code and the etag of a 409 version conflict' do
|
|
50
|
+
body = { message: 'The email was changed.', code: 'version_conflict', etag: '45' }.to_json
|
|
51
|
+
error = response_for(status: 409, body: body).api_error
|
|
52
|
+
|
|
53
|
+
expect(error).to be_a(MailerLite::ApiError)
|
|
54
|
+
expect(error.status).to eq 409
|
|
55
|
+
expect(error.code).to eq 'version_conflict'
|
|
56
|
+
expect(error.etag).to eq '45'
|
|
57
|
+
expect(error.api_message).to eq 'The email was changed.'
|
|
58
|
+
expect(error.errors).to be_nil
|
|
59
|
+
expect(error.message).to start_with('409')
|
|
60
|
+
expect(error.message).to include('(version_conflict): The email was changed.')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'has no code for a 409 without a code' do
|
|
64
|
+
error = response_for(status: 409, body: { message: 'The campaign is not a draft.' }.to_json).api_error
|
|
65
|
+
|
|
66
|
+
expect(error.status).to eq 409
|
|
67
|
+
expect(error.code).to be_nil
|
|
68
|
+
expect(error.etag).to be_nil
|
|
69
|
+
expect(error.message).to include(': The campaign is not a draft.')
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'has the errors of a 422' do
|
|
73
|
+
body = { message: 'The etag field is required.', errors: { etag: ['The etag field is required.'] } }.to_json
|
|
74
|
+
error = response_for(status: 422, body: body).api_error
|
|
75
|
+
|
|
76
|
+
expect(error.status).to eq 422
|
|
77
|
+
expect(error.errors).to eq('etag' => ['The etag field is required.'])
|
|
78
|
+
expect(error.api_message).to eq 'The etag field is required.'
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'has no errors for a 422 with only a message' do
|
|
82
|
+
error = response_for(status: 422, body: { message: 'This email was not built with the simple editor.' }.to_json).api_error
|
|
83
|
+
|
|
84
|
+
expect(error.errors).to be_nil
|
|
85
|
+
expect(error.api_message).to eq 'This email was not built with the simple editor.'
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'does not raise for a 503 with an HTML body' do
|
|
89
|
+
html = '<html><body>Service Unavailable</body></html>'
|
|
90
|
+
error = response_for(status: 503, body: html, headers: { 'Content-Type' => 'text/html' }).api_error
|
|
91
|
+
|
|
92
|
+
expect(error.status).to eq 503
|
|
93
|
+
expect(error.api_message).to be_nil
|
|
94
|
+
expect(error.code).to be_nil
|
|
95
|
+
expect(error.body).to eq html
|
|
96
|
+
expect(error.headers['Content-Type']).to eq 'text/html'
|
|
97
|
+
expect(error.message).to start_with('503')
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it 'accepts a JSON body that is not an object' do
|
|
101
|
+
error = response_for(status: 500, body: '[1, 2]').api_error
|
|
102
|
+
|
|
103
|
+
expect(error.status).to eq 500
|
|
104
|
+
expect(error.api_message).to be_nil
|
|
105
|
+
expect(error.body).to eq '[1, 2]'
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it 'survives a Marshal round trip' do
|
|
109
|
+
body = { message: 'The email was changed.', code: 'version_conflict', etag: '45' }.to_json
|
|
110
|
+
error = response_for(status: 409, body: body).api_error
|
|
111
|
+
copy = Marshal.load(Marshal.dump(error))
|
|
112
|
+
|
|
113
|
+
expect(copy).to be_a(MailerLite::ApiError)
|
|
114
|
+
expect(copy.message).to eq error.message
|
|
115
|
+
expect([copy.status, copy.code, copy.etag, copy.api_message, copy.body]).to eq [409, 'version_conflict', '45', 'The email was changed.', body]
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe MailerLite::ApiError do
|
|
120
|
+
it 'is a MailerLite::Error and not an HTTP::Error' do
|
|
121
|
+
expect(described_class.ancestors).to include(MailerLite::Error)
|
|
122
|
+
expect(described_class.ancestors).not_to include(HTTP::Error)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it 'builds an error from any HTTP::Response' do
|
|
126
|
+
stub_request(:get, url).to_return(status: 404, body: { message: 'Not found.' }.to_json)
|
|
127
|
+
error = described_class.from_response(MailerLite::Client.new.http.get(url))
|
|
128
|
+
|
|
129
|
+
expect(error.status).to eq 404
|
|
130
|
+
expect(error.api_message).to eq 'Not found.'
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it 'can be raised' do
|
|
134
|
+
stub_request(:get, url).to_return(status: 404, body: { message: 'Not found.' }.to_json)
|
|
135
|
+
error = described_class.from_response(MailerLite::Client.new.http.get(url))
|
|
136
|
+
|
|
137
|
+
expect { raise error }.to raise_error(described_class, /Not found\./)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe MailerLite::AutomationEmails do
|
|
6
|
+
let(:client) { MailerLite::Client.new }
|
|
7
|
+
let(:emails) { described_class.new(client: client) }
|
|
8
|
+
|
|
9
|
+
describe '#content' do
|
|
10
|
+
it 'sends GET /automations/{automation}/emails/{email}/content' do
|
|
11
|
+
stub = stub_api(:get, '/automations/1/emails/2/content')
|
|
12
|
+
emails.content(automation_id: 1, email_id: 2)
|
|
13
|
+
expect(stub).to have_been_requested
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe '#update_content' do
|
|
18
|
+
it 'sends PUT /automations/{automation}/emails/{email}/content' do
|
|
19
|
+
stub = stub_api(:put, '/automations/1/emails/2/content', body: { 'html' => '<p>Hi</p>' })
|
|
20
|
+
emails.update_content(automation_id: 1, email_id: 2, html: '<p>Hi</p>')
|
|
21
|
+
expect(stub).to have_been_requested
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe '#report_stats' do
|
|
26
|
+
it 'sends GET /automations/{automation}/emails/{email}/report-stats with the query' do
|
|
27
|
+
stub = stub_api(:get, '/automations/1/emails/2/report-stats', query: { 'start_date' => '2026-01-01', 'end_date' => '2026-01-31', 'shop_id' => '3', 'excel' => '0' })
|
|
28
|
+
emails.report_stats(automation_id: 1, email_id: 2, start_date: '2026-01-01', end_date: '2026-01-31', shop_id: '3', excel: false)
|
|
29
|
+
expect(stub).to have_been_requested
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe '#unsubscribe_reasons' do
|
|
34
|
+
it 'sends GET /automations/{automation}/emails/{email}/unsubscribe-reasons' do
|
|
35
|
+
stub = stub_api(:get, '/automations/1/emails/2/unsubscribe-reasons')
|
|
36
|
+
emails.unsubscribe_reasons(automation_id: 1, email_id: 2)
|
|
37
|
+
expect(stub).to have_been_requested
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe MailerLite::AutomationReports do
|
|
6
|
+
let(:client) { MailerLite::Client.new }
|
|
7
|
+
let(:reports) { described_class.new(client: client) }
|
|
8
|
+
|
|
9
|
+
describe '#step_run_counts' do
|
|
10
|
+
it 'sends GET /automations/{automation}/steps/run-counts' do
|
|
11
|
+
stub = stub_api(:get, '/automations/1/steps/run-counts')
|
|
12
|
+
reports.step_run_counts(1)
|
|
13
|
+
expect(stub).to have_been_requested
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe '#step_run_count' do
|
|
18
|
+
it 'sends GET /automations/{automation}/steps/run-counts/{step}' do
|
|
19
|
+
stub = stub_api(:get, '/automations/1/steps/run-counts/2')
|
|
20
|
+
reports.step_run_count(automation_id: 1, step_id: 2)
|
|
21
|
+
expect(stub).to have_been_requested
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe '#trigger_run_counts' do
|
|
26
|
+
it 'sends GET /automations/{automation}/triggers/run-counts' do
|
|
27
|
+
stub = stub_api(:get, '/automations/1/triggers/run-counts')
|
|
28
|
+
reports.trigger_run_counts(1)
|
|
29
|
+
expect(stub).to have_been_requested
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe '#trigger_run_count' do
|
|
34
|
+
it 'sends GET /automations/{automation}/triggers/run-counts/{trigger}' do
|
|
35
|
+
stub = stub_api(:get, '/automations/1/triggers/run-counts/2')
|
|
36
|
+
reports.trigger_run_count(automation_id: 1, trigger_id: 2)
|
|
37
|
+
expect(stub).to have_been_requested
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe '#ab_steps_stats' do
|
|
42
|
+
it 'sends GET /automations/{automation}/ab-steps-stats' do
|
|
43
|
+
stub = stub_api(:get, '/automations/1/ab-steps-stats')
|
|
44
|
+
reports.ab_steps_stats(1)
|
|
45
|
+
expect(stub).to have_been_requested
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe '#ab_step_stats' do
|
|
50
|
+
it 'sends GET /automations/{automation}/ab-steps-stats/{step}' do
|
|
51
|
+
stub = stub_api(:get, '/automations/1/ab-steps-stats/2')
|
|
52
|
+
reports.ab_step_stats(automation_id: 1, step_id: 2)
|
|
53
|
+
expect(stub).to have_been_requested
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe '#step_subscribers' do
|
|
58
|
+
it 'sends GET /automations/{automation}/activity/steps/{step}/subscribers with the query' do
|
|
59
|
+
stub = stub_api(:get, '/automations/1/activity/steps/2/subscribers', query: {
|
|
60
|
+
'status' => 'queued', 'limit' => '10', 'page' => '2', 'sort' => '-id',
|
|
61
|
+
'filter[date_from]' => '2026-01-01', 'filter[date_to]' => '2026-01-31', 'filter[search]' => 'a@b.com', 'filter[id]' => '5'
|
|
62
|
+
})
|
|
63
|
+
reports.step_subscribers(automation_id: 1, step_id: 2, status: 'queued', limit: 10, page: 2, sort: '-id',
|
|
64
|
+
filter: { date_from: '2026-01-01', date_to: '2026-01-31', search: 'a@b.com', id: '5' })
|
|
65
|
+
expect(stub).to have_been_requested
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
describe '#step_subscribers with an Array filter[:id]' do
|
|
70
|
+
it 'sends the ids as one comma-separated value' do
|
|
71
|
+
stub = stub_api(:get, '/automations/1/activity/steps/2/subscribers', query: { 'status' => 'queued', 'filter[id]' => '5,6' })
|
|
72
|
+
reports.step_subscribers(automation_id: 1, step_id: 2, status: 'queued', filter: { id: %w[5 6] })
|
|
73
|
+
expect(stub).to have_been_requested
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
describe '#trigger_subscribers' do
|
|
78
|
+
it 'sends GET /automations/{automation}/activity/triggers/{trigger}/subscribers with the query' do
|
|
79
|
+
stub = stub_api(:get, '/automations/1/activity/triggers/2/subscribers', query: { 'status' => 'completed', 'filter[search]' => 'a@b.com' })
|
|
80
|
+
reports.trigger_subscribers(automation_id: 1, trigger_id: 2, status: 'completed', filter: { search: 'a@b.com' })
|
|
81
|
+
expect(stub).to have_been_requested
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
describe '#email_monthly_totals' do
|
|
86
|
+
it 'sends GET /automations/{automation}/monthly-totals/email with the query' do
|
|
87
|
+
stub = stub_api(:get, '/automations/1/monthly-totals/email', query: { 'start_date' => '2026-01-01', 'end_date' => '2026-06-30', 'shop_id' => '3', 'excel' => '1' })
|
|
88
|
+
reports.email_monthly_totals(automation_id: 1, start_date: '2026-01-01', end_date: '2026-06-30', shop_id: '3', excel: true)
|
|
89
|
+
expect(stub).to have_been_requested
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe MailerLite::AutomationSteps do
|
|
6
|
+
let(:client) { MailerLite::Client.new }
|
|
7
|
+
let(:steps) { described_class.new(client: client) }
|
|
8
|
+
|
|
9
|
+
describe '#list' do
|
|
10
|
+
it 'sends GET /automations/{automation}/steps/index' do
|
|
11
|
+
stub = stub_api(:get, '/automations/1/steps/index')
|
|
12
|
+
steps.list(1)
|
|
13
|
+
expect(stub).to have_been_requested
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe '#create' do
|
|
18
|
+
it 'sends POST /automations/{automation}/steps' do
|
|
19
|
+
stub = stub_api(:post, '/automations/1/steps', body: {
|
|
20
|
+
'type' => 'action', 'parent_id' => 2, 'branch' => 'yes', 'move_to_branch' => 'no',
|
|
21
|
+
'data' => { 'action_type' => 'copy_to_group', 'group_id' => '3' }
|
|
22
|
+
})
|
|
23
|
+
steps.create(automation_id: 1, type: 'action', parent_id: 2, branch: 'yes', move_to_branch: 'no',
|
|
24
|
+
data: { 'action_type' => 'copy_to_group', 'group_id' => '3' })
|
|
25
|
+
expect(stub).to have_been_requested
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe '#fetch' do
|
|
30
|
+
it 'sends GET /automations/{automation}/steps/{step}' do
|
|
31
|
+
stub = stub_api(:get, '/automations/1/steps/2')
|
|
32
|
+
steps.fetch(automation_id: 1, step_id: 2)
|
|
33
|
+
expect(stub).to have_been_requested
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe '#update' do
|
|
38
|
+
it 'sends PUT /automations/{automation}/steps/{step}' do
|
|
39
|
+
stub = stub_api(:put, '/automations/1/steps/2', body: { 'parent_id' => 3, 'data' => { 'value' => 2 }, 'reschedule_waiting_subscribers' => true })
|
|
40
|
+
steps.update(automation_id: 1, step_id: 2, parent_id: 3, data: { 'value' => 2 }, reschedule_waiting_subscribers: true)
|
|
41
|
+
expect(stub).to have_been_requested
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe '#delete' do
|
|
46
|
+
it 'sends DELETE /automations/{automation}/steps/{step} with branch_to_delete' do
|
|
47
|
+
stub = stub_api(:delete, '/automations/1/steps/2', query: { 'branch_to_delete' => 'yes' })
|
|
48
|
+
steps.delete(automation_id: 1, step_id: 2, branch_to_delete: 'yes')
|
|
49
|
+
expect(stub).to have_been_requested
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe '#copy' do
|
|
54
|
+
it 'sends POST /automations/{automation}/steps/{step}/copy' do
|
|
55
|
+
stub = stub_api(:post, '/automations/1/steps/2/copy', body: { 'parent_id' => 3, 'branch' => 'a_path', 'move_to_branch' => 'yes' })
|
|
56
|
+
steps.copy(automation_id: 1, step_id: 2, parent_id: 3, branch: 'a_path', move_to_branch: 'yes')
|
|
57
|
+
expect(stub).to have_been_requested
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe '#reorder' do
|
|
62
|
+
it 'sends POST /automations/{automation}/steps/reorder' do
|
|
63
|
+
stub = stub_api(:post, '/automations/1/steps/reorder', body: { 'dragged_step_id' => 2, 'new_parent_step_id' => 3, 'branch' => 'no', 'branch_for_child' => 'yes' })
|
|
64
|
+
steps.reorder(automation_id: 1, dragged_step_id: 2, new_parent_step_id: 3, branch: 'no', branch_for_child: 'yes')
|
|
65
|
+
expect(stub).to have_been_requested
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
describe '#select_ab_test_winner' do
|
|
70
|
+
it 'sends PUT /automations/{automation}/steps/{step}/select-ab-test-winner' do
|
|
71
|
+
stub = stub_api(:put, '/automations/1/steps/2/select-ab-test-winner', body: { 'ab_test_winner' => 'b_path' })
|
|
72
|
+
steps.select_ab_test_winner(automation_id: 1, step_id: 2, ab_test_winner: 'b_path')
|
|
73
|
+
expect(stub).to have_been_requested
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe MailerLite::AutomationTemplates do
|
|
6
|
+
let(:client) { MailerLite::Client.new }
|
|
7
|
+
let(:templates) { described_class.new(client: client) }
|
|
8
|
+
|
|
9
|
+
describe '#list' do
|
|
10
|
+
it 'sends GET /automation-templates with the query' do
|
|
11
|
+
stub = stub_api(:get, '/automation-templates', query: { 'filter[type]' => 'ecommerce', 'filter[category]' => '2', 'limit' => '10', 'page' => '3' })
|
|
12
|
+
templates.list(filter: { type: 'ecommerce', category: 2 }, limit: 10, page: 3)
|
|
13
|
+
expect(stub).to have_been_requested
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe '#select_template' do
|
|
18
|
+
it 'sends POST /automations/select-template/{automationTemplate}' do
|
|
19
|
+
stub = stub_api(:post, '/automations/select-template/welcome')
|
|
20
|
+
templates.select_template('welcome')
|
|
21
|
+
expect(stub).to have_been_requested
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe MailerLite::AutomationTriggers do
|
|
6
|
+
let(:client) { MailerLite::Client.new }
|
|
7
|
+
let(:triggers) { described_class.new(client: client) }
|
|
8
|
+
|
|
9
|
+
describe '#list' do
|
|
10
|
+
it 'sends GET /automations/{automation}/triggers' do
|
|
11
|
+
stub = stub_api(:get, '/automations/1/triggers')
|
|
12
|
+
triggers.list(1)
|
|
13
|
+
expect(stub).to have_been_requested
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe '#create' do
|
|
18
|
+
it 'sends POST /automations/{automation}/triggers' do
|
|
19
|
+
stub = stub_api(:post, '/automations/1/triggers', body: { 'type' => 'subscriber_joins_group', 'data' => { 'group_ids' => ['2'] } })
|
|
20
|
+
triggers.create(automation_id: 1, type: 'subscriber_joins_group', data: { 'group_ids' => ['2'] })
|
|
21
|
+
expect(stub).to have_been_requested
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe '#fetch' do
|
|
26
|
+
it 'sends GET /automations/{automation}/triggers/{trigger}' do
|
|
27
|
+
stub = stub_api(:get, '/automations/1/triggers/2')
|
|
28
|
+
triggers.fetch(automation_id: 1, trigger_id: 2)
|
|
29
|
+
expect(stub).to have_been_requested
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe '#update' do
|
|
34
|
+
it 'sends PUT /automations/{automation}/triggers/{trigger}' do
|
|
35
|
+
stub = stub_api(:put, '/automations/1/triggers/2', body: { 'data' => { 'group_ids' => ['3'] } })
|
|
36
|
+
triggers.update(automation_id: 1, trigger_id: 2, data: { 'group_ids' => ['3'] })
|
|
37
|
+
expect(stub).to have_been_requested
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe '#delete' do
|
|
42
|
+
it 'sends DELETE /automations/{automation}/triggers/{trigger}' do
|
|
43
|
+
stub = stub_api(:delete, '/automations/1/triggers/2')
|
|
44
|
+
triggers.delete(automation_id: 1, trigger_id: 2)
|
|
45
|
+
expect(stub).to have_been_requested
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|