rdstation-ruby-client 2.0.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +116 -4
  3. data/README.md +114 -22
  4. data/lib/rdstation-ruby-client.rb +6 -1
  5. data/lib/rdstation.rb +19 -0
  6. data/lib/rdstation/api_response.rb +1 -2
  7. data/lib/rdstation/authentication.rb +32 -3
  8. data/lib/rdstation/{authorization_header.rb → authorization.rb} +11 -8
  9. data/lib/rdstation/builder/field.rb +70 -0
  10. data/lib/rdstation/client.rb +17 -7
  11. data/lib/rdstation/contacts.rb +22 -13
  12. data/lib/rdstation/error.rb +2 -0
  13. data/lib/rdstation/error/format.rb +29 -3
  14. data/lib/rdstation/error/formatter.rb +69 -8
  15. data/lib/rdstation/error_handler.rb +6 -1
  16. data/lib/rdstation/events.rb +7 -12
  17. data/lib/rdstation/fields.rb +35 -6
  18. data/lib/rdstation/retryable_request.rb +35 -0
  19. data/lib/rdstation/version.rb +1 -1
  20. data/lib/rdstation/webhooks.rb +25 -13
  21. data/rdstation-ruby-client.gemspec +2 -1
  22. data/spec/lib/rdstation/api_response_spec.rb +34 -0
  23. data/spec/lib/rdstation/authentication_spec.rb +164 -0
  24. data/spec/lib/rdstation/{authorization_header_spec.rb → authorization_spec.rb} +3 -3
  25. data/spec/lib/rdstation/builder/field_spec.rb +69 -0
  26. data/spec/lib/rdstation/client_spec.rb +6 -6
  27. data/spec/lib/rdstation/contacts_spec.rb +23 -3
  28. data/spec/lib/rdstation/error/format_spec.rb +63 -0
  29. data/spec/lib/rdstation/error/formatter_spec.rb +113 -0
  30. data/spec/lib/rdstation/error_handler_spec.rb +23 -0
  31. data/spec/lib/rdstation/events_spec.rb +8 -3
  32. data/spec/lib/rdstation/fields_spec.rb +6 -1
  33. data/spec/lib/rdstation/retryable_request_spec.rb +142 -0
  34. data/spec/lib/rdstation/webhooks_spec.rb +26 -1
  35. data/spec/lib/rdstation_spec.rb +18 -0
  36. metadata +36 -11
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe RDStation::AuthorizationHeader do
3
+ RSpec.describe RDStation::Authorization do
4
4
 
5
5
  describe ".initialize" do
6
6
  context "when access_token is nil" do
@@ -12,11 +12,11 @@ RSpec.describe RDStation::AuthorizationHeader do
12
12
  end
13
13
  end
14
14
 
15
- describe "#to_h" do
15
+ describe "#headers" do
16
16
  let(:access_token) { 'access_token' }
17
17
 
18
18
  it "generates the correct header" do
19
- header = described_class.new(access_token: access_token).to_h
19
+ header = described_class.new(access_token: access_token).headers
20
20
  expect(header['Authorization']).to eq "Bearer #{access_token}"
21
21
  expect(header['Content-Type']).to eq "application/json"
22
22
  end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe RDStation::Builder::Field do
6
+ def valid_builder
7
+ described_class.new('cf_identifier')
8
+ end
9
+
10
+ describe 'when create a builder' do
11
+ context 'valid' do
12
+ let(:initial_parameters) do
13
+ 'cf_api_identifier'
14
+ end
15
+
16
+ let(:builder) { described_class.new(initial_parameters) }
17
+
18
+ let(:expected_result) do
19
+ {
20
+ 'api_identifier' => 'cf_api_identifier',
21
+ 'data_type' => 'STRING',
22
+ 'presentation_type' => 'TEXT_INPUT',
23
+ 'label' => { 'pt-BR' => 'My label' },
24
+ 'name' => { 'pt-BR' => 'My name' }
25
+ }
26
+ end
27
+
28
+ it 'returns an hash of required values' do
29
+ builder.label 'pt-BR', 'My label'
30
+ builder.name 'pt-BR', 'My name'
31
+ builder.data_type 'STRING'
32
+ builder.presentation_type 'TEXT_INPUT'
33
+
34
+ result = builder.build
35
+ expect(result).to eq(expected_result)
36
+ end
37
+ end
38
+
39
+ context 'invalid' do
40
+ it 'using invalid api_identifier ' do
41
+ expect { described_class.new('invald_identifier') }.to raise_error(
42
+ 'api_identifier is not in a valid format, need start with "cf_"'
43
+ )
44
+ end
45
+
46
+ it 'using invalid data_type ' do
47
+ expect { valid_builder.data_type('invalid_data_type') }.to raise_error(
48
+ 'Not valid data_type - ["STRING", "INTEGER", "BOOLEAN", "STRING[]"]'
49
+ )
50
+ end
51
+
52
+ it 'using invalid presentation_type ' do
53
+ expect { valid_builder.presentation_type('invalid presentation_type') }.to raise_error(
54
+ 'Not valid presentation_type - ["TEXT_INPUT", "TEXT_AREA", "URL_INPUT", "PHONE_INPUT", "EMAIL_INPUT", "CHECK_BOX", "NUMBER_INPUT", "COMBO_BOX", "RADIO_BUTTON", "MULTIPLE_CHOICE"]'
55
+ )
56
+ end
57
+
58
+ it 'without api_identifier' do
59
+ expect { described_class.new(nil) }.to raise_error('api_identifier required')
60
+ end
61
+
62
+ it 'without required fields' do
63
+ expect { valid_builder.build }.to raise_error(
64
+ 'Required fields are missing - ["data_type", "presentation_type", "label", "name"]'
65
+ )
66
+ end
67
+ end
68
+ end
69
+ end
@@ -4,27 +4,27 @@ RSpec.describe RDStation::Client do
4
4
  context "when access_token is given" do
5
5
  let(:access_token) { 'access_token' }
6
6
  let(:client) { described_class.new(access_token: access_token) }
7
- let(:mock_authorization_header) { double(RDStation::AuthorizationHeader) }
7
+ let(:mock_authorization) { double(RDStation::Authorization) }
8
8
 
9
- before { allow(RDStation::AuthorizationHeader).to receive(:new).and_return mock_authorization_header }
9
+ before { allow(RDStation::Authorization).to receive(:new).and_return mock_authorization }
10
10
 
11
11
  it 'returns Contacts endpoint' do
12
- expect(RDStation::Contacts).to receive(:new).with({ authorization_header: mock_authorization_header }).and_call_original
12
+ expect(RDStation::Contacts).to receive(:new).with({ authorization: mock_authorization }).and_call_original
13
13
  expect(client.contacts).to be_instance_of RDStation::Contacts
14
14
  end
15
15
 
16
16
  it 'returns Events endpoint' do
17
- expect(RDStation::Events).to receive(:new).with({ authorization_header: mock_authorization_header }).and_call_original
17
+ expect(RDStation::Events).to receive(:new).with({ authorization: mock_authorization }).and_call_original
18
18
  expect(client.events).to be_instance_of RDStation::Events
19
19
  end
20
20
 
21
21
  it 'returns Fields endpoint' do
22
- expect(RDStation::Fields).to receive(:new).with({ authorization_header: mock_authorization_header }).and_call_original
22
+ expect(RDStation::Fields).to receive(:new).with({ authorization: mock_authorization }).and_call_original
23
23
  expect(client.fields).to be_instance_of RDStation::Fields
24
24
  end
25
25
 
26
26
  it 'returns Webhooks endpoint' do
27
- expect(RDStation::Webhooks).to receive(:new).with({ authorization_header: mock_authorization_header }).and_call_original
27
+ expect(RDStation::Webhooks).to receive(:new).with({ authorization: mock_authorization }).and_call_original
28
28
  expect(client.webhooks).to be_instance_of RDStation::Webhooks
29
29
  end
30
30
  end
@@ -16,13 +16,13 @@ RSpec.describe RDStation::Contacts do
16
16
  let(:expired_access_token) { 'expired_access_token' }
17
17
 
18
18
  let(:contact_with_valid_token) do
19
- described_class.new(authorization_header: RDStation::AuthorizationHeader.new(access_token: valid_access_token))
19
+ described_class.new(authorization: RDStation::Authorization.new(access_token: valid_access_token))
20
20
  end
21
21
  let(:contact_with_expired_token) do
22
- described_class.new(authorization_header: RDStation::AuthorizationHeader.new(access_token: expired_access_token))
22
+ described_class.new(authorization: RDStation::Authorization.new(access_token: expired_access_token))
23
23
  end
24
24
  let(:contact_with_invalid_token) do
25
- described_class.new(authorization_header: RDStation::AuthorizationHeader.new(access_token: invalid_access_token))
25
+ described_class.new(authorization: RDStation::Authorization.new(access_token: invalid_access_token))
26
26
  end
27
27
 
28
28
 
@@ -109,6 +109,11 @@ RSpec.describe RDStation::Contacts do
109
109
  end
110
110
 
111
111
  describe '#by_uuid' do
112
+ it 'calls retryable_request' do
113
+ expect(contact_with_valid_token).to receive(:retryable_request)
114
+ contact_with_valid_token.by_uuid('valid_uuid')
115
+ end
116
+
112
117
  context 'with a valid auth token' do
113
118
  context 'when the contact exists' do
114
119
  let(:contact) do
@@ -172,6 +177,11 @@ RSpec.describe RDStation::Contacts do
172
177
  end
173
178
 
174
179
  describe '#by_email' do
180
+ it 'calls retryable_request' do
181
+ expect(contact_with_valid_token).to receive(:retryable_request)
182
+ contact_with_valid_token.by_email('x@xpto.com')
183
+ end
184
+
175
185
  context 'with a valid auth token' do
176
186
  context 'when the contact exists' do
177
187
  let(:contact) do
@@ -235,6 +245,11 @@ RSpec.describe RDStation::Contacts do
235
245
  end
236
246
 
237
247
  describe '#update' do
248
+ it 'calls retryable_request' do
249
+ expect(contact_with_valid_token).to receive(:retryable_request)
250
+ contact_with_valid_token.update('valid_uuid', {})
251
+ end
252
+
238
253
  context 'with a valid access_token' do
239
254
  let(:valid_access_token) { 'valid_access_token' }
240
255
  let(:headers) do
@@ -322,6 +337,11 @@ RSpec.describe RDStation::Contacts do
322
337
  end
323
338
 
324
339
  describe '#upsert' do
340
+ it 'calls retryable_request' do
341
+ expect(contact_with_valid_token).to receive(:retryable_request)
342
+ contact_with_valid_token.upsert('email', 'valid@email.com', {})
343
+ end
344
+
325
345
  context 'with a valid access_token' do
326
346
  let(:valid_access_token) { 'valid_access_token' }
327
347
 
@@ -52,5 +52,68 @@ RSpec.describe RDStation::Error::Format do
52
52
  expect(result).to eq(RDStation::Error::Format::ARRAY_OF_HASHES)
53
53
  end
54
54
  end
55
+
56
+ context 'when receives a mixed type of errors' do
57
+ let(:errors) do
58
+ {
59
+ 'label': {
60
+ 'pt-BR': [
61
+ {
62
+ 'error_type': 'CANNOT_BE_BLANK',
63
+ 'error_message': 'cannot be blank'
64
+ }
65
+ ]
66
+ },
67
+ 'api_identifier': [
68
+ {
69
+ 'error_type': 'CANNOT_BE_BLANK',
70
+ 'error_message': 'cannot be blank'
71
+ }
72
+ ]
73
+ }
74
+ end
75
+
76
+ it 'returns the HASH_OF_MULTIPLE_TYPES format' do
77
+ result = error_format.format
78
+ expect(result).to eq(RDStation::Error::Format::HASH_OF_MULTIPLE_TYPES)
79
+ end
80
+ end
81
+
82
+ context 'when receives a hash of hashes errors' do
83
+ let(:errors) do
84
+ {
85
+ label: {
86
+ 'pt-BR': [
87
+ {
88
+ 'error_type': 'CANNOT_BE_BLANK',
89
+ 'error_message': 'cannot be blank'
90
+ }
91
+ ]
92
+ }
93
+ }
94
+ end
95
+
96
+ it 'returns the HASH_OF_MULTILINGUAL format' do
97
+ result = error_format.format
98
+ expect(result).to eq(RDStation::Error::Format::HASH_OF_HASHES)
99
+ end
100
+ end
101
+
102
+ context 'when receives a single hash with error' do
103
+ let(:errors) do
104
+ {
105
+ 'error' => "'lead_limiter' rate limit exceeded for 86400 second(s) period for key ...",
106
+ 'max' => 24,
107
+ 'usage' => 55,
108
+ 'remaining_time' => 20745,
109
+ }
110
+ end
111
+
112
+ it 'returns the SINGLE_HASH format' do
113
+ result = error_format.format
114
+ expect(result).to eq(RDStation::Error::Format::SINGLE_HASH)
115
+ end
116
+
117
+ end
55
118
  end
56
119
  end
@@ -132,5 +132,118 @@ RSpec.describe RDStation::Error::Formatter do
132
132
  expect(result).to eq(expected_result)
133
133
  end
134
134
  end
135
+
136
+ context 'when receives a hash of multiple type errors' do
137
+ let(:error_format) { instance_double(RDStation::Error::Format, format: RDStation::Error::Format::HASH_OF_MULTIPLE_TYPES) }
138
+
139
+ let(:error_response) do
140
+ {
141
+ 'errors' => {
142
+ 'label' => {
143
+ 'pt-BR' => [
144
+ {
145
+ 'error_type' => 'CANNOT_BE_BLANK',
146
+ 'error_message' => 'cannot be blank'
147
+ }
148
+ ]
149
+ },
150
+ 'api_identifier' => [
151
+ {
152
+ 'error_type' => 'CANNOT_BE_BLANK',
153
+ 'error_message' => 'cannot be blank'
154
+ }
155
+ ]
156
+ }
157
+ }
158
+ end
159
+
160
+ let(:error_formatter) { described_class.new(error_response) }
161
+
162
+ let(:expected_result) do
163
+ [
164
+ {
165
+ 'error_type' => 'CANNOT_BE_BLANK',
166
+ 'error_message' => 'cannot be blank',
167
+ 'path' => 'body.label.pt-BR'
168
+ },
169
+ {
170
+ 'error_type' => 'CANNOT_BE_BLANK',
171
+ 'error_message' => 'cannot be blank',
172
+ 'path' => 'body.api_identifier'
173
+ }
174
+ ]
175
+ end
176
+
177
+ it 'returns an array of errors' do
178
+ result = error_formatter.to_array
179
+ expect(result).to eq(expected_result)
180
+ end
181
+ end
182
+
183
+ context 'when receives a hash of hashes type errors' do
184
+ let(:error_format) { instance_double(RDStation::Error::Format, format: RDStation::Error::Format::HASH_OF_HASHES) }
185
+
186
+ let(:error_response) do
187
+ {
188
+ 'errors' => {
189
+ 'label' => {
190
+ 'pt-BR' => [
191
+ {
192
+ 'error_type' => 'CANNOT_BE_BLANK',
193
+ 'error_message' => 'cannot be blank'
194
+ }
195
+ ]
196
+ }
197
+ }
198
+ }
199
+ end
200
+
201
+ let(:error_formatter) { described_class.new(error_response) }
202
+
203
+ let(:expected_result) do
204
+ [
205
+ {
206
+ 'error_type' => 'CANNOT_BE_BLANK',
207
+ 'error_message' => 'cannot be blank',
208
+ 'path' => 'body.label.pt-BR'
209
+ }
210
+ ]
211
+ end
212
+
213
+ it 'returns an array of errors' do
214
+ result = error_formatter.to_array
215
+ expect(result).to eq(expected_result)
216
+ end
217
+ end
218
+
219
+ context 'when receives a single hash of errors' do
220
+ let(:error_format) { instance_double(RDStation::Error::Format, format: RDStation::Error::Format::SINGLE_HASH) }
221
+
222
+ let(:error_response) do
223
+ {
224
+ 'error' => "'lead_limiter' rate limit exceeded for 86400 second(s) period for key",
225
+ 'max' => 24,
226
+ 'usage' => 55,
227
+ 'remaining_time' => 20745
228
+ }
229
+ end
230
+
231
+ let(:error_formatter) { described_class.new(error_response) }
232
+
233
+ let(:expected_result) do
234
+ [
235
+ {
236
+ 'error_type' => 'TOO_MANY_REQUESTS',
237
+ 'error_message' => "'lead_limiter' rate limit exceeded for 86400 second(s) period for key",
238
+ 'details' => { 'max' => 24, 'usage' => 55, 'remaining_time' => 20745 }
239
+ }
240
+ ]
241
+ end
242
+
243
+ it 'returns an array of errors' do
244
+ result = error_formatter.to_array
245
+ expect(result).to eq(expected_result)
246
+ end
247
+ end
135
248
  end
136
249
  end
@@ -151,6 +151,7 @@ RSpec.describe RDStation::ErrorHandler do
151
151
  expect { error_handler.raise_error }.to raise_error(RDStation::Error::ServiceUnavailable, 'Error Message')
152
152
  end
153
153
  end
154
+
154
155
  context 'with 5xx error' do
155
156
  let(:http_status) { 505 }
156
157
 
@@ -158,5 +159,27 @@ RSpec.describe RDStation::ErrorHandler do
158
159
  expect { error_handler.raise_error }.to raise_error(RDStation::Error::ServerError, 'Error Message')
159
160
  end
160
161
  end
162
+
163
+ context "when response body is not JSON-parseable" do
164
+ let(:error_response) do
165
+ OpenStruct.new(
166
+ code: 502,
167
+ headers: { 'error' => 'header' },
168
+ body: '<html><body>HTML error response</body></html>'
169
+ )
170
+ end
171
+
172
+ it 'raises the correct error' do
173
+ expect { error_handler.raise_error }.to raise_error(RDStation::Error::BadGateway, '<html><body>HTML error response</body></html>')
174
+ end
175
+ end
176
+
177
+ context 'with an unknown error' do
178
+ let(:http_status) { 123 }
179
+
180
+ it 'raises a unknown error' do
181
+ expect { error_handler.raise_error }.to raise_error(RDStation::Error::UnknownError, 'Error Message')
182
+ end
183
+ end
161
184
  end
162
185
  end
@@ -6,13 +6,13 @@ RSpec.describe RDStation::Events do
6
6
  let(:expired_access_token) { 'expired_access_token' }
7
7
 
8
8
  let(:event_with_valid_token) do
9
- described_class.new(authorization_header: RDStation::AuthorizationHeader.new(access_token: valid_access_token))
9
+ described_class.new(authorization: RDStation::Authorization.new(access_token: valid_access_token))
10
10
  end
11
11
  let(:event_with_expired_token) do
12
- described_class.new(authorization_header: RDStation::AuthorizationHeader.new(access_token: expired_access_token))
12
+ described_class.new(authorization: RDStation::Authorization.new(access_token: expired_access_token))
13
13
  end
14
14
  let(:event_with_invalid_token) do
15
- described_class.new(authorization_header: RDStation::AuthorizationHeader.new(access_token: invalid_access_token))
15
+ described_class.new(authorization: RDStation::Authorization.new(access_token: invalid_access_token))
16
16
  end
17
17
 
18
18
  let(:events_endpoint) { 'https://api.rd.services/platform/events' }
@@ -108,6 +108,11 @@ RSpec.describe RDStation::Events do
108
108
  }
109
109
  end
110
110
 
111
+ it 'calls retryable_request' do
112
+ expect(event_with_valid_token).to receive(:retryable_request)
113
+ event_with_valid_token.create({})
114
+ end
115
+
111
116
  context 'with a valid auth token' do
112
117
  before do
113
118
  stub_request(:post, events_endpoint)
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  RSpec.describe RDStation::Fields do
4
4
  let(:valid_access_token) { 'valid_access_token' }
5
5
  let(:rdstation_fields_with_valid_token) do
6
- described_class.new(authorization_header: RDStation::AuthorizationHeader.new(access_token: valid_access_token))
6
+ described_class.new(authorization: RDStation::Authorization.new(access_token: valid_access_token))
7
7
  end
8
8
 
9
9
  let(:valid_headers) do
@@ -38,6 +38,11 @@ RSpec.describe RDStation::Fields do
38
38
  }
39
39
  end
40
40
 
41
+ it 'calls retryable_request' do
42
+ expect(rdstation_fields_with_valid_token).to receive(:retryable_request)
43
+ rdstation_fields_with_valid_token.all
44
+ end
45
+
41
46
  context 'with a valid auth token' do
42
47
  before do
43
48
  stub_request(:get, fields_endpoint)