4me-sdk 2.0.3 → 2.0.6

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