mailgun-ruby 1.0.3 → 1.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/.gitignore +2 -0
- data/.rubocop.yml +8 -0
- data/.rubocop_todo.yml +22 -0
- data/.ruby-env.yml.example +12 -0
- data/.travis.yml +6 -12
- data/Domains.md +36 -0
- data/MessageBuilder.md +14 -14
- data/Messages.md +44 -30
- data/OptInHandler.md +34 -34
- data/README.md +74 -24
- data/Rakefile +22 -20
- data/Snippets.md +26 -26
- data/Webhooks.md +40 -0
- data/lib/mailgun.rb +26 -228
- data/lib/mailgun/chains.rb +16 -0
- data/lib/mailgun/client.rb +143 -0
- data/lib/mailgun/domains/domains.rb +84 -0
- data/lib/mailgun/events/events.rb +53 -35
- data/lib/mailgun/exceptions/exceptions.rb +43 -10
- data/lib/mailgun/lists/opt_in_handler.rb +18 -19
- data/lib/mailgun/messages/batch_message.rb +31 -48
- data/lib/mailgun/messages/message_builder.rb +160 -144
- data/lib/mailgun/response.rb +55 -0
- data/lib/mailgun/version.rb +2 -3
- data/lib/mailgun/webhooks/webhooks.rb +101 -0
- data/mailgun.gemspec +16 -10
- data/spec/integration/bounces_spec.rb +44 -0
- data/spec/integration/campaign_spec.rb +60 -0
- data/spec/integration/complaints_spec.rb +38 -0
- data/spec/integration/domains_spec.rb +39 -0
- data/spec/integration/email_validation_spec.rb +29 -0
- data/spec/integration/events_spec.rb +20 -0
- data/spec/integration/list_members_spec.rb +63 -0
- data/spec/integration/list_spec.rb +58 -0
- data/spec/integration/mailgun_spec.rb +26 -550
- data/spec/integration/routes_spec.rb +74 -0
- data/spec/integration/stats_spec.rb +15 -0
- data/spec/integration/unsubscribes_spec.rb +42 -0
- data/spec/integration/webhook_spec.rb +54 -0
- data/spec/spec_helper.rb +37 -7
- data/spec/unit/connection/test_client.rb +15 -95
- data/spec/unit/events/events_spec.rb +9 -6
- data/spec/unit/lists/opt_in_handler_spec.rb +6 -4
- data/spec/unit/mailgun_spec.rb +25 -19
- data/spec/unit/messages/batch_message_spec.rb +47 -38
- data/spec/unit/messages/message_builder_spec.rb +282 -111
- data/vcr_cassettes/bounces.yml +175 -0
- data/vcr_cassettes/complaints.yml +175 -0
- data/vcr_cassettes/domains.todo.yml +42 -0
- data/vcr_cassettes/domains.yml +360 -0
- data/vcr_cassettes/email_validation.yml +104 -0
- data/vcr_cassettes/events.yml +61 -0
- data/vcr_cassettes/list_members.yml +320 -0
- data/vcr_cassettes/mailing_list.todo.yml +43 -0
- data/vcr_cassettes/mailing_list.yml +390 -0
- data/vcr_cassettes/routes.yml +359 -0
- data/vcr_cassettes/send_message.yml +107 -0
- data/vcr_cassettes/stats.yml +44 -0
- data/vcr_cassettes/unsubscribes.yml +191 -0
- data/vcr_cassettes/webhooks.yml +276 -0
- metadata +114 -10
@@ -0,0 +1,55 @@
|
|
1
|
+
module Mailgun
|
2
|
+
# A Mailgun::Response object is instantiated for each response generated
|
3
|
+
# by the Client request. The Response object supports deserialization of
|
4
|
+
# the JSON result. Or, if you prefer JSON or YAML formatting, call the
|
5
|
+
# method for conversion.
|
6
|
+
#
|
7
|
+
# See the Github documentation for full examples.
|
8
|
+
class Response
|
9
|
+
# All responses have a payload and a code corresponding to http, though
|
10
|
+
# slightly different
|
11
|
+
attr_accessor :body, :code
|
12
|
+
|
13
|
+
def initialize(response)
|
14
|
+
@body = response.body
|
15
|
+
@code = response.code
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return response as Ruby Hash
|
19
|
+
#
|
20
|
+
# @return [Hash] A standard Ruby Hash containing the HTTP result.
|
21
|
+
|
22
|
+
def to_h
|
23
|
+
JSON.parse(@body)
|
24
|
+
rescue => err
|
25
|
+
raise ParseError.new(err), err
|
26
|
+
end
|
27
|
+
|
28
|
+
# Replace @body with Ruby Hash
|
29
|
+
#
|
30
|
+
# @return [Hash] A standard Ruby Hash containing the HTTP result.
|
31
|
+
def to_h!
|
32
|
+
@body = JSON.parse(@body)
|
33
|
+
rescue => err
|
34
|
+
raise ParseError.new(err), err
|
35
|
+
end
|
36
|
+
|
37
|
+
# Return response as Yaml
|
38
|
+
#
|
39
|
+
# @return [String] A string containing response as YAML
|
40
|
+
def to_yaml
|
41
|
+
YAML.dump(to_h)
|
42
|
+
rescue => err
|
43
|
+
raise ParseError.new(err), err
|
44
|
+
end
|
45
|
+
|
46
|
+
# Replace @body with YAML
|
47
|
+
#
|
48
|
+
# @return [String] A string containing response as YAML
|
49
|
+
def to_yaml!
|
50
|
+
@body = YAML.dump(to_h)
|
51
|
+
rescue => err
|
52
|
+
raise ParseError.new(err), err
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/mailgun/version.rb
CHANGED
@@ -0,0 +1,101 @@
|
|
1
|
+
module Mailgun
|
2
|
+
|
3
|
+
# A Mailgun::Webhooks object is a simple CRUD interface to Mailgun Webhooks.
|
4
|
+
# Uses Mailgun
|
5
|
+
class Webhooks
|
6
|
+
|
7
|
+
# Public creates a new Mailgun::Webhooks instance.
|
8
|
+
# Defaults to Mailgun::Client
|
9
|
+
def initialize(client = Mailgun::Client.new)
|
10
|
+
@client = client
|
11
|
+
end
|
12
|
+
|
13
|
+
# Public: Get Webhooks
|
14
|
+
#
|
15
|
+
# domain - a string the domain name to retrieve webhooks for
|
16
|
+
# options - a Hash of options
|
17
|
+
#
|
18
|
+
# Returns a Hash of the list of domains or nil
|
19
|
+
def list(domain, options = {})
|
20
|
+
res = @client.get("domains/#{domain}/webhooks", options)
|
21
|
+
res.to_h['webhooks']
|
22
|
+
end
|
23
|
+
alias_method :get_webhooks, :list
|
24
|
+
|
25
|
+
# Public: Get webook information for a specific action
|
26
|
+
#
|
27
|
+
# domain - a String of Domain name to find a webhook url for
|
28
|
+
# action - a String identifying the webhook to get the URL for
|
29
|
+
#
|
30
|
+
# Returns a String of the url for the identified webhook or an
|
31
|
+
# empty String if one is not set
|
32
|
+
def info(domain, action)
|
33
|
+
res = @client.get("domains/#{domain}/webhooks/#{action}")
|
34
|
+
res.to_h['webhook']['url'] || ''
|
35
|
+
rescue NoMethodError
|
36
|
+
''
|
37
|
+
end
|
38
|
+
alias_method :get_webhook_url, :info
|
39
|
+
|
40
|
+
# Public: Add webhook
|
41
|
+
#
|
42
|
+
# domain - A String of the domain name (ex. domain.com)
|
43
|
+
# action - A String of the action to create a webhook for
|
44
|
+
# url - A String of the url of the webhook
|
45
|
+
#
|
46
|
+
# Returns a Boolean of whether the webhook was created
|
47
|
+
def create(domain, action, url = '')
|
48
|
+
res = @client.post("domains/#{domain}/webhooks", id: action, url: url)
|
49
|
+
res.to_h['webhook'] == url && res.to_h[message] == 'Webhook has been created'
|
50
|
+
end
|
51
|
+
alias_method :add, :create
|
52
|
+
alias_method :add_webhook, :create
|
53
|
+
|
54
|
+
# Public: Sets all webhooks to the same URL
|
55
|
+
#
|
56
|
+
# domain - A String of the domain name
|
57
|
+
# url - A String of the url to set all webhooks to
|
58
|
+
#
|
59
|
+
# Returns true or false
|
60
|
+
def create_all(domain, url = '')
|
61
|
+
%w(bounce click deliver drop open spam unsubscribe).each do |action|
|
62
|
+
add_webhook domain, action, url
|
63
|
+
end
|
64
|
+
true
|
65
|
+
rescue
|
66
|
+
false
|
67
|
+
end
|
68
|
+
alias_method :add_all_webhooks, :create_all
|
69
|
+
|
70
|
+
# Public: Delete a specific webhook
|
71
|
+
#
|
72
|
+
# domain - The required String of domain name
|
73
|
+
# action - The required String of the webhook action to delete
|
74
|
+
#
|
75
|
+
# Returns a Boolean of the success
|
76
|
+
def remove(domain, action)
|
77
|
+
fail Mailgun::ParameterError('Domain not provided to remove webhook from') unless domain
|
78
|
+
fail Mailgun::ParameterError('Action not provided to identify webhook to remove') unless action
|
79
|
+
@client.delete("domains/#{domain}/webhooks/#{action}").to_h['message'] == 'Webhook has been deleted'
|
80
|
+
rescue Mailgun::CommunicationError
|
81
|
+
false
|
82
|
+
end
|
83
|
+
alias_method :delete, :remove
|
84
|
+
alias_method :delete_webhook, :remove
|
85
|
+
|
86
|
+
# Public: Delete all webhooks for a domain
|
87
|
+
#
|
88
|
+
# domain - A required String of the domain to remove all webhooks for
|
89
|
+
#
|
90
|
+
# Returns a Boolean on the success
|
91
|
+
def remove_all(domain)
|
92
|
+
fail Mailgun::ParameterError('Domain not provided to remove webhooks from') unless domain
|
93
|
+
%w(bounce click deliver drop open spam unsubscribe).each do |action|
|
94
|
+
delete_webhook domain, action
|
95
|
+
end
|
96
|
+
end
|
97
|
+
alias_method :delete_all, :remove_all
|
98
|
+
alias_method :delete_all_webooks, :remove_all
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
data/mailgun.gemspec
CHANGED
@@ -5,28 +5,34 @@ require 'mailgun/version'
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
|
8
|
-
spec.name =
|
8
|
+
spec.name = 'mailgun-ruby'
|
9
9
|
spec.version = Mailgun::VERSION
|
10
|
-
spec.homepage =
|
10
|
+
spec.homepage = 'http://www.mailgun.com'
|
11
11
|
spec.platform = Gem::Platform::RUBY
|
12
12
|
spec.license = 'Apache'
|
13
13
|
|
14
14
|
spec.summary = "Mailgun's Official Ruby SDK"
|
15
15
|
spec.description = "Mailgun's Official Ruby SDK for interacting with the Mailgun API."
|
16
16
|
|
17
|
-
spec.authors = [
|
18
|
-
spec.email =
|
17
|
+
spec.authors = ['Mailgun', 'Travis Swientek']
|
18
|
+
spec.email = 'support@mailgunhq.com'
|
19
19
|
|
20
|
-
spec.files = `git ls-files`.split(
|
20
|
+
spec.files = `git ls-files -z`.split("\x0")
|
21
21
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
22
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
23
|
spec.require_paths = ["lib"]
|
24
24
|
|
25
|
-
spec.
|
26
|
-
spec.add_development_dependency "rspec", "~> 2.14"
|
27
|
-
spec.add_development_dependency "rake", "~> 10.1"
|
25
|
+
spec.required_ruby_version = '>= 1.9.3'
|
28
26
|
|
29
|
-
spec.
|
30
|
-
spec.
|
27
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
29
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
30
|
+
spec.add_development_dependency 'webmock', '~> 1.22'
|
31
|
+
spec.add_development_dependency 'pry', '~> 0.9'
|
32
|
+
spec.add_development_dependency 'vcr', '~> 3.0'
|
33
|
+
spec.add_development_dependency 'simplecov', '~> 0.11'
|
34
|
+
|
35
|
+
spec.add_dependency 'rest-client', '~> 1.6'
|
36
|
+
spec.add_dependency 'json', '~> 1.8'
|
31
37
|
|
32
38
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mailgun'
|
3
|
+
|
4
|
+
vcr_opts = { :cassette_name => "bounces" }
|
5
|
+
|
6
|
+
describe 'For the Bounces endpoint', order: :defined, vcr: vcr_opts do
|
7
|
+
before(:all) do
|
8
|
+
@mg_obj = Mailgun::Client.new(APIKEY, APIHOST, APIVERSION, SSL)
|
9
|
+
@domain = TESTDOMAIN
|
10
|
+
@email = "integration-test-email@#{TESTDOMAIN}"
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'creates a bounce' do
|
14
|
+
@result = @mg_obj.post("#{@domain}/bounces",
|
15
|
+
{:address => @email,
|
16
|
+
:code => 550,
|
17
|
+
:error => "Integration Test"})
|
18
|
+
|
19
|
+
@result.to_h!
|
20
|
+
expect(@result.body["message"]).to eq("Address has been added to the bounces table")
|
21
|
+
expect(@result.body["address"]).to eq(@email)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'get a bounce.' do
|
25
|
+
result = @mg_obj.get("#{@domain}/bounces/#{@email}")
|
26
|
+
|
27
|
+
result.to_h!
|
28
|
+
expect(result.body["code"]).to eq("550")
|
29
|
+
expect(result.body["address"]).to eq(@email)
|
30
|
+
expect(result.body["error"]).to eq("Integration Test")
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'gets a list of bounces.' do
|
34
|
+
result = @mg_obj.get("#{@domain}/bounces")
|
35
|
+
|
36
|
+
result.to_h!
|
37
|
+
expect(result.body["items"].length).to be > 0
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'deletes a bounce' do
|
41
|
+
@mg_obj.delete("#{@domain}/bounces/#{@email}")
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mailgun'
|
3
|
+
|
4
|
+
vcr_opts = { :cassette_name => "campaigns" }
|
5
|
+
|
6
|
+
describe 'For the campaigns endpoint', vcr: vcr_opts do
|
7
|
+
before(:all) do
|
8
|
+
skip 'pending removal'
|
9
|
+
@mg_obj = Mailgun::Client.new(APIKEY, APIHOST, APIVERSION, SSL)
|
10
|
+
@domain = TESTDOMAIN
|
11
|
+
@campaign_id = "integration_test_campaign"
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'creates a campaign' do
|
15
|
+
result = @mg_obj.post("#{@domain}/campaigns", {:name => 'My Campaign',
|
16
|
+
:id => @campaign_id})
|
17
|
+
|
18
|
+
result.to_h!
|
19
|
+
expect(result.body["message"]).to eq("Campaign created")
|
20
|
+
expect(result.body["campaign"]["id"]).to eq(@campaign_id)
|
21
|
+
expect(result.body["campaign"]["name"]).to eq('My Campaign')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'get a campaign.' do
|
25
|
+
result = @mg_obj.get("#{@domain}/campaigns/#{@campaign_id}")
|
26
|
+
|
27
|
+
result.to_h!
|
28
|
+
expect(result.body["id"]).to eq(@campaign_id)
|
29
|
+
expect(result.body["name"]).to eq('My Campaign')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'gets a list of all campaigns.' do
|
33
|
+
result = @mg_obj.get("#{@domain}/campaigns", {:limit => 50})
|
34
|
+
|
35
|
+
result.to_h!
|
36
|
+
expect(result.body["total_count"]).to be > 0
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'update a campaign.' do
|
40
|
+
result = @mg_obj.put("#{@domain}/campaigns/#{@campaign_id}", {:name => 'My Updated Campaign',
|
41
|
+
:id => @campaign_id})
|
42
|
+
|
43
|
+
result.to_h!
|
44
|
+
expect(result.body["message"]).to eq("Campaign updated")
|
45
|
+
expect(result.body["campaign"]["id"]).to eq(@campaign_id)
|
46
|
+
expect(result.body["campaign"]["name"]).to eq('My Updated Campaign')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'get campaign events.' do
|
50
|
+
expect{@mg_obj.get("#{@domain}/campaigns/#{@campaign_id}/events", {:groupby => "clicked"})}.not_to raise_error
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'get campaign stats.' do
|
54
|
+
expect{@mg_obj.get("#{@domain}/campaigns/#{@campaign_id}/stats", {:groupby => "domain"})}.not_to raise_error
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'removes a campaign' do
|
58
|
+
@mg_obj.delete("#{@domain}/campaigns/#{@campaign_id}")
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mailgun'
|
3
|
+
|
4
|
+
vcr_opts = { :cassette_name => "complaints" }
|
5
|
+
|
6
|
+
describe 'For the Complaints endpoint', order: :defined, vcr: vcr_opts do
|
7
|
+
before(:all) do
|
8
|
+
@mg_obj = Mailgun::Client.new(APIKEY, APIHOST, APIVERSION, SSL)
|
9
|
+
@domain = TESTDOMAIN
|
10
|
+
@email = "integration-test-email@#{TESTDOMAIN}"
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'creates a complaint' do
|
14
|
+
@result = @mg_obj.post("#{@domain}/complaints", {:address => @email})
|
15
|
+
|
16
|
+
@result.to_h!
|
17
|
+
expect(@result.body["message"]).to eq("Address has been added to the complaints table")
|
18
|
+
expect(@result.body["address"]).to eq(@email)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'get a complaint.' do
|
22
|
+
result = @mg_obj.get("#{@domain}/complaints/#{@email}")
|
23
|
+
|
24
|
+
result.to_h!
|
25
|
+
expect(result.body["address"]).to eq(@email)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'gets a list of complaints.' do
|
29
|
+
result = @mg_obj.get("#{@domain}/complaints")
|
30
|
+
|
31
|
+
result.to_h!
|
32
|
+
expect(result.body["items"].length).to be > 0
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'removes a complaint' do
|
36
|
+
@mg_obj.delete("#{@domain}/complaints/#{@email}")
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mailgun'
|
3
|
+
|
4
|
+
vcr_opts = { :cassette_name => "domains" }
|
5
|
+
|
6
|
+
describe 'For the domains endpoint', vcr: vcr_opts do
|
7
|
+
before(:all) do
|
8
|
+
@mg_client = Mailgun::Client.new(APIKEY, APIHOST, APIVERSION, SSL)
|
9
|
+
@mg_obj = Mailgun::Domains.new(@mg_client)
|
10
|
+
@domain = "integration-test.domain.invalid"
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'creates the domain' do
|
14
|
+
result = @mg_obj.add_domain(@domain, { smtp_password: 'super_secret', spam_action: 'tag' })
|
15
|
+
|
16
|
+
expect(result['domain']["name"]).to eq(@domain)
|
17
|
+
expect(result['domain']["spam_action"]).to eq("tag")
|
18
|
+
expect(result['domain']["smtp_password"]).to eq("super_secret")
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'get the domain.' do
|
22
|
+
result = @mg_obj.get(@domain)
|
23
|
+
|
24
|
+
expect(result).to include("domain")
|
25
|
+
expect(result["domain"]["name"]).to eq(@domain)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'gets a list of domains.' do
|
29
|
+
result = @mg_obj.get_domains
|
30
|
+
|
31
|
+
expect(result.size).to be > 0
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'deletes a domain.' do
|
35
|
+
result = @mg_obj.delete(@domain)
|
36
|
+
|
37
|
+
expect(result).to be_truthy
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mailgun'
|
3
|
+
|
4
|
+
vcr_opts = { :cassette_name => "email_validation" }
|
5
|
+
|
6
|
+
describe 'For the Email Validation endpoint', vcr: vcr_opts do
|
7
|
+
before(:all) do
|
8
|
+
@mg_obj = Mailgun::Client.new(PUB_APIKEY)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'validates an address.' do
|
12
|
+
result = @mg_obj.get("address/validate",
|
13
|
+
{:address => "test@#{TESTDOMAIN}"})
|
14
|
+
|
15
|
+
result.to_h!
|
16
|
+
expect(result.body["is_valid"]).to eq(true)
|
17
|
+
expect(result.body["address"]).to eq("test@#{TESTDOMAIN}")
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'parses an address.' do
|
21
|
+
result = @mg_obj.get("address/parse",
|
22
|
+
{:addresses => "Alice <alice@#{TESTDOMAIN}>,bob@#{TESTDOMAIN},#{TESTDOMAIN}"})
|
23
|
+
|
24
|
+
result.to_h!
|
25
|
+
expect(result.body["parsed"]).to include("Alice <alice@#{TESTDOMAIN}>")
|
26
|
+
expect(result.body["parsed"]).to include("bob@#{TESTDOMAIN}")
|
27
|
+
expect(result.body["unparseable"]).to include("#{TESTDOMAIN}")
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mailgun'
|
3
|
+
|
4
|
+
vcr_opts = { :cassette_name => "events" }
|
5
|
+
|
6
|
+
describe 'For the Events endpoint', vcr: vcr_opts do
|
7
|
+
before(:all) do
|
8
|
+
@mg_obj = Mailgun::Client.new(APIKEY, APIHOST, APIVERSION, SSL)
|
9
|
+
@domain = TESTDOMAIN
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'get an event.' do
|
13
|
+
result = @mg_obj.get("#{@domain}/events", {:limit => 1})
|
14
|
+
|
15
|
+
result.to_h!
|
16
|
+
expect(result.body["items"].length).to be_within(1).of(1)
|
17
|
+
expect(result.body["paging"]).to include("next")
|
18
|
+
expect(result.body["paging"]).to include("previous")
|
19
|
+
end
|
20
|
+
end
|