4me-sdk 1.1.5 → 2.0.0.pre.rc.1
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 +12 -13
- data/Gemfile.lock +58 -39
- data/LICENSE +1 -1
- data/README.md +64 -39
- data/lib/sdk4me.rb +10 -8
- data/lib/sdk4me/ca-bundle.crt +1327 -1802
- data/lib/sdk4me/client.rb +116 -92
- data/lib/sdk4me/client/attachments.rb +106 -76
- data/lib/sdk4me/client/multipart.rb +16 -18
- data/lib/sdk4me/client/response.rb +22 -19
- data/lib/sdk4me/client/version.rb +1 -1
- data/spec/lib/sdk4me/attachments_spec.rb +307 -143
- data/spec/lib/sdk4me/certificate_spec.rb +17 -4
- data/spec/lib/sdk4me/client_spec.rb +490 -475
- data/spec/lib/sdk4me/response_spec.rb +249 -233
- data/spec/lib/sdk4me_spec.rb +9 -9
- data/spec/spec_helper.rb +5 -8
- data/spec/support/matchers/never_raise.rb +16 -21
- data/spec/support/util.rb +2 -2
- metadata +38 -24
@@ -1,8 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'ca-bundle.crt' do
|
4
|
-
|
5
|
-
it 'should be able to connect to the 4me API' do
|
4
|
+
it 'should be able to connect to the 4me REST API' do
|
6
5
|
WebMock.allow_net_connect!
|
7
6
|
client = Sdk4me::Client.new(api_token: 'invalid', max_retry_time: -1)
|
8
7
|
result = {}
|
@@ -13,7 +12,21 @@ describe 'ca-bundle.crt' do
|
|
13
12
|
expect(response.valid?).to be_falsey
|
14
13
|
|
15
14
|
# expecting 401 error
|
16
|
-
expect(response.message).to eq('401:
|
15
|
+
expect(response.message).to eq('401: Bad credentials')
|
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 eq('401: Bad credentials')
|
17
30
|
end
|
18
31
|
|
19
32
|
it 'should be able to connect to S3' do
|
@@ -23,6 +36,6 @@ describe 'ca-bundle.crt' do
|
|
23
36
|
http.use_ssl = true
|
24
37
|
|
25
38
|
# no SSL error please
|
26
|
-
expect{ http.start{ |
|
39
|
+
expect { http.start { |transport| transport.request(Net::HTTP::Get.new('/exports/20141107/')) } }.to never_raise(OpenSSL::SSL::SSLError)
|
27
40
|
end
|
28
41
|
end
|
@@ -1,583 +1,598 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Sdk4me::Client do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
it 'should define the MAX_PAGE_SIZE' do
|
14
|
-
expect(Sdk4me::Client::MAX_PAGE_SIZE).to eq(100)
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'should use the Sdk4me configuration' do
|
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
|
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)
|
22
13
|
end
|
14
|
+
end
|
23
15
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
16
|
+
def credentials(authentication)
|
17
|
+
if authentication == :api_token
|
18
|
+
{ basic_auth: %w[secret x] }
|
19
|
+
else
|
20
|
+
{ headers: { 'Authorization' => 'Bearer secret' } }
|
31
21
|
end
|
22
|
+
end
|
32
23
|
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
36
35
|
end
|
37
|
-
end
|
38
36
|
|
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)
|
37
|
+
it 'should define the MAX_PAGE_SIZE' do
|
38
|
+
expect(Sdk4me::Client::MAX_PAGE_SIZE).to eq(100)
|
49
39
|
end
|
50
|
-
end
|
51
|
-
end
|
52
40
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
61
51
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
66
60
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
72
66
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
client.get('me')
|
77
|
-
expect(stub).to have_been_requested
|
78
|
-
end
|
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
|
79
70
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
85
82
|
end
|
86
83
|
|
87
|
-
it 'should
|
88
|
-
|
89
|
-
|
90
|
-
expect(stub).to have_been_requested
|
91
|
-
end
|
84
|
+
it 'should set the ca-bundle.crt file' do
|
85
|
+
http = Net::HTTP.new('https://api.4me.com')
|
86
|
+
http.use_ssl = true
|
92
87
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
expect(stub).to have_been_requested
|
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$})
|
97
91
|
end
|
98
92
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
103
98
|
end
|
104
|
-
expect(stub).to have_been_requested
|
105
|
-
end
|
106
|
-
end
|
107
99
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
it 'should yield each result' do
|
114
|
-
stub_request(:get, 'https://api.4me.com/v1/requests?fields=subject&page=1&per_page=100').with(basic_auth: ['secret', 'x']).to_return(body: [{id: 1, subject: 'Subject 1'}, {id: 2, subject: 'Subject 2'}, {id: 3, subject: 'Subject 3'}].to_json)
|
115
|
-
nr_of_requests = @client.each('requests', {fields: 'subject'}) do |request|
|
116
|
-
expect(request[:subject]).to eq("Subject #{request[:id]}")
|
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
|
117
105
|
end
|
118
|
-
expect(nr_of_requests).to eq(3)
|
119
|
-
end
|
120
106
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
expect(
|
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
|
126
112
|
end
|
127
|
-
expect(nr_of_requests).to eq(3)
|
128
|
-
expect(stub_page2).to have_been_requested
|
129
|
-
end
|
130
|
-
end
|
131
113
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
it 'should return a response' do
|
138
|
-
stub_request(:get, 'https://api.4me.com/v1/me').with(basic_auth: ['secret', 'x']).to_return(body: {name: 'my name'}.to_json)
|
139
|
-
response = @client.get('me')
|
140
|
-
expect(response[:name]).to eq('my name')
|
141
|
-
end
|
142
|
-
|
143
|
-
describe 'parameters' do
|
144
|
-
|
145
|
-
[[nil, ''],
|
146
|
-
[ 'normal', 'normal'],
|
147
|
-
[ 'hello;<', 'hello%3B%3C'],
|
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
|
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
|
160
119
|
end
|
161
120
|
|
162
|
-
it 'should
|
163
|
-
client =
|
164
|
-
stub = stub_request(:
|
165
|
-
client.
|
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')
|
166
125
|
expect(stub).to have_been_requested
|
167
126
|
end
|
168
127
|
|
169
|
-
it 'should
|
170
|
-
|
171
|
-
|
172
|
-
client.put('people/55', {contacts_attributes: {0 => {protocol: :email, label: :work, uri: 'work@example.com'}}}, {'X-4me-Custom' => 'custom'})
|
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' })
|
173
131
|
expect(stub).to have_been_requested
|
174
132
|
end
|
175
133
|
|
176
|
-
it 'should
|
177
|
-
|
178
|
-
|
179
|
-
client.put('people/55', {status: :waiting_for})
|
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' })
|
180
137
|
expect(stub).to have_been_requested
|
181
138
|
end
|
182
139
|
|
183
|
-
it 'should
|
184
|
-
|
185
|
-
|
186
|
-
@client.get('people', {'created_at=>' => now, 'id!=' => 15})
|
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' })
|
187
143
|
expect(stub).to have_been_requested
|
188
144
|
end
|
189
145
|
|
190
|
-
it 'should
|
191
|
-
stub = stub_request(:get, 'https://api.4me.com/v1/
|
192
|
-
|
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
|
193
151
|
expect(stub).to have_been_requested
|
194
152
|
end
|
195
153
|
end
|
196
|
-
end
|
197
154
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
expect(
|
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)
|
205
162
|
end
|
206
|
-
end
|
207
|
-
end
|
208
163
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
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
|
215
174
|
end
|
216
|
-
end
|
217
175
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
expect(response.valid?).to be_truthy
|
225
|
-
expect(response.json).to eq({})
|
226
|
-
end
|
227
|
-
end
|
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
|
228
182
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
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
|
233
199
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
response = @client.get('me')
|
240
|
-
expect(response.valid?).to be_falsey
|
241
|
-
expect(response.raw.body).to eq(xml)
|
242
|
-
end
|
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
|
243
205
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
expect(response.valid?).to be_falsey
|
250
|
-
expect(response.raw.body).to be_nil
|
251
|
-
end
|
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
|
252
211
|
|
253
|
-
|
254
|
-
|
255
|
-
|
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
|
256
217
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
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
|
261
224
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
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'
|
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
|
270
229
|
end
|
271
|
-
|
272
|
-
|
230
|
+
end
|
231
|
+
end
|
273
232
|
|
274
|
-
|
275
|
-
|
276
|
-
|
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
|
277
240
|
end
|
278
241
|
end
|
279
242
|
|
280
|
-
|
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
|
281
250
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
@import_processing_response = {body: {state: 'processing'}.to_json}
|
291
|
-
@import_done_response = {body: {state: 'done', results: {errors: 0, updated: 1, created: 1, failures: 0, unchanged: 0, deleted: 0}}.to_json}
|
292
|
-
@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}
|
293
|
-
allow(@client).to receive(:sleep)
|
294
|
-
WebMock.disable_net_connect!
|
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
|
295
259
|
end
|
296
260
|
|
297
|
-
|
298
|
-
|
299
|
-
|
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
|
300
271
|
|
301
|
-
|
302
|
-
|
303
|
-
|
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
|
304
280
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
expect(response[:token]).to eq('68ef5ef0f64c0')
|
309
|
-
end
|
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¬e=note%20%5Battachment:/tmp/third.gif%5D').with(credentials(authentication)).to_return(body: { id: 777, upload_called: false }.to_json)
|
310
284
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
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)
|
332
|
-
|
333
|
-
response = @client.import("#{@fixture_dir}/people.csv", 'people', true)
|
334
|
-
expect(response[:state]).to eq('done')
|
335
|
-
expect(response[:results][:updated]).to eq(1)
|
336
|
-
expect(progress_stub).to have_been_requested.times(4)
|
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
|
337
305
|
end
|
338
306
|
|
339
|
-
|
340
|
-
|
341
|
-
|
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}" }
|
342
312
|
|
343
|
-
|
344
|
-
|
345
|
-
|
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
|
346
320
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
.to_return(@import_queued_response, @import_processing_response)
|
351
|
-
.then.to_raise(StandardError.new('network error')) # twice
|
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'.")
|
352
324
|
|
353
|
-
|
354
|
-
|
355
|
-
|
325
|
+
response = client(authentication).import(File.new("#{@fixture_dir}/people.csv"), 'people')
|
326
|
+
expect(response[:token]).to eq('68ef5ef0f64c0')
|
327
|
+
end
|
356
328
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
end
|
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
|
363
334
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
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
|
368
362
|
|
369
|
-
|
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)
|
370
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
|
371
370
|
|
372
|
-
|
373
|
-
|
374
|
-
|
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
|
375
376
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
allow(@client).to receive(:sleep)
|
380
|
-
end
|
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
|
381
380
|
|
382
|
-
|
383
|
-
|
384
|
-
|
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
|
385
387
|
|
386
|
-
|
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
|
388
392
|
end
|
389
393
|
|
390
|
-
|
391
|
-
|
392
|
-
|
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
|
393
401
|
|
394
|
-
|
395
|
-
|
396
|
-
|
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'.")
|
397
405
|
|
398
|
-
|
399
|
-
|
400
|
-
|
406
|
+
response = client(authentication).export(%w[people people_contact_details])
|
407
|
+
expect(response[:token]).to eq('68ef5ef0f64c0')
|
408
|
+
end
|
401
409
|
|
402
|
-
|
403
|
-
|
404
|
-
|
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.")
|
405
413
|
|
406
|
-
|
407
|
-
|
408
|
-
|
414
|
+
response = client(authentication).export('people', DateTime.new(2012, 0o3, 30, 23, 0o0, 0o0))
|
415
|
+
expect(response[:token]).to be_nil
|
416
|
+
end
|
409
417
|
|
410
|
-
|
411
|
-
|
412
|
-
|
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'.")
|
413
421
|
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
.to_return(@export_queued_response, @export_processing_response)
|
418
|
-
.then.to_raise(StandardError.new('network error'))
|
419
|
-
.then.to_return(@export_done_response)
|
420
|
-
|
421
|
-
# verify the correct log statement are made
|
422
|
-
expect_log('Sending POST request to api.4me.com:443/v1/export', :debug)
|
423
|
-
expect_log(%(Response:\n{\n "token": "68ef5ef0f64c0"\n}), :debug)
|
424
|
-
expect_log("Export for 'people' successfully queued with token '68ef5ef0f64c0'.")
|
425
|
-
expect_log('Sending GET request to api.4me.com:443/v1/export/68ef5ef0f64c0', :debug)
|
426
|
-
expect_log(%(Response:\n{\n "state": "queued"\n}), :debug)
|
427
|
-
expect_log("Export of 'people' is queued. Checking again in 30 seconds.", :debug)
|
428
|
-
expect_log('Sending GET request to api.4me.com:443/v1/export/68ef5ef0f64c0', :debug)
|
429
|
-
expect_log(%(Response:\n{\n "state": "processing"\n}), :debug)
|
430
|
-
expect_log("Export of 'people' is processing. Checking again in 30 seconds.", :debug)
|
431
|
-
expect_log('Sending GET request to api.4me.com:443/v1/export/68ef5ef0f64c0', :debug)
|
432
|
-
expect_log("Request failed: 500: No Response from Server - network error for 'api.4me.com:443/v1/export/68ef5ef0f64c0'", :error)
|
433
|
-
expect_log('Sending GET request to api.4me.com:443/v1/export/68ef5ef0f64c0', :debug)
|
434
|
-
expect_log(%(Response:\n{\n "state": "done",\n "url": "https://download.example.com/export.zip?AWSAccessKeyId=12345"\n}), :debug)
|
435
|
-
|
436
|
-
response = @client.export('people', nil, true)
|
437
|
-
expect(response[:state]).to eq('done')
|
438
|
-
expect(response[:url]).to eq('https://download.example.com/export.zip?AWSAccessKeyId=12345')
|
439
|
-
expect(progress_stub).to have_been_requested.times(4)
|
440
|
-
end
|
422
|
+
response = client(authentication).export('people', DateTime.new(2012, 0o3, 30, 23, 0o0, 0o0))
|
423
|
+
expect(response[:token]).to eq('68ef5ef0f64c0')
|
424
|
+
end
|
441
425
|
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
.to_return(@export_queued_response, @export_processing_response)
|
446
|
-
.then.to_raise(StandardError.new('network error')) # twice
|
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'.")
|
447
429
|
|
448
|
-
|
449
|
-
|
450
|
-
|
430
|
+
response = client(authentication).export('translations', nil, nil, 'nl')
|
431
|
+
expect(response[:token]).to eq('68ef5ef0f64c0')
|
432
|
+
end
|
451
433
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
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
|
458
461
|
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
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
|
463
467
|
|
464
|
-
|
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
|
465
478
|
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
expect_log("Request failed: 500: No Response from Server - network error for 'api.4me.com:443/v1/me'", :error)
|
471
|
-
|
472
|
-
client = Sdk4me::Client.new(api_token: 'secret', max_retry_time: -1)
|
473
|
-
response = client.get('me')
|
474
|
-
expect(stub).to have_been_requested.times(1)
|
475
|
-
expect(response.valid?).to be_falsey
|
476
|
-
expect(response.message).to eq("500: No Response from Server - network error for 'api.4me.com:443/v1/me'")
|
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
|
477
483
|
end
|
478
484
|
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug
|
483
|
-
expect_log("
|
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'")
|
484
495
|
end
|
485
|
-
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
486
496
|
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
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
|
494
512
|
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
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
|
508
527
|
end
|
509
|
-
end
|
510
528
|
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
client = Sdk4me::Client.new(api_token: 'secret', block_at_rate_limit: false)
|
518
|
-
response = client.get('me')
|
519
|
-
expect(stub).to have_been_requested.times(1)
|
520
|
-
expect(response.valid?).to be_falsey
|
521
|
-
expect(response.message).to eq('429: Too Many Requests')
|
522
|
-
end
|
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)
|
523
534
|
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug )
|
530
|
-
|
531
|
-
client = Sdk4me::Client.new(api_token: 'secret', block_at_rate_limit: true)
|
532
|
-
allow(client).to receive(:sleep)
|
533
|
-
response = client.get('me')
|
534
|
-
expect(stub).to have_been_requested.times(2)
|
535
|
-
expect(response.valid?).to be_truthy
|
536
|
-
expect(response[:name]).to eq('my name')
|
537
|
-
end
|
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
|
538
540
|
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
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
|
553
555
|
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
end
|
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
|
569
570
|
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
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
|
574
585
|
end
|
575
586
|
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
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
|
581
596
|
end
|
582
597
|
end
|
583
598
|
end
|