akamai_api 0.4.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +2 -6
  4. data/Gemfile +1 -0
  5. data/README.md +6 -11
  6. data/akamai_api.gemspec +1 -0
  7. data/cassettes/akamai_api_ccu_arl_invalidate/invalid_credentials.yml +32 -21
  8. data/cassettes/akamai_api_ccu_arl_invalidate/invalid_item.yml +2 -2
  9. data/cassettes/akamai_api_ccu_arl_invalidate/multiple_items.yml +4 -5
  10. data/cassettes/akamai_api_ccu_arl_invalidate/single_item.yml +4 -5
  11. data/cassettes/akamai_api_ccu_arl_remove/invalid_credentials.yml +30 -19
  12. data/cassettes/akamai_api_ccu_arl_remove/invalid_item.yml +1 -1
  13. data/cassettes/akamai_api_ccu_arl_remove/multiple_items.yml +1 -1
  14. data/cassettes/akamai_api_ccu_arl_remove/single_item.yml +1 -1
  15. data/cassettes/akamai_api_ccu_cpcode_invalidate/invalid_credentials.yml +31 -20
  16. data/cassettes/akamai_api_ccu_cpcode_invalidate/invalid_item.yml +1 -1
  17. data/cassettes/akamai_api_ccu_cpcode_invalidate/multiple_items.yml +1 -1
  18. data/cassettes/akamai_api_ccu_cpcode_invalidate/single_item.yml +1 -1
  19. data/cassettes/akamai_api_ccu_cpcode_remove/invalid_credentials.yml +31 -20
  20. data/cassettes/akamai_api_ccu_cpcode_remove/invalid_item.yml +1 -1
  21. data/cassettes/akamai_api_ccu_cpcode_remove/multiple_items.yml +1 -1
  22. data/cassettes/akamai_api_ccu_cpcode_remove/single_item.yml +1 -1
  23. data/cassettes/akamai_api_ccu_purge_status/completed_request.yml +4 -1
  24. data/cassettes/akamai_api_ccu_purge_status/enqueued_request.yml +1 -1
  25. data/cassettes/akamai_api_ccu_purge_status/invalid_credentials.yml +4 -1
  26. data/cassettes/akamai_api_ccu_purge_status/not_found_request.yml +4 -1
  27. data/cassettes/akamai_api_ccu_status/invalid_credentials.yml +1 -1
  28. data/cassettes/akamai_api_ccu_status/valid_credentials.yml +1 -1
  29. data/cassettes/akamai_api_eccu_all_ids/invalid_credentials.yml +3 -1
  30. data/cassettes/akamai_api_eccu_all_ids/successful.yml +3 -1
  31. data/cassettes/akamai_api_eccu_destroy/invalid_credentials.yml +10 -8
  32. data/cassettes/akamai_api_eccu_destroy/not_found_request.yml +3 -1
  33. data/cassettes/akamai_api_eccu_destroy/successful.yml +4 -2
  34. data/cassettes/akamai_api_eccu_find/invalid_credentials.yml +9 -7
  35. data/cassettes/akamai_api_eccu_find/not_found_request.yml +3 -1
  36. data/cassettes/akamai_api_eccu_find/successful.yml +4 -2
  37. data/cassettes/akamai_api_eccu_find/successful_without_content.yml +4 -2
  38. data/cassettes/akamai_api_eccu_last_request/invalid_credentials.yml +1 -1
  39. data/cassettes/akamai_api_eccu_last_request/valid_credentials.yml +6 -2
  40. data/cassettes/akamai_api_eccu_publish/invalid_credentials.yml +12 -10
  41. data/cassettes/akamai_api_eccu_publish/invalid_domain.yml +4 -2
  42. data/cassettes/akamai_api_eccu_publish/successful.yml +7 -3
  43. data/cassettes/akamai_api_eccu_requests/invalid_credentials.yml +3 -1
  44. data/cassettes/akamai_api_eccu_requests/valid_credentials.yml +15 -5
  45. data/cassettes/akamai_api_eccu_update_email/invalid_credentials.yml +11 -9
  46. data/cassettes/akamai_api_eccu_update_email/not_found_request.yml +3 -1
  47. data/cassettes/akamai_api_eccu_update_email/successful.yml +4 -2
  48. data/cassettes/akamai_api_eccu_update_notes/invalid_credentials.yml +11 -9
  49. data/cassettes/akamai_api_eccu_update_notes/not_found_request.yml +3 -1
  50. data/cassettes/akamai_api_eccu_update_notes/successful.yml +4 -2
  51. data/features/ccu_arl_invalidate.feature +1 -25
  52. data/features/support/env.rb +7 -2
  53. data/lib/akamai_api.rb +8 -2
  54. data/lib/akamai_api/ccu/purge/request.rb +52 -12
  55. data/lib/akamai_api/cli/ccu/arl.rb +8 -23
  56. data/lib/akamai_api/cli/ccu/purge_renderer.rb +5 -8
  57. data/lib/akamai_api/cli/command.rb +14 -29
  58. data/lib/akamai_api/version.rb +1 -1
  59. data/spec/features/ccu/purge_request_spec.rb +5 -2
  60. data/spec/lib/akamai_api/ccu/purge/request_spec.rb +29 -46
  61. data/spec/lib/akamai_api/ccu_spec.rb +0 -18
  62. data/spec/spec_helper.rb +8 -2
  63. metadata +17 -10
  64. data/spec/features/ccu/purge_status_request_spec.rb +0 -31
  65. data/spec/features/ccu/status_request_spec.rb +0 -17
  66. data/spec/lib/akamai_api/cli/ccu/arl_spec.rb +0 -40
@@ -1,5 +1,3 @@
1
- require "httparty"
2
-
3
1
  require 'active_support'
4
2
  require 'active_support/core_ext/array'
5
3
 
@@ -7,6 +5,11 @@ require "akamai_api/unauthorized"
7
5
  require "akamai_api/ccu/unrecognized_option"
8
6
  require "akamai_api/ccu/purge/response"
9
7
 
8
+ require "akamai/edgegrid"
9
+ require "net/http"
10
+ require "uri"
11
+ require "json"
12
+
10
13
  module AkamaiApi::CCU::Purge
11
14
  # {AkamaiApi::CCU::Purge} encapsulates the behavior needed to purge a resource from Akamai via CCU.
12
15
  #
@@ -15,10 +18,7 @@ module AkamaiApi::CCU::Purge
15
18
  # @example Invalidate multiple CPCodes
16
19
  # AkamaiApi::CCU::Purge::Request.new(:invalidate, :cpcode).execute(12345, 12346)
17
20
  class Request
18
- include HTTParty
19
- format :json
20
- base_uri 'https://api.ccu.akamai.com/ccu/v2/queues/default'
21
- headers 'Content-Type' => 'application/json'
21
+ @@headers = { "Content-Type" => "application/json" }
22
22
 
23
23
  attr_reader :type, :action, :domain
24
24
 
@@ -72,9 +72,25 @@ module AkamaiApi::CCU::Purge
72
72
  # @example Clean multiple resources
73
73
  # request.execute '12345', '12346'
74
74
  def execute *items
75
+ akamai = Akamai::Edgegrid::HTTP.new(address=baseuri.host, port=baseuri.port)
76
+ akamai.setup_edgegrid(creds)
77
+ http = Net::HTTP.new(address=baseuri.host, port=baseuri.port)
78
+ http.use_ssl = true
79
+
75
80
  items = Array.wrap(items.first) if items.length == 1
76
- response = self.class.post('/', basic_auth: AkamaiApi.auth, body: request_body(items))
77
- parse_response response
81
+ req = Net::HTTP::Post.new(resource, initheader = @@headers).tap do |pq|
82
+ if for_ccu_v2?
83
+ pq.body = request_body items
84
+ else
85
+ pq.body = {"objects" => items}.to_json
86
+ end
87
+ end
88
+
89
+ timestamp = Akamai::Edgegrid::HTTP.eg_timestamp()
90
+ nonce = Akamai::Edgegrid::HTTP.new_nonce()
91
+ req['Authorization'] = akamai.make_auth_header(req, timestamp, nonce)
92
+
93
+ parse_response http.request(req)
78
94
  end
79
95
 
80
96
  # Request body to send to the API.
@@ -85,11 +101,35 @@ module AkamaiApi::CCU::Purge
85
101
  end
86
102
 
87
103
  private
104
+ def creds
105
+ AkamaiApi.auth.tap do |auth|
106
+ auth.delete(:base_url)
107
+ auth[:max_body] = 128*1024
108
+ end
109
+ end
110
+
111
+ def for_ccu_v2?
112
+ @type == :cpcode || (@type == :arl && @action == :remove)
113
+ end
114
+
115
+ def resource
116
+ if for_ccu_v2?
117
+ URI.join(baseuri.to_s, "/ccu/v2/queues/default").to_s
118
+ else
119
+ URI.join(baseuri.to_s, "/ccu/v3/#{action}/url/#{domain}").to_s
120
+ end
121
+ end
122
+
123
+ def baseuri
124
+ URI(AkamaiApi.auth[:base_url])
125
+ end
88
126
 
89
127
  def parse_response response
90
- raise ::AkamaiApi::Unauthorized if response.code == 401
91
- raise AkamaiApi::CCU::Error.new response.parsed_response unless successful_response? response
92
- Response.new response.parsed_response
128
+ parsed_response = JSON.load(response.body)
129
+
130
+ raise ::AkamaiApi::Unauthorized if ["400", 401].include?(response.code)
131
+ raise AkamaiApi::CCU::Error.new parsed_response unless successful_response? parsed_response
132
+ Response.new parsed_response
93
133
  end
94
134
 
95
135
  def raise_unrecognized_action bad_action
@@ -117,7 +157,7 @@ module AkamaiApi::CCU::Purge
117
157
  end
118
158
 
119
159
  def successful_response? response
120
- (200...300).include? response.parsed_response['httpStatus']
160
+ (200...300).include? response['httpStatus']
121
161
  end
122
162
  end
123
163
  end
@@ -22,10 +22,6 @@ module AkamaiApi::CLI::CCU
22
22
  :desc => 'Optional argument used to specify the environment. Usually you will not need this option'
23
23
  method_option :banner => "foo@foo.com bar@bar.com",
24
24
  :desc => 'Email(s) used to send notification when the purge has been completed'
25
- method_option :poll,
26
- :desc => 'whether or not to poll for status',
27
- :type => :boolean,
28
- :default => false
29
25
 
30
26
  def invalidate(*arls)
31
27
  purge_action :invalidate, arls
@@ -34,32 +30,21 @@ module AkamaiApi::CLI::CCU
34
30
  no_tasks do
35
31
  def purge_action type, arls
36
32
  raise 'You should provide at least one valid URL' if arls.blank?
37
- load_config
33
+
34
+ begin
35
+ load_config
36
+ rescue ArgumentError
37
+ return
38
+ end
39
+
38
40
  res = AkamaiApi::CCU.purge type, :arl, arls, :domain => options[:domain]
39
41
  puts PurgeRenderer.new(res).render
40
42
 
41
- if options[:poll] == true && res.code == 201
42
- poll_status res
43
- end
44
- rescue AkamaiApi::CCU::Error
43
+ rescue AkamaiApi::CCU::Error => e
45
44
  puts StatusRenderer.new($!).render_error
46
45
  rescue AkamaiApi::Unauthorized
47
46
  puts 'Your login credentials are invalid.'
48
47
  end
49
48
  end
50
-
51
- no_commands do
52
- def poll_status purge_response
53
- if purge_response.time_to_wait
54
- status = AkamaiApi::CCU.status purge_response.uri
55
- while status.completed_at.nil?
56
- puts StatusRenderer.new(status).render
57
- sleep 60
58
- status = AkamaiApi::CCU.status purge_response.uri
59
- end
60
- end
61
- puts StatusRenderer.new(status).render
62
- end
63
- end
64
49
  end
65
50
  end
@@ -34,15 +34,12 @@ module AkamaiApi::CLI::CCU
34
34
  result = [
35
35
  "Purge request successfully submitted:",
36
36
  "\t* Result: #{response.code} - #{response.message}",
37
- "\t* Purge ID: #{response.purge_id} | Support ID: #{response.support_id}"
37
+ "\t* Purge ID: #{response.purge_id} | Support ID: #{response.support_id}",
38
+ "\t* Estimated time: #{response.estimated_time} secs.",
39
+ "\t* Progress URI: #{response.uri}",
40
+ "\t* Time to wait before check: #{response.time_to_wait} secs."
38
41
  ]
39
- if response.time_to_wait
40
- result.concat [
41
- "\t* Estimated time: #{response.estimated_time} secs.",
42
- "\t* Progress URI: #{response.uri}",
43
- "\t* Time to wait before check: #{response.time_to_wait} secs.",
44
- ]
45
- end
42
+
46
43
  result
47
44
  end
48
45
  end
@@ -11,12 +11,10 @@ module AkamaiApi::CLI
11
11
 
12
12
  no_tasks do
13
13
  def load_config
14
+ return if AkamaiApi.auth_ok?
14
15
  load_config_from_file
15
- load_config_from_env
16
- load_config_from_options
17
16
  if AkamaiApi.auth_empty?
18
17
  render_auth_info
19
- exit 1
20
18
  end
21
19
  end
22
20
 
@@ -27,37 +25,24 @@ module AkamaiApi::CLI
27
25
  def load_config_from_file
28
26
  if File.exists?(config_file)
29
27
  AkamaiApi.config.merge! YAML::load_file(config_file).symbolize_keys
30
- end
31
- end
32
-
33
- def load_config_from_options
34
- AkamaiApi.config[:auth].tap do |auth|
35
- auth[0] = options.fetch 'username', auth[0]
36
- auth[1] = options.fetch 'password', auth[1]
37
- end
38
- end
39
-
40
- def load_config_from_env
41
- AkamaiApi.config[:auth].tap do |auth|
42
- auth[0] = ENV.fetch 'AKAMAI_USERNAME', auth[0]
43
- auth[1] = ENV.fetch 'AKAMAI_PASSWORD', auth[1]
28
+ else
29
+ render_auth_info
30
+ raise ArgumentError
44
31
  end
45
32
  end
46
33
 
47
34
  def render_auth_info
48
35
  puts <<-OUTPUT
49
- No authentication config found. You can specify auth credentials with one of the following methods:"
50
-
51
- * Creating a file in your home directory named `.akamai_api.yml` with the following content:"
52
- auth:"
53
- - my_username"
54
- - my_password"
55
-
56
- * Using the environment variables AKAMAI_USERNAME and AKAMAI_PASSWORD. E.g:"
57
- AKAMAI_USERNAME=my_username AKAMAI_PASSWORD=my_password akamai_api ECCU last_request"
58
-
59
- * Passing username and password options from command line. E.g.:"
60
- akamai_api ECCU last_request -u my_username -p my_password"
36
+ No authentication config found. At the very least, specify auth credentials by creating a file in your home directory named `.akamai_api.yml` with the following content:"
37
+ auth:
38
+ - my_username
39
+ - my_password
40
+ If using "ccu arl invalidate", your CCU api credentials should also be added:
41
+ openapi:
42
+ base_url:
43
+ client_token:
44
+ client_secret:
45
+ access_token:
61
46
  OUTPUT
62
47
  end
63
48
  end
@@ -1,3 +1,3 @@
1
1
  module AkamaiApi
2
- VERSION = "0.4.1"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -3,6 +3,8 @@ require 'spec_helper'
3
3
  describe 'Given I request to purge an asset' do
4
4
  subject { AkamaiApi::CCU }
5
5
 
6
+ let(:estimated_time) { 420 }
7
+
6
8
  shared_examples 'purge helper' do
7
9
  let(:method) { "#{action}_#{type}" }
8
10
 
@@ -14,7 +16,6 @@ describe 'Given I request to purge an asset' do
14
16
 
15
17
  it 'raises error when user is not authorized' do
16
18
  VCR.use_cassette "akamai_api_ccu_#{type}_#{action}/invalid_credentials" do
17
- allow(AkamaiApi).to receive(:auth) { { username: 'foo', password: 'bar' } }
18
19
  expect { subject.send(method, items) }.to raise_error AkamaiApi::Unauthorized
19
20
  end
20
21
  end
@@ -52,7 +53,7 @@ describe 'Given I request to purge an asset' do
52
53
 
53
54
  it 'returns the estimated time in seconds' do
54
55
  VCR.use_cassette "akamai_api_ccu_#{type}_#{action}/single_item" do
55
- expect(subject.send(method, items).estimated_time).to eq 420
56
+ expect(subject.send(method, items).estimated_time).to eq estimated_time
56
57
  end
57
58
  end
58
59
  end
@@ -63,6 +64,8 @@ describe 'Given I request to purge an asset' do
63
64
  let(:type) { 'arl' }
64
65
  let(:items) { ['http://www.foo.bar/t.txt'] }
65
66
 
67
+ let(:estimated_time) { 5 }
68
+
66
69
  it_should_behave_like 'purge helper'
67
70
  end
68
71
 
@@ -1,18 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe AkamaiApi::CCU::Purge::Request do
4
- it "includes httparty" do
5
- expect(subject.class.included_modules).to include HTTParty
6
- end
7
-
8
- it "sets a base_uri" do
9
- expect(subject.class.base_uri).to eq 'https://api.ccu.akamai.com/ccu/v2/queues/default'
10
- end
11
-
12
- it "sets a content type header" do
13
- expect(subject.class.headers).to eq 'Content-Type' => 'application/json'
14
- end
15
-
16
4
  describe "#action" do
17
5
  it "is :remove by default" do
18
6
  expect(subject.action).to eq :remove
@@ -59,59 +47,54 @@ describe AkamaiApi::CCU::Purge::Request do
59
47
  let(:fake_response) { double code: 201, parsed_response: { 'httpStatus' => 201, 'submissionTime' => 1 } }
60
48
  let(:sample_arl) { 'http://www.foo.bar/t.txt' }
61
49
 
50
+ before(:each) do
51
+ allow_any_instance_of(Net::HTTP).to receive(:request).and_return(double code: 201, body: '{ "httpStatus" : 201}')
52
+ allow_any_instance_of(Akamai::Edgegrid::HTTP).to receive(:make_auth_header)
53
+ end
54
+
62
55
  it "executes a post on the base url" do
63
- expect(subject.class).to receive :post do |path, args|
64
- expect(path).to eq '/'
65
- fake_response
66
- end
56
+ expect_any_instance_of(Net::HTTP).to receive(:request).with an_instance_of(Net::HTTP::Post)
67
57
  subject.execute sample_arl
68
58
  end
69
59
 
70
60
  it "sets the auth in the post" do
71
- allow(AkamaiApi).to receive(:auth) { 'foo' }
72
- expect(subject.class).to receive :post do |path, args|
73
- expect(args[:basic_auth]).to eq 'foo'
74
- fake_response
75
- end
61
+ expect_any_instance_of(Akamai::Edgegrid::HTTP).to receive(:setup_edgegrid).with({:username => 'USERNAME', :password => 'PASSWORD', :max_body=>131072, :client_token => "client_token", :client_secret => "client_secret", :access_token => "access_token"})
76
62
  subject.execute sample_arl
77
63
  end
78
64
 
79
- it "accepts a single element" do
80
- expect(subject).to receive(:request_body).with([sample_arl])
81
- allow(subject.class).to receive(:post) { fake_response }
65
+ it "sets headers and base uri" do
66
+ fake_net_http_post = double
67
+ allow(fake_net_http_post).to receive(:body=)
68
+ allow(fake_net_http_post).to receive(:[]=)
69
+ expect(Net::HTTP::Post).to receive(:new) do |base_uri, initheaders|
70
+ expect(base_uri).to eq "https://some-subdomain.purge.akamaiapis.net/ccu/v2/queues/default"
71
+ expect(initheaders)
72
+ fake_net_http_post
73
+ end
82
74
  subject.execute sample_arl
83
75
  end
84
76
 
85
- it "accepts a collection with a single element" do
86
- expect(subject).to receive(:request_body).with([sample_arl])
87
- allow(subject.class).to receive(:post) { fake_response }
77
+ it "accepts a single element" do
78
+ require "json"
79
+ expect_any_instance_of(Net::HTTP::Post).to receive(:body=).with({type: :arl, action: :remove, domain: :production, objects: [ sample_arl ] }.to_json)
88
80
  subject.execute sample_arl
89
81
  end
90
82
 
91
83
  it "accepts a collection" do
92
- expect(subject).to receive(:request_body).with(['a', 'b'])
93
- allow(subject.class).to receive(:post) { fake_response }
94
- subject.execute ['a', 'b']
84
+ require "json"
85
+ expect_any_instance_of(Net::HTTP::Post).to receive(:body=).with({type: :arl, action: :remove, domain: :production, objects: ["a", "b"]}.to_json)
86
+ subject.execute ["a", "b"]
95
87
  end
96
88
 
97
89
  it "works with splatting" do
98
- expect(subject).to receive(:request_body).with(['a', 'b'])
99
- allow(subject.class).to receive(:post) { fake_response }
100
- subject.execute 'a', 'b'
101
- end
102
-
103
- it "sets the request body" do
104
- expect(subject).to receive(:request_body).with([sample_arl]).and_return 'foo'
105
- expect(subject.class).to receive :post do |path, args|
106
- expect(args[:body]).to eq 'foo'
107
- fake_response
108
- end
109
- subject.execute sample_arl
90
+ require "json"
91
+ expect_any_instance_of(Net::HTTP::Post).to receive(:body=).with({type: :arl, action: :remove, domain: :production, objects: ["a", "b"]}.to_json)
92
+ subject.execute "a", "b"
110
93
  end
111
94
 
112
95
  it "raises an exception when the response code is 401" do
113
- fake_response = double code: 401
114
- allow(subject.class).to receive(:post) { fake_response }
96
+ fake_response = double code: 401, body: '{ "httpStatus" : 401}'
97
+ allow_any_instance_of(Net::HTTP).to receive(:request) { fake_response }
115
98
  expect { subject.execute sample_arl }.to raise_error AkamaiApi::Unauthorized
116
99
  end
117
100
 
@@ -122,8 +105,8 @@ describe AkamaiApi::CCU::Purge::Request do
122
105
  end
123
106
 
124
107
  it "raises an error when json code in response is not successful" do
125
- fake_response = double code: 201, parsed_response: { 'httpStatus' => 403 }
126
- allow(subject.class).to receive(:post) { fake_response }
108
+ fake_response = double code: 403, body: '{ "httpStatus" : 403 }'
109
+ allow_any_instance_of(Net::HTTP).to receive(:request) { fake_response }
127
110
  expect { subject.execute sample_arl }.to raise_error AkamaiApi::CCU::Error
128
111
  end
129
112
  end
@@ -54,22 +54,4 @@ describe AkamaiApi::CCU do
54
54
  end
55
55
  end
56
56
  end
57
-
58
- describe '#status' do
59
- context "when no argument is given" do
60
- it "delegates to Status request instance" do
61
- expect(AkamaiApi::CCU::Status::Request).to receive(:new).and_return double execute: 'foo'
62
- expect(subject.status).to eq 'foo'
63
- end
64
- end
65
-
66
- context "when a progress uri is passed as argument" do
67
- it "delegates to PurgeStatus request instance" do
68
- fake_instance = double
69
- expect(AkamaiApi::CCU::PurgeStatus::Request).to receive(:new).and_return fake_instance
70
- expect(fake_instance).to receive(:execute).with('foo').and_return 'asd'
71
- expect(subject.status 'foo').to eq 'asd'
72
- end
73
- end
74
- end
75
57
  end
data/spec/spec_helper.rb CHANGED
@@ -4,7 +4,14 @@ require File.expand_path '../../lib/akamai_api/cli', __FILE__
4
4
  begin
5
5
  require File.expand_path '../auth.rb', __FILE__
6
6
  rescue LoadError
7
- AkamaiApi.config[:auth] = ['test_username', 'test_password']
7
+ AkamaiApi.config[:auth] = ['USERNAME', 'PASSWORD']
8
+ AkamaiApi.config[:log] = true
9
+ AkamaiApi.config[:openapi] = {
10
+ :base_url => "https://some-subdomain.purge.akamaiapis.net",
11
+ :client_token => "client_token",
12
+ :client_secret => "client_secret",
13
+ :access_token => "access_token"
14
+ }
8
15
  end
9
16
  require 'savon/mock/spec_helper'
10
17
  require 'webmock/rspec'
@@ -16,7 +23,6 @@ VCR.configure do |c|
16
23
  c.cassette_library_dir = 'cassettes'
17
24
  c.hook_into :webmock
18
25
  c.default_cassette_options = {
19
- record: :once,
20
26
  match_requests_on: [:method, :uri, :body]
21
27
  }
22
28
  c.configure_rspec_metadata!