intercom 3.5.10 → 4.1.2
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 +5 -5
- data/.circleci/config.yml +35 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +5 -0
- data/Gemfile +1 -4
- data/README.md +418 -216
- data/RELEASING.md +9 -0
- data/Rakefile +1 -1
- data/changes.txt +141 -0
- data/intercom.gemspec +1 -2
- data/lib/intercom.rb +34 -19
- data/lib/intercom/api_operations/archive.rb +16 -0
- data/lib/intercom/api_operations/delete.rb +4 -1
- data/lib/intercom/api_operations/find.rb +5 -2
- data/lib/intercom/api_operations/find_all.rb +4 -3
- data/lib/intercom/api_operations/list.rb +4 -1
- data/lib/intercom/api_operations/load.rb +4 -2
- data/lib/intercom/api_operations/nested_resource.rb +70 -0
- data/lib/intercom/api_operations/request_hard_delete.rb +12 -0
- data/lib/intercom/api_operations/save.rb +6 -4
- data/lib/intercom/api_operations/scroll.rb +4 -5
- data/lib/intercom/api_operations/search.rb +18 -0
- data/lib/intercom/article.rb +7 -0
- data/lib/intercom/base_collection_proxy.rb +72 -0
- data/lib/intercom/client.rb +66 -18
- data/lib/intercom/client_collection_proxy.rb +17 -39
- data/lib/intercom/collection.rb +7 -0
- data/lib/intercom/company.rb +8 -0
- data/lib/intercom/contact.rb +22 -3
- data/lib/intercom/conversation.rb +5 -0
- data/lib/intercom/data_attribute.rb +7 -0
- data/lib/intercom/deprecated_leads_collection_proxy.rb +22 -0
- data/lib/intercom/deprecated_resources.rb +13 -0
- data/lib/intercom/errors.rb +44 -4
- data/lib/intercom/extended_api_operations/segments.rb +3 -1
- data/lib/intercom/extended_api_operations/tags.rb +3 -1
- data/lib/intercom/lead.rb +21 -0
- data/lib/intercom/lib/typed_json_deserializer.rb +42 -37
- data/lib/intercom/note.rb +4 -0
- data/lib/intercom/request.rb +162 -95
- data/lib/intercom/scroll_collection_proxy.rb +38 -42
- data/lib/intercom/search_collection_proxy.rb +47 -0
- data/lib/intercom/section.rb +23 -0
- data/lib/intercom/segment.rb +4 -0
- data/lib/intercom/service/article.rb +20 -0
- data/lib/intercom/service/base_service.rb +13 -0
- data/lib/intercom/service/collection.rb +24 -0
- data/lib/intercom/service/company.rb +4 -2
- data/lib/intercom/service/contact.rb +29 -6
- data/lib/intercom/service/conversation.rb +23 -2
- data/lib/intercom/service/data_attribute.rb +20 -0
- data/lib/intercom/service/event.rb +12 -0
- data/lib/intercom/service/lead.rb +41 -0
- data/lib/intercom/service/note.rb +4 -8
- data/lib/intercom/service/section.rb +7 -0
- data/lib/intercom/service/tag.rb +8 -8
- data/lib/intercom/service/team.rb +17 -0
- data/lib/intercom/service/user.rb +4 -2
- data/lib/intercom/service/visitor.rb +15 -6
- data/lib/intercom/tag.rb +4 -0
- data/lib/intercom/team.rb +7 -0
- data/lib/intercom/traits/api_resource.rb +48 -27
- data/lib/intercom/traits/dirty_tracking.rb +8 -1
- data/lib/intercom/user.rb +12 -3
- data/lib/intercom/utils.rb +13 -2
- data/lib/intercom/version.rb +1 -1
- data/lib/intercom/visitor.rb +0 -2
- data/spec/spec_helper.rb +881 -436
- data/spec/unit/intercom/admin_spec.rb +2 -2
- data/spec/unit/intercom/article_spec.rb +40 -0
- data/spec/unit/intercom/base_collection_proxy_spec.rb +30 -0
- data/spec/unit/intercom/client_collection_proxy_spec.rb +41 -41
- data/spec/unit/intercom/client_spec.rb +76 -9
- data/spec/unit/intercom/collection_spec.rb +32 -0
- data/spec/unit/intercom/company_spec.rb +29 -21
- data/spec/unit/intercom/contact_spec.rb +365 -29
- data/spec/unit/intercom/conversation_spec.rb +70 -7
- data/spec/unit/intercom/count_spec.rb +4 -4
- data/spec/unit/intercom/data_attribute_spec.rb +40 -0
- data/spec/unit/intercom/deprecated_leads_collection_proxy_spec.rb +17 -0
- data/spec/unit/intercom/event_spec.rb +25 -8
- data/spec/unit/intercom/job_spec.rb +24 -24
- data/spec/unit/intercom/lead_spec.rb +57 -0
- data/spec/unit/intercom/lib/flat_store_spec.rb +22 -20
- data/spec/unit/intercom/message_spec.rb +1 -1
- data/spec/unit/intercom/note_spec.rb +4 -10
- data/spec/unit/intercom/request_spec.rb +150 -9
- data/spec/unit/intercom/scroll_collection_proxy_spec.rb +40 -39
- data/spec/unit/intercom/search_collection_proxy_spec.rb +60 -0
- data/spec/unit/intercom/section_spec.rb +32 -0
- data/spec/unit/intercom/segment_spec.rb +2 -2
- data/spec/unit/intercom/subscription_spec.rb +5 -6
- data/spec/unit/intercom/tag_spec.rb +22 -14
- data/spec/unit/intercom/team_spec.rb +21 -0
- data/spec/unit/intercom/traits/api_resource_spec.rb +129 -47
- data/spec/unit/intercom/user_spec.rb +227 -217
- data/spec/unit/intercom/visitor_spec.rb +49 -0
- data/spec/unit/intercom_spec.rb +5 -3
- metadata +63 -26
- data/.travis.yml +0 -6
- data/lib/intercom/extended_api_operations/users.rb +0 -16
- data/spec/unit/intercom/visitors_spec.rb +0 -61
@@ -1,7 +1,12 @@
|
|
1
1
|
require 'intercom/traits/api_resource'
|
2
|
+
require 'intercom/api_operations/nested_resource'
|
2
3
|
|
3
4
|
module Intercom
|
4
5
|
class Conversation
|
5
6
|
include Traits::ApiResource
|
7
|
+
include ApiOperations::NestedResource
|
8
|
+
|
9
|
+
nested_resource_methods :tag, operations: %i[add delete]
|
10
|
+
nested_resource_methods :contact, operations: %i[add delete], path: :customers
|
6
11
|
end
|
7
12
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Intercom
|
4
|
+
class DeprecatedLeadsCollectionProxy < ClientCollectionProxy
|
5
|
+
def fetch(next_page)
|
6
|
+
response_hash = if next_page
|
7
|
+
@client.get(next_page, {})
|
8
|
+
else
|
9
|
+
@client.get(@url, @params)
|
10
|
+
end
|
11
|
+
transform(response_hash)
|
12
|
+
end
|
13
|
+
|
14
|
+
def transform(response_hash)
|
15
|
+
response_hash['type'] = 'lead.list'
|
16
|
+
leads_list = response_hash.delete('contacts')
|
17
|
+
leads_list.each { |lead| lead['type'] = 'lead' }
|
18
|
+
response_hash['leads'] = leads_list
|
19
|
+
response_hash
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/intercom/errors.rb
CHANGED
@@ -23,16 +23,28 @@ module Intercom
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
# Raised when the
|
27
|
-
# Check that you have set
|
26
|
+
# Raised when the token you provided is incorrect or not authorized to access certain type of data.
|
27
|
+
# Check that you have set Intercom.token correctly.
|
28
28
|
class AuthenticationError < IntercomError; end
|
29
29
|
|
30
|
+
# Raised when the token provided is linked to a deleted application.
|
31
|
+
class AppSuspendedError < AuthenticationError; end
|
32
|
+
|
33
|
+
# Raised when the token provided has been revoked.
|
34
|
+
class TokenRevokedError < AuthenticationError; end
|
35
|
+
|
36
|
+
# Raised when the token provided can't be decoded, and is most likely invalid.
|
37
|
+
class TokenUnauthorizedError < AuthenticationError; end
|
38
|
+
|
30
39
|
# Raised when something goes wrong on within the Intercom API service.
|
31
40
|
class ServerError < IntercomError; end
|
32
41
|
|
33
42
|
# Raised when we have bad gateway errors.
|
34
43
|
class BadGatewayError < IntercomError; end
|
35
44
|
|
45
|
+
# Raised when we have gateway timeout errors.
|
46
|
+
class GatewayTimeoutError < IntercomError; end
|
47
|
+
|
36
48
|
# Raised when we experience a socket read timeout
|
37
49
|
class ServiceUnavailableError < IntercomError; end
|
38
50
|
|
@@ -42,23 +54,50 @@ module Intercom
|
|
42
54
|
# Raised when requesting resources on behalf of a user that doesn't exist in your application on Intercom.
|
43
55
|
class ResourceNotFound < IntercomError; end
|
44
56
|
|
57
|
+
# Raised when requesting an admin that doesn't exist in your Intercom workspace.
|
58
|
+
class AdminNotFound < IntercomError; end
|
59
|
+
|
60
|
+
# Raised when trying to create a resource that already exists in Intercom.
|
61
|
+
class ResourceNotUniqueError < IntercomError; end
|
62
|
+
|
45
63
|
# Raised when the request has bad syntax
|
46
64
|
class BadRequestError < IntercomError; end
|
47
65
|
|
48
66
|
# Raised when you have exceeded the API rate limit
|
49
67
|
class RateLimitExceeded < IntercomError; end
|
50
68
|
|
69
|
+
# Raised when some attribute of the response cannot be handled
|
70
|
+
class UnexpectedResponseError < IntercomError; end
|
71
|
+
|
51
72
|
# Raised when the request throws an error not accounted for
|
52
73
|
class UnexpectedError < IntercomError; end
|
53
74
|
|
75
|
+
# Raised when the CDA limit for the app has been reached
|
76
|
+
class CDALimitReachedError < IntercomError; end
|
77
|
+
|
54
78
|
# Raised when multiple users match the query (typically duplicate email addresses)
|
55
79
|
class MultipleMatchingUsersError < IntercomError; end
|
56
80
|
|
81
|
+
# Raised when restoring a blocked user
|
82
|
+
class BlockedUserError < IntercomError; end
|
83
|
+
|
57
84
|
# Raised when you try to call a non-setter method that does not exist on an object
|
58
|
-
class Intercom::AttributeNotSetError < IntercomError
|
85
|
+
class Intercom::AttributeNotSetError < IntercomError; end
|
59
86
|
|
60
87
|
# Raised when unexpected nil returned from server
|
61
|
-
class Intercom::HttpError < IntercomError
|
88
|
+
class Intercom::HttpError < IntercomError; end
|
89
|
+
|
90
|
+
# Raised when an invalid api version is used
|
91
|
+
class ApiVersionInvalid < IntercomError; end
|
92
|
+
|
93
|
+
# Raised when an creating a scroll if one already exists
|
94
|
+
class ScrollAlreadyExistsError < IntercomError; end
|
95
|
+
|
96
|
+
# Raised when a CDA is invalid
|
97
|
+
class InvalidDocumentError < IntercomError; end
|
98
|
+
|
99
|
+
# Raised when a merge is invalid
|
100
|
+
class InvalidMergeError < IntercomError; end
|
62
101
|
|
63
102
|
#
|
64
103
|
# Non-public errors (internal to the gem)
|
@@ -71,4 +110,5 @@ module Intercom
|
|
71
110
|
class Intercom::NoMethodMissingHandler < IntercomInternalError; end
|
72
111
|
|
73
112
|
class Intercom::DeserializationError < IntercomInternalError; end
|
113
|
+
|
74
114
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'intercom/client_collection_proxy'
|
2
4
|
require 'intercom/utils'
|
3
5
|
|
@@ -6,7 +8,7 @@ module Intercom
|
|
6
8
|
module Segments
|
7
9
|
def by_segment(id)
|
8
10
|
collection_name = Utils.resource_class_to_collection_name(collection_class)
|
9
|
-
ClientCollectionProxy.new(collection_name,
|
11
|
+
ClientCollectionProxy.new(collection_name, collection_class, details: { url: "/#{collection_name}?segment_id=#{id}" }, client: @client)
|
10
12
|
end
|
11
13
|
end
|
12
14
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'intercom/client_collection_proxy'
|
2
4
|
require 'intercom/utils'
|
3
5
|
|
@@ -6,7 +8,7 @@ module Intercom
|
|
6
8
|
module Tags
|
7
9
|
def by_tag(id)
|
8
10
|
collection_name = Utils.resource_class_to_collection_name(collection_class)
|
9
|
-
ClientCollectionProxy.new(collection_name,
|
11
|
+
ClientCollectionProxy.new(collection_name, collection_class, details: { url: "/#{collection_name}?tag_id=#{id}" }, client: @client)
|
10
12
|
end
|
11
13
|
end
|
12
14
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'intercom/traits/api_resource'
|
4
|
+
|
5
|
+
module Intercom
|
6
|
+
class Lead
|
7
|
+
include Traits::ApiResource
|
8
|
+
|
9
|
+
def identity_vars
|
10
|
+
%i[email user_id]
|
11
|
+
end
|
12
|
+
|
13
|
+
def flat_store_attributes
|
14
|
+
[:custom_attributes]
|
15
|
+
end
|
16
|
+
|
17
|
+
def update_verb
|
18
|
+
'put'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,54 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'intercom/utils'
|
2
4
|
|
3
5
|
module Intercom
|
4
6
|
module Lib
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
# Responsibility: To decide whether we are deserializing a collection or an
|
8
|
+
# entity of a particular type and to dispatch deserialization
|
9
|
+
class TypedJsonDeserializer
|
10
|
+
attr_reader :json
|
11
|
+
|
12
|
+
def initialize(json, client)
|
13
|
+
@json = json
|
14
|
+
@client = client
|
15
|
+
end
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
17
|
+
def deserialize
|
18
|
+
if blank_object_type?(object_type)
|
19
|
+
raise DeserializationError, 'No type field was found to facilitate deserialization'
|
20
|
+
elsif list_object_type?(object_type)
|
21
|
+
deserialize_collection(json[object_entity_key])
|
22
|
+
else # singular object type
|
23
|
+
deserialize_object(json)
|
22
24
|
end
|
25
|
+
end
|
23
26
|
|
24
|
-
|
27
|
+
private
|
25
28
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
+
def blank_object_type?(object_type)
|
30
|
+
object_type.nil? || object_type == ''
|
31
|
+
end
|
29
32
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
+
def list_object_type?(object_type)
|
34
|
+
object_type.end_with?('.list')
|
35
|
+
end
|
33
36
|
|
34
|
-
|
35
|
-
|
36
|
-
collection_json.map { |item_json| TypedJsonDeserializer.new(item_json).deserialize }
|
37
|
-
end
|
37
|
+
def deserialize_collection(collection_json)
|
38
|
+
return [] if collection_json.nil?
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
entity_class.from_api(object_json)
|
42
|
-
end
|
40
|
+
collection_json.map { |item_json| TypedJsonDeserializer.new(item_json, @client).deserialize }
|
41
|
+
end
|
43
42
|
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
def deserialize_object(object_json)
|
44
|
+
entity_class = Utils.constantize_singular_resource_name(object_entity_key)
|
45
|
+
deserialized = entity_class.from_api(object_json)
|
46
|
+
deserialized.client = @client
|
47
|
+
deserialized
|
48
|
+
end
|
47
49
|
|
48
|
-
|
49
|
-
|
50
|
-
|
50
|
+
def object_type
|
51
|
+
@object_type ||= json['type']
|
52
|
+
end
|
51
53
|
|
54
|
+
def object_entity_key
|
55
|
+
@object_entity_key ||= Utils.entity_key_from_type(object_type)
|
52
56
|
end
|
57
|
+
end
|
53
58
|
end
|
54
59
|
end
|
data/lib/intercom/note.rb
CHANGED
data/lib/intercom/request.rb
CHANGED
@@ -1,101 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cgi'
|
2
4
|
require 'net/https'
|
3
5
|
|
4
6
|
module Intercom
|
5
7
|
class Request
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
self.net_http_method = net_http_method
|
11
|
-
end
|
8
|
+
class << self
|
9
|
+
def get(path, params)
|
10
|
+
new(path, Net::HTTP::Get.new(append_query_string_to_url(path, params), default_headers))
|
11
|
+
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
def post(path, form_data)
|
14
|
+
new(path, method_with_body(Net::HTTP::Post, path, form_data))
|
15
|
+
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
def delete(path, params)
|
18
|
+
new(path, method_with_body(Net::HTTP::Delete, path, params))
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
def put(path, form_data)
|
22
|
+
new(path, method_with_body(Net::HTTP::Put, path, form_data))
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
private def method_with_body(http_method, path, params)
|
26
|
+
request = http_method.send(:new, path, default_headers)
|
27
|
+
request.body = params.to_json
|
28
|
+
request['Content-Type'] = 'application/json'
|
29
|
+
request
|
30
|
+
end
|
28
31
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
+
private def default_headers
|
33
|
+
{ 'Accept-Encoding' => 'gzip, deflate', 'Accept' => 'application/vnd.intercom.3+json', 'User-Agent' => "Intercom-Ruby/#{Intercom::VERSION}" }
|
34
|
+
end
|
32
35
|
|
33
|
-
|
34
|
-
|
35
|
-
end
|
36
|
+
private def append_query_string_to_url(url, params)
|
37
|
+
return url if params.empty?
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
request["Content-Type"] = "application/json"
|
41
|
-
request
|
39
|
+
query_string = params.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&')
|
40
|
+
url + "?#{query_string}"
|
41
|
+
end
|
42
42
|
end
|
43
43
|
|
44
|
-
def
|
45
|
-
|
44
|
+
def initialize(path, net_http_method)
|
45
|
+
self.path = path
|
46
|
+
self.net_http_method = net_http_method
|
47
|
+
self.handle_rate_limit = false
|
46
48
|
end
|
47
49
|
|
48
|
-
|
49
|
-
net = Net::HTTP.new(uri.host, uri.port)
|
50
|
-
if uri.is_a?(URI::HTTPS)
|
51
|
-
net.use_ssl = true
|
52
|
-
net.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
53
|
-
net.ca_file = File.join(File.dirname(__FILE__), '../data/cacert.pem')
|
54
|
-
end
|
55
|
-
net.read_timeout = 90
|
56
|
-
net.open_timeout = 30
|
57
|
-
net
|
58
|
-
end
|
50
|
+
attr_accessor :handle_rate_limit
|
59
51
|
|
60
|
-
def execute(target_base_url=nil,
|
52
|
+
def execute(target_base_url = nil, token:, read_timeout: 90, open_timeout: 30, api_version: nil)
|
53
|
+
retries = 3
|
61
54
|
base_uri = URI.parse(target_base_url)
|
62
55
|
set_common_headers(net_http_method, base_uri)
|
63
|
-
|
56
|
+
set_auth_header(net_http_method, token)
|
57
|
+
set_api_version(net_http_method, api_version) if api_version
|
64
58
|
begin
|
65
|
-
client(base_uri).start do |http|
|
59
|
+
client(base_uri, read_timeout: read_timeout, open_timeout: open_timeout).start do |http|
|
66
60
|
begin
|
67
61
|
response = http.request(net_http_method)
|
62
|
+
|
68
63
|
set_rate_limit_details(response)
|
69
|
-
decoded_body = decode_body(response)
|
70
|
-
parsed_body = parse_body(decoded_body, response)
|
71
64
|
raise_errors_on_failure(response)
|
65
|
+
|
66
|
+
parsed_body = extract_response_body(response)
|
67
|
+
|
68
|
+
return nil if parsed_body.nil?
|
69
|
+
|
70
|
+
raise_application_errors_on_failure(parsed_body, response.code.to_i) if parsed_body['type'] == 'error.list'
|
71
|
+
|
72
72
|
parsed_body
|
73
|
+
rescue Intercom::RateLimitExceeded => e
|
74
|
+
if @handle_rate_limit
|
75
|
+
seconds_to_retry = (@rate_limit_details[:reset_at] - Time.now.utc).ceil
|
76
|
+
if (retries -= 1) < 0
|
77
|
+
raise Intercom::RateLimitExceeded, 'Rate limit retries exceeded. Please examine current API Usage.'
|
78
|
+
else
|
79
|
+
sleep seconds_to_retry unless seconds_to_retry < 0
|
80
|
+
retry
|
81
|
+
end
|
82
|
+
else
|
83
|
+
raise e
|
84
|
+
end
|
73
85
|
rescue Timeout::Error
|
74
|
-
raise Intercom::ServiceUnavailableError
|
86
|
+
raise Intercom::ServiceUnavailableError, 'Service Unavailable [request timed out]'
|
75
87
|
end
|
76
88
|
end
|
77
89
|
rescue Timeout::Error
|
78
|
-
raise Intercom::ServiceConnectionError
|
90
|
+
raise Intercom::ServiceConnectionError, 'Failed to connect to service [connection attempt timed out]'
|
79
91
|
end
|
80
92
|
end
|
81
93
|
|
82
|
-
|
83
|
-
|
84
|
-
|
94
|
+
attr_accessor :path,
|
95
|
+
:net_http_method,
|
96
|
+
:rate_limit_details
|
85
97
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
98
|
+
private :path,
|
99
|
+
:net_http_method
|
100
|
+
|
101
|
+
private def client(uri, read_timeout:, open_timeout:)
|
102
|
+
net = Net::HTTP.new(uri.host, uri.port)
|
103
|
+
if uri.is_a?(URI::HTTPS)
|
104
|
+
net.use_ssl = true
|
105
|
+
net.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
106
|
+
net.ca_file = File.join(File.dirname(__FILE__), '../data/cacert.pem')
|
93
107
|
end
|
94
|
-
|
95
|
-
|
108
|
+
net.read_timeout = read_timeout
|
109
|
+
net.open_timeout = open_timeout
|
110
|
+
net
|
111
|
+
end
|
112
|
+
|
113
|
+
private def extract_response_body(response)
|
114
|
+
decoded_body = decode(response['content-encoding'], response.body)
|
115
|
+
|
116
|
+
json_parse_response(decoded_body, response.code)
|
96
117
|
end
|
97
118
|
|
98
|
-
def
|
119
|
+
private def decode(content_encoding, body)
|
120
|
+
return body if !body || body.empty? || content_encoding != 'gzip'
|
121
|
+
|
122
|
+
Zlib::GzipReader.new(StringIO.new(body)).read.force_encoding('utf-8')
|
123
|
+
end
|
124
|
+
|
125
|
+
private def json_parse_response(str, code)
|
126
|
+
return nil if str.to_s.empty?
|
127
|
+
|
128
|
+
JSON.parse(str)
|
129
|
+
rescue JSON::ParserError
|
130
|
+
msg = <<~MSG.gsub(/[[:space:]]+/, ' ').strip # #squish from ActiveSuppor
|
131
|
+
Expected a JSON response body. Instead got '#{str}'
|
132
|
+
with status code '#{code}'.
|
133
|
+
MSG
|
134
|
+
|
135
|
+
raise UnexpectedResponseError, msg
|
136
|
+
end
|
137
|
+
|
138
|
+
private def set_rate_limit_details(response)
|
99
139
|
rate_limit_details = {}
|
100
140
|
rate_limit_details[:limit] = response['X-RateLimit-Limit'].to_i if response['X-RateLimit-Limit']
|
101
141
|
rate_limit_details[:remaining] = response['X-RateLimit-Remaining'].to_i if response['X-RateLimit-Remaining']
|
@@ -103,52 +143,85 @@ module Intercom
|
|
103
143
|
@rate_limit_details = rate_limit_details
|
104
144
|
end
|
105
145
|
|
106
|
-
def
|
107
|
-
|
108
|
-
Zlib::GzipReader.new(StringIO.new(body)).read.force_encoding("utf-8")
|
146
|
+
private def set_common_headers(method, _base_uri)
|
147
|
+
method.add_field('AcceptEncoding', 'gzip, deflate')
|
109
148
|
end
|
110
149
|
|
111
|
-
def
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
raise Intercom::
|
150
|
+
private def set_auth_header(method, token)
|
151
|
+
method.add_field('Authorization', "Bearer #{token}")
|
152
|
+
end
|
153
|
+
|
154
|
+
private def set_api_version(method, api_version)
|
155
|
+
method.add_field('Intercom-Version', api_version)
|
156
|
+
end
|
157
|
+
|
158
|
+
private def raise_errors_on_failure(res)
|
159
|
+
code = res.code.to_i
|
160
|
+
|
161
|
+
if code == 404
|
162
|
+
raise Intercom::ResourceNotFound, 'Resource Not Found'
|
163
|
+
elsif code == 401
|
164
|
+
raise Intercom::AuthenticationError, 'Unauthorized'
|
165
|
+
elsif code == 403
|
166
|
+
raise Intercom::AuthenticationError, 'Forbidden'
|
167
|
+
elsif code == 429
|
168
|
+
raise Intercom::RateLimitExceeded, 'Rate Limit Exceeded'
|
169
|
+
elsif code == 500
|
170
|
+
raise Intercom::ServerError, 'Server Error'
|
171
|
+
elsif code == 502
|
172
|
+
raise Intercom::BadGatewayError, 'Bad Gateway Error'
|
173
|
+
elsif code == 503
|
174
|
+
raise Intercom::ServiceUnavailableError, 'Service Unavailable'
|
175
|
+
elsif code == 504
|
176
|
+
raise Intercom::GatewayTimeoutError, 'Gateway Timeout'
|
124
177
|
end
|
125
178
|
end
|
126
179
|
|
127
|
-
def raise_application_errors_on_failure(error_list_details, http_code)
|
180
|
+
private def raise_application_errors_on_failure(error_list_details, http_code)
|
128
181
|
# Currently, we don't support multiple errors
|
129
182
|
error_details = error_list_details['errors'].first
|
130
183
|
error_code = error_details['type'] || error_details['code']
|
131
184
|
error_field = error_details['field']
|
132
185
|
parsed_http_code = (http_code > 0 ? http_code : nil)
|
133
186
|
error_context = {
|
134
|
-
:
|
135
|
-
:
|
136
|
-
:
|
137
|
-
:
|
187
|
+
http_code: parsed_http_code,
|
188
|
+
application_error_code: error_code,
|
189
|
+
field: error_field,
|
190
|
+
request_id: error_list_details['request_id']
|
138
191
|
}
|
139
192
|
case error_code
|
140
|
-
when 'unauthorized', 'forbidden'
|
193
|
+
when 'unauthorized', 'forbidden', 'token_not_found'
|
141
194
|
raise Intercom::AuthenticationError.new(error_details['message'], error_context)
|
142
|
-
when
|
195
|
+
when 'token_suspended'
|
196
|
+
raise Intercom::AppSuspendedError.new(error_details['message'], error_context)
|
197
|
+
when 'token_revoked'
|
198
|
+
raise Intercom::TokenRevokedError.new(error_details['message'], error_context)
|
199
|
+
when 'token_unauthorized'
|
200
|
+
raise Intercom::TokenUnauthorizedError.new(error_details['message'], error_context)
|
201
|
+
when 'bad_request', 'missing_parameter', 'parameter_invalid', 'parameter_not_found'
|
143
202
|
raise Intercom::BadRequestError.new(error_details['message'], error_context)
|
144
|
-
when
|
203
|
+
when 'not_restorable'
|
204
|
+
raise Intercom::BlockedUserError.new(error_details['message'], error_context)
|
205
|
+
when 'not_found', 'company_not_found'
|
145
206
|
raise Intercom::ResourceNotFound.new(error_details['message'], error_context)
|
146
|
-
when
|
207
|
+
when 'admin_not_found'
|
208
|
+
raise Intercom::AdminNotFound.new(error_details['message'], error_context)
|
209
|
+
when 'rate_limit_exceeded'
|
147
210
|
raise Intercom::RateLimitExceeded.new(error_details['message'], error_context)
|
211
|
+
when 'custom_data_limit_reached'
|
212
|
+
raise Intercom::CDALimitReachedError.new(error_details['message'], error_context)
|
213
|
+
when 'invalid_document'
|
214
|
+
raise Intercom::InvalidDocumentError.new(error_details['message'], error_context)
|
148
215
|
when 'service_unavailable'
|
149
216
|
raise Intercom::ServiceUnavailableError.new(error_details['message'], error_context)
|
150
217
|
when 'conflict', 'unique_user_constraint'
|
151
218
|
raise Intercom::MultipleMatchingUsersError.new(error_details['message'], error_context)
|
219
|
+
when 'resource_conflict'
|
220
|
+
raise Intercom::ResourceNotUniqueError.new(error_details['message'], error_context)
|
221
|
+
when 'intercom_version_invalid'
|
222
|
+
raise Intercom::ApiVersionInvalid.new(error_details['message'], error_context)
|
223
|
+
when 'scroll_exists'
|
224
|
+
raise Intercom::ScrollAlreadyExistsError.new(error_details['message'], error_context)
|
152
225
|
when nil, ''
|
153
226
|
raise Intercom::UnexpectedError.new(message_for_unexpected_error_without_type(error_details, parsed_http_code), error_context)
|
154
227
|
else
|
@@ -156,18 +229,12 @@ module Intercom
|
|
156
229
|
end
|
157
230
|
end
|
158
231
|
|
159
|
-
def message_for_unexpected_error_with_type(error_details, parsed_http_code)
|
232
|
+
private def message_for_unexpected_error_with_type(error_details, parsed_http_code)
|
160
233
|
"The error of type '#{error_details['type']}' is not recognized. It occurred with the message: #{error_details['message']} and http_code: '#{parsed_http_code}'. Please contact Intercom with these details."
|
161
234
|
end
|
162
235
|
|
163
|
-
def message_for_unexpected_error_without_type(error_details, parsed_http_code)
|
236
|
+
private def message_for_unexpected_error_without_type(error_details, parsed_http_code)
|
164
237
|
"An unexpected error occured. It occurred with the message: #{error_details['message']} and http_code: '#{parsed_http_code}'. Please contact Intercom with these details."
|
165
238
|
end
|
166
|
-
|
167
|
-
def self.append_query_string_to_url(url, params)
|
168
|
-
return url if params.empty?
|
169
|
-
query_string = params.map { |k, v| "#{k.to_s}=#{CGI::escape(v.to_s)}" }.join('&')
|
170
|
-
url + "?#{query_string}"
|
171
|
-
end
|
172
239
|
end
|
173
240
|
end
|