rdstation-ruby-client 2.0.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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)