rdstation-ruby-client 0.1.1 → 1.0.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +12 -12
- data/README.md +13 -13
- data/lib/rdstation-ruby-client.rb +11 -4
- data/lib/rdstation/authentication.rb +34 -21
- data/lib/rdstation/contacts.rb +26 -14
- data/lib/rdstation/error.rb +21 -0
- data/lib/rdstation/error_handler.rb +38 -0
- data/lib/rdstation/error_handler/conflicting_field.rb +26 -0
- data/lib/rdstation/error_handler/default.rb +19 -0
- data/lib/rdstation/error_handler/expired_access_token.rb +30 -0
- data/lib/rdstation/error_handler/expired_code_grant.rb +27 -0
- data/lib/rdstation/error_handler/invalid_credentials.rb +26 -0
- data/lib/rdstation/error_handler/resource_not_found.rb +26 -0
- data/lib/rdstation/error_handler/unauthorized.rb +26 -0
- data/lib/rdstation/fields.rb +26 -0
- data/lib/rdstation/version.rb +1 -1
- data/rdstation-ruby-client.gemspec +1 -2
- data/spec/lib/rdstation/authentication_spec.rb +177 -0
- data/spec/lib/rdstation/contacts_spec.rb +434 -0
- data/spec/lib/rdstation/fields_spec.rb +52 -0
- data/spec/spec_helper.rb +1 -13
- metadata +22 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c632e94efb06e81ab00f5272d042b0c5b26cf55f
|
4
|
+
data.tar.gz: c947e738844beb8671118a0d76df0468f8361528
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: edc33857fcf8af5535d2446da9d0cd3327cd2a092b47d722cb1107887ff649f127a65c301cb80e45c4fae9997713e568eb88b81cfe72498971bbc348f07e6a4e
|
7
|
+
data.tar.gz: cd2315fe944d7790336f50586ad68ef9240092550ca75e2b8327863f880d59ba36ccb8a0012cf9d9fefda4901d2d2ab348a033a51812488136d38c463f7d94de
|
data/Gemfile.lock
CHANGED
@@ -1,23 +1,24 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rdstation-ruby-client (0.0
|
4
|
+
rdstation-ruby-client (1.0.0)
|
5
5
|
httparty (~> 0.12)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
addressable (2.
|
10
|
+
addressable (2.5.2)
|
11
|
+
public_suffix (>= 2.0.2, < 4.0)
|
11
12
|
ansi (1.5.0)
|
12
|
-
crack (0.4.
|
13
|
+
crack (0.4.3)
|
13
14
|
safe_yaml (~> 1.0.0)
|
14
15
|
diff-lcs (1.2.5)
|
15
|
-
|
16
|
-
|
16
|
+
hashdiff (0.3.7)
|
17
|
+
httparty (0.16.2)
|
17
18
|
multi_xml (>= 0.5.2)
|
18
|
-
json (1.8.3)
|
19
19
|
minitest (4.7.5)
|
20
|
-
multi_xml (0.
|
20
|
+
multi_xml (0.6.0)
|
21
|
+
public_suffix (3.0.2)
|
21
22
|
rake (10.1.0)
|
22
23
|
rspec (3.1.0)
|
23
24
|
rspec-core (~> 3.1.0)
|
@@ -35,10 +36,10 @@ GEM
|
|
35
36
|
turn (0.9.7)
|
36
37
|
ansi
|
37
38
|
minitest (~> 4)
|
38
|
-
|
39
|
-
webmock (1.20.4)
|
39
|
+
webmock (2.3.2)
|
40
40
|
addressable (>= 2.3.6)
|
41
41
|
crack (>= 0.3.2)
|
42
|
+
hashdiff
|
42
43
|
|
43
44
|
PLATFORMS
|
44
45
|
ruby
|
@@ -49,8 +50,7 @@ DEPENDENCIES
|
|
49
50
|
rdstation-ruby-client!
|
50
51
|
rspec
|
51
52
|
turn
|
52
|
-
|
53
|
-
webmock
|
53
|
+
webmock (~> 2.1)
|
54
54
|
|
55
55
|
BUNDLED WITH
|
56
|
-
1.
|
56
|
+
1.16.1
|
data/README.md
CHANGED
@@ -49,7 +49,7 @@ rdstation_client.change_lead_status(email: 'joe@foo.bar', status: 'won', value:
|
|
49
49
|
|
50
50
|
### Authentication
|
51
51
|
|
52
|
-
#### Getting authentication URL
|
52
|
+
#### Getting authentication URL
|
53
53
|
|
54
54
|
```ruby
|
55
55
|
rdstation_authentication = RDStation::Authentication.new('client_id', 'client_secret')
|
@@ -58,7 +58,7 @@ redirect_url = 'https://yourapp.org/auth/callback'
|
|
58
58
|
rdstation_authentication.auth_url(redirect_url)
|
59
59
|
```
|
60
60
|
|
61
|
-
#### Getting access_token
|
61
|
+
#### Getting access_token
|
62
62
|
|
63
63
|
You will need the code param that is returned from RD Station to your application after the user confirms the access at the authorization dialog.
|
64
64
|
|
@@ -67,7 +67,7 @@ rdstation_authentication = RDStation::Authentication.new('client_id', 'client_se
|
|
67
67
|
rdstation_authentication.authenticate(code_returned_from_rdstation)
|
68
68
|
```
|
69
69
|
|
70
|
-
#### Updating access_token
|
70
|
+
#### Updating access_token
|
71
71
|
|
72
72
|
```ruby
|
73
73
|
rdstation_authentication = RDStation::Authentication.new('client_id', 'client_secret')
|
@@ -81,8 +81,8 @@ rdstation_authentication.update_access_token('refresh_token')
|
|
81
81
|
Returns data about a specific Contact
|
82
82
|
|
83
83
|
```ruby
|
84
|
-
|
85
|
-
|
84
|
+
contact = RDStation::Contacts.new('auth_token')
|
85
|
+
contact.by_uuid('uuid')
|
86
86
|
```
|
87
87
|
|
88
88
|
More info: https://developers.rdstation.com/pt-BR/reference/contacts#methodGetDetailsuuid
|
@@ -92,23 +92,23 @@ More info: https://developers.rdstation.com/pt-BR/reference/contacts#methodGetDe
|
|
92
92
|
Returns data about a specific Contact
|
93
93
|
|
94
94
|
```ruby
|
95
|
-
|
96
|
-
|
95
|
+
contact = RDStation::Contacts.new('auth_token')
|
96
|
+
contact.by_email('email')
|
97
97
|
```
|
98
98
|
|
99
99
|
More info: https://developers.rdstation.com/pt-BR/reference/contacts#methodGetDetailsemail
|
100
100
|
|
101
101
|
#### Update a Contact by UUID
|
102
102
|
|
103
|
-
Updates the properties of a Contact.
|
103
|
+
Updates the properties of a Contact.
|
104
104
|
|
105
105
|
```ruby
|
106
106
|
contact_info = {
|
107
107
|
name: "Joe Foo"
|
108
108
|
}
|
109
109
|
|
110
|
-
|
111
|
-
|
110
|
+
contact = RDStation::Contacts.new('auth_token')
|
111
|
+
contact.update('uuid', contact_info)
|
112
112
|
```
|
113
113
|
Contact Default Parameters
|
114
114
|
- email
|
@@ -127,7 +127,7 @@ More info: https://developers.rdstation.com/pt-BR/reference/contacts#methodPatch
|
|
127
127
|
|
128
128
|
#### Upsert a Contact by identifier and value
|
129
129
|
|
130
|
-
With an UPSERT like behavior, this method is capable of both updating the properties of a Contact or creating a new Contact. Whatever is used as an identifier cannot appear in the request payload as a field. This will result in a [BAD_REQUEST error](https://developers.rdstation.com/pt-BR/error-states#conflicting).
|
130
|
+
With an UPSERT like behavior, this method is capable of both updating the properties of a Contact or creating a new Contact. Whatever is used as an identifier cannot appear in the request payload as a field. This will result in a [BAD_REQUEST error](https://developers.rdstation.com/pt-BR/error-states#conflicting).
|
131
131
|
|
132
132
|
```ruby
|
133
133
|
contact_info = {
|
@@ -137,8 +137,8 @@ contact_info = {
|
|
137
137
|
identifier = "email"
|
138
138
|
identifier_value = "joe@foo.bar"
|
139
139
|
|
140
|
-
|
141
|
-
|
140
|
+
contact = RDStation::Contacts.new('auth_token')
|
141
|
+
contact.upsert(identifier, identifier_value, contact_info)
|
142
142
|
```
|
143
143
|
|
144
144
|
More info: https://developers.rdstation.com/pt-BR/reference/contacts#methodPatchUpsertDetails
|
@@ -1,4 +1,11 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
|
4
|
-
require
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
# API requests
|
4
|
+
require 'rdstation/authentication'
|
5
|
+
require 'rdstation/client'
|
6
|
+
require 'rdstation/contacts'
|
7
|
+
require 'rdstation/fields'
|
8
|
+
|
9
|
+
# Error handling
|
10
|
+
require 'rdstation/error'
|
11
|
+
require 'rdstation/error_handler'
|
@@ -3,6 +3,9 @@ module RDStation
|
|
3
3
|
class Authentication
|
4
4
|
include HTTParty
|
5
5
|
|
6
|
+
AUTH_TOKEN_URL = 'https://api.rd.services/auth/token'.freeze
|
7
|
+
DEFAULT_HEADERS = { 'Content-Type' => 'application/json' }.freeze
|
8
|
+
|
6
9
|
def initialize(client_id, client_secret)
|
7
10
|
@client_id = client_id
|
8
11
|
@client_secret = client_secret
|
@@ -11,44 +14,54 @@ module RDStation
|
|
11
14
|
#
|
12
15
|
# param redirect_url
|
13
16
|
# URL that the user will be redirected
|
14
|
-
# after confirming application authorization
|
17
|
+
# after confirming application authorization
|
15
18
|
#
|
16
19
|
def auth_url(redirect_url)
|
17
20
|
"https://api.rd.services/auth/dialog?client_id=#{@client_id}&redirect_url=#{redirect_url}"
|
18
21
|
end
|
19
22
|
|
23
|
+
# Public: Get the credentials from RD Station API
|
24
|
+
#
|
25
|
+
# code - The code String sent by RDStation after the user confirms authorization.
|
26
|
+
#
|
27
|
+
# Examples
|
20
28
|
#
|
21
|
-
#
|
22
|
-
#
|
29
|
+
# authenticate("123")
|
30
|
+
# # => { 'access_token' => '54321', 'expires_in' => 86_400, 'refresh_token' => 'refresh' }
|
23
31
|
#
|
32
|
+
# Returns the credentials Hash.
|
33
|
+
# Raises RDStation::Error::ExpiredCodeGrant if the code has expired
|
34
|
+
# Raises RDStation::Error::InvalidCredentials if the client_id, client_secret
|
35
|
+
# or code is invalid.
|
24
36
|
def authenticate(code)
|
25
|
-
post_to_auth_endpoint(
|
37
|
+
response = post_to_auth_endpoint(code: code)
|
38
|
+
parsed_body = JSON.parse(response.body)
|
39
|
+
return parsed_body unless parsed_body['errors']
|
40
|
+
RDStation::ErrorHandler.new(response).raise_errors
|
26
41
|
end
|
27
42
|
|
28
43
|
#
|
29
44
|
# param refresh_token
|
30
|
-
# parameter sent by RDStation after authenticate
|
45
|
+
# parameter sent by RDStation after authenticate
|
31
46
|
#
|
32
47
|
def update_access_token(refresh_token)
|
33
|
-
post_to_auth_endpoint(
|
48
|
+
response = post_to_auth_endpoint(refresh_token: refresh_token)
|
49
|
+
parsed_body = JSON.parse(response.body)
|
50
|
+
return parsed_body unless parsed_body['errors']
|
51
|
+
RDStation::ErrorHandler.new(response).raise_errors
|
34
52
|
end
|
35
53
|
|
36
54
|
private
|
37
55
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
"Accept-Encoding": "identity"
|
49
|
-
},
|
50
|
-
:body => default_body.merge(params)
|
51
|
-
)
|
52
|
-
end
|
56
|
+
def post_to_auth_endpoint(params)
|
57
|
+
default_body = { client_id: @client_id, client_secret: @client_secret }
|
58
|
+
body = default_body.merge(params)
|
59
|
+
|
60
|
+
self.class.post(
|
61
|
+
AUTH_TOKEN_URL,
|
62
|
+
body: body.to_json,
|
63
|
+
headers: DEFAULT_HEADERS
|
64
|
+
)
|
65
|
+
end
|
53
66
|
end
|
54
67
|
end
|
data/lib/rdstation/contacts.rb
CHANGED
@@ -12,12 +12,18 @@ module RDStation
|
|
12
12
|
# param uuid:
|
13
13
|
# The unique uuid associated to each RD Station Contact.
|
14
14
|
#
|
15
|
-
def
|
16
|
-
self.class.get(base_url(uuid), :
|
15
|
+
def by_uuid(uuid)
|
16
|
+
response = self.class.get(base_url(uuid), headers: required_headers)
|
17
|
+
response_body = JSON.parse(response.body)
|
18
|
+
return response_body unless response_body['errors']
|
19
|
+
RDStation::ErrorHandler.new(response).raise_errors
|
17
20
|
end
|
18
21
|
|
19
|
-
def
|
20
|
-
self.class.get(base_url("email:#{email}"), :
|
22
|
+
def by_email(email)
|
23
|
+
response = self.class.get(base_url("email:#{email}"), headers: required_headers)
|
24
|
+
response_body = JSON.parse(response.body)
|
25
|
+
return response_body unless response_body['errors']
|
26
|
+
RDStation::ErrorHandler.new(response).raise_errors
|
21
27
|
end
|
22
28
|
|
23
29
|
# The Contact hash may contain the following parameters:
|
@@ -31,8 +37,11 @@ module RDStation
|
|
31
37
|
# :mobile_phone
|
32
38
|
# :website
|
33
39
|
# :tags
|
34
|
-
def
|
35
|
-
self.class.patch(base_url(uuid), :body => contact_hash.to_json, :headers => required_headers)
|
40
|
+
def update(uuid, contact_hash)
|
41
|
+
response = self.class.patch(base_url(uuid), :body => contact_hash.to_json, :headers => required_headers)
|
42
|
+
response_body = JSON.parse(response.body)
|
43
|
+
return response_body unless response_body['errors']
|
44
|
+
RDStation::ErrorHandler.new(response).raise_errors
|
36
45
|
end
|
37
46
|
|
38
47
|
#
|
@@ -43,19 +52,22 @@ module RDStation
|
|
43
52
|
# param contact_hash:
|
44
53
|
# Contact data
|
45
54
|
#
|
46
|
-
def
|
55
|
+
def upsert(identifier, identifier_value, contact_hash)
|
47
56
|
path = "#{identifier}:#{identifier_value}"
|
48
|
-
self.class.patch(base_url(path), :
|
57
|
+
response = self.class.patch(base_url(path), body: contact_hash.to_json, headers: required_headers)
|
58
|
+
response_body = JSON.parse(response.body)
|
59
|
+
return response_body unless response_body['errors']
|
60
|
+
RDStation::ErrorHandler.new(response).raise_errors
|
49
61
|
end
|
50
62
|
|
51
63
|
private
|
52
64
|
|
53
|
-
|
54
|
-
|
55
|
-
|
65
|
+
def base_url(path = "")
|
66
|
+
"https://api.rd.services/platform/contacts/#{path}"
|
67
|
+
end
|
56
68
|
|
57
|
-
|
58
|
-
|
59
|
-
|
69
|
+
def required_headers
|
70
|
+
{ "Authorization" => "Bearer #{@auth_token}", "Content-Type" => "application/json" }
|
71
|
+
end
|
60
72
|
end
|
61
73
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RDStation
|
2
|
+
|
3
|
+
class Error < StandardError
|
4
|
+
attr_reader :http_status, :headers, :body
|
5
|
+
|
6
|
+
def initialize(message, api_response_error)
|
7
|
+
@http_status = api_response_error.code
|
8
|
+
@headers = api_response_error.headers
|
9
|
+
@body = JSON.parse(api_response_error.body)
|
10
|
+
super(message)
|
11
|
+
end
|
12
|
+
|
13
|
+
class ConflictingField < Error; end
|
14
|
+
class Default < Error; end
|
15
|
+
class ExpiredAccessToken < Error; end
|
16
|
+
class ExpiredCodeGrant < Error; end
|
17
|
+
class InvalidCredentials < Error; end
|
18
|
+
class ResourceNotFound < Error; end
|
19
|
+
class Unauthorized < Error; end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative 'error_handler/conflicting_field'
|
2
|
+
require_relative 'error_handler/default'
|
3
|
+
require_relative 'error_handler/expired_access_token'
|
4
|
+
require_relative 'error_handler/expired_code_grant'
|
5
|
+
require_relative 'error_handler/invalid_credentials'
|
6
|
+
require_relative 'error_handler/resource_not_found'
|
7
|
+
require_relative 'error_handler/unauthorized'
|
8
|
+
|
9
|
+
module RDStation
|
10
|
+
class ErrorHandler
|
11
|
+
ERROR_TYPES = [
|
12
|
+
ErrorHandler::ConflictingField,
|
13
|
+
ErrorHandler::ExpiredAccessToken,
|
14
|
+
ErrorHandler::ExpiredCodeGrant,
|
15
|
+
ErrorHandler::InvalidCredentials,
|
16
|
+
ErrorHandler::ResourceNotFound,
|
17
|
+
ErrorHandler::Unauthorized,
|
18
|
+
ErrorHandler::Default
|
19
|
+
].freeze
|
20
|
+
|
21
|
+
def initialize(response)
|
22
|
+
@response = response
|
23
|
+
end
|
24
|
+
|
25
|
+
def raise_errors
|
26
|
+
errors.each(&:raise_error)
|
27
|
+
# Raise only the exception message when the error is not recognized
|
28
|
+
unrecognized_error = @response['errors']
|
29
|
+
raise unrecognized_error['error_message']
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def errors
|
35
|
+
ERROR_TYPES.map { |error| error.new(@response) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RDStation
|
2
|
+
class ErrorHandler
|
3
|
+
class ConflictingField
|
4
|
+
attr_reader :api_response, :response_body, :error
|
5
|
+
|
6
|
+
ERROR_CODE = 'CONFLICTING_FIELD'.freeze
|
7
|
+
EXCEPTION_CLASS = RDStation::Error::ConflictingField
|
8
|
+
|
9
|
+
def initialize(api_response)
|
10
|
+
@api_response = api_response
|
11
|
+
@error = JSON.parse(api_response.body)['errors']
|
12
|
+
end
|
13
|
+
|
14
|
+
def raise_error
|
15
|
+
return unless conflicting_field?
|
16
|
+
raise EXCEPTION_CLASS.new(error['error_message'], api_response)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def conflicting_field?
|
22
|
+
error['error_type'] == ERROR_CODE
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RDStation
|
2
|
+
class ErrorHandler
|
3
|
+
class Default
|
4
|
+
attr_reader :api_response
|
5
|
+
EXCEPTION_CLASS = RDStation::Error::Default
|
6
|
+
|
7
|
+
def initialize(api_response)
|
8
|
+
@api_response = api_response
|
9
|
+
end
|
10
|
+
|
11
|
+
def raise_error
|
12
|
+
raise RDStation::Error::Default.new(
|
13
|
+
'An unrecognized error has occurred.',
|
14
|
+
api_response
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RDStation
|
2
|
+
class ErrorHandler
|
3
|
+
class ExpiredAccessToken
|
4
|
+
attr_reader :api_response, :response_headers, :error
|
5
|
+
|
6
|
+
EXCEPTION_CLASS = RDStation::Error::ExpiredAccessToken
|
7
|
+
|
8
|
+
def initialize(api_response)
|
9
|
+
@api_response = api_response
|
10
|
+
@error = JSON.parse(api_response.body)['errors']
|
11
|
+
@response_headers = api_response.headers
|
12
|
+
end
|
13
|
+
|
14
|
+
def raise_error
|
15
|
+
return unless expired_token?
|
16
|
+
raise EXCEPTION_CLASS.new(error['error_message'], api_response)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def expired_token?
|
22
|
+
auth_header = response_headers['x-amzn-remapped-www-authenticate'] ||
|
23
|
+
response_headers['www-authenticate']
|
24
|
+
|
25
|
+
return unless auth_header
|
26
|
+
auth_header.include?('error="expired_token"')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|