rdstation-ruby-client 2.2.0 → 2.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +79 -2
- data/README.md +51 -1
- data/Rakefile +4 -0
- data/lib/rdstation-ruby-client.rb +3 -0
- data/lib/rdstation/builder/field.rb +70 -0
- data/lib/rdstation/error.rb +3 -0
- data/lib/rdstation/error/format.rb +29 -3
- data/lib/rdstation/error/formatter.rb +69 -8
- data/lib/rdstation/error_handler.rb +4 -1
- data/lib/rdstation/error_handler/invalid_refresh_token.rb +25 -0
- data/lib/rdstation/error_handler/unauthorized.rb +2 -0
- data/lib/rdstation/events.rb +1 -9
- data/lib/rdstation/fields.rb +28 -2
- data/lib/rdstation/version.rb +1 -1
- data/spec/lib/rdstation/authentication_spec.rb +15 -2
- data/spec/lib/rdstation/builder/field_spec.rb +69 -0
- data/spec/lib/rdstation/error/format_spec.rb +63 -0
- data/spec/lib/rdstation/error/formatter_spec.rb +113 -0
- data/spec/lib/rdstation/error_handler/invalid_refresh_token_spec.rb +53 -0
- data/spec/lib/rdstation/error_handler_spec.rb +9 -0
- metadata +12 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97ef40283572e980be5fee4c7d98d02d267a48e2eca8c948b8d51be95f5e807b
|
4
|
+
data.tar.gz: ebba8f50fef8eaa254c1187f6f2e5e0e9bbf393ad94e185870f250cb2f9bd14d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 467b42ba1b17c256dba0ed6ab2ed632e83c437ae1067d043ccb97c4274244376cc26dd770ef112c8b81eb31c40fd37486bfb4b28f83b16f9dc0a47730a3d6e07
|
7
|
+
data.tar.gz: 6bdcada4a7418e8fd4cab9d4b121d3192667ebccac2869f9fdae759966e291e0698cc914dd1681c6c8493f78717599d063ac868a5ea5e9e23e13da003215563e
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,80 @@
|
|
1
|
+
## 2.5.1
|
2
|
+
|
3
|
+
- Fixed checking `empty?` for nil values inside of InvalidRefreshToken class
|
4
|
+
|
5
|
+
## 2.5.0
|
6
|
+
|
7
|
+
- InvalidRefreshToken error added. This error will be raised when the refresh token is invalid or it was revoked. When you get this error, you can safely disconnect the user from RD Station.
|
8
|
+
|
9
|
+
Usage example:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
begin
|
13
|
+
rdstation.update_access_token(refresh_token)
|
14
|
+
rescue RDStation::Error::InvalidRefreshToken
|
15
|
+
# Disconnect user
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
## 2.4.0
|
20
|
+
|
21
|
+
- Add the TooManyRequests errors in case of rate limit exceeded. See [API request limit](https://developers.rdstation.com/en/request-limit) for more details
|
22
|
+
|
23
|
+
## 2.3.1
|
24
|
+
|
25
|
+
- Fixed a bug when no error is found in the known errors list (issue [#52](https://github.com/ResultadosDigitais/rdstation-ruby-client/issues/52))
|
26
|
+
|
27
|
+
## 2.3.0
|
28
|
+
|
29
|
+
### Additions
|
30
|
+
|
31
|
+
#### 1. New Field methods
|
32
|
+
|
33
|
+
The following methods were added to "Fields" client:
|
34
|
+
|
35
|
+
- create
|
36
|
+
- update
|
37
|
+
- delete
|
38
|
+
|
39
|
+
Besides reading, this client is now capable of create, update or delete a field.
|
40
|
+
|
41
|
+
Usage example:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
client = RDStation::Client.new(access_token: 'ACCESS_TOKEN', refresh_token: 'REFRESH_TOKEN')
|
45
|
+
client.fields.delete('FIELD_UUID')
|
46
|
+
```
|
47
|
+
|
48
|
+
#### 2. New format of errors supported
|
49
|
+
|
50
|
+
Two new formats of errors are now supported by the error handler:
|
51
|
+
|
52
|
+
##### `HASH_OF_HASHES`
|
53
|
+
|
54
|
+
When the error message is a hash containing other hashes as values, for example:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
{
|
58
|
+
'error' => {
|
59
|
+
'field1' => {...},
|
60
|
+
'field2' => {...}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
```
|
64
|
+
|
65
|
+
##### `HASH_OF_MULTIPLE_TYPES`
|
66
|
+
|
67
|
+
When the error message is a hash that could contain multiple data types as values, for example:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
{
|
71
|
+
'error' => {
|
72
|
+
'field1' => [...] # Array,
|
73
|
+
'field2' => {...} # Hash
|
74
|
+
}
|
75
|
+
}
|
76
|
+
```
|
77
|
+
|
1
78
|
## 2.2.0
|
2
79
|
|
3
80
|
### Additions
|
@@ -37,7 +114,7 @@ end
|
|
37
114
|
|
38
115
|
Providing `client_id` and `client_secret` directly to `RDStation::Authentication.new` is deprecated and will be removed in future versions. Use `RDStation.configure` instead.
|
39
116
|
|
40
|
-
Specifying refresh_token in `RDStation::Client.new(access_token: 'at', refresh_token: 'rt')` is optional right now, but will be mandatory in future versions.
|
117
|
+
Specifying refresh_token in `RDStation::Client.new(access_token: 'at', refresh_token: 'rt')` is optional right now, but will be mandatory in future versions.
|
41
118
|
|
42
119
|
## 2.1.1
|
43
120
|
|
@@ -94,7 +171,7 @@ In case of a Bad Request (400), the following specific errors may be raised (tho
|
|
94
171
|
- `RDStation::Error::ConflictingField`
|
95
172
|
- `RDStation::Error::InvalidEventType`
|
96
173
|
|
97
|
-
In cause of
|
174
|
+
In cause of Unauthorized (401), the following specific errors may be raised (those are subclasses of `RDStation::Error::Unauthorized`):
|
98
175
|
- `RDStation::Error::ExpiredAccessToken`
|
99
176
|
- `RDStation::Error::ExpiredCodeGrant`
|
100
177
|
- `RDStation::Error::InvalidCredentials`
|
data/README.md
CHANGED
@@ -93,7 +93,7 @@ RDStation.configure do |config|
|
|
93
93
|
# authorization.access_token_expires_in is the time (in seconds for with the token is valid)
|
94
94
|
# authorization.access_token is the new token
|
95
95
|
# authorization.refresh_token is the existing refresh_token
|
96
|
-
#
|
96
|
+
#
|
97
97
|
# If you are using ActiveRecord, you may want to update the stored access_token, like in the following code:
|
98
98
|
MyStoredAuth.where(refresh_token: authorization.refresh_token).update_all(access_token: authorization.access_token)
|
99
99
|
end
|
@@ -206,6 +206,55 @@ client = RDStation::Client.new(access_token: 'access_token', refresh_token: 'ref
|
|
206
206
|
client.fields.all
|
207
207
|
```
|
208
208
|
|
209
|
+
#### Create a field
|
210
|
+
|
211
|
+
```ruby
|
212
|
+
payload = {} # hash representing the payload
|
213
|
+
client = RDStation::Client.new(access_token: 'access_token', refresh_token: 'refresh_token')
|
214
|
+
client.fields.create payload
|
215
|
+
```
|
216
|
+
Or you can use the new `RDStation::Builder::Field`
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
payload = {} # hash representing the payload
|
220
|
+
builder = RDStation::Builder::Field.new payload['api_identifier']
|
221
|
+
builder.data_type(payload['data_type'])
|
222
|
+
builder.presentation_type(payload['presentation_type'])
|
223
|
+
builder.name('pt-BR', payload['name'])
|
224
|
+
builder.label('pt-BR', payload['label'])
|
225
|
+
|
226
|
+
client = RDStation::Client.new(access_token: 'access_token', refresh_token: 'refresh_token')
|
227
|
+
client.fields.create builder.build
|
228
|
+
```
|
229
|
+
|
230
|
+
#### Update a field
|
231
|
+
|
232
|
+
```ruby
|
233
|
+
payload = {} # hash representing the payload
|
234
|
+
client = RDStation::Client.new(access_token: 'access_token', refresh_token: 'refresh_token')
|
235
|
+
client.fields.update('FIELD_UUID', payload)
|
236
|
+
```
|
237
|
+
Or you can use the new `RDStation::Builder::Field`
|
238
|
+
|
239
|
+
```ruby
|
240
|
+
payload = {} # hash representing the payload
|
241
|
+
builder = RDStation::Builder::Field.new payload['api_identifier']
|
242
|
+
builder.data_type(payload['data_type'])
|
243
|
+
builder.presentation_type(payload['presentation_type'])
|
244
|
+
builder.name('pt-BR', payload['name'])
|
245
|
+
builder.label('pt-BR', payload['label'])
|
246
|
+
|
247
|
+
client = RDStation::Client.new(access_token: 'access_token', refresh_token: 'refresh_token')
|
248
|
+
client.fields.update('FIELD_UUID', builder.build)
|
249
|
+
```
|
250
|
+
#### Deleting a field
|
251
|
+
|
252
|
+
```ruby
|
253
|
+
client = RDStation::Client.new(access_token: 'access_token', refresh_token: 'refresh_token')
|
254
|
+
client.fields.delete('FIELD_UUID')
|
255
|
+
```
|
256
|
+
|
257
|
+
|
209
258
|
### Webhooks
|
210
259
|
|
211
260
|
Webhooks provide the ability to receive real-time data updates about your contact activity.
|
@@ -266,6 +315,7 @@ Each endpoint may raise errors accoording to the HTTP response code from RDStati
|
|
266
315
|
- `RDStation::Error::Conflict` (409)
|
267
316
|
- `RDStation::Error::UnsupportedMediaType` (415)
|
268
317
|
- `RDStation::Error::UnprocessableEntity` (422)
|
318
|
+
- `RDStation::Error::TooManyRequests` (429)
|
269
319
|
- `RDStation::Error::InternalServerError` (500)
|
270
320
|
- `RDStation::Error::NotImplemented` (501)
|
271
321
|
- `RDStation::Error::BadGateway` (502)
|
data/Rakefile
CHANGED
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RDStation
|
4
|
+
class Builder
|
5
|
+
# More info: https://developers.rdstation.com/pt-BR/reference/fields#methodPostDetails
|
6
|
+
class Field
|
7
|
+
DATA_TYPES = %w(STRING INTEGER BOOLEAN STRING[]).freeze
|
8
|
+
PRESENTATION_TYPES = %w[TEXT_INPUT TEXT_AREA URL_INPUT PHONE_INPUT
|
9
|
+
EMAIL_INPUT CHECK_BOX NUMBER_INPUT COMBO_BOX
|
10
|
+
RADIO_BUTTON MULTIPLE_CHOICE].freeze
|
11
|
+
|
12
|
+
REQUIRED_FIELDS = %w[api_identifier data_type presentation_type label name].freeze
|
13
|
+
|
14
|
+
def initialize(api_identifier)
|
15
|
+
raise 'api_identifier required' unless api_identifier
|
16
|
+
unless valid_identifier(api_identifier)
|
17
|
+
raise 'api_identifier is not in a valid format, need start with "cf_"'
|
18
|
+
end
|
19
|
+
|
20
|
+
@values = {}
|
21
|
+
@values['api_identifier'] = api_identifier
|
22
|
+
end
|
23
|
+
|
24
|
+
def data_type(data_type)
|
25
|
+
raise "Not valid data_type - #{DATA_TYPES}" unless DATA_TYPES.include? data_type
|
26
|
+
|
27
|
+
@values['data_type'] = data_type
|
28
|
+
end
|
29
|
+
|
30
|
+
def presentation_type(presentation_type)
|
31
|
+
unless PRESENTATION_TYPES.include? presentation_type
|
32
|
+
raise "Not valid presentation_type - #{PRESENTATION_TYPES}"
|
33
|
+
end
|
34
|
+
|
35
|
+
@values['presentation_type'] = presentation_type
|
36
|
+
end
|
37
|
+
|
38
|
+
def label(language, label)
|
39
|
+
@values['label'] = { language.to_s => label }
|
40
|
+
end
|
41
|
+
|
42
|
+
def name(language, name)
|
43
|
+
@values['name'] = { language.to_s => name }
|
44
|
+
end
|
45
|
+
|
46
|
+
def validation_rules(validation_rules)
|
47
|
+
@values['validation_rules'] = validation_rules
|
48
|
+
end
|
49
|
+
|
50
|
+
def valid_options(valid_options)
|
51
|
+
@values['valid_options'] = valid_options
|
52
|
+
end
|
53
|
+
|
54
|
+
def build
|
55
|
+
empty_fields = REQUIRED_FIELDS.select { |field| @values[field].nil? }
|
56
|
+
unless empty_fields.empty?
|
57
|
+
raise "Required fields are missing - #{empty_fields}"
|
58
|
+
end
|
59
|
+
|
60
|
+
@values
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def valid_identifier(api_identifier)
|
66
|
+
api_identifier.start_with? 'cf_'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/rdstation/error.rb
CHANGED
@@ -19,11 +19,13 @@ module RDStation
|
|
19
19
|
class Conflict < Error; end
|
20
20
|
class UnsupportedMediaType < Error; end
|
21
21
|
class UnprocessableEntity < Error; end
|
22
|
+
class TooManyRequests < Error; end
|
22
23
|
class InternalServerError < Error; end
|
23
24
|
class NotImplemented < Error; end
|
24
25
|
class BadGateway < Error; end
|
25
26
|
class ServiceUnavailable < Error; end
|
26
27
|
class ServerError < Error; end
|
28
|
+
class UnknownError < Error; end
|
27
29
|
|
28
30
|
# 400 - Bad Request
|
29
31
|
class ConflictingField < BadRequest; end
|
@@ -33,5 +35,6 @@ module RDStation
|
|
33
35
|
class ExpiredAccessToken < Unauthorized; end
|
34
36
|
class ExpiredCodeGrant < Unauthorized; end
|
35
37
|
class InvalidCredentials < Unauthorized; end
|
38
|
+
class InvalidRefreshToken < Unauthorized; end
|
36
39
|
end
|
37
40
|
end
|
@@ -1,9 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RDStation
|
2
4
|
class Error
|
3
5
|
class Format
|
4
|
-
FLAT_HASH = 'FLAT_HASH'
|
5
|
-
HASH_OF_ARRAYS = 'HASH_OF_ARRAYS'
|
6
|
-
ARRAY_OF_HASHES = 'ARRAY_OF_HASHES'
|
6
|
+
FLAT_HASH = 'FLAT_HASH'
|
7
|
+
HASH_OF_ARRAYS = 'HASH_OF_ARRAYS'
|
8
|
+
ARRAY_OF_HASHES = 'ARRAY_OF_HASHES'
|
9
|
+
HASH_OF_MULTIPLE_TYPES = 'HASH_OF_MULTIPLE_TYPES'
|
10
|
+
HASH_OF_HASHES = 'HASH_OF_HASHES'
|
11
|
+
SINGLE_HASH = 'SINGLE_HASH'
|
7
12
|
|
8
13
|
def initialize(errors)
|
9
14
|
@errors = errors
|
@@ -11,20 +16,41 @@ module RDStation
|
|
11
16
|
|
12
17
|
def format
|
13
18
|
return FLAT_HASH if flat_hash?
|
19
|
+
return SINGLE_HASH if single_hash?
|
14
20
|
return HASH_OF_ARRAYS if hash_of_arrays?
|
21
|
+
return HASH_OF_HASHES if hash_of_hashes?
|
22
|
+
return HASH_OF_MULTIPLE_TYPES if hash_of_multiple_types?
|
23
|
+
|
15
24
|
ARRAY_OF_HASHES
|
16
25
|
end
|
17
26
|
|
18
27
|
private
|
19
28
|
|
29
|
+
def single_hash?
|
30
|
+
return unless @errors.is_a?(Hash)
|
31
|
+
|
32
|
+
@errors.key?('error')
|
33
|
+
end
|
34
|
+
|
20
35
|
def flat_hash?
|
21
36
|
return unless @errors.is_a?(Hash)
|
37
|
+
|
22
38
|
@errors.key?('error_type')
|
23
39
|
end
|
24
40
|
|
25
41
|
def hash_of_arrays?
|
26
42
|
@errors.is_a?(Hash) && @errors.values.all? { |error| error.is_a? Array }
|
27
43
|
end
|
44
|
+
|
45
|
+
def hash_of_hashes?
|
46
|
+
@errors.is_a?(Hash) && @errors.values.all? { |error| error.is_a? Hash }
|
47
|
+
end
|
48
|
+
|
49
|
+
def hash_of_multiple_types?
|
50
|
+
@errors.is_a?(Hash) &&
|
51
|
+
@errors.values.any? { |error| error.is_a? Hash } &&
|
52
|
+
@errors.values.any? { |error| error.is_a? Array }
|
53
|
+
end
|
28
54
|
end
|
29
55
|
end
|
30
56
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative './format'
|
2
4
|
|
3
5
|
module RDStation
|
@@ -11,27 +13,61 @@ module RDStation
|
|
11
13
|
return @error_response unless @error_response.is_a?(Hash)
|
12
14
|
|
13
15
|
case error_format.format
|
16
|
+
when RDStation::Error::Format::SINGLE_HASH
|
17
|
+
return from_single_hash
|
14
18
|
when RDStation::Error::Format::FLAT_HASH
|
15
19
|
return from_flat_hash
|
16
20
|
when RDStation::Error::Format::HASH_OF_ARRAYS
|
17
21
|
return from_hash_of_arrays
|
22
|
+
when RDStation::Error::Format::HASH_OF_HASHES
|
23
|
+
return from_hash_of_hashes
|
24
|
+
when RDStation::Error::Format::HASH_OF_MULTIPLE_TYPES
|
25
|
+
return from_hash_of_multiple_types
|
18
26
|
end
|
19
27
|
|
20
28
|
errors
|
21
29
|
end
|
22
30
|
|
31
|
+
def from_single_hash
|
32
|
+
error_hash = @error_response.dup
|
33
|
+
error_message = error_hash.delete('error')
|
34
|
+
|
35
|
+
[
|
36
|
+
{
|
37
|
+
'error_type' => 'TOO_MANY_REQUESTS',
|
38
|
+
'error_message' => error_message,
|
39
|
+
'details' => error_hash
|
40
|
+
}
|
41
|
+
]
|
42
|
+
end
|
43
|
+
|
23
44
|
def from_flat_hash
|
24
45
|
[errors]
|
25
46
|
end
|
26
47
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
48
|
+
def from_hash_of_multiple_types
|
49
|
+
array_of_errors = []
|
50
|
+
errors.each do |attribute_name, errors|
|
51
|
+
if errors.is_a? Array
|
52
|
+
result = build_error_from_array(attribute_name, errors)
|
53
|
+
end
|
54
|
+
if errors.is_a? Hash
|
55
|
+
result = build_error_from_multilingual_hash(attribute_name, errors)
|
56
|
+
end
|
57
|
+
array_of_errors.push(*result)
|
34
58
|
end
|
59
|
+
|
60
|
+
array_of_errors
|
61
|
+
end
|
62
|
+
|
63
|
+
def from_hash_of_hashes
|
64
|
+
array_of_errors = []
|
65
|
+
errors.each do |attribute_name, errors|
|
66
|
+
result = build_error_from_multilingual_hash(attribute_name, errors)
|
67
|
+
array_of_errors.push(*result)
|
68
|
+
end
|
69
|
+
|
70
|
+
array_of_errors
|
35
71
|
end
|
36
72
|
|
37
73
|
def error_format
|
@@ -39,7 +75,32 @@ module RDStation
|
|
39
75
|
end
|
40
76
|
|
41
77
|
def errors
|
42
|
-
@errors ||= @error_response
|
78
|
+
@errors ||= @error_response.fetch('errors', @error_response)
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def build_error_from_array(attribute_name, attribute_errors)
|
84
|
+
path = { 'path' => "body.#{attribute_name}" }
|
85
|
+
attribute_errors.map { |error| error.merge(path) }
|
86
|
+
end
|
87
|
+
|
88
|
+
def build_error_from_multilingual_hash(attribute_name, errors_by_language)
|
89
|
+
array_of_errors = []
|
90
|
+
errors_by_language.each do |language, errors|
|
91
|
+
result = build_error_from_array("#{attribute_name}.#{language}", errors)
|
92
|
+
array_of_errors.push(*result)
|
93
|
+
end
|
94
|
+
array_of_errors
|
95
|
+
end
|
96
|
+
|
97
|
+
def from_hash_of_arrays
|
98
|
+
errors.each_with_object([]) do |errors, array_of_errors|
|
99
|
+
attribute_name = errors.first
|
100
|
+
attribute_errors = errors.last
|
101
|
+
errors = build_error_from_array(attribute_name, attribute_errors)
|
102
|
+
array_of_errors.push(*errors)
|
103
|
+
end
|
43
104
|
end
|
44
105
|
end
|
45
106
|
end
|
@@ -32,11 +32,14 @@ module RDStation
|
|
32
32
|
when 409 then RDStation::Error::Conflict
|
33
33
|
when 415 then RDStation::Error::UnsupportedMediaType
|
34
34
|
when 422 then RDStation::Error::UnprocessableEntity
|
35
|
+
when 429 then RDStation::Error::TooManyRequests
|
35
36
|
when 500 then RDStation::Error::InternalServerError
|
36
37
|
when 501 then RDStation::Error::NotImplemented
|
37
38
|
when 502 then RDStation::Error::BadGateway
|
38
39
|
when 503 then RDStation::Error::ServiceUnavailable
|
39
40
|
when 500..599 then RDStation::Error::ServerError
|
41
|
+
else
|
42
|
+
RDStation::Error::UnknownError
|
40
43
|
end
|
41
44
|
end
|
42
45
|
|
@@ -55,7 +58,7 @@ module RDStation
|
|
55
58
|
end
|
56
59
|
|
57
60
|
def additional_error_attributes
|
58
|
-
{
|
61
|
+
attrs = {
|
59
62
|
'headers' => response.headers,
|
60
63
|
'body' => JSON.parse(response.body),
|
61
64
|
'http_status' => response.code,
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RDStation
|
2
|
+
class ErrorHandler
|
3
|
+
class InvalidRefreshToken
|
4
|
+
attr_reader :errors
|
5
|
+
|
6
|
+
ERROR_CODE = 'INVALID_REFRESH_TOKEN'.freeze
|
7
|
+
|
8
|
+
def initialize(errors)
|
9
|
+
@errors = errors
|
10
|
+
end
|
11
|
+
|
12
|
+
def raise_error
|
13
|
+
return unless invalid_refresh_token_error
|
14
|
+
|
15
|
+
raise RDStation::Error::InvalidRefreshToken, invalid_refresh_token_error
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def invalid_refresh_token_error
|
21
|
+
errors.find { |error| error['error_type'] == ERROR_CODE }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require_relative 'expired_access_token'
|
2
2
|
require_relative 'expired_code_grant'
|
3
3
|
require_relative 'invalid_credentials'
|
4
|
+
require_relative 'invalid_refresh_token'
|
4
5
|
|
5
6
|
module RDStation
|
6
7
|
class ErrorHandler
|
@@ -9,6 +10,7 @@ module RDStation
|
|
9
10
|
ErrorHandler::ExpiredAccessToken,
|
10
11
|
ErrorHandler::ExpiredCodeGrant,
|
11
12
|
ErrorHandler::InvalidCredentials,
|
13
|
+
ErrorHandler::InvalidRefreshToken,
|
12
14
|
].freeze
|
13
15
|
|
14
16
|
def initialize(array_of_errors)
|
data/lib/rdstation/events.rb
CHANGED
@@ -12,16 +12,8 @@ module RDStation
|
|
12
12
|
def create(payload)
|
13
13
|
retryable_request(@authorization) do |authorization|
|
14
14
|
response = self.class.post(EVENTS_ENDPOINT, headers: authorization.headers, body: payload.to_json)
|
15
|
-
|
16
|
-
return response_body unless errors?(response_body)
|
17
|
-
RDStation::ErrorHandler.new(response).raise_error
|
15
|
+
ApiResponse.build(response)
|
18
16
|
end
|
19
17
|
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def errors?(response_body)
|
24
|
-
response_body.is_a?(Array) || response_body['errors']
|
25
|
-
end
|
26
18
|
end
|
27
19
|
end
|
data/lib/rdstation/fields.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module RDStation
|
3
|
-
# More info: https://developers.rdstation.com/pt-BR/reference/
|
3
|
+
# More info: https://developers.rdstation.com/pt-BR/reference/fields
|
4
4
|
class Fields
|
5
5
|
include HTTParty
|
6
6
|
include ::RDStation::RetryableRequest
|
7
7
|
|
8
8
|
BASE_URL = 'https://api.rd.services/platform/contacts/fields'.freeze
|
9
|
-
|
9
|
+
|
10
10
|
def initialize(authorization:)
|
11
11
|
@authorization = authorization
|
12
12
|
end
|
@@ -18,5 +18,31 @@ module RDStation
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
+
def create(payload)
|
22
|
+
retryable_request(@authorization) do |authorization|
|
23
|
+
response = self.class.post(BASE_URL, headers: authorization.headers, body: payload.to_json)
|
24
|
+
ApiResponse.build(response)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def update(uuid, payload)
|
29
|
+
retryable_request(@authorization) do |authorization|
|
30
|
+
response = self.class.patch(base_url(uuid), headers: authorization.headers, body: payload.to_json)
|
31
|
+
ApiResponse.build(response)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete(uuid)
|
36
|
+
retryable_request(@authorization) do |authorization|
|
37
|
+
response = self.class.delete(base_url(uuid), headers: authorization.headers)
|
38
|
+
ApiResponse.build(response)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def base_url(path = '')
|
45
|
+
"#{BASE_URL}/#{path}"
|
46
|
+
end
|
21
47
|
end
|
22
48
|
end
|
data/lib/rdstation/version.rb
CHANGED
@@ -217,19 +217,32 @@ RSpec.describe RDStation::Authentication do
|
|
217
217
|
end
|
218
218
|
|
219
219
|
context 'when the refresh token is invalid' do
|
220
|
+
let(:invalid_refresh_token_response) do
|
221
|
+
{
|
222
|
+
status: 401,
|
223
|
+
headers: { 'Content-Type' => 'application/json' },
|
224
|
+
body: {
|
225
|
+
errors: {
|
226
|
+
error_type: 'INVALID_REFRESH_TOKEN',
|
227
|
+
error_message: 'The provided refresh token is invalid or was revoked.'
|
228
|
+
}
|
229
|
+
}.to_json
|
230
|
+
}
|
231
|
+
end
|
232
|
+
|
220
233
|
before do
|
221
234
|
stub_request(:post, token_endpoint)
|
222
235
|
.with(
|
223
236
|
headers: request_headers,
|
224
237
|
body: token_request_with_invalid_refresh_token.to_json
|
225
238
|
)
|
226
|
-
.to_return(
|
239
|
+
.to_return(invalid_refresh_token_response)
|
227
240
|
end
|
228
241
|
|
229
242
|
it 'returns an auth error' do
|
230
243
|
expect do
|
231
244
|
authentication.update_access_token('invalid_refresh_token')
|
232
|
-
end.to raise_error(RDStation::Error::
|
245
|
+
end.to raise_error(RDStation::Error::InvalidRefreshToken)
|
233
246
|
end
|
234
247
|
end
|
235
248
|
|
@@ -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
|
@@ -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
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RDStation::ErrorHandler::InvalidRefreshToken do
|
4
|
+
describe '#raise_error' do
|
5
|
+
subject(:invalid_refresh_token) { described_class.new(errors) }
|
6
|
+
|
7
|
+
context 'when the refresh token is invalid or was revoked' do
|
8
|
+
let(:errors) do
|
9
|
+
[
|
10
|
+
{
|
11
|
+
'error_type' => 'INVALID_REFRESH_TOKEN',
|
12
|
+
'error_message' => 'Error Message',
|
13
|
+
}
|
14
|
+
]
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'raises an InvalidRefreshToken error' do
|
18
|
+
expect do
|
19
|
+
invalid_refresh_token.raise_error
|
20
|
+
end.to raise_error(RDStation::Error::InvalidRefreshToken, 'Error Message')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when none of the errors are invalid refresh token errors' do
|
25
|
+
let(:errors) do
|
26
|
+
[
|
27
|
+
{
|
28
|
+
'error_message' => 'Error Message',
|
29
|
+
'error_type' => 'RANDOM_ERROR_TYPE'
|
30
|
+
},
|
31
|
+
{
|
32
|
+
'error_message' => 'Another Error Message',
|
33
|
+
'error_type' => 'ANOTHER_RANDOM_ERROR_TYPE'
|
34
|
+
}
|
35
|
+
]
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'does not raise an InvalidRefreshToken error' do
|
39
|
+
result = invalid_refresh_token.raise_error
|
40
|
+
expect(result).to be_nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when there are no errors' do
|
45
|
+
let(:errors) { [] }
|
46
|
+
|
47
|
+
it 'does not raise an InvalidRefreshToken error' do
|
48
|
+
result = invalid_refresh_token.raise_error
|
49
|
+
expect(result).to be_nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
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
|
|
@@ -172,5 +173,13 @@ RSpec.describe RDStation::ErrorHandler do
|
|
172
173
|
expect { error_handler.raise_error }.to raise_error(RDStation::Error::BadGateway, '<html><body>HTML error response</body></html>')
|
173
174
|
end
|
174
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
|
175
184
|
end
|
176
185
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rdstation-ruby-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paulo L F Casaretto
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-10-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -143,6 +143,7 @@ files:
|
|
143
143
|
- lib/rdstation/api_response.rb
|
144
144
|
- lib/rdstation/authentication.rb
|
145
145
|
- lib/rdstation/authorization.rb
|
146
|
+
- lib/rdstation/builder/field.rb
|
146
147
|
- lib/rdstation/client.rb
|
147
148
|
- lib/rdstation/contacts.rb
|
148
149
|
- lib/rdstation/error.rb
|
@@ -155,6 +156,7 @@ files:
|
|
155
156
|
- lib/rdstation/error_handler/expired_code_grant.rb
|
156
157
|
- lib/rdstation/error_handler/invalid_credentials.rb
|
157
158
|
- lib/rdstation/error_handler/invalid_event_type.rb
|
159
|
+
- lib/rdstation/error_handler/invalid_refresh_token.rb
|
158
160
|
- lib/rdstation/error_handler/unauthorized.rb
|
159
161
|
- lib/rdstation/events.rb
|
160
162
|
- lib/rdstation/fields.rb
|
@@ -166,6 +168,7 @@ files:
|
|
166
168
|
- spec/lib/rdstation/api_response_spec.rb
|
167
169
|
- spec/lib/rdstation/authentication_spec.rb
|
168
170
|
- spec/lib/rdstation/authorization_spec.rb
|
171
|
+
- spec/lib/rdstation/builder/field_spec.rb
|
169
172
|
- spec/lib/rdstation/client_spec.rb
|
170
173
|
- spec/lib/rdstation/contacts_spec.rb
|
171
174
|
- spec/lib/rdstation/error/format_spec.rb
|
@@ -175,6 +178,7 @@ files:
|
|
175
178
|
- spec/lib/rdstation/error_handler/expired_code_grant_spec.rb
|
176
179
|
- spec/lib/rdstation/error_handler/invalid_credentials_spec.rb
|
177
180
|
- spec/lib/rdstation/error_handler/invalid_event_type_spec.rb
|
181
|
+
- spec/lib/rdstation/error_handler/invalid_refresh_token_spec.rb
|
178
182
|
- spec/lib/rdstation/error_handler/unauthorized_spec.rb
|
179
183
|
- spec/lib/rdstation/error_handler_spec.rb
|
180
184
|
- spec/lib/rdstation/error_spec.rb
|
@@ -188,7 +192,7 @@ homepage: http://resultadosdigitais.com.br
|
|
188
192
|
licenses:
|
189
193
|
- MIT
|
190
194
|
metadata: {}
|
191
|
-
post_install_message:
|
195
|
+
post_install_message:
|
192
196
|
rdoc_options: []
|
193
197
|
require_paths:
|
194
198
|
- lib
|
@@ -203,8 +207,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
203
207
|
- !ruby/object:Gem::Version
|
204
208
|
version: '0'
|
205
209
|
requirements: []
|
206
|
-
rubygems_version: 3.0.
|
207
|
-
signing_key:
|
210
|
+
rubygems_version: 3.0.8
|
211
|
+
signing_key:
|
208
212
|
specification_version: 4
|
209
213
|
summary: Ruby API wrapper for RD Station
|
210
214
|
test_files:
|
@@ -212,6 +216,7 @@ test_files:
|
|
212
216
|
- spec/lib/rdstation/api_response_spec.rb
|
213
217
|
- spec/lib/rdstation/authentication_spec.rb
|
214
218
|
- spec/lib/rdstation/authorization_spec.rb
|
219
|
+
- spec/lib/rdstation/builder/field_spec.rb
|
215
220
|
- spec/lib/rdstation/client_spec.rb
|
216
221
|
- spec/lib/rdstation/contacts_spec.rb
|
217
222
|
- spec/lib/rdstation/error/format_spec.rb
|
@@ -221,6 +226,7 @@ test_files:
|
|
221
226
|
- spec/lib/rdstation/error_handler/expired_code_grant_spec.rb
|
222
227
|
- spec/lib/rdstation/error_handler/invalid_credentials_spec.rb
|
223
228
|
- spec/lib/rdstation/error_handler/invalid_event_type_spec.rb
|
229
|
+
- spec/lib/rdstation/error_handler/invalid_refresh_token_spec.rb
|
224
230
|
- spec/lib/rdstation/error_handler/unauthorized_spec.rb
|
225
231
|
- spec/lib/rdstation/error_handler_spec.rb
|
226
232
|
- spec/lib/rdstation/error_spec.rb
|