Skip to content

Commit ec6435c

Browse files
authored
Merge pull request #479 from intercom/AP/customer-search-core
Add support for Customer Search API
2 parents 43a0b77 + f641a57 commit ec6435c

File tree

9 files changed

+218
-4
lines changed

9 files changed

+218
-4
lines changed

README.md

+10-4
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ intercom.users.find_all(type: 'users', page: 1, per_page: 10, created_since: 2,
9797
# Paginate through your list of users choosing how many to return per page (default and max is 50 per page)
9898
intercom.users.find_all(type: 'users', page: 1, per_page: 10, order: :asc).to_a.each_with_index {|usr, i| puts "#{i+1}: #{usr.name}"}
9999

100-
# Duplicate users? If you have duplicate users you can search for them via their email address.
100+
# Duplicate users? If you have duplicate users you can search for them via their email address.
101101
# Note this feature is only available from version 1.1 of the API so you will need to switch to that version
102102
# This will return multiple users if they have the same email address
103103
usrs = intercom.users.find_all(type: 'users', email: '[email protected]', page: 1, per_page: 10, order: :asc)
@@ -402,7 +402,7 @@ The metadata key values in the example are treated as follows-
402402

403403
*NB:* This version of the gem reserves the field name `type` in Event data.
404404

405-
### Contacts
405+
#### Contacts
406406

407407
`Contacts` represent logged out users of your application.
408408
Note that `contacts` are referred to as `leads` in the [Intercom](https://developers.intercom.com/intercom-api-reference/reference#leads)
@@ -458,7 +458,13 @@ intercom.contacts.scroll.each { |lead| puts lead.id}
458458
# Please see users scroll for more details of how to use scroll
459459
```
460460

461-
### Counts
461+
#### Customers
462+
463+
# Search for customers
464+
customers = intercom.customers.search(query: { "field": "name", "operator": "=", "value": "Alice"}, per_page: 50, sort_field: "name", sort_order: "ascending")
465+
customers.each { |customer| p customer.name }
466+
467+
#### Counts
462468

463469
```ruby
464470
# App-wide counts
@@ -468,7 +474,7 @@ intercom.counts.for_app
468474
intercom.counts.for_type(type: 'user', count: 'segment')
469475
```
470476

471-
### Subscriptions
477+
#### Subscriptions
472478

473479
Subscribe to events in Intercom to receive webhooks.
474480

lib/intercom.rb

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
require 'intercom/service/contact'
55
require 'intercom/service/conversation'
66
require 'intercom/service/count'
7+
require 'intercom/service/customer'
78
require 'intercom/service/event'
89
require 'intercom/service/message'
910
require 'intercom/service/note'
@@ -17,6 +18,7 @@
1718
require 'intercom/client'
1819
require "intercom/contact"
1920
require "intercom/count"
21+
require "intercom/customer"
2022
require "intercom/user"
2123
require "intercom/company"
2224
require "intercom/note"

lib/intercom/api_operations/search.rb

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
require 'intercom/search_collection_proxy'
2+
require 'intercom/utils'
3+
4+
module Intercom
5+
module ApiOperations
6+
module Search
7+
def search(params)
8+
collection_name = Utils.resource_class_to_collection_name(collection_class)
9+
search_details = {
10+
url: "/#{collection_name}/search",
11+
params: params
12+
}
13+
SearchCollectionProxy.new(collection_name, search_details: search_details, client: @client)
14+
end
15+
end
16+
end
17+
end

lib/intercom/client.rb

+4
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ def counts
6767
Intercom::Service::Counts.new(self)
6868
end
6969

70+
def customers
71+
Intercom::Service::Customer.new(self)
72+
end
73+
7074
def events
7175
Intercom::Service::Event.new(self)
7276
end

lib/intercom/customer.rb

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
require 'intercom/traits/api_resource'
2+
3+
module Intercom
4+
class Customer
5+
include Traits::ApiResource
6+
7+
def identity_vars ; [:id, :email, :user_id] ; end
8+
def flat_store_attributes ; [:custom_attributes] ; end
9+
end
10+
end
+82
Original file line numberDiff line numberDiff line change
@@ -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

lib/intercom/service/customer.rb

+14
Original file line numberDiff line numberDiff line change
@@ -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

spec/spec_helper.rb

+23
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@
55
require 'time'
66
include WebMock::API
77

8+
def test_customer(email="[email protected]")
9+
customer = test_user(email)
10+
customer["type"] = "customer"
11+
customer["role"] = "user"
12+
customer
13+
end
14+
815
def test_user(email="[email protected]")
916
{
1017
"type" =>"user",
@@ -228,6 +235,22 @@ def page_of_users(include_next_link= false)
228235
}
229236
end
230237

238+
def page_of_customers(include_starting_after= false)
239+
{
240+
"type"=>"customer.list",
241+
"pages"=>
242+
{
243+
"type"=>"pages",
244+
"next"=> (include_starting_after ? { "page" => 2, "starting_after" => "EnCrYpTeDsTrInG" } : nil),
245+
"page"=>1,
246+
"per_page"=>50,
247+
"total_pages"=>7
248+
},
249+
"customers"=> [test_customer("[email protected]"), test_customer("[email protected]"), test_customer("[email protected]")],
250+
"total_count"=>314
251+
}
252+
end
253+
231254
def users_scroll(include_users= false)
232255
{
233256
"type"=>"user.list",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
require "spec_helper"
2+
3+
describe Intercom::SearchCollectionProxy do
4+
let (:client) { Intercom::Client.new(app_id: 'app_id', api_key: 'api_key') }
5+
6+
it "send query to the customer search endpoint" do
7+
client.expects(:post).with("/customers/search", { query: {} }). returns(page_of_customers(false))
8+
client.customers.search(query: {}).first
9+
end
10+
11+
it "send query to the customer search endpoint with sort_field" do
12+
client.expects(:post).with("/customers/search", { query: {}, sort: { field: "name" } }). returns(page_of_customers(false))
13+
client.customers.search(query: {}, sort_field: "name").first
14+
end
15+
16+
it "send query to the customer search endpoint with sort_field and sort_order" do
17+
client.expects(:post).with("/customers/search", { query: {}, sort: { field: "name", order: "ascending" } }). returns(page_of_customers(false))
18+
client.customers.search(query: {}, sort_field: "name", sort_order: "ascending").first
19+
end
20+
21+
it "send query to the customer search endpoint with per_page" do
22+
client.expects(:post).with("/customers/search", { query: {}, pagination: { per_page: 10 }}). returns(page_of_customers(false))
23+
client.customers.search(query: {}, per_page: 10).first
24+
end
25+
26+
it "send query to the customer search endpoint with starting_after" do
27+
client.expects(:post).with("/customers/search", { query: {}, pagination: { starting_after: "EnCrYpTeDsTrInG" }}). returns(page_of_customers(false))
28+
client.customers.search(query: {}, starting_after: "EnCrYpTeDsTrInG").first
29+
end
30+
31+
it "stops iterating if no starting_after value" do
32+
client.expects(:post).with("/customers/search", { query: {} }). returns(page_of_customers(false))
33+
emails = []
34+
client.customers.search(query: {}).each { |user| emails << user.email }
35+
36+
end
37+
38+
it "keeps iterating if starting_after value" do
39+
client.expects(:post).with("/customers/search", { query: {} }).returns(page_of_customers(true))
40+
client.expects(:post).with("/customers/search", { query: {}, pagination: { starting_after: "EnCrYpTeDsTrInG" }}).returns(page_of_customers(false))
41+
emails = []
42+
client.customers.search(query: {}).each { |user| emails << user.email }
43+
end
44+
45+
it "supports indexed array access" do
46+
client.expects(:post).with("/customers/search", { query: {} }).returns(page_of_customers(false))
47+
client.customers.search(query: {})[0].email.must_equal '[email protected]'
48+
end
49+
50+
it "supports map" do
51+
client.expects(:post).with("/customers/search", { query: {} }).returns(page_of_customers(false))
52+
emails = client.customers.search(query: {}).map { |user| user.email }
53+
54+
end
55+
56+
end

0 commit comments

Comments
 (0)