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.
Files changed (101) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +35 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +5 -0
  4. data/Gemfile +1 -4
  5. data/README.md +418 -216
  6. data/RELEASING.md +9 -0
  7. data/Rakefile +1 -1
  8. data/changes.txt +141 -0
  9. data/intercom.gemspec +1 -2
  10. data/lib/intercom.rb +34 -19
  11. data/lib/intercom/api_operations/archive.rb +16 -0
  12. data/lib/intercom/api_operations/delete.rb +4 -1
  13. data/lib/intercom/api_operations/find.rb +5 -2
  14. data/lib/intercom/api_operations/find_all.rb +4 -3
  15. data/lib/intercom/api_operations/list.rb +4 -1
  16. data/lib/intercom/api_operations/load.rb +4 -2
  17. data/lib/intercom/api_operations/nested_resource.rb +70 -0
  18. data/lib/intercom/api_operations/request_hard_delete.rb +12 -0
  19. data/lib/intercom/api_operations/save.rb +6 -4
  20. data/lib/intercom/api_operations/scroll.rb +4 -5
  21. data/lib/intercom/api_operations/search.rb +18 -0
  22. data/lib/intercom/article.rb +7 -0
  23. data/lib/intercom/base_collection_proxy.rb +72 -0
  24. data/lib/intercom/client.rb +66 -18
  25. data/lib/intercom/client_collection_proxy.rb +17 -39
  26. data/lib/intercom/collection.rb +7 -0
  27. data/lib/intercom/company.rb +8 -0
  28. data/lib/intercom/contact.rb +22 -3
  29. data/lib/intercom/conversation.rb +5 -0
  30. data/lib/intercom/data_attribute.rb +7 -0
  31. data/lib/intercom/deprecated_leads_collection_proxy.rb +22 -0
  32. data/lib/intercom/deprecated_resources.rb +13 -0
  33. data/lib/intercom/errors.rb +44 -4
  34. data/lib/intercom/extended_api_operations/segments.rb +3 -1
  35. data/lib/intercom/extended_api_operations/tags.rb +3 -1
  36. data/lib/intercom/lead.rb +21 -0
  37. data/lib/intercom/lib/typed_json_deserializer.rb +42 -37
  38. data/lib/intercom/note.rb +4 -0
  39. data/lib/intercom/request.rb +162 -95
  40. data/lib/intercom/scroll_collection_proxy.rb +38 -42
  41. data/lib/intercom/search_collection_proxy.rb +47 -0
  42. data/lib/intercom/section.rb +23 -0
  43. data/lib/intercom/segment.rb +4 -0
  44. data/lib/intercom/service/article.rb +20 -0
  45. data/lib/intercom/service/base_service.rb +13 -0
  46. data/lib/intercom/service/collection.rb +24 -0
  47. data/lib/intercom/service/company.rb +4 -2
  48. data/lib/intercom/service/contact.rb +29 -6
  49. data/lib/intercom/service/conversation.rb +23 -2
  50. data/lib/intercom/service/data_attribute.rb +20 -0
  51. data/lib/intercom/service/event.rb +12 -0
  52. data/lib/intercom/service/lead.rb +41 -0
  53. data/lib/intercom/service/note.rb +4 -8
  54. data/lib/intercom/service/section.rb +7 -0
  55. data/lib/intercom/service/tag.rb +8 -8
  56. data/lib/intercom/service/team.rb +17 -0
  57. data/lib/intercom/service/user.rb +4 -2
  58. data/lib/intercom/service/visitor.rb +15 -6
  59. data/lib/intercom/tag.rb +4 -0
  60. data/lib/intercom/team.rb +7 -0
  61. data/lib/intercom/traits/api_resource.rb +48 -27
  62. data/lib/intercom/traits/dirty_tracking.rb +8 -1
  63. data/lib/intercom/user.rb +12 -3
  64. data/lib/intercom/utils.rb +13 -2
  65. data/lib/intercom/version.rb +1 -1
  66. data/lib/intercom/visitor.rb +0 -2
  67. data/spec/spec_helper.rb +881 -436
  68. data/spec/unit/intercom/admin_spec.rb +2 -2
  69. data/spec/unit/intercom/article_spec.rb +40 -0
  70. data/spec/unit/intercom/base_collection_proxy_spec.rb +30 -0
  71. data/spec/unit/intercom/client_collection_proxy_spec.rb +41 -41
  72. data/spec/unit/intercom/client_spec.rb +76 -9
  73. data/spec/unit/intercom/collection_spec.rb +32 -0
  74. data/spec/unit/intercom/company_spec.rb +29 -21
  75. data/spec/unit/intercom/contact_spec.rb +365 -29
  76. data/spec/unit/intercom/conversation_spec.rb +70 -7
  77. data/spec/unit/intercom/count_spec.rb +4 -4
  78. data/spec/unit/intercom/data_attribute_spec.rb +40 -0
  79. data/spec/unit/intercom/deprecated_leads_collection_proxy_spec.rb +17 -0
  80. data/spec/unit/intercom/event_spec.rb +25 -8
  81. data/spec/unit/intercom/job_spec.rb +24 -24
  82. data/spec/unit/intercom/lead_spec.rb +57 -0
  83. data/spec/unit/intercom/lib/flat_store_spec.rb +22 -20
  84. data/spec/unit/intercom/message_spec.rb +1 -1
  85. data/spec/unit/intercom/note_spec.rb +4 -10
  86. data/spec/unit/intercom/request_spec.rb +150 -9
  87. data/spec/unit/intercom/scroll_collection_proxy_spec.rb +40 -39
  88. data/spec/unit/intercom/search_collection_proxy_spec.rb +60 -0
  89. data/spec/unit/intercom/section_spec.rb +32 -0
  90. data/spec/unit/intercom/segment_spec.rb +2 -2
  91. data/spec/unit/intercom/subscription_spec.rb +5 -6
  92. data/spec/unit/intercom/tag_spec.rb +22 -14
  93. data/spec/unit/intercom/team_spec.rb +21 -0
  94. data/spec/unit/intercom/traits/api_resource_spec.rb +129 -47
  95. data/spec/unit/intercom/user_spec.rb +227 -217
  96. data/spec/unit/intercom/visitor_spec.rb +49 -0
  97. data/spec/unit/intercom_spec.rb +5 -3
  98. metadata +63 -26
  99. data/.travis.yml +0 -6
  100. data/lib/intercom/extended_api_operations/users.rb +0 -16
  101. 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,7 @@
1
+ require 'intercom/traits/api_resource'
2
+
3
+ module Intercom
4
+ class DataAttribute
5
+ include Traits::ApiResource
6
+ end
7
+ 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
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Intercom
4
+ module DeprecatedResources
5
+ def deprecated__leads
6
+ Intercom::Service::Lead.new(self)
7
+ end
8
+
9
+ def deprecated__users
10
+ Intercom::Service::User.new(self)
11
+ end
12
+ end
13
+ end
@@ -23,16 +23,28 @@ module Intercom
23
23
  end
24
24
  end
25
25
 
26
- # Raised when the credentials you provide don't match a valid account on Intercom.
27
- # Check that you have set <b>Intercom.app_id=</b> and <b>Intercom.app_api_key=</b> correctly.
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 ; end
85
+ class Intercom::AttributeNotSetError < IntercomError; end
59
86
 
60
87
  # Raised when unexpected nil returned from server
61
- class Intercom::HttpError < IntercomError ; end
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, finder_details: {url: "/#{collection_name}?segment_id=#{id}"}, client: @client)
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, finder_details: {url: "/#{collection_name}?tag_id=#{id}"}, client: @client)
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
- # Responsibility: To decide whether we are deserializing a collection or an
6
- # entity of a particular type and to dispatch deserialization
7
- class TypedJsonDeserializer
8
- attr_reader :json
9
-
10
- def initialize(json)
11
- @json = json
12
- end
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
- def deserialize
15
- if blank_object_type?(object_type)
16
- raise DeserializationError, "No type field was found to facilitate deserialization"
17
- elsif list_object_type?(object_type)
18
- deserialize_collection(json[object_entity_key])
19
- else # singular object type
20
- deserialize_object(json)
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
- private
27
+ private
25
28
 
26
- def blank_object_type?(object_type)
27
- object_type.nil? || object_type == ''
28
- end
29
+ def blank_object_type?(object_type)
30
+ object_type.nil? || object_type == ''
31
+ end
29
32
 
30
- def list_object_type?(object_type)
31
- object_type.end_with?('.list')
32
- end
33
+ def list_object_type?(object_type)
34
+ object_type.end_with?('.list')
35
+ end
33
36
 
34
- def deserialize_collection(collection_json)
35
- return [] if collection_json == nil
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
- def deserialize_object(object_json)
40
- entity_class = Utils.constantize_singular_resource_name(object_entity_key)
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
- def object_type
45
- @object_type ||= json['type']
46
- end
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
- def object_entity_key
49
- @object_entity_key ||= Utils.entity_key_from_type(object_type)
50
- end
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
@@ -4,5 +4,9 @@ require 'intercom/traits/api_resource'
4
4
  module Intercom
5
5
  class Note
6
6
  include Traits::ApiResource
7
+
8
+ def self.collection_proxy_class
9
+ Intercom::BaseCollectionProxy
10
+ end
7
11
  end
8
12
  end
@@ -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
- attr_accessor :path, :net_http_method, :rate_limit_details
7
-
8
- def initialize(path, net_http_method)
9
- self.path = path
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
- def set_common_headers(method, base_uri)
14
- method.add_field('AcceptEncoding', 'gzip, deflate')
15
- end
13
+ def post(path, form_data)
14
+ new(path, method_with_body(Net::HTTP::Post, path, form_data))
15
+ end
16
16
 
17
- def set_basic_auth(method, username, secret)
18
- method.basic_auth(CGI.unescape(username), CGI.unescape(secret))
19
- end
17
+ def delete(path, params)
18
+ new(path, method_with_body(Net::HTTP::Delete, path, params))
19
+ end
20
20
 
21
- def self.get(path, params)
22
- new(path, Net::HTTP::Get.new(append_query_string_to_url(path, params), default_headers))
23
- end
21
+ def put(path, form_data)
22
+ new(path, method_with_body(Net::HTTP::Put, path, form_data))
23
+ end
24
24
 
25
- def self.post(path, form_data)
26
- new(path, method_with_body(Net::HTTP::Post, path, form_data))
27
- end
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
- def self.delete(path, params)
30
- new(path, method_with_body(Net::HTTP::Delete, path, params))
31
- end
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
- def self.put(path, form_data)
34
- new(path, method_with_body(Net::HTTP::Put, path, form_data))
35
- end
36
+ private def append_query_string_to_url(url, params)
37
+ return url if params.empty?
36
38
 
37
- def self.method_with_body(http_method, path, params)
38
- request = http_method.send(:new, path, default_headers)
39
- request.body = params.to_json
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 self.default_headers
45
- {'Accept-Encoding' => 'gzip, deflate', 'Accept' => 'application/vnd.intercom.3+json', 'User-Agent' => "Intercom-Ruby/#{Intercom::VERSION}"}
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
- def client(uri)
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, username:, secret: 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
- set_basic_auth(net_http_method, username, secret)
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.new('Service Unavailable [request timed out]')
86
+ raise Intercom::ServiceUnavailableError, 'Service Unavailable [request timed out]'
75
87
  end
76
88
  end
77
89
  rescue Timeout::Error
78
- raise Intercom::ServiceConnectionError.new('Failed to connect to service [connection attempt timed out]')
90
+ raise Intercom::ServiceConnectionError, 'Failed to connect to service [connection attempt timed out]'
79
91
  end
80
92
  end
81
93
 
82
- def decode_body(response)
83
- decode(response['content-encoding'], response.body)
84
- end
94
+ attr_accessor :path,
95
+ :net_http_method,
96
+ :rate_limit_details
85
97
 
86
- def parse_body(decoded_body, response)
87
- parsed_body = nil
88
- return parsed_body if decoded_body.nil? || decoded_body.strip.empty?
89
- begin
90
- parsed_body = JSON.parse(decoded_body)
91
- rescue JSON::ParserError => _
92
- raise_errors_on_failure(response)
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
- raise_application_errors_on_failure(parsed_body, response.code.to_i) if parsed_body['type'] == 'error.list'
95
- parsed_body
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 set_rate_limit_details(response)
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 decode(content_encoding, body)
107
- return body if (!body) || body.empty? || content_encoding != 'gzip'
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 raise_errors_on_failure(res)
112
- if res.code.to_i.eql?(404)
113
- raise Intercom::ResourceNotFound.new('Resource Not Found')
114
- elsif res.code.to_i.eql?(401)
115
- raise Intercom::AuthenticationError.new('Unauthorized')
116
- elsif res.code.to_i.eql?(403)
117
- raise Intercom::AuthenticationError.new('Forbidden')
118
- elsif res.code.to_i.eql?(500)
119
- raise Intercom::ServerError.new('Server Error')
120
- elsif res.code.to_i.eql?(502)
121
- raise Intercom::BadGatewayError.new('Bad Gateway Error')
122
- elsif res.code.to_i.eql?(503)
123
- raise Intercom::ServiceUnavailableError.new('Service Unavailable')
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
- :http_code => parsed_http_code,
135
- :application_error_code => error_code,
136
- :field => error_field,
137
- :request_id => error_list_details['request_id']
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 "bad_request", "missing_parameter", 'parameter_invalid', 'parameter_not_found'
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 "not_found"
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 "rate_limit_exceeded"
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