intercom 3.5.10 → 3.9.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) 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 +152 -83
  6. data/RELEASING.md +9 -0
  7. data/changes.txt +116 -0
  8. data/intercom.gemspec +1 -2
  9. data/lib/intercom.rb +4 -0
  10. data/lib/intercom/api_operations/{delete.rb → archive.rb} +4 -2
  11. data/lib/intercom/api_operations/find_all.rb +1 -1
  12. data/lib/intercom/api_operations/request_hard_delete.rb +12 -0
  13. data/lib/intercom/api_operations/search.rb +17 -0
  14. data/lib/intercom/client.rb +42 -5
  15. data/lib/intercom/customer.rb +10 -0
  16. data/lib/intercom/errors.rb +41 -4
  17. data/lib/intercom/request.rb +144 -81
  18. data/lib/intercom/search_collection_proxy.rb +82 -0
  19. data/lib/intercom/service/base_service.rb +6 -0
  20. data/lib/intercom/service/company.rb +14 -2
  21. data/lib/intercom/service/contact.rb +4 -2
  22. data/lib/intercom/service/conversation.rb +12 -0
  23. data/lib/intercom/service/customer.rb +14 -0
  24. data/lib/intercom/service/event.rb +12 -0
  25. data/lib/intercom/service/subscription.rb +2 -2
  26. data/lib/intercom/service/tag.rb +1 -1
  27. data/lib/intercom/service/team.rb +17 -0
  28. data/lib/intercom/service/user.rb +4 -2
  29. data/lib/intercom/service/visitor.rb +2 -2
  30. data/lib/intercom/team.rb +7 -0
  31. data/lib/intercom/traits/api_resource.rb +4 -9
  32. data/lib/intercom/version.rb +1 -1
  33. data/spec/spec_helper.rb +124 -2
  34. data/spec/unit/intercom/client_collection_proxy_spec.rb +5 -5
  35. data/spec/unit/intercom/client_spec.rb +69 -1
  36. data/spec/unit/intercom/company_spec.rb +20 -16
  37. data/spec/unit/intercom/contact_spec.rb +6 -0
  38. data/spec/unit/intercom/conversation_spec.rb +15 -0
  39. data/spec/unit/intercom/event_spec.rb +19 -0
  40. data/spec/unit/intercom/request_spec.rb +150 -9
  41. data/spec/unit/intercom/search_collection_proxy_spec.rb +56 -0
  42. data/spec/unit/intercom/team_spec.rb +21 -0
  43. data/spec/unit/intercom/traits/api_resource_spec.rb +34 -7
  44. data/spec/unit/intercom/user_spec.rb +15 -3
  45. metadata +33 -22
  46. data/.travis.yml +0 -6
  47. data/lib/intercom/extended_api_operations/users.rb +0 -16
@@ -0,0 +1,82 @@
1
+ require "intercom/utils"
2
+
3
+ module Intercom
4
+ class SearchCollectionProxy
5
+
6
+ attr_reader :resource_name, :resource_class
7
+
8
+ def initialize(resource_name, search_details: {}, client:)
9
+ @resource_name = resource_name
10
+ @resource_class = Utils.constantize_resource_name(resource_name)
11
+ @search_url = search_details[:url]
12
+ @search_params = search_details[:params]
13
+ @client = client
14
+ end
15
+
16
+ def each(&block)
17
+ loop do
18
+ response_hash = @client.post(@search_url, payload)
19
+ raise Intercom::HttpError.new('Http Error - No response entity returned') unless response_hash
20
+ deserialize_response_hash(response_hash, block)
21
+ break unless has_next_link?(response_hash)
22
+ end
23
+ self
24
+ end
25
+
26
+ def [](target_index)
27
+ self.each_with_index do |item, index|
28
+ return item if index == target_index
29
+ end
30
+ nil
31
+ end
32
+
33
+ include Enumerable
34
+
35
+ private
36
+
37
+ def deserialize_response_hash(response_hash, block)
38
+ top_level_type = response_hash.delete('type')
39
+ top_level_entity_key = Utils.entity_key_from_type(top_level_type)
40
+ response_hash[top_level_entity_key].each do |object_json|
41
+ block.call Lib::TypedJsonDeserializer.new(object_json).deserialize
42
+ end
43
+ end
44
+
45
+ def has_next_link?(response_hash)
46
+ paging_info = response_hash.delete('pages')
47
+ paging_next = paging_info["next"]
48
+ if paging_next
49
+ @search_params[:starting_after] = paging_next["starting_after"]
50
+ return true
51
+ else
52
+ return false
53
+ end
54
+ end
55
+
56
+ def payload
57
+ payload = {
58
+ query: @search_params[:query]
59
+ }
60
+ if @search_params[:sort_field] || @search_params[:sort_order]
61
+ payload[:sort] = {}
62
+ if @search_params[:sort_field]
63
+ payload[:sort][:field] = @search_params[:sort_field]
64
+ end
65
+ if @search_params[:sort_order]
66
+ payload[:sort][:order] = @search_params[:sort_order]
67
+ end
68
+ end
69
+ if @search_params[:per_page] || @search_params[:starting_after]
70
+ payload[:pagination] = {}
71
+ if @search_params[:per_page]
72
+ payload[:pagination][:per_page] = @search_params[:per_page]
73
+ end
74
+ if @search_params[:starting_after]
75
+ payload[:pagination][:starting_after] = @search_params[:starting_after]
76
+ end
77
+ end
78
+ return payload
79
+ end
80
+
81
+ end
82
+ end
@@ -1,3 +1,5 @@
1
+ require 'intercom/client_collection_proxy'
2
+
1
3
  module Intercom
2
4
  module Service
3
5
  class BaseService
@@ -11,6 +13,10 @@ module Intercom
11
13
  raise NotImplementedError
12
14
  end
13
15
 
16
+ def collection_proxy_class
17
+ Intercom::ClientCollectionProxy
18
+ end
19
+
14
20
  def from_api(api_response)
15
21
  object = collection_class.new
16
22
  object.from_response(api_response)
@@ -1,10 +1,10 @@
1
1
  require 'intercom/service/base_service'
2
2
  require 'intercom/api_operations/list'
3
+ require 'intercom/api_operations/scroll'
3
4
  require 'intercom/api_operations/find'
4
5
  require 'intercom/api_operations/find_all'
5
6
  require 'intercom/api_operations/save'
6
7
  require 'intercom/api_operations/load'
7
- require 'intercom/extended_api_operations/users'
8
8
  require 'intercom/extended_api_operations/tags'
9
9
  require 'intercom/extended_api_operations/segments'
10
10
 
@@ -15,14 +15,26 @@ module Intercom
15
15
  include ApiOperations::FindAll
16
16
  include ApiOperations::Load
17
17
  include ApiOperations::List
18
+ include ApiOperations::Scroll
18
19
  include ApiOperations::Save
19
- include ExtendedApiOperations::Users
20
20
  include ExtendedApiOperations::Tags
21
21
  include ExtendedApiOperations::Segments
22
22
 
23
23
  def collection_class
24
24
  Intercom::Company
25
25
  end
26
+
27
+ def users_by_intercom_company_id(id)
28
+ get_users(url: "/companies/#{id}/users")
29
+ end
30
+
31
+ def users_by_company_id(id)
32
+ get_users(url: "/companies", params: { company_id: id, type: "user" })
33
+ end
34
+
35
+ private def get_users(url:, params: {})
36
+ ClientCollectionProxy.new("users", finder_details: { url: url, params: params }, client: @client)
37
+ end
26
38
  end
27
39
  end
28
40
  end
@@ -6,7 +6,8 @@ require 'intercom/api_operations/find_all'
6
6
  require 'intercom/api_operations/save'
7
7
  require 'intercom/api_operations/scroll'
8
8
  require 'intercom/api_operations/convert'
9
- require 'intercom/api_operations/delete'
9
+ require 'intercom/api_operations/archive'
10
+ require 'intercom/api_operations/request_hard_delete'
10
11
 
11
12
  module Intercom
12
13
  module Service
@@ -18,7 +19,8 @@ module Intercom
18
19
  include ApiOperations::Save
19
20
  include ApiOperations::Scroll
20
21
  include ApiOperations::Convert
21
- include ApiOperations::Delete
22
+ include ApiOperations::Archive
23
+ include ApiOperations::RequestHardDelete
22
24
 
23
25
  def collection_class
24
26
  Intercom::Contact
@@ -9,6 +9,7 @@ module Intercom
9
9
  module Service
10
10
  class Conversation < BaseService
11
11
  include ApiOperations::FindAll
12
+ include ApiOperations::List
12
13
  include ApiOperations::Find
13
14
  include ApiOperations::Load
14
15
  include ApiOperations::Save
@@ -28,6 +29,12 @@ module Intercom
28
29
  collection_class.new.from_response(response)
29
30
  end
30
31
 
32
+ def reply_to_last(reply_data)
33
+ collection_name = Utils.resource_class_to_collection_name(collection_class)
34
+ response = @client.post("/#{collection_name}/last/reply", reply_data)
35
+ collection_class.new.from_response(response)
36
+ end
37
+
31
38
  def open(reply_data)
32
39
  reply reply_data.merge(message_type: 'open', type: 'admin')
33
40
  end
@@ -36,6 +43,11 @@ module Intercom
36
43
  reply reply_data.merge(message_type: 'close', type: 'admin')
37
44
  end
38
45
 
46
+ def snooze(reply_data)
47
+ snoozed_until = reply_data.fetch(:snoozed_until) { fail 'snoozed_until field is required' }
48
+ reply reply_data.merge(message_type: 'snoozed', type: 'admin')
49
+ end
50
+
39
51
  def assign(reply_data)
40
52
  assignee_id = reply_data.fetch(:assignee_id) { fail 'assignee_id is required' }
41
53
  reply reply_data.merge(message_type: 'assignment', assignee_id: assignee_id, type: 'admin')
@@ -0,0 +1,14 @@
1
+ require 'intercom/service/base_service'
2
+ require 'intercom/api_operations/search'
3
+
4
+ module Intercom
5
+ module Service
6
+ class Customer < BaseService
7
+ include ApiOperations::Search
8
+
9
+ def collection_class
10
+ Intercom::Customer
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,8 +1,16 @@
1
+ require 'intercom/client_collection_proxy'
1
2
  require 'intercom/service/base_service'
2
3
  require 'intercom/api_operations/save'
3
4
  require 'intercom/api_operations/bulk/submit'
4
5
 
5
6
  module Intercom
7
+ class EventCollectionProxy < ClientCollectionProxy
8
+
9
+ def paging_info_present?(response_hash)
10
+ !!(response_hash['pages'])
11
+ end
12
+ end
13
+
6
14
  module Service
7
15
  class Event < BaseService
8
16
  include ApiOperations::FindAll
@@ -12,6 +20,10 @@ module Intercom
12
20
  def collection_class
13
21
  Intercom::Event
14
22
  end
23
+
24
+ def collection_proxy_class
25
+ Intercom::EventCollectionProxy
26
+ end
15
27
  end
16
28
  end
17
29
  end
@@ -2,7 +2,7 @@ require 'intercom/api_operations/list'
2
2
  require 'intercom/api_operations/find_all'
3
3
  require 'intercom/api_operations/find'
4
4
  require 'intercom/api_operations/save'
5
- require 'intercom/api_operations/delete'
5
+ require 'intercom/api_operations/archive'
6
6
 
7
7
  module Intercom
8
8
  module Service
@@ -11,7 +11,7 @@ module Intercom
11
11
  include ApiOperations::Find
12
12
  include ApiOperations::FindAll
13
13
  include ApiOperations::Save
14
- include ApiOperations::Delete
14
+ include ApiOperations::Archive
15
15
 
16
16
  def collection_class
17
17
  Intercom::Subscription
@@ -9,7 +9,7 @@ module Intercom
9
9
  include ApiOperations::Save
10
10
  include ApiOperations::List
11
11
  include ApiOperations::FindAll
12
- include ApiOperations::Delete
12
+ include ApiOperations::Archive
13
13
 
14
14
  def collection_class
15
15
  Intercom::Tag
@@ -0,0 +1,17 @@
1
+ require 'intercom/service/base_service'
2
+ require 'intercom/api_operations/list'
3
+ require 'intercom/api_operations/find'
4
+
5
+ module Intercom
6
+ module Service
7
+ class Team < BaseService
8
+ include ApiOperations::List
9
+ include ApiOperations::Find
10
+
11
+ def collection_class
12
+ Intercom::Team
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -5,8 +5,9 @@ require 'intercom/api_operations/scroll'
5
5
  require 'intercom/api_operations/find'
6
6
  require 'intercom/api_operations/find_all'
7
7
  require 'intercom/api_operations/save'
8
- require 'intercom/api_operations/delete'
8
+ require 'intercom/api_operations/archive'
9
9
  require 'intercom/api_operations/bulk/submit'
10
+ require 'intercom/api_operations/request_hard_delete'
10
11
  require 'intercom/extended_api_operations/tags'
11
12
  require 'intercom/extended_api_operations/segments'
12
13
 
@@ -19,7 +20,8 @@ module Intercom
19
20
  include ApiOperations::Find
20
21
  include ApiOperations::FindAll
21
22
  include ApiOperations::Save
22
- include ApiOperations::Delete
23
+ include ApiOperations::Archive
24
+ include ApiOperations::RequestHardDelete
23
25
  include ApiOperations::Bulk::Submit
24
26
  include ExtendedApiOperations::Tags
25
27
  include ExtendedApiOperations::Segments
@@ -5,7 +5,7 @@ require 'intercom/api_operations/find'
5
5
  require 'intercom/api_operations/find_all'
6
6
  require 'intercom/api_operations/save'
7
7
  require 'intercom/api_operations/convert'
8
- require 'intercom/api_operations/delete'
8
+ require 'intercom/api_operations/archive'
9
9
 
10
10
  module Intercom
11
11
  module Service
@@ -16,7 +16,7 @@ module Intercom
16
16
  include ApiOperations::FindAll
17
17
  include ApiOperations::Save
18
18
  include ApiOperations::Convert
19
- include ApiOperations::Delete
19
+ include ApiOperations::Archive
20
20
 
21
21
  def collection_class
22
22
  Intercom::Visitor
@@ -0,0 +1,7 @@
1
+ require 'intercom/traits/api_resource'
2
+
3
+ module Intercom
4
+ class Team
5
+ include Traits::ApiResource
6
+ end
7
+ end
@@ -24,7 +24,6 @@ module Intercom
24
24
 
25
25
  def from_hash(hash)
26
26
  hash.each do |attribute, value|
27
- next if type_field?(attribute)
28
27
  initialize_property(attribute, value)
29
28
  end
30
29
  initialize_missing_flat_store_attributes if respond_to? :flat_store_attributes
@@ -78,13 +77,13 @@ module Intercom
78
77
  end
79
78
 
80
79
  def custom_attribute_field?(attribute)
81
- attribute == 'custom_attributes'
80
+ attribute.to_s == 'custom_attributes'
82
81
  end
83
-
82
+
84
83
  def message_from_field?(attribute, value)
85
84
  attribute.to_s == 'from' && value.is_a?(Hash) && value['type']
86
85
  end
87
-
86
+
88
87
  def message_to_field?(attribute, value)
89
88
  attribute.to_s == 'to' && value.is_a?(Hash) && value['type']
90
89
  end
@@ -94,7 +93,7 @@ module Intercom
94
93
  !custom_attribute_field?(attribute) &&
95
94
  !message_from_field?(attribute, value) &&
96
95
  !message_to_field?(attribute, value) &&
97
- attribute != 'metadata'
96
+ attribute.to_s != 'metadata'
98
97
  end
99
98
 
100
99
  def typed_value?(value)
@@ -106,10 +105,6 @@ module Intercom
106
105
  self.send(setter_method, value)
107
106
  end
108
107
 
109
- def type_field?(attribute)
110
- attribute == 'type'
111
- end
112
-
113
108
  def initialize_missing_flat_store_attributes
114
109
  flat_store_attributes.each do |attribute|
115
110
  unless instance_variables_excluding_dirty_tracking_field.map(&:to_s).include? "@#{attribute}"
@@ -1,3 +1,3 @@
1
1
  module Intercom #:nodoc:
2
- VERSION = "3.5.10"
2
+ VERSION = "3.9.5"
3
3
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,17 @@
1
1
  require 'intercom'
2
2
  require 'minitest/autorun'
3
3
  require 'mocha/setup'
4
+ require 'webmock'
5
+ require 'time'
6
+ require 'pry'
7
+ include WebMock::API
8
+
9
+ def test_customer(email="bob@example.com")
10
+ customer = test_user(email)
11
+ customer["type"] = "customer"
12
+ customer["role"] = "user"
13
+ customer
14
+ end
4
15
 
5
16
  def test_user(email="bob@example.com")
6
17
  {
@@ -135,6 +146,35 @@ def test_admin
135
146
  }
136
147
  end
137
148
 
149
+ def test_team_list
150
+ {
151
+ "type"=> "team.list",
152
+ "teams" => [
153
+ {
154
+ "type"=> "team",
155
+ "id"=> "2744328",
156
+ "name"=> "the_a_team",
157
+ "admin_ids"=> [646303, 814860],
158
+ },
159
+ {
160
+ "type"=> "team",
161
+ "id"=> "814865",
162
+ "name"=> "BA_App",
163
+ "admin_ids" => [492881, 1195856]
164
+ },
165
+ ]
166
+ }
167
+ end
168
+
169
+ def test_team
170
+ {
171
+ "type" => "team",
172
+ "id" => "2744328",
173
+ "name" => "the_a_team",
174
+ "admin_ids" => [646303, 814860]
175
+ }
176
+ end
177
+
138
178
  def test_company
139
179
  {
140
180
  "type" => "company",
@@ -225,6 +265,22 @@ def page_of_users(include_next_link= false)
225
265
  }
226
266
  end
227
267
 
268
+ def page_of_customers(include_starting_after= false)
269
+ {
270
+ "type"=>"customer.list",
271
+ "pages"=>
272
+ {
273
+ "type"=>"pages",
274
+ "next"=> (include_starting_after ? { "page" => 2, "starting_after" => "EnCrYpTeDsTrInG" } : nil),
275
+ "page"=>1,
276
+ "per_page"=>50,
277
+ "total_pages"=>7
278
+ },
279
+ "customers"=> [test_customer("user1@example.com"), test_customer("user2@example.com"), test_customer("user3@example.com")],
280
+ "total_count"=>314
281
+ }
282
+ end
283
+
228
284
  def users_scroll(include_users= false)
229
285
  {
230
286
  "type"=>"user.list",
@@ -233,7 +289,7 @@ def users_scroll(include_users= false)
233
289
  }
234
290
  end
235
291
 
236
- def users_pagination(include_next_link=false, per_page=0, page=0, total_pages=0, total_count=0, user_list=[])
292
+ def users_pagination(include_next_link:, per_page:, page:, total_pages:, total_count:, user_list:)
237
293
  {
238
294
  "type"=>"user.list",
239
295
  "pages"=>
@@ -289,6 +345,56 @@ def test_conversation
289
345
  }
290
346
  end
291
347
 
348
+ def test_conversation_list
349
+ {
350
+ "type" => "conversation.list",
351
+ "pages" => {
352
+ "type" => "pages",
353
+ "page" => 1,
354
+ "per_page" => 20,
355
+ "total_pages" => 1
356
+ },
357
+ "conversations" => [
358
+ {
359
+ "type" => "conversation",
360
+ "id" => "147",
361
+ "created_at" => 1400850973,
362
+ "updated_at" => 1400857494,
363
+ "conversation_message" => {
364
+ "type" => "conversation_message",
365
+ "subject" => "",
366
+ "body" => "<p>Hi Alice,</p>\n\n<p>We noticed you using our Product, do you have any questions?</p> \n<p>- Jane</p>",
367
+ "author" => {
368
+ "type" => "admin",
369
+ "id" => "25"
370
+ },
371
+ "attachments" => [
372
+ {
373
+ "name" => "signature",
374
+ "url" => "http =>//someurl.com/signature.jpg"
375
+ }
376
+ ]
377
+ },
378
+ "user" => {
379
+ "type" => "user",
380
+ "id" => "536e564f316c83104c000020"
381
+ },
382
+ "assignee" => {
383
+ "type" => "admin",
384
+ "id" => "25"
385
+ },
386
+ "open" => true,
387
+ "read" => true,
388
+ "conversation_parts" => {
389
+ "type" => "conversation_part.list",
390
+ "conversation_parts" => [
391
+ ]
392
+ }
393
+ }
394
+ ]
395
+ }
396
+ end
397
+
292
398
  def segment
293
399
  {
294
400
  "type" => "segment",
@@ -568,11 +674,27 @@ def test_event_list
568
674
  "type" => "event.list",
569
675
  "events" => [ test_event ],
570
676
  "pages" => {
571
- "next" => "https =>//api.intercom.io/events?type=user&intercom_user_id=55a3b&before=144474756550"
677
+ "next" => "https://api.intercom.io/events?type=user&intercom_user_id=55a3b&before=144474756550"
572
678
  }
573
679
  }
574
680
  end
575
681
 
682
+ def tomorrow
683
+ (DateTime.now.to_time + 1).to_i
684
+ end
685
+
686
+ def page_of_events(include_next_link=false)
687
+ {
688
+ "type" => "event.list",
689
+ "events" => [ test_event ],
690
+ "pages" =>
691
+ {
692
+ "next" => (include_next_link ? "https://api.intercom.io/events?type=user&intercom_user_id=55a3b&before=144474756550" : nil),
693
+ }
694
+ }
695
+ end
696
+
697
+
576
698
  def error_on_modify_frozen
577
699
  RUBY_VERSION =~ /1.8/ ? TypeError : RuntimeError
578
700
  end