itrp-client 1.1.3 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +15 -13
- data/README.md +7 -0
- data/lib/itrp/client.rb +3 -1
- data/lib/itrp/client/version.rb +1 -1
- data/spec/lib/itrp/attachments_spec.rb +178 -0
- data/spec/lib/itrp/certificate_spec.rb +28 -0
- data/spec/lib/itrp/client_spec.rb +550 -0
- data/spec/lib/itrp/response_spec.rb +271 -0
- data/spec/lib/itrp_spec.rb +33 -0
- data/spec/spec_helper.rb +48 -0
- data/spec/support/fixtures/people.csv +3 -0
- data/spec/support/fixtures/upload.txt +1 -0
- data/spec/support/matchers/never_raise.rb +46 -0
- data/spec/support/util.rb +3 -0
- metadata +24 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3191ba404199a9c372100cfbaf4d098b4bf421ea
|
4
|
+
data.tar.gz: 6af16dbe18fe503f3186cb8ed1110bf8e00b518c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 722a14bab6be83a23c483d2dece405bf680901dbdff3f7389211e7dcea9f6af2028cdc0f07510e091f045ce8909fa7fbb432e486436307e10481ad96150a583e
|
7
|
+
data.tar.gz: fb8f9090c5ce0c3da2c81e4d8431521a132f448c94a493ebed8ff459f527eecf0b5173341ad5e36a949f2f5fe3438d170d5f5790c0c7d048905cbbbb64bd029a
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
itrp-client (1.
|
4
|
+
itrp-client (1.1.1)
|
5
5
|
activesupport
|
6
6
|
gem_config
|
7
7
|
mime-types
|
@@ -9,26 +9,28 @@ PATH
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
activesupport (4.2.
|
12
|
+
activesupport (4.2.7.1)
|
13
13
|
i18n (~> 0.7)
|
14
14
|
json (~> 1.7, >= 1.7.7)
|
15
15
|
minitest (~> 5.1)
|
16
16
|
thread_safe (~> 0.3, >= 0.3.4)
|
17
17
|
tzinfo (~> 1.1)
|
18
|
-
addressable (2.
|
18
|
+
addressable (2.5.0)
|
19
|
+
public_suffix (~> 2.0, >= 2.0.2)
|
19
20
|
crack (0.4.3)
|
20
21
|
safe_yaml (~> 1.0.0)
|
21
22
|
diff-lcs (1.2.5)
|
22
23
|
docile (1.1.5)
|
23
24
|
gem_config (0.3.1)
|
24
|
-
hashdiff (0.
|
25
|
+
hashdiff (0.3.1)
|
25
26
|
i18n (0.7.0)
|
26
27
|
json (1.8.3)
|
27
|
-
mime-types (3.
|
28
|
+
mime-types (3.1)
|
28
29
|
mime-types-data (~> 3.2015)
|
29
|
-
mime-types-data (3.
|
30
|
-
minitest (5.
|
31
|
-
|
30
|
+
mime-types-data (3.2016.0521)
|
31
|
+
minitest (5.9.1)
|
32
|
+
public_suffix (2.0.4)
|
33
|
+
rake (11.3.0)
|
32
34
|
rspec (3.3.0)
|
33
35
|
rspec-core (~> 3.3.0)
|
34
36
|
rspec-expectations (~> 3.3.0)
|
@@ -43,15 +45,15 @@ GEM
|
|
43
45
|
rspec-support (~> 3.3.0)
|
44
46
|
rspec-support (3.3.0)
|
45
47
|
safe_yaml (1.0.4)
|
46
|
-
simplecov (0.
|
48
|
+
simplecov (0.12.0)
|
47
49
|
docile (~> 1.1.0)
|
48
|
-
json (
|
50
|
+
json (>= 1.8, < 3)
|
49
51
|
simplecov-html (~> 0.10.0)
|
50
52
|
simplecov-html (0.10.0)
|
51
53
|
thread_safe (0.3.5)
|
52
54
|
tzinfo (1.2.2)
|
53
55
|
thread_safe (~> 0.1)
|
54
|
-
webmock (1.
|
56
|
+
webmock (2.1.0)
|
55
57
|
addressable (>= 2.3.6)
|
56
58
|
crack (>= 0.3.2)
|
57
59
|
hashdiff
|
@@ -65,7 +67,7 @@ DEPENDENCIES
|
|
65
67
|
rake
|
66
68
|
rspec (~> 3.3.0)
|
67
69
|
simplecov
|
68
|
-
webmock
|
70
|
+
webmock (~> 2)
|
69
71
|
|
70
72
|
BUNDLED WITH
|
71
|
-
1.
|
73
|
+
1.11.2
|
data/README.md
CHANGED
@@ -308,6 +308,13 @@ By default all actions on the ITRP Client will block until the ITRP API is acces
|
|
308
308
|
|
309
309
|
By setting the _block_at_rate_limit_ to `true` in the [configuration](#global-configuration) all actions will also block in case the [rate limit](http://developer.itrp.com/v1/#rate-limiting) is reached. The action is retried every 5 minutes until the [rate limit](http://developer.itrp.com/v1/#rate-limiting) is lifted again, which might take up to 1 hour.
|
310
310
|
|
311
|
+
### Translations
|
312
|
+
|
313
|
+
When exporting translations, the _locale_ parameter is required:
|
314
|
+
|
315
|
+
```
|
316
|
+
response = Itrp::Client.new.export('translations', nil, false, 'nl')
|
317
|
+
```
|
311
318
|
|
312
319
|
### Exception handling
|
313
320
|
|
data/lib/itrp/client.rb
CHANGED
@@ -151,10 +151,12 @@ module Itrp
|
|
151
151
|
# @param types: The types to export, e.g. person, organization, people_contact_details
|
152
152
|
# @param from: Retrieve all files since a given data and time
|
153
153
|
# @param block_until_completed: Set to true to monitor the export progress
|
154
|
+
# @param locale: Required for translations export
|
154
155
|
# @raise Itrp::Exception in case the export progress could not be monitored
|
155
|
-
def export(types, from = nil, block_until_completed = false)
|
156
|
+
def export(types, from = nil, block_until_completed = false, locale = nil)
|
156
157
|
data = {type: [types].flatten.join(',')}
|
157
158
|
data[:from] = from unless from.blank?
|
159
|
+
data[:locale] = locale unless locale.blank?
|
158
160
|
response = post('/export', data)
|
159
161
|
if response.valid?
|
160
162
|
if response.raw.code.to_s == '204'
|
data/lib/itrp/client/version.rb
CHANGED
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Itrp::Attachments do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
7
|
+
@attachments = Itrp::Attachments.new(@client)
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'upload_attachments!' do
|
11
|
+
it 'should not do anything when no :attachments are present' do
|
12
|
+
expect(@attachments.upload_attachments!('/requests', {status: :in_progress})).to be_nil
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should not do anything when :attachments is nil' do
|
16
|
+
expect(@attachments.upload_attachments!('/requests', {attachments: nil})).to be_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should not do anything when :attachments is empty' do
|
20
|
+
expect(@attachments.upload_attachments!('/requests', {attachments: []})).to be_nil
|
21
|
+
expect(@attachments.upload_attachments!('/requests', {attachments: [nil]})).to be_nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should show a error if no attachment may be uploaded' do
|
25
|
+
stub_request(:get, 'https://api.itrp.com/v1/sites/1?attachment_upload_token=true').with(basic_auth: ['secret', 'x']).to_return(body: {name: 'site 1'}.to_json)
|
26
|
+
expect_log('Attachments not allowed for /sites/1', :error)
|
27
|
+
expect(@attachments.upload_attachments!('/sites/1', {attachments: ['file1.png']})).to be_nil
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should raise an exception if no attachment may be uploaded' do
|
31
|
+
stub_request(:get, 'https://api.itrp.com/v1/sites/1?attachment_upload_token=true').with(basic_auth: ['secret', 'x']).to_return(body: {name: 'site 1'}.to_json)
|
32
|
+
message = 'Attachments not allowed for /sites/1'
|
33
|
+
expect{ @attachments.upload_attachments!('/sites/1', {attachments: ['file1.png'], attachments_exception: true}) }.to raise_error(::Itrp::UploadFailed, message)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should add /new to the path for new records' do
|
37
|
+
stub_request(:get, 'https://api.itrp.com/v1/sites/new?attachment_upload_token=true').with(basic_auth: ['secret', 'x']).to_return(body: {missing: 'storage'}.to_json)
|
38
|
+
expect_log('Attachments not allowed for /sites', :error)
|
39
|
+
expect(@attachments.upload_attachments!('/sites', {attachments: ['file1.png']})).to be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
[ [:requests, :note],
|
43
|
+
[:problems, :note],
|
44
|
+
[:contracts, :remarks],
|
45
|
+
[:cis, :remarks],
|
46
|
+
[:flsas, :remarks],
|
47
|
+
[:slas, :remarks],
|
48
|
+
[:service_instances, :remarks],
|
49
|
+
[:service_offerings, :summary],
|
50
|
+
[:any_other_model, :note]].each do |model, attribute|
|
51
|
+
|
52
|
+
it "should replace :attachments with :#{attribute}_attachments after upload at /#{model}" do
|
53
|
+
stub_request(:get, "https://api.itrp.com/v1/#{model}/new?attachment_upload_token=true").with(basic_auth: ['secret', 'x']).to_return(body: {storage_upload: 'conf'}.to_json)
|
54
|
+
expect(@attachments).to receive(:upload_attachment).with('conf', 'file1.png', false).ordered{ 'uploaded file1.png' }
|
55
|
+
expect(@attachments).to receive(:upload_attachment).with('conf', 'file2.zip', false).ordered{ 'uploaded file2.zip' }
|
56
|
+
data = {leave: 'me alone', attachments: %w(file1.png file2.zip)}
|
57
|
+
@attachments.upload_attachments!("/#{model}", data)
|
58
|
+
expect(data[:attachments]).to be_nil
|
59
|
+
expect(data[:leave]).to eq('me alone')
|
60
|
+
expect(data[:"#{attribute}_attachments"]).to eq(['uploaded file1.png', 'uploaded file2.zip'].to_json)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should set raise_exception flag to true when :attachments_exception is set' do
|
65
|
+
stub_request(:get, 'https://api.itrp.com/v1/requests/new?attachment_upload_token=true').with(basic_auth: ['secret', 'x']).to_return(body: {storage_upload: 'conf'}.to_json)
|
66
|
+
expect(@attachments).to receive(:upload_attachment).with('conf', 'file1.png', true).ordered{ 'uploaded file1.png' }
|
67
|
+
data = {leave: 'me alone', attachments: 'file1.png', attachments_exception: true}
|
68
|
+
@attachments.upload_attachments!('/requests', data)
|
69
|
+
expect(data[:attachments]).to be_nil
|
70
|
+
expect(data[:attachments_exception]).to be_nil
|
71
|
+
expect(data[:leave]).to eq('me alone')
|
72
|
+
expect(data[:note_attachments]).to eq(['uploaded file1.png'].to_json)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'upload_attachment' do
|
77
|
+
|
78
|
+
it 'should log an exception when the file could not be found' do
|
79
|
+
expect_log('Attachment upload failed: file does not exist: unknown_file', :error)
|
80
|
+
expect(@attachments.send(:upload_attachment, nil, 'unknown_file', false)).to be_nil
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should raise an exception when the file could not be found' do
|
84
|
+
message = 'Attachment upload failed: file does not exist: unknown_file'
|
85
|
+
expect{ @attachments.send(:upload_attachment, nil, 'unknown_file', true) }.to raise_error(::Itrp::UploadFailed, message)
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'aws' do
|
89
|
+
before(:each) do
|
90
|
+
@aws_conf = {
|
91
|
+
provider: 'aws',
|
92
|
+
upload_uri: 'https://itrp.s3.amazonaws.com/',
|
93
|
+
access_key: "AKIA6RYQ",
|
94
|
+
success_url: "https://mycompany.itrp.com/s3_success?sig=99e82e8a046",
|
95
|
+
policy: "eydlgIH0=",
|
96
|
+
signature: 'nbhdec4k=',
|
97
|
+
upload_path: 'attachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/'
|
98
|
+
}
|
99
|
+
@key_template = 'attachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/${filename}'
|
100
|
+
@key = 'attachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/upload.txt'
|
101
|
+
|
102
|
+
@multi_part_body = "--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"Content-Type\"\r\n\r\napplication/octet-stream\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"x-amz-server-side-encryption\"\r\n\r\nAES256\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"key\"\r\n\r\nattachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/${filename}\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"AWSAccessKeyId\"\r\n\r\nAKIA6RYQ\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"acl\"\r\n\r\nprivate\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"signature\"\r\n\r\nnbhdec4k=\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"success_action_status\"\r\n\r\n201\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"policy\"\r\n\r\neydlgIH0=\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"file\"; filename=\"#{@fixture_dir}/upload.txt\"\r\nContent-Type: text/plain\r\n\r\ncontent\r\n--0123456789ABLEWASIEREISAWELBA9876543210--"
|
103
|
+
@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'}
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should open a file from disk' do
|
107
|
+
expect(@attachments).to receive(:aws_upload).with(@aws_conf, @key_template, @key, kind_of(File))
|
108
|
+
expect(@attachments.send(:upload_attachment, @aws_conf, "#{@fixture_dir}/upload.txt", false)).to eq({key: @key, filesize: 7})
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should sent the upload to AWS' do
|
112
|
+
stub_request(:post, 'https://itrp.s3.amazonaws.com/').with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: 'OK', status: 303, headers: {'Location' => 'https://mycompany.itrp.com/s3_success?sig=99e82e8a046'})
|
113
|
+
stub_request(:get, "https://api.itrp.com/v1/s3_success?sig=99e82e8a046&key=#{@key}").with(basic_auth: ['secret', 'x']).to_return(body: {}.to_json)
|
114
|
+
expect(@attachments.send(:upload_attachment, @aws_conf, "#{@fixture_dir}/upload.txt", false)).to eq({key: @key, filesize: 7})
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should report an error when AWS upload fails' do
|
118
|
+
stub_request(:post, 'https://itrp.s3.amazonaws.com/').with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: %(<?xml version="1.0" encoding="UTF-8"?>\n<Error><Code>AccessDenied</Code><Message>Invalid according to Policy</Message><RequestId>1FECC4B719E426B1</RequestId><HostId>15+14lXt+HlF</HostId></Error>), status: 303, headers: {'Location' => 'https://mycompany.itrp.com/s3_success?sig=99e82e8a046'})
|
119
|
+
expect_log("Attachment upload failed: AWS upload to https://itrp.s3.amazonaws.com/ for #{@key} failed: Invalid according to Policy", :error)
|
120
|
+
expect(@attachments.send(:upload_attachment, @aws_conf, "#{@fixture_dir}/upload.txt", false)).to be_nil
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'should report an error when ITRP confirmation fails' do
|
124
|
+
stub_request(:post, 'https://itrp.s3.amazonaws.com/').with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: 'OK', status: 303, headers: {'Location' => 'https://mycompany.itrp.com/s3_success?sig=99e82e8a046'})
|
125
|
+
stub_request(:get, "https://api.itrp.com/v1/s3_success?sig=99e82e8a046&key=#{@key}").with(basic_auth: ['secret', 'x']).to_return(body: {message: 'oops!'}.to_json)
|
126
|
+
expect_log('Request failed: oops!', :error)
|
127
|
+
expect_log("Attachment upload failed: ITRP confirmation s3_success?sig=99e82e8a046 for #{@key} failed: oops!", :error)
|
128
|
+
expect(@attachments.send(:upload_attachment, @aws_conf, "#{@fixture_dir}/upload.txt", false)).to be_nil
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'should raise an exception when AWS upload fails' do
|
132
|
+
stub_request(:post, 'https://itrp.s3.amazonaws.com/').with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: %(<?xml version="1.0" encoding="UTF-8"?>\n<Error><Code>AccessDenied</Code><Message>Invalid according to Policy</Message><RequestId>1FECC4B719E426B1</RequestId><HostId>15+14lXt+HlF</HostId></Error>), status: 303, headers: {'Location' => 'https://mycompany.itrp.com/s3_success?sig=99e82e8a046'})
|
133
|
+
message = "Attachment upload failed: AWS upload to https://itrp.s3.amazonaws.com/ for #{@key} failed: Invalid according to Policy"
|
134
|
+
expect{ @attachments.send(:upload_attachment, @aws_conf, "#{@fixture_dir}/upload.txt", true) }.to raise_error(::Itrp::UploadFailed, message)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'itrp' do
|
139
|
+
before(:each) do
|
140
|
+
@itrp_conf = {
|
141
|
+
provider: 'local',
|
142
|
+
upload_uri: 'https://api.itrp.com/attachments',
|
143
|
+
upload_path: 'attachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/'
|
144
|
+
}
|
145
|
+
@key_template = 'attachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/${filename}'
|
146
|
+
@key = 'attachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/upload.txt'
|
147
|
+
|
148
|
+
@multi_part_body = "--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"Content-Type\"\r\n\r\napplication/octet-stream\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"file\"; filename=\"#{@spec_dir}/support/fixtures/upload.txt\"\r\nContent-Type: text/plain\r\n\r\ncontent\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"key\"\r\n\r\nattachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/${filename}\r\n--0123456789ABLEWASIEREISAWELBA9876543210--"
|
149
|
+
@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'}
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should open a file from disk' do
|
153
|
+
expect(@attachments).to receive(:itrp_upload).with(@itrp_conf, @key_template, @key, kind_of(File))
|
154
|
+
expect(@attachments.send(:upload_attachment, @itrp_conf, "#{@fixture_dir}/upload.txt", false)).to eq({key: @key, filesize: 7})
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should sent the upload to ITRP' do
|
158
|
+
stub_request(:post, 'https://api.itrp.com/v1/attachments').with(basic_auth: ['secret', 'x']).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {}.to_json)
|
159
|
+
expect(@attachments.send(:upload_attachment, @itrp_conf, "#{@fixture_dir}/upload.txt", false)).to eq({key: @key, filesize: 7})
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should report an error when ITRP upload fails' do
|
163
|
+
stub_request(:post, 'https://api.itrp.com/v1/attachments').with(basic_auth: ['secret', 'x']).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {message: 'oops!'}.to_json)
|
164
|
+
expect_log('Request failed: oops!', :error)
|
165
|
+
expect_log("Attachment upload failed: ITRP upload to https://api.itrp.com/attachments for #{@key} failed: oops!", :error)
|
166
|
+
expect(@attachments.send(:upload_attachment, @itrp_conf, "#{@fixture_dir}/upload.txt", false)).to be_nil
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'should raise an exception when ITRP upload fails' do
|
170
|
+
stub_request(:post, 'https://api.itrp.com/v1/attachments').with(basic_auth: ['secret', 'x']).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {message: 'oops!'}.to_json)
|
171
|
+
expect_log('Request failed: oops!', :error)
|
172
|
+
message = "Attachment upload failed: ITRP upload to https://api.itrp.com/attachments for #{@key} failed: oops!"
|
173
|
+
expect{ @attachments.send(:upload_attachment, @itrp_conf, "#{@fixture_dir}/upload.txt", true) }.to raise_error(::Itrp::UploadFailed, message)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'ca-bundle.crt' do
|
4
|
+
|
5
|
+
it 'should be able to connect to the ITRP API' do
|
6
|
+
WebMock.allow_net_connect!
|
7
|
+
client = Itrp::Client.new(api_token: 'invalid', max_retry_time: -1)
|
8
|
+
result = {}
|
9
|
+
|
10
|
+
# no exception concerning the certificate
|
11
|
+
expect { result[:response] = client.get('me') }.not_to raise_error
|
12
|
+
response = result[:response]
|
13
|
+
expect(response.valid?).to be_falsey
|
14
|
+
|
15
|
+
# expecting 401 error
|
16
|
+
expect(response.message).to eq('401: Access credentials required')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should be able to connect to S3' do
|
20
|
+
WebMock.allow_net_connect!
|
21
|
+
http = Net::HTTP.new('itrp-eu.s3-eu-west-1.amazonaws.com', 443)
|
22
|
+
http.read_timeout = 1
|
23
|
+
http.use_ssl = true
|
24
|
+
|
25
|
+
# no SSL error please
|
26
|
+
expect{ http.start{ |_http| _http.request(Net::HTTP::Get.new('/exports/20141107/')) } }.to never_raise(OpenSSL::SSL::SSLError)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,550 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Itrp::Client do
|
4
|
+
|
5
|
+
context 'Itrp.config' do
|
6
|
+
before(:each) do
|
7
|
+
Itrp.configure do |config|
|
8
|
+
config.max_retry_time = 120 # override default value (5400)
|
9
|
+
config.api_token = 'secret' # set value
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should define the MAX_PAGE_SIZE' do
|
14
|
+
expect(Itrp::Client::MAX_PAGE_SIZE).to eq(100)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should use the Itrp configuration' do
|
18
|
+
client = Itrp::Client.new
|
19
|
+
expect(client.option(:host)).to eq('https://api.itrp.com') # default value
|
20
|
+
expect(client.option(:api_token)).to eq('secret') # value set using Itrp.config
|
21
|
+
expect(client.option(:max_retry_time)).to eq(120) # value overridden in Itrp.config
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should override the Itrp configuration' do
|
25
|
+
client = Itrp::Client.new(host: 'https://demo.itrp.com', api_token: 'unknown', block_at_rate_limit: true)
|
26
|
+
expect(client.option(:read_timeout)).to eq(25) # default value
|
27
|
+
expect(client.option(:host)).to eq('https://demo.itrp.com') # default value overridden in Client.new
|
28
|
+
expect(client.option(:api_token)).to eq('unknown') # value set using Itrp.config and overridden in Client.new
|
29
|
+
expect(client.option(:max_retry_time)).to eq(120) # value overridden in Itrp.config
|
30
|
+
expect(client.option(:block_at_rate_limit)).to eq(true) # value overridden in Client.new
|
31
|
+
end
|
32
|
+
|
33
|
+
[:host, :api_version, :api_token].each do |required_option|
|
34
|
+
it "should require option #{required_option}" do
|
35
|
+
expect { Itrp::Client.new(required_option => '') }.to raise_error("Missing required configuration option #{required_option}")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
[ ['https://api.itrp.com', true, 'api.itrp.com', 443],
|
40
|
+
['https://api.example.com:777', true, 'api.example.com', 777],
|
41
|
+
['http://itrp.example.com', false, 'itrp.example.com', 80],
|
42
|
+
['http://itrp.example.com:777', false, 'itrp.example.com', 777]
|
43
|
+
].each do |host, ssl, domain, port|
|
44
|
+
it 'should parse ssl, host and port' do
|
45
|
+
client = Itrp::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)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should set the ca-bundle.crt file' do
|
54
|
+
http = Net::HTTP.new('https://api.itrp.com')
|
55
|
+
http.use_ssl = true
|
56
|
+
|
57
|
+
on_disk = `ls #{http.ca_file}`
|
58
|
+
expect(on_disk).not_to match(/cannot access/)
|
59
|
+
expect(on_disk).to match(/\/ca-bundle.crt$/)
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'headers' do
|
63
|
+
before(:each) do
|
64
|
+
@client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should set the content type header' do
|
68
|
+
stub = stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).with(headers: {'Content-Type' => 'application/json'}).to_return(body: {name: 'my name'}.to_json)
|
69
|
+
@client.get('me')
|
70
|
+
expect(stub).to have_been_requested
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should add the X-ITRP-Account header' do
|
74
|
+
client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1, account: 'test')
|
75
|
+
stub = stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).with(headers: {'X-ITRP-Account' => 'test'}).to_return(body: {name: 'my name'}.to_json)
|
76
|
+
client.get('me')
|
77
|
+
expect(stub).to have_been_requested
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should add the X-ITRP-Source header' do
|
81
|
+
client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1, source: 'myapp')
|
82
|
+
stub = stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).with(headers: {'X-ITRP-Source' => 'myapp'}).to_return(body: {name: 'my name'}.to_json)
|
83
|
+
client.get('me')
|
84
|
+
expect(stub).to have_been_requested
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should be able to override headers' do
|
88
|
+
stub = stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).with(headers: {'Content-Type' => 'application/x-www-form-urlencoded'}).to_return(body: {name: 'my name'}.to_json)
|
89
|
+
@client.get('me', {}, {'Content-Type' => 'application/x-www-form-urlencoded'})
|
90
|
+
expect(stub).to have_been_requested
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should set the other headers' do
|
94
|
+
stub = stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).with(headers: {'X-ITRP-Other' => 'value'}).to_return(body: {name: 'my name'}.to_json)
|
95
|
+
@client.get('me', {}, {'X-ITRP-Other' => 'value'})
|
96
|
+
expect(stub).to have_been_requested
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should accept headers in the each call' do
|
100
|
+
stub = stub_request(:get, 'https://api.itrp.com/v1/requests?fields=subject&page=1&per_page=100').with(basic_auth: ['secret', 'x']).with(headers: {'X-ITRP-Secret' => 'special'}).to_return(body: [{id: 1, subject: 'Subject 1'}, {id: 2, subject: 'Subject 2'}, {id: 3, subject: 'Subject 3'}].to_json)
|
101
|
+
@client.each('requests', {fields: 'subject'}, {'X-ITRP-Secret' => 'special'}) do |request|
|
102
|
+
expect(request[:subject]).to eq("Subject #{request[:id]}")
|
103
|
+
end
|
104
|
+
expect(stub).to have_been_requested
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'each' do
|
109
|
+
before(:each) do
|
110
|
+
@client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should yield each result' do
|
114
|
+
stub_request(:get, 'https://api.itrp.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]}")
|
117
|
+
end
|
118
|
+
expect(nr_of_requests).to eq(3)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should retrieve multiple pages' do
|
122
|
+
stub_page1 = stub_request(:get, 'https://api.itrp.com/v1/requests?page=1&per_page=2').with(basic_auth: ['secret', 'x']).to_return(body: [{id: 1, subject: 'Subject 1'}, {id: 2, subject: 'Subject 2'}].to_json, headers: {'Link' => '<https://api.itrp.com/v1/requests?page=1&per_page=2>; rel="first",<https://api.itrp.com/v1/requests?page=2&per_page=2>; rel="next",<https://api.itrp.com/v1/requests?page=2&per_page=2>; rel="last"'})
|
123
|
+
stub_page2 = stub_request(:get, 'https://api.itrp.com/v1/requests?page=2&per_page=2').with(basic_auth: ['secret', 'x']).to_return(body: [{id: 3, subject: 'Subject 3'}].to_json, headers: {'Link' => '<https://api.itrp.com/v1/requests?page=1&per_page=2>; rel="first",<https://api.itrp.com/v1/requests?page=1&per_page=2>; rel="prev",<https://api.itrp.com/v1/requests?page=2&per_page=2>; rel="last"'})
|
124
|
+
nr_of_requests = @client.each('requests', {per_page: 2}) do |request|
|
125
|
+
expect(request[:subject]).to eq("Subject #{request[:id]}")
|
126
|
+
end
|
127
|
+
expect(nr_of_requests).to eq(3)
|
128
|
+
expect(stub_page2).to have_been_requested
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context 'get' do
|
133
|
+
before(:each) do
|
134
|
+
@client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should return a response' do
|
138
|
+
stub_request(:get, 'https://api.itrp.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.itrp.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
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should not cast arrays in post and put calls' do
|
163
|
+
client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
164
|
+
stub = stub_request(:post, 'https://api.itrp.com/v1/people').with(basic_auth: ['secret', 'x']).with(body: {user_ids: [1, 2, 3]}, headers: {'X-ITRP-Custom' => 'custom'}).to_return(body: {id: 101}.to_json)
|
165
|
+
client.post('people', {user_ids: [1, 2, 3]}, {'X-ITRP-Custom' => 'custom'})
|
166
|
+
expect(stub).to have_been_requested
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'should not cast hashes in post and put calls' do
|
170
|
+
client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
171
|
+
stub = stub_request(:patch, 'https://api.itrp.com/v1/people/55').with(basic_auth: ['secret', 'x']).with(body: '{"contacts_attributes":{"0":{"protocol":"email","label":"work","uri":"work@example.com"}}}', headers: {'X-ITRP-Custom' => 'custom'}).to_return(body: {id: 101}.to_json)
|
172
|
+
client.put('people/55', {contacts_attributes: {0 => {protocol: :email, label: :work, uri: 'work@example.com'}}}, {'X-ITRP-Custom' => 'custom'})
|
173
|
+
expect(stub).to have_been_requested
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'should not double escape symbols' do
|
177
|
+
client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
178
|
+
stub = stub_request(:patch, 'https://api.itrp.com/v1/people/55').with(basic_auth: ['secret', 'x']).with(body: '{"status":"waiting_for"}').to_return(body: {id: 101}.to_json)
|
179
|
+
client.put('people/55', {status: :waiting_for})
|
180
|
+
expect(stub).to have_been_requested
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'should handle fancy filter operations' do
|
184
|
+
now = DateTime.now
|
185
|
+
stub = stub_request(:get, "https://api.itrp.com/v1/people?created_at=>#{now.new_offset(0).iso8601.gsub('+', '%2B')}&id!=15").with(basic_auth: ['secret', 'x']).to_return(body: {name: 'my name'}.to_json)
|
186
|
+
@client.get('people', {'created_at=>' => now, 'id!=' => 15})
|
187
|
+
expect(stub).to have_been_requested
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'should append parameters' do
|
191
|
+
stub = stub_request(:get, 'https://api.itrp.com/v1/people?id!=15&primary_email=me@example.com').with(basic_auth: ['secret', 'x']).to_return(body: {name: 'my name'}.to_json)
|
192
|
+
@client.get('people?id!=15', {primary_email: 'me@example.com'})
|
193
|
+
expect(stub).to have_been_requested
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context 'patch' do
|
199
|
+
[:put, :patch].each do |method|
|
200
|
+
it 'should send patch requests with parameters and headers for #{method} calls' do
|
201
|
+
client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
202
|
+
stub = stub_request(:patch, 'https://api.itrp.com/v1/people/1').with(basic_auth: ['secret', 'x']).with(body: {name: 'New Name'}, headers: {'X-ITRP-Custom' => 'custom'}).to_return(body: {id: 1}.to_json)
|
203
|
+
client.send(method, 'people/1', {name: 'New Name'}, {'X-ITRP-Custom' => 'custom'})
|
204
|
+
expect(stub).to have_been_requested
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context 'post' do
|
210
|
+
it 'should send post requests with parameters and headers' do
|
211
|
+
client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
212
|
+
stub = stub_request(:post, 'https://api.itrp.com/v1/people').with(basic_auth: ['secret', 'x']).with(body: {name: 'New Name'}, headers: {'X-ITRP-Custom' => 'custom'}).to_return(body: {id: 101}.to_json)
|
213
|
+
client.post('people', {name: 'New Name'}, {'X-ITRP-Custom' => 'custom'})
|
214
|
+
expect(stub).to have_been_requested
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
context 'delete' do
|
219
|
+
it 'should send delete requests with parameters and headers' do
|
220
|
+
client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
221
|
+
stub = stub_request(:delete, 'https://api.itrp.com/v1/people?id=value').with(basic_auth: ['secret', 'x']).with(headers: {'X-ITRP-Custom' => 'custom'}).to_return(body: '', status: 204)
|
222
|
+
response = client.delete('people', {id: 'value'}, {'X-ITRP-Custom' => 'custom'})
|
223
|
+
expect(stub).to have_been_requested
|
224
|
+
expect(response.valid?).to be_truthy
|
225
|
+
expect(response.json).to eq({})
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context 'attachments' do
|
230
|
+
before(:each) do
|
231
|
+
@client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'should not log an error for XML responses' do
|
235
|
+
xml = %(<?xml version="1.0" encoding="UTF-8"?>\n<details>some info</details>)
|
236
|
+
stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).to_return(body: xml)
|
237
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug)
|
238
|
+
expect_log("XML response:\n#{xml}", :debug)
|
239
|
+
response = @client.get('me')
|
240
|
+
expect(response.valid?).to be_falsey
|
241
|
+
expect(response.raw.body).to eq(xml)
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'should not log an error for redirects' do
|
245
|
+
stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).to_return(body: '', status: 303, headers: {'Location' => 'http://redirect.example.com/to/here'})
|
246
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug)
|
247
|
+
expect_log('Redirect: http://redirect.example.com/to/here', :debug)
|
248
|
+
response = @client.get('me')
|
249
|
+
expect(response.valid?).to be_falsey
|
250
|
+
expect(response.raw.body).to be_nil
|
251
|
+
end
|
252
|
+
|
253
|
+
it "should not parse attachments for get requests" do
|
254
|
+
expect(Itrp::Attachments).not_to receive(:new)
|
255
|
+
stub_request(:get, 'https://api.itrp.com/v1/requests/777?attachments=/tmp/first.png,/tmp/second.zip¬e=note').with(basic_auth: ['secret', 'x']).to_return(body: {id: 777, upload_called: false}.to_json)
|
256
|
+
|
257
|
+
response = @client.get('/requests/777', {note: 'note', attachments: ['/tmp/first.png', '/tmp/second.zip'] })
|
258
|
+
expect(response.valid?).to be_truthy
|
259
|
+
expect(response[:upload_called]).to be_falsey
|
260
|
+
end
|
261
|
+
|
262
|
+
[:post, :patch].each do |method|
|
263
|
+
it "should parse attachments for #{method} requests" do
|
264
|
+
attachments = double('Itrp::Attachments')
|
265
|
+
expect(attachments).to receive(:upload_attachments!) do |path, data|
|
266
|
+
expect(path).to eq '/requests/777'
|
267
|
+
expect(data[:attachments]).to eq ['/tmp/first.png', '/tmp/second.zip']
|
268
|
+
data.delete(:attachments)
|
269
|
+
data[:note_attachments] = 'processed'
|
270
|
+
end
|
271
|
+
expect(Itrp::Attachments).to receive(:new).with(@client){ attachments }
|
272
|
+
stub_request(method, 'https://api.itrp.com/v1/requests/777').with(basic_auth: ['secret', 'x']).with(body: {note: 'note', note_attachments: 'processed' }).to_return(body: {id: 777, upload_called: true}.to_json)
|
273
|
+
|
274
|
+
response = @client.send(method, '/requests/777', {note: 'note', attachments: ['/tmp/first.png', '/tmp/second.zip'] })
|
275
|
+
expect(response.valid?).to be_truthy
|
276
|
+
expect(response[:upload_called]).to be_truthy
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|
281
|
+
|
282
|
+
context 'import' do
|
283
|
+
before(:each) do
|
284
|
+
@client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
285
|
+
@multi_part_body = "--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\npeople\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"file\"; filename=\"#{@fixture_dir}/people.csv\"\r\nContent-Type: text/comma-separated-values\r\n\r\nPrimary Email,Name\nchess.cole@example.com,Chess Cole\ned.turner@example.com,Ed Turner\r\n--0123456789ABLEWASIEREISAWELBA9876543210--"
|
286
|
+
@multi_part_headers = {'Accept'=>'*/*', 'Content-Type'=>'multipart/form-data; boundary=0123456789ABLEWASIEREISAWELBA9876543210', 'User-Agent'=>'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6'}
|
287
|
+
|
288
|
+
@import_queued_response = {body: {state: 'queued'}.to_json}
|
289
|
+
@import_processing_response = {body: {state: 'processing'}.to_json}
|
290
|
+
@import_done_response = {body: {state: 'done', results: {errors: 0, updated: 1, created: 1, failures: 0, unchanged: 0, deleted: 0}}.to_json}
|
291
|
+
@import_failed_response = {body: {state: 'error', message: 'Invalid byte sequence in UTF-8 on line 2', results: {errors: 1, updated: 1, created: 0, failures: 1, unchanged: 0, deleted: 0}}.to_json}
|
292
|
+
allow(@client).to receive(:sleep)
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'should import a CSV file' do
|
296
|
+
stub_request(:post, 'https://api.itrp.com/v1/import').with(basic_auth: ['secret', 'x']).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
297
|
+
expect_log("Import file '#{@fixture_dir}/people.csv' successfully uploaded with token '68ef5ef0f64c0'.")
|
298
|
+
|
299
|
+
response = @client.import(File.new("#{@fixture_dir}/people.csv"), 'people')
|
300
|
+
expect(response[:token]).to eq('68ef5ef0f64c0')
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'should import a CSV file by filename' do
|
304
|
+
stub_request(:post, 'https://api.itrp.com/v1/import').with(basic_auth: ['secret', 'x']).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
305
|
+
response = @client.import("#{@fixture_dir}/people.csv", 'people')
|
306
|
+
expect(response[:token]).to eq('68ef5ef0f64c0')
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'should wait for the import to complete' do
|
310
|
+
stub_request(:post, 'https://api.itrp.com/v1/import').with(basic_auth: ['secret', 'x']).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
311
|
+
progress_stub = stub_request(:get, 'https://api.itrp.com/v1/import/68ef5ef0f64c0').with(basic_auth: ['secret', 'x'])
|
312
|
+
.to_return(@import_queued_response, @import_processing_response)
|
313
|
+
.then.to_raise(StandardError.new('network error'))
|
314
|
+
.then.to_return(@import_done_response)
|
315
|
+
|
316
|
+
# verify the correct log statement are made
|
317
|
+
expect_log('Sending POST request to api.itrp.com:443/v1/import', :debug)
|
318
|
+
expect_log("Response:\n{\n \"token\": \"68ef5ef0f64c0\"\n}", :debug)
|
319
|
+
expect_log("Import file '#{@fixture_dir}/people.csv' successfully uploaded with token '68ef5ef0f64c0'.")
|
320
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/import/68ef5ef0f64c0', :debug)
|
321
|
+
expect_log("Response:\n{\n \"state\": \"queued\"\n}", :debug)
|
322
|
+
expect_log("Import of '#{@fixture_dir}/people.csv' is queued. Checking again in 30 seconds.", :debug)
|
323
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/import/68ef5ef0f64c0', :debug)
|
324
|
+
expect_log("Response:\n{\n \"state\": \"processing\"\n}", :debug)
|
325
|
+
expect_log("Import of '#{@fixture_dir}/people.csv' is processing. Checking again in 30 seconds.", :debug)
|
326
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/import/68ef5ef0f64c0', :debug)
|
327
|
+
expect_log("Request failed: 500: No Response from Server - network error for 'api.itrp.com:443/v1/import/68ef5ef0f64c0'", :error)
|
328
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/import/68ef5ef0f64c0', :debug)
|
329
|
+
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)
|
330
|
+
|
331
|
+
response = @client.import("#{@fixture_dir}/people.csv", 'people', true)
|
332
|
+
expect(response[:state]).to eq('done')
|
333
|
+
expect(response[:results][:updated]).to eq(1)
|
334
|
+
expect(progress_stub).to have_been_requested.times(4)
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'should wait for the import to fail' do
|
338
|
+
stub_request(:post, 'https://api.itrp.com/v1/import').with(basic_auth: ['secret', 'x']).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
339
|
+
progress_stub = stub_request(:get, 'https://api.itrp.com/v1/import/68ef5ef0f64c0').with(basic_auth: ['secret', 'x']).to_return(@import_queued_response, @import_processing_response, @import_failed_response)
|
340
|
+
|
341
|
+
expect{ @client.import("#{@fixture_dir}/people.csv", 'people', true) }.to raise_error(Itrp::Exception, "Unable to monitor progress for people import. Invalid byte sequence in UTF-8 on line 2")
|
342
|
+
expect(progress_stub).to have_been_requested.times(4)
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'should not continue when there is an error connecting to ITRP' do
|
346
|
+
stub_request(:post, 'https://api.itrp.com/v1/import').with(basic_auth: ['secret', 'x']).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
347
|
+
progress_stub = stub_request(:get, 'https://api.itrp.com/v1/import/68ef5ef0f64c0').with(basic_auth: ['secret', 'x'])
|
348
|
+
.to_return(@import_queued_response, @import_processing_response)
|
349
|
+
.then.to_raise(StandardError.new('network error')) # twice
|
350
|
+
|
351
|
+
expect{ @client.import("#{@fixture_dir}/people.csv", 'people', true) }.to raise_error(Itrp::Exception, "Unable to monitor progress for people import. 500: No Response from Server - network error for 'api.itrp.com:443/v1/import/68ef5ef0f64c0'")
|
352
|
+
expect(progress_stub).to have_been_requested.times(4)
|
353
|
+
end
|
354
|
+
|
355
|
+
it 'should return an invalid response in case waiting for progress is false' do
|
356
|
+
stub_request(:post, 'https://api.itrp.com/v1/import').with(basic_auth: ['secret', 'x']).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {message: 'oops!'}.to_json)
|
357
|
+
response = @client.import("#{@fixture_dir}/people.csv", 'people', false)
|
358
|
+
expect(response.valid?).to be_falsey
|
359
|
+
expect(response.message).to eq('oops!')
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'should raise an UploadFailed exception in case waiting for progress is true' do
|
363
|
+
stub_request(:post, 'https://api.itrp.com/v1/import').with(basic_auth: ['secret', 'x']).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {message: 'oops!'}.to_json)
|
364
|
+
expect{ @client.import("#{@fixture_dir}/people.csv", 'people', true) }.to raise_error(Itrp::UploadFailed, 'Failed to queue people import. oops!')
|
365
|
+
end
|
366
|
+
|
367
|
+
end
|
368
|
+
|
369
|
+
|
370
|
+
context 'export' do
|
371
|
+
before(:each) do
|
372
|
+
@client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
373
|
+
|
374
|
+
@export_queued_response = {body: {state: 'queued'}.to_json}
|
375
|
+
@export_processing_response = {body: {state: 'processing'}.to_json}
|
376
|
+
@export_done_response = {body: {state: 'done', url: 'https://download.example.com/export.zip?AWSAccessKeyId=12345'}.to_json}
|
377
|
+
allow(@client).to receive(:sleep)
|
378
|
+
end
|
379
|
+
|
380
|
+
it 'should export multiple types' do
|
381
|
+
stub_request(:post, 'https://api.itrp.com/v1/export').with(basic_auth: ['secret', 'x']).with(body: {type: 'people,people_contact_details'}).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
382
|
+
expect_log("Export for 'people,people_contact_details' successfully queued with token '68ef5ef0f64c0'.")
|
383
|
+
|
384
|
+
response = @client.export(['people', 'people_contact_details'])
|
385
|
+
expect(response[:token]).to eq('68ef5ef0f64c0')
|
386
|
+
end
|
387
|
+
|
388
|
+
it 'should indicate when nothing is exported' do
|
389
|
+
stub_request(:post, 'https://api.itrp.com/v1/export').with(basic_auth: ['secret', 'x']).with(body: {type: 'people', from: '2012-03-30T23:00:00+00:00'}).to_return(status: 204)
|
390
|
+
expect_log("No changed records for 'people' since 2012-03-30T23:00:00+00:00.")
|
391
|
+
|
392
|
+
response = @client.export('people', DateTime.new(2012,03,30,23,00,00))
|
393
|
+
expect(response[:token]).to be_nil
|
394
|
+
end
|
395
|
+
|
396
|
+
it 'should export since a certain time' do
|
397
|
+
stub_request(:post, 'https://api.itrp.com/v1/export').with(basic_auth: ['secret', 'x']).with(body: {type: 'people', from: '2012-03-30T23:00:00+00:00'}).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
398
|
+
expect_log("Export for 'people' successfully queued with token '68ef5ef0f64c0'.")
|
399
|
+
|
400
|
+
response = @client.export('people', DateTime.new(2012,03,30,23,00,00))
|
401
|
+
expect(response[:token]).to eq('68ef5ef0f64c0')
|
402
|
+
end
|
403
|
+
|
404
|
+
it 'should export with locale' do
|
405
|
+
stub_request(:post, 'https://api.itrp.com/v1/export').with(basic_auth: ['secret', 'x']).with(body: {type: 'translations', locale: 'nl'}).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
406
|
+
expect_log("Export for 'translations' successfully queued with token '68ef5ef0f64c0'.")
|
407
|
+
|
408
|
+
response = @client.export('translations', nil, nil, 'nl')
|
409
|
+
expect(response[:token]).to eq('68ef5ef0f64c0')
|
410
|
+
end
|
411
|
+
|
412
|
+
it 'should wait for the export to complete' do
|
413
|
+
stub_request(:post, 'https://api.itrp.com/v1/export').with(basic_auth: ['secret', 'x']).with(body: {type: 'people'}).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
414
|
+
progress_stub = stub_request(:get, 'https://api.itrp.com/v1/export/68ef5ef0f64c0').with(basic_auth: ['secret', 'x'])
|
415
|
+
.to_return(@export_queued_response, @export_processing_response)
|
416
|
+
.then.to_raise(StandardError.new('network error'))
|
417
|
+
.then.to_return(@export_done_response)
|
418
|
+
|
419
|
+
# verify the correct log statement are made
|
420
|
+
expect_log('Sending POST request to api.itrp.com:443/v1/export', :debug)
|
421
|
+
expect_log(%(Response:\n{\n "token": "68ef5ef0f64c0"\n}), :debug)
|
422
|
+
expect_log("Export for 'people' successfully queued with token '68ef5ef0f64c0'.")
|
423
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/export/68ef5ef0f64c0', :debug)
|
424
|
+
expect_log(%(Response:\n{\n "state": "queued"\n}), :debug)
|
425
|
+
expect_log("Export of 'people' is queued. Checking again in 30 seconds.", :debug)
|
426
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/export/68ef5ef0f64c0', :debug)
|
427
|
+
expect_log(%(Response:\n{\n "state": "processing"\n}), :debug)
|
428
|
+
expect_log("Export of 'people' is processing. Checking again in 30 seconds.", :debug)
|
429
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/export/68ef5ef0f64c0', :debug)
|
430
|
+
expect_log("Request failed: 500: No Response from Server - network error for 'api.itrp.com:443/v1/export/68ef5ef0f64c0'", :error)
|
431
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/export/68ef5ef0f64c0', :debug)
|
432
|
+
expect_log(%(Response:\n{\n "state": "done",\n "url": "https://download.example.com/export.zip?AWSAccessKeyId=12345"\n}), :debug)
|
433
|
+
|
434
|
+
response = @client.export('people', nil, true)
|
435
|
+
expect(response[:state]).to eq('done')
|
436
|
+
expect(response[:url]).to eq('https://download.example.com/export.zip?AWSAccessKeyId=12345')
|
437
|
+
expect(progress_stub).to have_been_requested.times(4)
|
438
|
+
end
|
439
|
+
|
440
|
+
it 'should not continue when there is an error connecting to ITRP' do
|
441
|
+
stub_request(:post, 'https://api.itrp.com/v1/export').with(basic_auth: ['secret', 'x']).with(body: {type: 'people'}).to_return(body: {token: '68ef5ef0f64c0'}.to_json)
|
442
|
+
progress_stub = stub_request(:get, 'https://api.itrp.com/v1/export/68ef5ef0f64c0').with(basic_auth: ['secret', 'x'])
|
443
|
+
.to_return(@export_queued_response, @export_processing_response)
|
444
|
+
.then.to_raise(StandardError.new('network error')) # twice
|
445
|
+
|
446
|
+
expect{ @client.export('people', nil, true) }.to raise_error(Itrp::Exception, "Unable to monitor progress for 'people' export. 500: No Response from Server - network error for 'api.itrp.com:443/v1/export/68ef5ef0f64c0'")
|
447
|
+
expect(progress_stub).to have_been_requested.times(4)
|
448
|
+
end
|
449
|
+
|
450
|
+
it 'should return an invalid response in case waiting for progress is false' do
|
451
|
+
stub_request(:post, 'https://api.itrp.com/v1/export').with(basic_auth: ['secret', 'x']).with(body: {type: 'people'}).to_return(body: {message: 'oops!'}.to_json)
|
452
|
+
response = @client.export('people')
|
453
|
+
expect(response.valid?).to be_falsey
|
454
|
+
expect(response.message).to eq('oops!')
|
455
|
+
end
|
456
|
+
|
457
|
+
it 'should raise an UploadFailed exception in case waiting for progress is true' do
|
458
|
+
stub_request(:post, 'https://api.itrp.com/v1/export').with(basic_auth: ['secret', 'x']).with(body: {type: 'people'}).to_return(body: {message: 'oops!'}.to_json)
|
459
|
+
expect{ @client.export('people', nil, true) }.to raise_error(Itrp::UploadFailed, "Failed to queue 'people' export. oops!")
|
460
|
+
end
|
461
|
+
|
462
|
+
end
|
463
|
+
|
464
|
+
context 'retry' do
|
465
|
+
it 'should not retry when max_retry_time = -1' do
|
466
|
+
stub = stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).to_raise(StandardError.new('network error'))
|
467
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug )
|
468
|
+
expect_log("Request failed: 500: No Response from Server - network error for 'api.itrp.com:443/v1/me'", :error)
|
469
|
+
|
470
|
+
client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
471
|
+
response = client.get('me')
|
472
|
+
expect(stub).to have_been_requested.times(1)
|
473
|
+
expect(response.valid?).to be_falsey
|
474
|
+
expect(response.message).to eq("500: No Response from Server - network error for 'api.itrp.com:443/v1/me'")
|
475
|
+
end
|
476
|
+
|
477
|
+
it 'should not retry 4 times when max_retry_time = 16' do
|
478
|
+
stub = stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).to_raise(StandardError.new('network error'))
|
479
|
+
[2,4,8,16].each_with_index do |secs, i|
|
480
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug )
|
481
|
+
expect_log("Request failed, retry ##{i+1} in #{secs} seconds: 500: No Response from Server - network error for 'api.itrp.com:443/v1/me'", :warn)
|
482
|
+
end
|
483
|
+
|
484
|
+
client = Itrp::Client.new(api_token: 'secret', max_retry_time: 16)
|
485
|
+
allow(client).to receive(:sleep)
|
486
|
+
response = client.get('me')
|
487
|
+
expect(stub).to have_been_requested.times(4)
|
488
|
+
expect(response.valid?).to be_falsey
|
489
|
+
expect(response.message).to eq("500: No Response from Server - network error for 'api.itrp.com:443/v1/me'")
|
490
|
+
end
|
491
|
+
|
492
|
+
it 'should return the response after retry succeeds' do
|
493
|
+
stub = stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).to_raise(StandardError.new('network error')).then.to_return(body: {name: 'my name'}.to_json)
|
494
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug )
|
495
|
+
expect_log("Request failed, retry #1 in 2 seconds: 500: No Response from Server - network error for 'api.itrp.com:443/v1/me'", :warn)
|
496
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug )
|
497
|
+
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug )
|
498
|
+
|
499
|
+
client = Itrp::Client.new(api_token: 'secret', max_retry_time: 16)
|
500
|
+
allow(client).to receive(:sleep)
|
501
|
+
response = client.get('me')
|
502
|
+
expect(stub).to have_been_requested.times(2)
|
503
|
+
expect(response.valid?).to be_truthy
|
504
|
+
expect(response[:name]).to eq('my name')
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
context 'rate limiting' do
|
509
|
+
it 'should not block on rate limit when block_at_rate_limit is false' do
|
510
|
+
stub = stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).to_return(status: 429, body: {message: 'Too Many Requests'}.to_json)
|
511
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug )
|
512
|
+
expect_log("Request failed: 429: Too Many Requests", :error)
|
513
|
+
|
514
|
+
client = Itrp::Client.new(api_token: 'secret', block_at_rate_limit: false)
|
515
|
+
response = client.get('me')
|
516
|
+
expect(stub).to have_been_requested.times(1)
|
517
|
+
expect(response.valid?).to be_falsey
|
518
|
+
expect(response.message).to eq('429: Too Many Requests')
|
519
|
+
end
|
520
|
+
|
521
|
+
it 'should block on rate limit when block_at_rate_limit is true' do
|
522
|
+
stub = stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).to_return(status: 429, body: {message: 'Too Many Requests'}.to_json).then.to_return(body: {name: 'my name'}.to_json)
|
523
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug )
|
524
|
+
expect_log('Request throttled, trying again in 5 minutes: 429: Too Many Requests', :warn)
|
525
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug )
|
526
|
+
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug )
|
527
|
+
|
528
|
+
client = Itrp::Client.new(api_token: 'secret', block_at_rate_limit: true)
|
529
|
+
allow(client).to receive(:sleep)
|
530
|
+
response = client.get('me')
|
531
|
+
expect(stub).to have_been_requested.times(2)
|
532
|
+
expect(response.valid?).to be_truthy
|
533
|
+
expect(response[:name]).to eq('my name')
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
context 'logger' do
|
538
|
+
before(:each) do
|
539
|
+
@logger = Logger.new(STDOUT)
|
540
|
+
@client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1, logger: @logger)
|
541
|
+
end
|
542
|
+
|
543
|
+
it 'should be possible to override the default logger' do
|
544
|
+
stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).to_return(body: {name: 'my name'}.to_json)
|
545
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug, @logger )
|
546
|
+
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug, @logger )
|
547
|
+
@client.get('me')
|
548
|
+
end
|
549
|
+
end
|
550
|
+
end
|