4me-sdk 1.1.4 → 1.2.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 +5 -5
- data/4me-sdk.gemspec +1 -1
- data/Gemfile.lock +15 -17
- data/LICENSE +1 -1
- data/README.md +45 -27
- data/lib/sdk4me.rb +4 -3
- data/lib/sdk4me/client.rb +52 -20
- data/lib/sdk4me/client/attachments.rb +52 -9
- data/lib/sdk4me/client/response.rb +9 -0
- data/lib/sdk4me/client/version.rb +1 -1
- data/spec/lib/sdk4me/attachments_spec.rb +230 -145
- data/spec/lib/sdk4me/certificate_spec.rb +15 -1
- data/spec/lib/sdk4me/client_spec.rb +480 -441
- data/spec/lib/sdk4me/response_spec.rb +251 -231
- data/spec/lib/sdk4me_spec.rb +4 -4
- metadata +8 -9
@@ -2,550 +2,589 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Sdk4me::Client do
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
def client(authentication, options = {})
|
6
|
+
(@client ||= {})["#{authentication}-#{options.to_s}"] ||= begin
|
7
|
+
options = {max_retry_time: -1}.merge(options)
|
8
|
+
if authentication == :api_token
|
9
|
+
options = {api_token: 'secret'}.merge(options)
|
10
|
+
else
|
11
|
+
options = {access_token: 'secret'}.merge(options)
|
10
12
|
end
|
13
|
+
Sdk4me::Client.new(options)
|
11
14
|
end
|
15
|
+
end
|
12
16
|
|
13
|
-
|
14
|
-
|
17
|
+
def credentials(authentication)
|
18
|
+
if authentication == :api_token
|
19
|
+
{ basic_auth: ['secret', 'x'] }
|
20
|
+
else
|
21
|
+
{ headers: {'Authorization' => 'Bearer secret'} }
|
15
22
|
end
|
23
|
+
end
|
16
24
|
|
17
|
-
|
18
|
-
client = Sdk4me::Client.new
|
19
|
-
expect(client.option(:host)).to eq('https://api.4me.com') # default value
|
20
|
-
expect(client.option(:api_token)).to eq('secret') # value set using Sdk4me.config
|
21
|
-
expect(client.option(:max_retry_time)).to eq(120) # value overridden in Sdk4me.config
|
22
|
-
end
|
25
|
+
[:api_token, :access_token].each do |authentication|
|
23
26
|
|
24
|
-
it 'should override the Sdk4me configuration' do
|
25
|
-
client = Sdk4me::Client.new(host: 'https://demo.4me.com', api_token: 'unknown', block_at_rate_limit: true)
|
26
|
-
expect(client.option(:read_timeout)).to eq(25) # default value
|
27
|
-
expect(client.option(:host)).to eq('https://demo.4me.com') # default value overridden in Client.new
|
28
|
-
expect(client.option(:api_token)).to eq('unknown') # value set using Sdk4me.config and overridden in Client.new
|
29
|
-
expect(client.option(:max_retry_time)).to eq(120) # value overridden in Sdk4me.config
|
30
|
-
expect(client.option(:block_at_rate_limit)).to eq(true) # value overridden in Client.new
|
31
|
-
end
|
32
27
|
|
33
|
-
|
34
|
-
|
35
|
-
|
28
|
+
context 'Sdk4me.config' do
|
29
|
+
before(:each) do
|
30
|
+
Sdk4me.configure do |config|
|
31
|
+
config.max_retry_time = 120 # override default value (5400)
|
32
|
+
if authentication == :api_token
|
33
|
+
config.api_token = 'secret' # set value
|
34
|
+
else
|
35
|
+
config.access_token = 'secret'
|
36
|
+
end
|
37
|
+
end
|
36
38
|
end
|
37
|
-
end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
['http://sdk4me.example.com', false, 'sdk4me.example.com', 80],
|
42
|
-
['http://sdk4me.example.com:777', false, 'sdk4me.example.com', 777]
|
43
|
-
].each do |host, ssl, domain, port|
|
44
|
-
it 'should parse ssl, host and port' do
|
45
|
-
client = Sdk4me::Client.new(host: host)
|
46
|
-
expect(client.instance_variable_get(:@ssl)).to eq(ssl)
|
47
|
-
expect(client.instance_variable_get(:@domain)).to eq(domain)
|
48
|
-
expect(client.instance_variable_get(:@port)).to eq(port)
|
40
|
+
it 'should define the MAX_PAGE_SIZE' do
|
41
|
+
expect(Sdk4me::Client::MAX_PAGE_SIZE).to eq(100)
|
49
42
|
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'should set the ca-bundle.crt file' do
|
54
|
-
http = Net::HTTP.new('https://api.4me.com')
|
55
|
-
http.use_ssl = true
|
56
|
-
|
57
|
-
on_disk = `ls #{http.ca_file}`
|
58
|
-
expect(on_disk).not_to match(/cannot access/)
|
59
|
-
expect(on_disk).to match(/\/ca-bundle.crt$/)
|
60
|
-
end
|
61
|
-
|
62
|
-
describe 'headers' do
|
63
|
-
before(:each) do
|
64
|
-
@client = Sdk4me::Client.new(api_token: 'secret', max_retry_time: -1)
|
65
|
-
end
|
66
|
-
|
67
|
-
it 'should set the content type header' do
|
68
|
-
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(basic_auth: ['secret', 'x']).with(headers: {'Content-Type' => 'application/json'}).to_return(body: {name: 'my name'}.to_json)
|
69
|
-
@client.get('me')
|
70
|
-
expect(stub).to have_been_requested
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'should add the X-4me-Account header' do
|
74
|
-
client = Sdk4me::Client.new(api_token: 'secret', max_retry_time: -1, account: 'test')
|
75
|
-
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(basic_auth: ['secret', 'x']).with(headers: {'X-4me-Account' => 'test'}).to_return(body: {name: 'my name'}.to_json)
|
76
|
-
client.get('me')
|
77
|
-
expect(stub).to have_been_requested
|
78
|
-
end
|
79
|
-
|
80
|
-
it 'should add the X-4me-Source header' do
|
81
|
-
client = Sdk4me::Client.new(api_token: 'secret', max_retry_time: -1, source: 'myapp')
|
82
|
-
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(basic_auth: ['secret', 'x']).with(headers: {'X-4me-Source' => 'myapp'}).to_return(body: {name: 'my name'}.to_json)
|
83
|
-
client.get('me')
|
84
|
-
expect(stub).to have_been_requested
|
85
|
-
end
|
86
43
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
44
|
+
it 'should use the Sdk4me configuration' do
|
45
|
+
client = Sdk4me::Client.new
|
46
|
+
expect(client.option(:host)).to eq('https://api.4me.com') # default value
|
47
|
+
if authentication == :api_token
|
48
|
+
expect(client.option(:api_token)).to eq('secret') # value set using Sdk4me.config
|
49
|
+
else
|
50
|
+
expect(client.option(:access_token)).to eq('secret') # value set using Sdk4me.config
|
51
|
+
end
|
52
|
+
expect(client.option(:max_retry_time)).to eq(120) # value overridden in Sdk4me.config
|
53
|
+
end
|
98
54
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
expect(
|
55
|
+
it 'should override the Sdk4me configuration' do
|
56
|
+
client = Sdk4me::Client.new(host: 'https://demo.4me.com', api_token: 'unknown', block_at_rate_limit: true)
|
57
|
+
expect(client.option(:read_timeout)).to eq(25) # default value
|
58
|
+
expect(client.option(:host)).to eq('https://demo.4me.com') # default value overridden in Client.new
|
59
|
+
expect(client.option(:api_token)).to eq('unknown') # value set using Sdk4me.config and overridden in Client.new
|
60
|
+
expect(client.option(:max_retry_time)).to eq(120) # value overridden in Sdk4me.config
|
61
|
+
expect(client.option(:block_at_rate_limit)).to eq(true) # value overridden in Client.new
|
103
62
|
end
|
104
|
-
expect(stub).to have_been_requested
|
105
|
-
end
|
106
|
-
end
|
107
63
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
64
|
+
[:host, :api_version].each do |required_option|
|
65
|
+
it "should require option #{required_option}" do
|
66
|
+
expect { Sdk4me::Client.new(required_option => '') }.to raise_error("Missing required configuration option #{required_option}")
|
67
|
+
end
|
68
|
+
end
|
112
69
|
|
113
|
-
|
114
|
-
|
115
|
-
nr_of_requests = @client.each('requests', {fields: 'subject'}) do |request|
|
116
|
-
expect(request[:subject]).to eq("Subject #{request[:id]}")
|
70
|
+
it 'should require access_token' do
|
71
|
+
expect { Sdk4me::Client.new(access_token: '', api_token: '') }.to raise_error('Missing required configuration option access_token')
|
117
72
|
end
|
118
|
-
expect(nr_of_requests).to eq(3)
|
119
|
-
end
|
120
73
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
74
|
+
[ ['https://api.4me.com', true, 'api.4me.com', 443],
|
75
|
+
['https://api.example.com:777', true, 'api.example.com', 777],
|
76
|
+
['http://sdk4me.example.com', false, 'sdk4me.example.com', 80],
|
77
|
+
['http://sdk4me.example.com:777', false, 'sdk4me.example.com', 777]
|
78
|
+
].each do |host, ssl, domain, port|
|
79
|
+
it 'should parse ssl, host and port' do
|
80
|
+
client = Sdk4me::Client.new(host: host)
|
81
|
+
expect(client.instance_variable_get(:@ssl)).to eq(ssl)
|
82
|
+
expect(client.instance_variable_get(:@domain)).to eq(domain)
|
83
|
+
expect(client.instance_variable_get(:@port)).to eq(port)
|
84
|
+
end
|
126
85
|
end
|
127
|
-
expect(nr_of_requests).to eq(3)
|
128
|
-
expect(stub_page2).to have_been_requested
|
129
86
|
end
|
130
|
-
end
|
131
87
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
end
|
88
|
+
it 'should set the ca-bundle.crt file' do
|
89
|
+
http = Net::HTTP.new('https://api.4me.com')
|
90
|
+
http.use_ssl = true
|
136
91
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
expect(response[:name]).to eq('my name')
|
92
|
+
on_disk = `ls #{http.ca_file}`
|
93
|
+
expect(on_disk).not_to match(/cannot access/)
|
94
|
+
expect(on_disk).to match(/\/ca-bundle.crt$/)
|
141
95
|
end
|
142
96
|
|
143
|
-
describe '
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
[ true, 'true'],
|
149
|
-
[ false, 'false'],
|
150
|
-
[ DateTime.now, DateTime.now.new_offset(0).iso8601.gsub('+', '%2B')],
|
151
|
-
[ Date.new, Date.new.strftime('%Y-%m-%d')],
|
152
|
-
[ Time.now, Time.now.strftime('%H:%M')],
|
153
|
-
[ ['first', 'second;<', true], 'first,second%3B%3C,true']
|
154
|
-
].each do |param_value, url_value|
|
155
|
-
it "should cast #{param_value.class.name}: '#{param_value}' to '#{url_value}'" do
|
156
|
-
stub = stub_request(:get, "https://api.4me.com/v1/me?value=#{url_value}").with(basic_auth: ['secret', 'x']).to_return(body: {name: 'my name'}.to_json)
|
157
|
-
@client.get('me', {value: param_value})
|
158
|
-
expect(stub).to have_been_requested
|
159
|
-
end
|
97
|
+
describe 'headers' do
|
98
|
+
it 'should set the content type header' do
|
99
|
+
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).with(headers: {'Content-Type' => 'application/json'}).to_return(body: {name: 'my name'}.to_json)
|
100
|
+
client(authentication).get('me')
|
101
|
+
expect(stub).to have_been_requested
|
160
102
|
end
|
161
103
|
|
162
|
-
it 'should
|
163
|
-
client =
|
164
|
-
stub = stub_request(:
|
165
|
-
client.
|
104
|
+
it 'should add the X-4me-Account header' do
|
105
|
+
client = client(authentication, account: 'test')
|
106
|
+
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).with(headers: {'X-4me-Account' => 'test'}).to_return(body: {name: 'my name'}.to_json)
|
107
|
+
client.get('me')
|
166
108
|
expect(stub).to have_been_requested
|
167
109
|
end
|
168
110
|
|
169
|
-
it 'should
|
170
|
-
client =
|
171
|
-
stub = stub_request(:
|
172
|
-
client.
|
111
|
+
it 'should add the X-4me-Source header' do
|
112
|
+
client = client(authentication, source: 'myapp')
|
113
|
+
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).with(headers: {'X-4me-Source' => 'myapp'}).to_return(body: {name: 'my name'}.to_json)
|
114
|
+
client.get('me')
|
173
115
|
expect(stub).to have_been_requested
|
174
116
|
end
|
175
117
|
|
176
|
-
it 'should
|
177
|
-
|
178
|
-
|
179
|
-
client.put('people/55', {status: :waiting_for})
|
118
|
+
it 'should be able to override headers' do
|
119
|
+
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).with(headers: {'Content-Type' => 'application/x-www-form-urlencoded'}).to_return(body: {name: 'my name'}.to_json)
|
120
|
+
client(authentication).get('me', {}, {'Content-Type' => 'application/x-www-form-urlencoded'})
|
180
121
|
expect(stub).to have_been_requested
|
181
122
|
end
|
182
123
|
|
183
|
-
it 'should
|
184
|
-
|
185
|
-
|
186
|
-
@client.get('people', {'created_at=>' => now, 'id!=' => 15})
|
124
|
+
it 'should set the other headers' do
|
125
|
+
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).with(headers: {'X-4me-Other' => 'value'}).to_return(body: {name: 'my name'}.to_json)
|
126
|
+
client(authentication).get('me', {}, {'X-4me-Other' => 'value'})
|
187
127
|
expect(stub).to have_been_requested
|
188
128
|
end
|
189
129
|
|
190
|
-
it 'should
|
191
|
-
stub = stub_request(:get, 'https://api.4me.com/v1/
|
192
|
-
|
130
|
+
it 'should accept headers in the each call' do
|
131
|
+
stub = stub_request(:get, 'https://api.4me.com/v1/requests?fields=subject&page=1&per_page=100').with(credentials(authentication)).with(headers: {'X-4me-Secret' => 'special'}).to_return(body: [{id: 1, subject: 'Subject 1'}, {id: 2, subject: 'Subject 2'}, {id: 3, subject: 'Subject 3'}].to_json)
|
132
|
+
client(authentication).each('requests', {fields: 'subject'}, {'X-4me-Secret' => 'special'}) do |request|
|
133
|
+
expect(request[:subject]).to eq("Subject #{request[:id]}")
|
134
|
+
end
|
193
135
|
expect(stub).to have_been_requested
|
194
136
|
end
|
195
137
|
end
|
196
|
-
end
|
197
138
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
expect(
|
139
|
+
context 'each' do
|
140
|
+
it 'should yield each result' do
|
141
|
+
stub_request(:get, 'https://api.4me.com/v1/requests?fields=subject&page=1&per_page=100').with(credentials(authentication)).to_return(body: [{id: 1, subject: 'Subject 1'}, {id: 2, subject: 'Subject 2'}, {id: 3, subject: 'Subject 3'}].to_json)
|
142
|
+
nr_of_requests = client(authentication).each('requests', {fields: 'subject'}) do |request|
|
143
|
+
expect(request[:subject]).to eq("Subject #{request[:id]}")
|
144
|
+
end
|
145
|
+
expect(nr_of_requests).to eq(3)
|
205
146
|
end
|
206
|
-
end
|
207
|
-
end
|
208
147
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
148
|
+
it 'should retrieve multiple pages' do
|
149
|
+
stub_page1 = stub_request(:get, 'https://api.4me.com/v1/requests?page=1&per_page=2').with(credentials(authentication)).to_return(body: [{id: 1, subject: 'Subject 1'}, {id: 2, subject: 'Subject 2'}].to_json, headers: {'Link' => '<https://api.4me.com/v1/requests?page=1&per_page=2>; rel="first",<https://api.4me.com/v1/requests?page=2&per_page=2>; rel="next",<https://api.4me.com/v1/requests?page=2&per_page=2>; rel="last"'})
|
150
|
+
stub_page2 = stub_request(:get, 'https://api.4me.com/v1/requests?page=2&per_page=2').with(credentials(authentication)).to_return(body: [{id: 3, subject: 'Subject 3'}].to_json, headers: {'Link' => '<https://api.4me.com/v1/requests?page=1&per_page=2>; rel="first",<https://api.4me.com/v1/requests?page=1&per_page=2>; rel="prev",<https://api.4me.com/v1/requests?page=2&per_page=2>; rel="last"'})
|
151
|
+
nr_of_requests = client(authentication).each('requests', {per_page: 2}) do |request|
|
152
|
+
expect(request[:subject]).to eq("Subject #{request[:id]}")
|
153
|
+
end
|
154
|
+
expect(nr_of_requests).to eq(3)
|
155
|
+
expect(stub_page2).to have_been_requested
|
156
|
+
end
|
215
157
|
end
|
216
|
-
end
|
217
158
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
159
|
+
context 'get' do
|
160
|
+
it 'should return a response' do
|
161
|
+
stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).to_return(body: {name: 'my name'}.to_json)
|
162
|
+
response = client(authentication).get('me')
|
163
|
+
expect(response[:name]).to eq('my name')
|
164
|
+
end
|
165
|
+
|
166
|
+
describe 'parameters' do
|
167
|
+
|
168
|
+
[[nil, ''],
|
169
|
+
[ 'normal', 'normal'],
|
170
|
+
[ 'hello;<', 'hello%3B%3C'],
|
171
|
+
[ true, 'true'],
|
172
|
+
[ false, 'false'],
|
173
|
+
[ DateTime.now, DateTime.now.new_offset(0).iso8601.gsub('+', '%2B')],
|
174
|
+
[ Date.new, Date.new.strftime('%Y-%m-%d')],
|
175
|
+
[ Time.now, Time.now.strftime('%H:%M')],
|
176
|
+
[ ['first', 'second;<', true], 'first,second%3B%3C,true']
|
177
|
+
].each do |param_value, url_value|
|
178
|
+
it "should cast #{param_value.class.name}: '#{param_value}' to '#{url_value}'" do
|
179
|
+
stub = stub_request(:get, "https://api.4me.com/v1/me?value=#{url_value}").with(credentials(authentication)).to_return(body: {name: 'my name'}.to_json)
|
180
|
+
client(authentication).get('me', {value: param_value})
|
181
|
+
expect(stub).to have_been_requested
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'should not cast arrays in post and put calls' do
|
186
|
+
stub = stub_request(:post, 'https://api.4me.com/v1/people').with(credentials(authentication)).with(body: {user_ids: [1, 2, 3]}, headers: {'X-4me-Custom' => 'custom'}).to_return(body: {id: 101}.to_json)
|
187
|
+
client(authentication).post('people', {user_ids: [1, 2, 3]}, {'X-4me-Custom' => 'custom'})
|
188
|
+
expect(stub).to have_been_requested
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'should not cast hashes in post and put calls' do
|
192
|
+
stub = stub_request(:patch, 'https://api.4me.com/v1/people/55').with(credentials(authentication)).with(body: '{"contacts_attributes":{"0":{"protocol":"email","label":"work","uri":"work@example.com"}}}', headers: {'X-4me-Custom' => 'custom'}).to_return(body: {id: 101}.to_json)
|
193
|
+
client(authentication).put('people/55', {contacts_attributes: {0 => {protocol: :email, label: :work, uri: 'work@example.com'}}}, {'X-4me-Custom' => 'custom'})
|
194
|
+
expect(stub).to have_been_requested
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'should not double escape symbols' do
|
198
|
+
stub = stub_request(:patch, 'https://api.4me.com/v1/people/55').with(credentials(authentication)).with(body: '{"status":"waiting_for"}').to_return(body: {id: 101}.to_json)
|
199
|
+
client(authentication).put('people/55', {status: :waiting_for})
|
200
|
+
expect(stub).to have_been_requested
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'should handle fancy filter operations' do
|
204
|
+
now = DateTime.now
|
205
|
+
stub = stub_request(:get, "https://api.4me.com/v1/people?created_at=>#{now.new_offset(0).iso8601.gsub('+', '%2B')}&id!=15").with(credentials(authentication)).to_return(body: {name: 'my name'}.to_json)
|
206
|
+
client(authentication).get('people', {'created_at=>' => now, 'id!=' => 15})
|
207
|
+
expect(stub).to have_been_requested
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'should append parameters' do
|
211
|
+
stub = stub_request(:get, 'https://api.4me.com/v1/people?id!=15&primary_email=me@example.com').with(credentials(authentication)).to_return(body: {name: 'my name'}.to_json)
|
212
|
+
client(authentication).get('people?id!=15', {primary_email: 'me@example.com'})
|
213
|
+
expect(stub).to have_been_requested
|
214
|
+
end
|
215
|
+
end
|
226
216
|
end
|
227
|
-
end
|
228
217
|
|
229
|
-
|
230
|
-
|
231
|
-
|
218
|
+
context 'patch' do
|
219
|
+
[:put, :patch].each do |method|
|
220
|
+
it 'should send patch requests with parameters and headers for #{method} calls' do
|
221
|
+
stub = stub_request(:patch, 'https://api.4me.com/v1/people/1').with(credentials(authentication)).with(body: {name: 'New Name'}, headers: {'X-4me-Custom' => 'custom'}).to_return(body: {id: 1}.to_json)
|
222
|
+
client(authentication).send(method, 'people/1', {name: 'New Name'}, {'X-4me-Custom' => 'custom'})
|
223
|
+
expect(stub).to have_been_requested
|
224
|
+
end
|
225
|
+
end
|
232
226
|
end
|
233
227
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
expect(response.valid?).to be_falsey
|
241
|
-
expect(response.raw.body).to eq(xml)
|
228
|
+
context 'post' do
|
229
|
+
it 'should send post requests with parameters and headers' do
|
230
|
+
stub = stub_request(:post, 'https://api.4me.com/v1/people').with(credentials(authentication)).with(body: {name: 'New Name'}, headers: {'X-4me-Custom' => 'custom'}).to_return(body: {id: 101}.to_json)
|
231
|
+
client(authentication).post('people', {name: 'New Name'}, {'X-4me-Custom' => 'custom'})
|
232
|
+
expect(stub).to have_been_requested
|
233
|
+
end
|
242
234
|
end
|
243
235
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
236
|
+
context 'delete' do
|
237
|
+
it 'should send delete requests with parameters and headers' do
|
238
|
+
stub = stub_request(:delete, 'https://api.4me.com/v1/people?id=value').with(credentials(authentication)).with(headers: {'X-4me-Custom' => 'custom'}).to_return(body: '', status: 204)
|
239
|
+
response = client(authentication).delete('people', {id: 'value'}, {'X-4me-Custom' => 'custom'})
|
240
|
+
expect(stub).to have_been_requested
|
241
|
+
expect(response.valid?).to be_truthy
|
242
|
+
expect(response.json).to eq({})
|
243
|
+
end
|
251
244
|
end
|
252
245
|
|
253
|
-
|
254
|
-
|
255
|
-
|
246
|
+
context 'attachments' do
|
247
|
+
it 'should not log an error for XML responses' do
|
248
|
+
xml = %(<?xml version="1.0" encoding="UTF-8"?>\n<details>some info</details>)
|
249
|
+
stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).to_return(body: xml)
|
250
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug)
|
251
|
+
expect_log("XML response:\n#{xml}", :debug)
|
252
|
+
response = client(authentication).get('me')
|
253
|
+
expect(response.valid?).to be_falsey
|
254
|
+
expect(response.raw.body).to eq(xml)
|
255
|
+
end
|
256
256
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
257
|
+
it 'should not log an error for redirects' do
|
258
|
+
stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).to_return(body: '', status: 303, headers: {'Location' => 'http://redirect.example.com/to/here'})
|
259
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug)
|
260
|
+
expect_log('Redirect: http://redirect.example.com/to/here', :debug)
|
261
|
+
response = client(authentication).get('me')
|
262
|
+
expect(response.valid?).to be_falsey
|
263
|
+
expect(response.raw.body).to eq('')
|
264
|
+
end
|
261
265
|
|
262
|
-
|
263
|
-
|
264
|
-
attachments
|
265
|
-
expect(attachments).to receive(:upload_attachments!) do |path, data|
|
266
|
-
expect(path).to eq '/requests/777'
|
267
|
-
expect(data[:attachments]).to eq ['/tmp/first.png', '/tmp/second.zip']
|
268
|
-
data.delete(:attachments)
|
269
|
-
data[:note_attachments] = 'processed'
|
270
|
-
end
|
271
|
-
expect(Sdk4me::Attachments).to receive(:new).with(@client){ attachments }
|
272
|
-
stub_request(method, 'https://api.4me.com/v1/requests/777').with(basic_auth: ['secret', 'x']).with(body: {note: 'note', note_attachments: 'processed' }).to_return(body: {id: 777, upload_called: true}.to_json)
|
266
|
+
it "should not parse attachments for get requests" do
|
267
|
+
expect(Sdk4me::Attachments).not_to receive(:new)
|
268
|
+
stub_request(:get, 'https://api.4me.com/v1/requests/777?attachments=/tmp/first.png,/tmp/second.zip¬e=note').with(credentials(authentication)).to_return(body: {id: 777, upload_called: false}.to_json)
|
273
269
|
|
274
|
-
response =
|
270
|
+
response = client(authentication).get('/requests/777', {note: 'note', attachments: ['/tmp/first.png', '/tmp/second.zip'] })
|
275
271
|
expect(response.valid?).to be_truthy
|
276
|
-
expect(response[:upload_called]).to
|
272
|
+
expect(response[:upload_called]).to be_falsey
|
277
273
|
end
|
278
|
-
end
|
279
274
|
|
280
|
-
|
275
|
+
[:post, :patch].each do |method|
|
276
|
+
it "should parse attachments for #{method} requests" do
|
277
|
+
attachments = double('Sdk4me::Attachments')
|
278
|
+
expect(attachments).to receive(:upload_attachments!) do |path, data|
|
279
|
+
expect(path).to eq '/requests/777'
|
280
|
+
expect(data[:attachments]).to eq ['/tmp/first.png', '/tmp/second.zip']
|
281
|
+
data.delete(:attachments)
|
282
|
+
data[:note_attachments] = 'processed'
|
283
|
+
end
|
284
|
+
expect(Sdk4me::Attachments).to receive(:new).with(client(authentication)){ attachments }
|
285
|
+
stub_request(method, 'https://api.4me.com/v1/requests/777').with(credentials(authentication)).with(body: {note: 'note', note_attachments: 'processed' }).to_return(body: {id: 777, upload_called: true}.to_json)
|
286
|
+
|
287
|
+
response = client(authentication).send(method, '/requests/777', {note: 'note', attachments: ['/tmp/first.png', '/tmp/second.zip'] })
|
288
|
+
expect(response.valid?).to be_truthy
|
289
|
+
expect(response[:upload_called]).to be_truthy
|
290
|
+
end
|
291
|
+
end
|
281
292
|
|
282
|
-
context 'import' do
|
283
|
-
before(:each) do
|
284
|
-
@client = Sdk4me::Client.new(api_token: 'secret', max_retry_time: -1)
|
285
|
-
@multi_part_body = "--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\npeople\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"file\"; filename=\"#{@fixture_dir}/people.csv\"\r\nContent-Type: text/csv\r\n\r\nPrimary Email,Name\nchess.cole@example.com,Chess Cole\ned.turner@example.com,Ed Turner\r\n--0123456789ABLEWASIEREISAWELBA9876543210--"
|
286
|
-
@multi_part_headers = {'Accept'=>'*/*', 'Content-Type'=>'multipart/form-data; boundary=0123456789ABLEWASIEREISAWELBA9876543210', 'User-Agent'=>'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6'}
|
287
|
-
|
288
|
-
@import_queued_response = {body: {state: 'queued'}.to_json}
|
289
|
-
@import_processing_response = {body: {state: 'processing'}.to_json}
|
290
|
-
@import_done_response = {body: {state: 'done', results: {errors: 0, updated: 1, created: 1, failures: 0, unchanged: 0, deleted: 0}}.to_json}
|
291
|
-
@import_failed_response = {body: {state: 'error', message: 'Invalid byte sequence in UTF-8 on line 2', results: {errors: 1, updated: 1, created: 0, failures: 1, unchanged: 0, deleted: 0}}.to_json}
|
292
|
-
allow(@client).to receive(:sleep)
|
293
|
-
WebMock.disable_net_connect!
|
294
293
|
end
|
295
294
|
|
296
|
-
|
297
|
-
|
298
|
-
|
295
|
+
context 'import' do
|
296
|
+
before(:each) do
|
297
|
+
csv_mime_type = ['text/csv', 'text/comma-separated-values'].detect{|t| MIME::Types[t].any?} # which mime type is used depends on version of mime-types gem
|
298
|
+
@multi_part_body = "--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\npeople\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"file\"; filename=\"#{@fixture_dir}/people.csv\"\r\nContent-Type: #{csv_mime_type}\r\n\r\nPrimary Email,Name\nchess.cole@example.com,Chess Cole\ned.turner@example.com,Ed Turner\r\n--0123456789ABLEWASIEREISAWELBA9876543210--"
|
299
|
+
@multi_part_headers = {'Accept'=>'*/*', 'Content-Type'=>'multipart/form-data; boundary=0123456789ABLEWASIEREISAWELBA9876543210', 'User-Agent'=>'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6'}
|
299
300
|
|
300
|
-
|
301
|
-
|
302
|
-
|
301
|
+
@import_queued_response = {body: {state: 'queued'}.to_json}
|
302
|
+
@import_processing_response = {body: {state: 'processing'}.to_json}
|
303
|
+
@import_done_response = {body: {state: 'done', results: {errors: 0, updated: 1, created: 1, failures: 0, unchanged: 0, deleted: 0}}.to_json}
|
304
|
+
@import_failed_response = {body: {state: 'error', message: 'Invalid byte sequence in UTF-8 on line 2', results: {errors: 1, updated: 1, created: 0, failures: 1, unchanged: 0, deleted: 0}}.to_json}
|
305
|
+
allow(client(authentication)).to receive(:sleep)
|
306
|
+
WebMock.disable_net_connect!
|
307
|
+
end
|
303
308
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
expect(response[:token]).to eq('68ef5ef0f64c0')
|
308
|
-
end
|
309
|
+
it 'should import a CSV file' do
|
310
|
+
stub_request(:post, 'https://api.4me.com/v1/import').with(credentials(authentication)).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
311
|
+
expect_log("Import file '#{@fixture_dir}/people.csv' successfully uploaded with token '68ef5ef0f64c0'.")
|
309
312
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
.to_return(@import_queued_response, @import_processing_response)
|
314
|
-
.then.to_raise(StandardError.new('network error'))
|
315
|
-
.then.to_return(@import_done_response)
|
316
|
-
|
317
|
-
# verify the correct log statement are made
|
318
|
-
expect_log('Sending POST request to api.4me.com:443/v1/import', :debug)
|
319
|
-
expect_log("Response:\n{\n \"token\": \"68ef5ef0f64c0\"\n}", :debug)
|
320
|
-
expect_log("Import file '#{@fixture_dir}/people.csv' successfully uploaded with token '68ef5ef0f64c0'.")
|
321
|
-
expect_log('Sending GET request to api.4me.com:443/v1/import/68ef5ef0f64c0', :debug)
|
322
|
-
expect_log("Response:\n{\n \"state\": \"queued\"\n}", :debug)
|
323
|
-
expect_log("Import of '#{@fixture_dir}/people.csv' is queued. Checking again in 30 seconds.", :debug)
|
324
|
-
expect_log('Sending GET request to api.4me.com:443/v1/import/68ef5ef0f64c0', :debug)
|
325
|
-
expect_log("Response:\n{\n \"state\": \"processing\"\n}", :debug)
|
326
|
-
expect_log("Import of '#{@fixture_dir}/people.csv' is processing. Checking again in 30 seconds.", :debug)
|
327
|
-
expect_log('Sending GET request to api.4me.com:443/v1/import/68ef5ef0f64c0', :debug)
|
328
|
-
expect_log("Request failed: 500: No Response from Server - network error for 'api.4me.com:443/v1/import/68ef5ef0f64c0'", :error)
|
329
|
-
expect_log('Sending GET request to api.4me.com:443/v1/import/68ef5ef0f64c0', :debug)
|
330
|
-
expect_log("Response:\n{\n \"state\": \"done\",\n \"results\": {\n \"errors\": 0,\n \"updated\": 1,\n \"created\": 1,\n \"failures\": 0,\n \"unchanged\": 0,\n \"deleted\": 0\n }\n}", :debug)
|
331
|
-
|
332
|
-
response = @client.import("#{@fixture_dir}/people.csv", 'people', true)
|
333
|
-
expect(response[:state]).to eq('done')
|
334
|
-
expect(response[:results][:updated]).to eq(1)
|
335
|
-
expect(progress_stub).to have_been_requested.times(4)
|
336
|
-
end
|
313
|
+
response = client(authentication).import(File.new("#{@fixture_dir}/people.csv"), 'people')
|
314
|
+
expect(response[:token]).to eq('68ef5ef0f64c0')
|
315
|
+
end
|
337
316
|
|
338
|
-
|
339
|
-
|
340
|
-
|
317
|
+
it 'should import a CSV file by filename' do
|
318
|
+
stub_request(:post, 'https://api.4me.com/v1/import').with(credentials(authentication)).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
319
|
+
response = client(authentication).import("#{@fixture_dir}/people.csv", 'people')
|
320
|
+
expect(response[:token]).to eq('68ef5ef0f64c0')
|
321
|
+
end
|
341
322
|
|
342
|
-
|
343
|
-
|
344
|
-
|
323
|
+
it 'should wait for the import to complete' do
|
324
|
+
stub_request(:post, 'https://api.4me.com/v1/import').with(credentials(authentication)).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
325
|
+
progress_stub = stub_request(:get, 'https://api.4me.com/v1/import/68ef5ef0f64c0').with(credentials(authentication))
|
326
|
+
.to_return(@import_queued_response, @import_processing_response)
|
327
|
+
.then.to_raise(StandardError.new('network error'))
|
328
|
+
.then.to_return(@import_done_response)
|
329
|
+
|
330
|
+
# verify the correct log statement are made
|
331
|
+
expect_log('Sending POST request to api.4me.com:443/v1/import', :debug)
|
332
|
+
expect_log("Response:\n{\n \"token\": \"68ef5ef0f64c0\"\n}", :debug)
|
333
|
+
expect_log("Import file '#{@fixture_dir}/people.csv' successfully uploaded with token '68ef5ef0f64c0'.")
|
334
|
+
expect_log('Sending GET request to api.4me.com:443/v1/import/68ef5ef0f64c0', :debug)
|
335
|
+
expect_log("Response:\n{\n \"state\": \"queued\"\n}", :debug)
|
336
|
+
expect_log("Import of '#{@fixture_dir}/people.csv' is queued. Checking again in 30 seconds.", :debug)
|
337
|
+
expect_log('Sending GET request to api.4me.com:443/v1/import/68ef5ef0f64c0', :debug)
|
338
|
+
expect_log("Response:\n{\n \"state\": \"processing\"\n}", :debug)
|
339
|
+
expect_log("Import of '#{@fixture_dir}/people.csv' is processing. Checking again in 30 seconds.", :debug)
|
340
|
+
expect_log('Sending GET request to api.4me.com:443/v1/import/68ef5ef0f64c0', :debug)
|
341
|
+
expect_log("GET request to api.4me.com:443/v1/import/68ef5ef0f64c0 failed: 500: No Response from Server - network error for 'api.4me.com:443/v1/import/68ef5ef0f64c0'", :error)
|
342
|
+
expect_log('Sending GET request to api.4me.com:443/v1/import/68ef5ef0f64c0', :debug)
|
343
|
+
expect_log("Response:\n{\n \"state\": \"done\",\n \"results\": {\n \"errors\": 0,\n \"updated\": 1,\n \"created\": 1,\n \"failures\": 0,\n \"unchanged\": 0,\n \"deleted\": 0\n }\n}", :debug)
|
344
|
+
|
345
|
+
response = client(authentication).import("#{@fixture_dir}/people.csv", 'people', true)
|
346
|
+
expect(response[:state]).to eq('done')
|
347
|
+
expect(response[:results][:updated]).to eq(1)
|
348
|
+
expect(progress_stub).to have_been_requested.times(4)
|
349
|
+
end
|
345
350
|
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
.to_return(@import_queued_response, @import_processing_response)
|
350
|
-
.then.to_raise(StandardError.new('network error')) # twice
|
351
|
+
it 'should wait for the import to fail' do
|
352
|
+
stub_request(:post, 'https://api.4me.com/v1/import').with(credentials(authentication)).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
353
|
+
progress_stub = stub_request(:get, 'https://api.4me.com/v1/import/68ef5ef0f64c0').with(credentials(authentication)).to_return(@import_queued_response, @import_processing_response, @import_failed_response)
|
351
354
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
+
expect{ client(authentication).import("#{@fixture_dir}/people.csv", 'people', true) }.to raise_error(Sdk4me::Exception, "Unable to monitor progress for people import. Invalid byte sequence in UTF-8 on line 2")
|
356
|
+
expect(progress_stub).to have_been_requested.times(4)
|
357
|
+
end
|
355
358
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
359
|
+
it 'should not continue when there is an error connecting to 4me' do
|
360
|
+
stub_request(:post, 'https://api.4me.com/v1/import').with(credentials(authentication)).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
361
|
+
progress_stub = stub_request(:get, 'https://api.4me.com/v1/import/68ef5ef0f64c0').with(credentials(authentication))
|
362
|
+
.to_return(@import_queued_response, @import_processing_response)
|
363
|
+
.then.to_raise(StandardError.new('network error')) # twice
|
364
|
+
|
365
|
+
expect{ client(authentication).import("#{@fixture_dir}/people.csv", 'people', true) }.to raise_error(Sdk4me::Exception, "Unable to monitor progress for people import. 500: No Response from Server - network error for 'api.4me.com:443/v1/import/68ef5ef0f64c0'")
|
366
|
+
expect(progress_stub).to have_been_requested.times(4)
|
367
|
+
end
|
368
|
+
|
369
|
+
it 'should return an invalid response in case waiting for progress is false' do
|
370
|
+
stub_request(:post, 'https://api.4me.com/v1/import').with(credentials(authentication)).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {message: 'oops!'}.to_json)
|
371
|
+
response = client(authentication).import("#{@fixture_dir}/people.csv", 'people', false)
|
372
|
+
expect(response.valid?).to be_falsey
|
373
|
+
expect(response.message).to eq('oops!')
|
374
|
+
end
|
375
|
+
|
376
|
+
it 'should raise an UploadFailed exception in case waiting for progress is true' do
|
377
|
+
stub_request(:post, 'https://api.4me.com/v1/import').with(credentials(authentication)).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {message: 'oops!'}.to_json)
|
378
|
+
expect{ client(authentication).import("#{@fixture_dir}/people.csv", 'people', true) }.to raise_error(Sdk4me::UploadFailed, 'Failed to queue people import. oops!')
|
379
|
+
end
|
362
380
|
|
363
|
-
it 'should raise an UploadFailed exception in case waiting for progress is true' do
|
364
|
-
stub_request(:post, 'https://api.4me.com/v1/import').with(basic_auth: ['secret', 'x']).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {message: 'oops!'}.to_json)
|
365
|
-
expect{ @client.import("#{@fixture_dir}/people.csv", 'people', true) }.to raise_error(Sdk4me::UploadFailed, 'Failed to queue people import. oops!')
|
366
381
|
end
|
367
382
|
|
368
|
-
end
|
369
383
|
|
384
|
+
context 'export' do
|
385
|
+
before(:each) do
|
370
386
|
|
371
|
-
|
372
|
-
|
373
|
-
|
387
|
+
@export_queued_response = {body: {state: 'queued'}.to_json}
|
388
|
+
@export_processing_response = {body: {state: 'processing'}.to_json}
|
389
|
+
@export_done_response = {body: {state: 'done', url: 'https://download.example.com/export.zip?AWSAccessKeyId=12345'}.to_json}
|
390
|
+
allow(client(authentication)).to receive(:sleep)
|
391
|
+
end
|
374
392
|
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
allow(@client).to receive(:sleep)
|
379
|
-
end
|
393
|
+
it 'should export multiple types' do
|
394
|
+
stub_request(:post, 'https://api.4me.com/v1/export').with(credentials(authentication)).with(body: {type: 'people,people_contact_details'}).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
395
|
+
expect_log("Export for 'people,people_contact_details' successfully queued with token '68ef5ef0f64c0'.")
|
380
396
|
|
381
|
-
|
382
|
-
|
383
|
-
|
397
|
+
response = client(authentication).export(['people', 'people_contact_details'])
|
398
|
+
expect(response[:token]).to eq('68ef5ef0f64c0')
|
399
|
+
end
|
384
400
|
|
385
|
-
|
386
|
-
|
387
|
-
|
401
|
+
it 'should indicate when nothing is exported' do
|
402
|
+
stub_request(:post, 'https://api.4me.com/v1/export').with(credentials(authentication)).with(body: {type: 'people', from: '2012-03-30T23:00:00+00:00'}).to_return(status: 204)
|
403
|
+
expect_log("No changed records for 'people' since 2012-03-30T23:00:00+00:00.")
|
388
404
|
|
389
|
-
|
390
|
-
|
391
|
-
|
405
|
+
response = client(authentication).export('people', DateTime.new(2012,03,30,23,00,00))
|
406
|
+
expect(response[:token]).to be_nil
|
407
|
+
end
|
392
408
|
|
393
|
-
|
394
|
-
|
395
|
-
|
409
|
+
it 'should export since a certain time' do
|
410
|
+
stub_request(:post, 'https://api.4me.com/v1/export').with(credentials(authentication)).with(body: {type: 'people', from: '2012-03-30T23:00:00+00:00'}).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
411
|
+
expect_log("Export for 'people' successfully queued with token '68ef5ef0f64c0'.")
|
396
412
|
|
397
|
-
|
398
|
-
|
399
|
-
|
413
|
+
response = client(authentication).export('people', DateTime.new(2012,03,30,23,00,00))
|
414
|
+
expect(response[:token]).to eq('68ef5ef0f64c0')
|
415
|
+
end
|
400
416
|
|
401
|
-
|
402
|
-
|
403
|
-
|
417
|
+
it 'should export with locale' do
|
418
|
+
stub_request(:post, 'https://api.4me.com/v1/export').with(credentials(authentication)).with(body: {type: 'translations', locale: 'nl'}).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
419
|
+
expect_log("Export for 'translations' successfully queued with token '68ef5ef0f64c0'.")
|
404
420
|
|
405
|
-
|
406
|
-
|
407
|
-
|
421
|
+
response = client(authentication).export('translations', nil, nil, 'nl')
|
422
|
+
expect(response[:token]).to eq('68ef5ef0f64c0')
|
423
|
+
end
|
408
424
|
|
409
|
-
|
410
|
-
|
411
|
-
|
425
|
+
it 'should wait for the export to complete' do
|
426
|
+
stub_request(:post, 'https://api.4me.com/v1/export').with(credentials(authentication)).with(body: {type: 'people'}).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
427
|
+
progress_stub = stub_request(:get, 'https://api.4me.com/v1/export/68ef5ef0f64c0').with(credentials(authentication))
|
428
|
+
.to_return(@export_queued_response, @export_processing_response)
|
429
|
+
.then.to_raise(StandardError.new('network error'))
|
430
|
+
.then.to_return(@export_done_response)
|
431
|
+
|
432
|
+
# verify the correct log statement are made
|
433
|
+
expect_log('Sending POST request to api.4me.com:443/v1/export', :debug)
|
434
|
+
expect_log(%(Response:\n{\n "token": "68ef5ef0f64c0"\n}), :debug)
|
435
|
+
expect_log("Export for 'people' successfully queued with token '68ef5ef0f64c0'.")
|
436
|
+
expect_log('Sending GET request to api.4me.com:443/v1/export/68ef5ef0f64c0', :debug)
|
437
|
+
expect_log(%(Response:\n{\n "state": "queued"\n}), :debug)
|
438
|
+
expect_log("Export of 'people' is queued. Checking again in 30 seconds.", :debug)
|
439
|
+
expect_log('Sending GET request to api.4me.com:443/v1/export/68ef5ef0f64c0', :debug)
|
440
|
+
expect_log(%(Response:\n{\n "state": "processing"\n}), :debug)
|
441
|
+
expect_log("Export of 'people' is processing. Checking again in 30 seconds.", :debug)
|
442
|
+
expect_log('Sending GET request to api.4me.com:443/v1/export/68ef5ef0f64c0', :debug)
|
443
|
+
expect_log("GET request to api.4me.com:443/v1/export/68ef5ef0f64c0 failed: 500: No Response from Server - network error for 'api.4me.com:443/v1/export/68ef5ef0f64c0'", :error)
|
444
|
+
expect_log('Sending GET request to api.4me.com:443/v1/export/68ef5ef0f64c0', :debug)
|
445
|
+
expect_log(%(Response:\n{\n "state": "done",\n "url": "https://download.example.com/export.zip?AWSAccessKeyId=12345"\n}), :debug)
|
446
|
+
|
447
|
+
response = client(authentication).export('people', nil, true)
|
448
|
+
expect(response[:state]).to eq('done')
|
449
|
+
expect(response[:url]).to eq('https://download.example.com/export.zip?AWSAccessKeyId=12345')
|
450
|
+
expect(progress_stub).to have_been_requested.times(4)
|
451
|
+
end
|
412
452
|
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
.then.to_return(@export_done_response)
|
419
|
-
|
420
|
-
# verify the correct log statement are made
|
421
|
-
expect_log('Sending POST request to api.4me.com:443/v1/export', :debug)
|
422
|
-
expect_log(%(Response:\n{\n "token": "68ef5ef0f64c0"\n}), :debug)
|
423
|
-
expect_log("Export for 'people' successfully queued with token '68ef5ef0f64c0'.")
|
424
|
-
expect_log('Sending GET request to api.4me.com:443/v1/export/68ef5ef0f64c0', :debug)
|
425
|
-
expect_log(%(Response:\n{\n "state": "queued"\n}), :debug)
|
426
|
-
expect_log("Export of 'people' is queued. Checking again in 30 seconds.", :debug)
|
427
|
-
expect_log('Sending GET request to api.4me.com:443/v1/export/68ef5ef0f64c0', :debug)
|
428
|
-
expect_log(%(Response:\n{\n "state": "processing"\n}), :debug)
|
429
|
-
expect_log("Export of 'people' is processing. Checking again in 30 seconds.", :debug)
|
430
|
-
expect_log('Sending GET request to api.4me.com:443/v1/export/68ef5ef0f64c0', :debug)
|
431
|
-
expect_log("Request failed: 500: No Response from Server - network error for 'api.4me.com:443/v1/export/68ef5ef0f64c0'", :error)
|
432
|
-
expect_log('Sending GET request to api.4me.com:443/v1/export/68ef5ef0f64c0', :debug)
|
433
|
-
expect_log(%(Response:\n{\n "state": "done",\n "url": "https://download.example.com/export.zip?AWSAccessKeyId=12345"\n}), :debug)
|
434
|
-
|
435
|
-
response = @client.export('people', nil, true)
|
436
|
-
expect(response[:state]).to eq('done')
|
437
|
-
expect(response[:url]).to eq('https://download.example.com/export.zip?AWSAccessKeyId=12345')
|
438
|
-
expect(progress_stub).to have_been_requested.times(4)
|
439
|
-
end
|
453
|
+
it 'should not continue when there is an error connecting to 4me' do
|
454
|
+
stub_request(:post, 'https://api.4me.com/v1/export').with(credentials(authentication)).with(body: {type: 'people'}).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
455
|
+
progress_stub = stub_request(:get, 'https://api.4me.com/v1/export/68ef5ef0f64c0').with(credentials(authentication))
|
456
|
+
.to_return(@export_queued_response, @export_processing_response)
|
457
|
+
.then.to_raise(StandardError.new('network error')) # twice
|
440
458
|
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
.to_return(@export_queued_response, @export_processing_response)
|
445
|
-
.then.to_raise(StandardError.new('network error')) # twice
|
459
|
+
expect{ client(authentication).export('people', nil, true) }.to raise_error(Sdk4me::Exception, "Unable to monitor progress for 'people' export. 500: No Response from Server - network error for 'api.4me.com:443/v1/export/68ef5ef0f64c0'")
|
460
|
+
expect(progress_stub).to have_been_requested.times(4)
|
461
|
+
end
|
446
462
|
|
447
|
-
|
448
|
-
|
449
|
-
|
463
|
+
it 'should return an invalid response in case waiting for progress is false' do
|
464
|
+
stub_request(:post, 'https://api.4me.com/v1/export').with(credentials(authentication)).with(body: {type: 'people'}).to_return(body: {message: 'oops!'}.to_json)
|
465
|
+
response = client(authentication).export('people')
|
466
|
+
expect(response.valid?).to be_falsey
|
467
|
+
expect(response.message).to eq('oops!')
|
468
|
+
end
|
450
469
|
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
expect(response.message).to eq('oops!')
|
456
|
-
end
|
470
|
+
it 'should raise an UploadFailed exception in case waiting for progress is true' do
|
471
|
+
stub_request(:post, 'https://api.4me.com/v1/export').with(credentials(authentication)).with(body: {type: 'people'}).to_return(body: {message: 'oops!'}.to_json)
|
472
|
+
expect{ client(authentication).export('people', nil, true) }.to raise_error(Sdk4me::UploadFailed, "Failed to queue 'people' export. oops!")
|
473
|
+
end
|
457
474
|
|
458
|
-
it 'should raise an UploadFailed exception in case waiting for progress is true' do
|
459
|
-
stub_request(:post, 'https://api.4me.com/v1/export').with(basic_auth: ['secret', 'x']).with(body: {type: 'people'}).to_return(body: {message: 'oops!'}.to_json)
|
460
|
-
expect{ @client.export('people', nil, true) }.to raise_error(Sdk4me::UploadFailed, "Failed to queue 'people' export. oops!")
|
461
475
|
end
|
462
476
|
|
463
|
-
|
477
|
+
context 'retry' do
|
478
|
+
it 'should not retry when max_retry_time = -1' do
|
479
|
+
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).to_raise(StandardError.new('network error'))
|
480
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
481
|
+
expect_log("GET request to api.4me.com:443/v1/me failed: 500: No Response from Server - network error for 'api.4me.com:443/v1/me'", :error)
|
464
482
|
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
client = Sdk4me::Client.new(api_token: 'secret', max_retry_time: -1)
|
472
|
-
response = client.get('me')
|
473
|
-
expect(stub).to have_been_requested.times(1)
|
474
|
-
expect(response.valid?).to be_falsey
|
475
|
-
expect(response.message).to eq("500: No Response from Server - network error for 'api.4me.com:443/v1/me'")
|
476
|
-
end
|
483
|
+
response = client(authentication).get('me')
|
484
|
+
expect(stub).to have_been_requested.times(1)
|
485
|
+
expect(response.valid?).to be_falsey
|
486
|
+
expect(response.message).to eq("500: No Response from Server - network error for 'api.4me.com:443/v1/me'")
|
487
|
+
end
|
477
488
|
|
478
|
-
|
479
|
-
|
480
|
-
|
489
|
+
it 'should not retry 4 times when max_retry_time = 16' do
|
490
|
+
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).to_raise(StandardError.new('network error'))
|
491
|
+
[2,4,8].each_with_index do |secs, i|
|
492
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
493
|
+
expect_log("Request failed, retry ##{i+1} in #{secs} seconds: 500: No Response from Server - network error for 'api.4me.com:443/v1/me'", :warn)
|
494
|
+
end
|
481
495
|
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
482
|
-
|
496
|
+
|
497
|
+
retry_client = client(authentication, max_retry_time: 16)
|
498
|
+
allow(retry_client).to receive(:sleep)
|
499
|
+
response = retry_client.get('me')
|
500
|
+
expect(stub).to have_been_requested.times(4)
|
501
|
+
expect(response.valid?).to be_falsey
|
502
|
+
expect(response.message).to eq("500: No Response from Server - network error for 'api.4me.com:443/v1/me'")
|
483
503
|
end
|
484
504
|
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
end
|
505
|
+
it 'should return the response after retry succeeds' do
|
506
|
+
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).to_raise(StandardError.new('network error')).then.to_return(body: {name: 'my name'}.to_json)
|
507
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
508
|
+
expect_log("Request failed, retry #1 in 2 seconds: 500: No Response from Server - network error for 'api.4me.com:443/v1/me'", :warn)
|
509
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
510
|
+
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug )
|
492
511
|
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
client = Sdk4me::Client.new(api_token: 'secret', max_retry_time: 16)
|
501
|
-
allow(client).to receive(:sleep)
|
502
|
-
response = client.get('me')
|
503
|
-
expect(stub).to have_been_requested.times(2)
|
504
|
-
expect(response.valid?).to be_truthy
|
505
|
-
expect(response[:name]).to eq('my name')
|
512
|
+
retry_client = client(authentication, max_retry_time: 16)
|
513
|
+
allow(retry_client).to receive(:sleep)
|
514
|
+
response = retry_client.get('me')
|
515
|
+
expect(stub).to have_been_requested.times(2)
|
516
|
+
expect(response.valid?).to be_truthy
|
517
|
+
expect(response[:name]).to eq('my name')
|
518
|
+
end
|
506
519
|
end
|
507
|
-
end
|
508
520
|
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
client = Sdk4me::Client.new(api_token: 'secret', block_at_rate_limit: false)
|
516
|
-
response = client.get('me')
|
517
|
-
expect(stub).to have_been_requested.times(1)
|
518
|
-
expect(response.valid?).to be_falsey
|
519
|
-
expect(response.message).to eq('429: Too Many Requests')
|
520
|
-
end
|
521
|
+
context 'rate limiting' do
|
522
|
+
it 'should not block on rate limit when block_at_rate_limit is false' do
|
523
|
+
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).to_return(status: 429, body: {message: 'Too Many Requests'}.to_json)
|
524
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
525
|
+
expect_log("GET request to api.4me.com:443/v1/me failed: 429: Too Many Requests", :error)
|
521
526
|
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug )
|
528
|
-
|
529
|
-
client = Sdk4me::Client.new(api_token: 'secret', block_at_rate_limit: true)
|
530
|
-
allow(client).to receive(:sleep)
|
531
|
-
response = client.get('me')
|
532
|
-
expect(stub).to have_been_requested.times(2)
|
533
|
-
expect(response.valid?).to be_truthy
|
534
|
-
expect(response[:name]).to eq('my name')
|
535
|
-
end
|
536
|
-
end
|
527
|
+
response = client(authentication, block_at_rate_limit: false).get('me')
|
528
|
+
expect(stub).to have_been_requested.times(1)
|
529
|
+
expect(response.valid?).to be_falsey
|
530
|
+
expect(response.message).to eq('429: Too Many Requests')
|
531
|
+
end
|
537
532
|
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
533
|
+
it 'should block on rate limit when block_at_rate_limit is true' do
|
534
|
+
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).to_return(status: 429, body: {message: 'Too Many Requests'}.to_json).then.to_return(body: {name: 'my name'}.to_json)
|
535
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
536
|
+
expect_log('Request throttled, trying again in 300 seconds: 429: Too Many Requests', :warn)
|
537
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
538
|
+
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug )
|
539
|
+
|
540
|
+
block_client = client(authentication, block_at_rate_limit: true, max_retry_time: 500)
|
541
|
+
allow(block_client).to receive(:sleep)
|
542
|
+
response = block_client.get('me')
|
543
|
+
expect(stub).to have_been_requested.times(2)
|
544
|
+
expect(response.valid?).to be_truthy
|
545
|
+
expect(response[:name]).to eq('my name')
|
546
|
+
end
|
547
|
+
|
548
|
+
it 'should block on rate limit using Retry-After when block_at_rate_limit is true' do
|
549
|
+
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).to_return(status: 429, body: {message: 'Too Many Requests'}.to_json, headers: {'Retry-After' => '20'}).then.to_return(body: {name: 'my name'}.to_json)
|
550
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
551
|
+
expect_log('Request throttled, trying again in 20 seconds: 429: Too Many Requests', :warn)
|
552
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
553
|
+
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug )
|
554
|
+
|
555
|
+
block_client = client(authentication, block_at_rate_limit: true)
|
556
|
+
allow(block_client).to receive(:sleep)
|
557
|
+
response = block_client.get('me')
|
558
|
+
expect(stub).to have_been_requested.times(2)
|
559
|
+
expect(response.valid?).to be_truthy
|
560
|
+
expect(response[:name]).to eq('my name')
|
561
|
+
end
|
562
|
+
|
563
|
+
it 'should block on rate limit using Retry-After with minimum of 2 seconds when block_at_rate_limit is true' do
|
564
|
+
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).to_return(status: 429, body: {message: 'Too Many Requests'}.to_json, headers: {'Retry-After' => '1'}).then.to_return(body: {name: 'my name'}.to_json)
|
565
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
566
|
+
expect_log('Request throttled, trying again in 2 seconds: 429: Too Many Requests', :warn)
|
567
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
568
|
+
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug )
|
569
|
+
|
570
|
+
block_client = client(authentication, block_at_rate_limit: true)
|
571
|
+
allow(block_client).to receive(:sleep)
|
572
|
+
response = block_client.get('me')
|
573
|
+
expect(stub).to have_been_requested.times(2)
|
574
|
+
expect(response.valid?).to be_truthy
|
575
|
+
expect(response[:name]).to eq('my name')
|
576
|
+
end
|
542
577
|
end
|
543
578
|
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
579
|
+
context 'logger' do
|
580
|
+
it 'should be possible to override the default logger' do
|
581
|
+
logger = Logger.new(STDOUT)
|
582
|
+
logger_client = client(authentication, max_retry_time: -1, logger: logger)
|
583
|
+
stub_request(:get, 'https://api.4me.com/v1/me').with(credentials(authentication)).to_return(body: {name: 'my name'}.to_json)
|
584
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug, logger )
|
585
|
+
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug, logger )
|
586
|
+
logger_client.get('me')
|
587
|
+
end
|
549
588
|
end
|
550
589
|
end
|
551
590
|
end
|