intercom 3.9.4 → 4.1.1

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +340 -222
  3. data/Rakefile +1 -1
  4. data/changes.txt +25 -0
  5. data/lib/intercom.rb +33 -22
  6. data/lib/intercom/api_operations/archive.rb +2 -1
  7. data/lib/intercom/api_operations/delete.rb +16 -0
  8. data/lib/intercom/api_operations/find.rb +5 -2
  9. data/lib/intercom/api_operations/find_all.rb +4 -3
  10. data/lib/intercom/api_operations/list.rb +4 -1
  11. data/lib/intercom/api_operations/load.rb +4 -2
  12. data/lib/intercom/api_operations/nested_resource.rb +70 -0
  13. data/lib/intercom/api_operations/save.rb +6 -4
  14. data/lib/intercom/api_operations/scroll.rb +4 -5
  15. data/lib/intercom/api_operations/search.rb +3 -2
  16. data/lib/intercom/article.rb +7 -0
  17. data/lib/intercom/base_collection_proxy.rb +72 -0
  18. data/lib/intercom/client.rb +36 -25
  19. data/lib/intercom/client_collection_proxy.rb +17 -39
  20. data/lib/intercom/collection.rb +7 -0
  21. data/lib/intercom/company.rb +8 -0
  22. data/lib/intercom/contact.rb +21 -3
  23. data/lib/intercom/conversation.rb +5 -0
  24. data/lib/intercom/data_attribute.rb +7 -0
  25. data/lib/intercom/deprecated_leads_collection_proxy.rb +22 -0
  26. data/lib/intercom/deprecated_resources.rb +13 -0
  27. data/lib/intercom/errors.rb +3 -0
  28. data/lib/intercom/extended_api_operations/segments.rb +3 -1
  29. data/lib/intercom/extended_api_operations/tags.rb +3 -1
  30. data/lib/intercom/lead.rb +21 -0
  31. data/lib/intercom/lib/typed_json_deserializer.rb +42 -37
  32. data/lib/intercom/note.rb +4 -0
  33. data/lib/intercom/request.rb +37 -33
  34. data/lib/intercom/scroll_collection_proxy.rb +33 -38
  35. data/lib/intercom/search_collection_proxy.rb +30 -65
  36. data/lib/intercom/section.rb +23 -0
  37. data/lib/intercom/service/article.rb +20 -0
  38. data/lib/intercom/service/base_service.rb +7 -0
  39. data/lib/intercom/service/collection.rb +24 -0
  40. data/lib/intercom/service/company.rb +0 -12
  41. data/lib/intercom/service/contact.rb +21 -10
  42. data/lib/intercom/service/conversation.rb +12 -3
  43. data/lib/intercom/service/data_attribute.rb +20 -0
  44. data/lib/intercom/service/lead.rb +41 -0
  45. data/lib/intercom/service/note.rb +4 -8
  46. data/lib/intercom/service/section.rb +7 -0
  47. data/lib/intercom/service/subscription.rb +2 -2
  48. data/lib/intercom/service/tag.rb +9 -9
  49. data/lib/intercom/service/visitor.rb +17 -8
  50. data/lib/intercom/tag.rb +4 -0
  51. data/lib/intercom/traits/api_resource.rb +44 -18
  52. data/lib/intercom/traits/dirty_tracking.rb +8 -1
  53. data/lib/intercom/user.rb +12 -3
  54. data/lib/intercom/utils.rb +13 -2
  55. data/lib/intercom/version.rb +1 -1
  56. data/lib/intercom/visitor.rb +0 -2
  57. data/spec/spec_helper.rb +843 -520
  58. data/spec/unit/intercom/admin_spec.rb +2 -2
  59. data/spec/unit/intercom/article_spec.rb +40 -0
  60. data/spec/unit/intercom/base_collection_proxy_spec.rb +30 -0
  61. data/spec/unit/intercom/client_collection_proxy_spec.rb +41 -41
  62. data/spec/unit/intercom/client_spec.rb +28 -25
  63. data/spec/unit/intercom/collection_spec.rb +32 -0
  64. data/spec/unit/intercom/company_spec.rb +13 -15
  65. data/spec/unit/intercom/contact_spec.rb +344 -33
  66. data/spec/unit/intercom/conversation_spec.rb +55 -7
  67. data/spec/unit/intercom/count_spec.rb +4 -4
  68. data/spec/unit/intercom/data_attribute_spec.rb +40 -0
  69. data/spec/unit/intercom/deprecated_leads_collection_proxy_spec.rb +17 -0
  70. data/spec/unit/intercom/event_spec.rb +9 -11
  71. data/spec/unit/intercom/job_spec.rb +24 -24
  72. data/spec/unit/intercom/lead_spec.rb +57 -0
  73. data/spec/unit/intercom/lib/flat_store_spec.rb +22 -20
  74. data/spec/unit/intercom/message_spec.rb +1 -1
  75. data/spec/unit/intercom/note_spec.rb +4 -10
  76. data/spec/unit/intercom/request_spec.rb +1 -1
  77. data/spec/unit/intercom/scroll_collection_proxy_spec.rb +40 -39
  78. data/spec/unit/intercom/search_collection_proxy_spec.rb +32 -28
  79. data/spec/unit/intercom/section_spec.rb +32 -0
  80. data/spec/unit/intercom/segment_spec.rb +2 -2
  81. data/spec/unit/intercom/subscription_spec.rb +5 -6
  82. data/spec/unit/intercom/tag_spec.rb +22 -14
  83. data/spec/unit/intercom/team_spec.rb +2 -2
  84. data/spec/unit/intercom/traits/api_resource_spec.rb +107 -52
  85. data/spec/unit/intercom/user_spec.rb +224 -226
  86. data/spec/unit/intercom/visitor_spec.rb +49 -0
  87. data/spec/unit/intercom_spec.rb +5 -3
  88. metadata +34 -8
  89. data/lib/intercom/customer.rb +0 -10
  90. data/lib/intercom/service/customer.rb +0 -14
  91. data/spec/unit/intercom/visitors_spec.rb +0 -61
@@ -1,38 +1,35 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Intercom
2
4
  class MisconfiguredClientError < StandardError; end
3
5
  class Client
4
6
  include Options
5
- attr_reader :base_url, :rate_limit_details, :username_part, :password_part, :handle_rate_limit, :timeouts, :api_version
7
+ include DeprecatedResources
8
+ attr_reader :base_url, :rate_limit_details, :token, :handle_rate_limit, :timeouts, :api_version
6
9
 
7
10
  class << self
8
11
  def set_base_url(base_url)
9
- return Proc.new do |o|
12
+ proc do |o|
10
13
  old_url = o.base_url
11
14
  o.send(:base_url=, base_url)
12
- Proc.new { |obj| set_base_url(old_url).call(o) }
15
+ proc { |_obj| set_base_url(old_url).call(o) }
13
16
  end
14
17
  end
15
18
 
16
19
  def set_timeouts(open_timeout: nil, read_timeout: nil)
17
- return Proc.new do |o|
20
+ proc do |o|
18
21
  old_timeouts = o.timeouts
19
22
  timeouts = {}
20
23
  timeouts[:open_timeout] = open_timeout if open_timeout
21
24
  timeouts[:read_timeout] = read_timeout if read_timeout
22
25
  o.send(:timeouts=, timeouts)
23
- Proc.new { |obj| set_timeouts(old_timeouts).call(o) }
26
+ proc { |_obj| set_timeouts(old_timeouts).call(o) }
24
27
  end
25
28
  end
26
29
  end
27
30
 
28
- def initialize(app_id: 'my_app_id', api_key: 'my_api_key', token: nil, base_url:'https://api.intercom.io', handle_rate_limit: false, api_version: nil)
29
- if token
30
- @username_part = token
31
- @password_part = ""
32
- else
33
- @username_part = app_id
34
- @password_part = api_key
35
- end
31
+ def initialize(token: nil, base_url: 'https://api.intercom.io', handle_rate_limit: false, api_version: nil)
32
+ @token = token
36
33
  validate_credentials!
37
34
 
38
35
  @api_version = api_version
@@ -51,6 +48,10 @@ module Intercom
51
48
  Intercom::Service::Admin.new(self)
52
49
  end
53
50
 
51
+ def articles
52
+ Intercom::Service::Article.new(self)
53
+ end
54
+
54
55
  def companies
55
56
  Intercom::Service::Company.new(self)
56
57
  end
@@ -67,10 +68,6 @@ module Intercom
67
68
  Intercom::Service::Counts.new(self)
68
69
  end
69
70
 
70
- def customers
71
- Intercom::Service::Customer.new(self)
72
- end
73
-
74
71
  def events
75
72
  Intercom::Service::Event.new(self)
76
73
  end
@@ -91,6 +88,10 @@ module Intercom
91
88
  Intercom::Service::Segment.new(self)
92
89
  end
93
90
 
91
+ def sections
92
+ Intercom::Service::Section.new(self)
93
+ end
94
+
94
95
  def tags
95
96
  Intercom::Service::Tag.new(self)
96
97
  end
@@ -103,6 +104,10 @@ module Intercom
103
104
  Intercom::Service::User.new(self)
104
105
  end
105
106
 
107
+ def leads
108
+ Intercom::Service::Lead.new(self)
109
+ end
110
+
106
111
  def visitors
107
112
  Intercom::Service::Visitor.new(self)
108
113
  end
@@ -111,6 +116,14 @@ module Intercom
111
116
  Intercom::Service::Job.new(self)
112
117
  end
113
118
 
119
+ def data_attributes
120
+ Intercom::Service::DataAttribute.new(self)
121
+ end
122
+
123
+ def collections
124
+ Intercom::Service::Collection.new(self)
125
+ end
126
+
114
127
  def get(path, params)
115
128
  execute_request Intercom::Request.get(path, params)
116
129
  end
@@ -130,25 +143,23 @@ module Intercom
130
143
  private
131
144
 
132
145
  def validate_credentials!
133
- error = MisconfiguredClientError.new("app_id and api_key must not be nil")
134
- fail error if @username_part.nil?
146
+ error = MisconfiguredClientError.new('an access token must be provided')
147
+ raise error if @token.nil?
135
148
  end
136
149
 
137
150
  def validate_api_version!
138
- error = MisconfiguredClientError.new("api_version must be either nil or a valid API version")
139
- fail error if (@api_version && Gem::Version.new(@api_version) < Gem::Version.new('1.0'))
151
+ error = MisconfiguredClientError.new('api_version must be either nil or a valid API version')
152
+ raise error if @api_version && @api_version != 'Unstable' && Gem::Version.new(@api_version) < Gem::Version.new('1.0')
140
153
  end
141
154
 
142
155
  def execute_request(request)
143
156
  request.handle_rate_limit = handle_rate_limit
144
- request.execute(@base_url, username: @username_part, secret: @password_part, api_version: @api_version, **timeouts)
157
+ request.execute(@base_url, token: @token, api_version: @api_version, **timeouts)
145
158
  ensure
146
159
  @rate_limit_details = request.rate_limit_details
147
160
  end
148
161
 
149
- def base_url=(new_url)
150
- @base_url = new_url
151
- end
162
+ attr_writer :base_url
152
163
 
153
164
  def timeouts=(timeouts)
154
165
  @timeouts = @timeouts.merge(timeouts)
@@ -1,71 +1,49 @@
1
- require "intercom/utils"
1
+ # frozen_string_literal: true
2
2
 
3
- module Intercom
4
- class ClientCollectionProxy
5
-
6
- attr_reader :resource_name, :finder_url, :resource_class
7
-
8
- def initialize(resource_name, finder_details: {}, client:)
9
- @resource_name = resource_name
10
- @resource_class = Utils.constantize_resource_name(resource_name)
11
- @finder_url = (finder_details[:url] || "/#{@resource_name}")
12
- @finder_params = (finder_details[:params] || {})
13
- @client = client
14
- end
3
+ require 'intercom/utils'
4
+ require 'intercom/base_collection_proxy'
15
5
 
6
+ module Intercom
7
+ class ClientCollectionProxy < BaseCollectionProxy
16
8
  def each(&block)
17
9
  next_page = nil
18
10
  current_page = nil
19
11
  loop do
20
- if next_page
21
- response_hash = @client.get(next_page, {})
22
- else
23
- response_hash = @client.get(@finder_url, @finder_params)
24
- end
25
- raise Intercom::HttpError.new('Http Error - No response entity returned') unless response_hash
12
+ response_hash = fetch(next_page)
13
+ raise Intercom::HttpError, 'Http Error - No response entity returned' unless response_hash
14
+
26
15
  current_page = extract_current_page(response_hash)
27
16
  deserialize_response_hash(response_hash, block)
28
17
  next_page = extract_next_link(response_hash)
29
- break if next_page.nil? or (@finder_params[:page] and (current_page >= @finder_params[:page]))
18
+ break if next_page.nil? || (@params[:page] && (current_page >= @params[:page]))
30
19
  end
31
20
  self
32
21
  end
33
22
 
34
- def [](target_index)
35
- self.each_with_index do |item, index|
36
- return item if index == target_index
23
+ def fetch(next_page)
24
+ if next_page
25
+ @client.get(next_page, {})
26
+ else
27
+ @client.get(@url, @params)
37
28
  end
38
- nil
39
29
  end
40
30
 
41
- include Enumerable
42
-
43
31
  private
44
32
 
45
- def deserialize_response_hash(response_hash, block)
46
- top_level_type = response_hash.delete('type')
47
- if resource_name == 'subscriptions'
48
- top_level_entity_key = 'items'
49
- else
50
- top_level_entity_key = Utils.entity_key_from_type(top_level_type)
51
- end
52
- response_hash[top_level_entity_key].each do |object_json|
53
- block.call Lib::TypedJsonDeserializer.new(object_json).deserialize
54
- end
55
- end
56
-
57
33
  def paging_info_present?(response_hash)
58
34
  !!(response_hash['pages'] && response_hash['pages']['type'])
59
35
  end
60
36
 
61
37
  def extract_next_link(response_hash)
62
38
  return nil unless paging_info_present?(response_hash)
39
+
63
40
  paging_info = response_hash.delete('pages')
64
- paging_info["next"]
41
+ paging_info['next']
65
42
  end
66
43
 
67
44
  def extract_current_page(response_hash)
68
45
  return nil unless paging_info_present?(response_hash)
46
+
69
47
  response_hash['pages']['page']
70
48
  end
71
49
  end
@@ -0,0 +1,7 @@
1
+ require 'intercom/traits/api_resource'
2
+
3
+ module Intercom
4
+ class Collection
5
+ include Traits::ApiResource
6
+ end
7
+ end
@@ -1,10 +1,18 @@
1
1
  require 'intercom/traits/incrementable_attributes'
2
2
  require 'intercom/traits/api_resource'
3
+ require 'intercom/api_operations/nested_resource'
3
4
 
4
5
  module Intercom
5
6
  class Company
6
7
  include Traits::IncrementableAttributes
7
8
  include Traits::ApiResource
9
+ include ApiOperations::NestedResource
10
+
11
+ nested_resource_methods :contact, operations: %i[list]
12
+
13
+ def self.collection_proxy_class
14
+ Intercom::ClientCollectionProxy
15
+ end
8
16
 
9
17
  def identity_vars ; [:id, :company_id] ; end
10
18
  def flat_store_attributes ; [:custom_attributes] ; end
@@ -1,11 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'intercom/traits/incrementable_attributes'
1
4
  require 'intercom/traits/api_resource'
5
+ require 'intercom/api_operations/nested_resource'
2
6
 
3
7
  module Intercom
4
8
  class Contact
9
+ include Traits::IncrementableAttributes
5
10
  include Traits::ApiResource
11
+ include ApiOperations::NestedResource
12
+
13
+ nested_resource_methods :tag, operations: %i[add delete list]
14
+ nested_resource_methods :note, operations: %i[create list]
15
+ nested_resource_methods :company, operations: %i[add delete list]
16
+
17
+ def self.collection_proxy_class
18
+ Intercom::BaseCollectionProxy
19
+ end
20
+
21
+ def identity_vars
22
+ [:id]
23
+ end
6
24
 
7
- def identity_vars ; [:email, :user_id] ; end
8
- def flat_store_attributes ; [:custom_attributes] ; end
9
- def update_verb; 'put' ; end
25
+ def flat_store_attributes
26
+ [:custom_attributes]
27
+ end
10
28
  end
11
29
  end
@@ -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
@@ -96,6 +96,9 @@ module Intercom
96
96
  # Raised when a CDA is invalid
97
97
  class InvalidDocumentError < IntercomError; end
98
98
 
99
+ # Raised when a merge is invalid
100
+ class InvalidMergeError < IntercomError; end
101
+
99
102
  #
100
103
  # Non-public errors (internal to the gem)
101
104
  #
@@ -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