Skip to content

Commit

Permalink
Merge pull request #479 from intercom/AP/customer-search-core
Browse files Browse the repository at this point in the history
Add support for Customer Search API
  • Loading branch information
apassant authored Aug 1, 2019
2 parents 43a0b77 + f641a57 commit ec6435c
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 4 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ intercom.users.find_all(type: 'users', page: 1, per_page: 10, created_since: 2,
# Paginate through your list of users choosing how many to return per page (default and max is 50 per page)
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}"}

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

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

### Contacts
#### Contacts

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

### Counts
#### Customers

# Search for customers
customers = intercom.customers.search(query: { "field": "name", "operator": "=", "value": "Alice"}, per_page: 50, sort_field: "name", sort_order: "ascending")
customers.each { |customer| p customer.name }

#### Counts

```ruby
# App-wide counts
Expand All @@ -468,7 +474,7 @@ intercom.counts.for_app
intercom.counts.for_type(type: 'user', count: 'segment')
```

### Subscriptions
#### Subscriptions

Subscribe to events in Intercom to receive webhooks.

Expand Down
2 changes: 2 additions & 0 deletions lib/intercom.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require 'intercom/service/contact'
require 'intercom/service/conversation'
require 'intercom/service/count'
require 'intercom/service/customer'
require 'intercom/service/event'
require 'intercom/service/message'
require 'intercom/service/note'
Expand All @@ -17,6 +18,7 @@
require 'intercom/client'
require "intercom/contact"
require "intercom/count"
require "intercom/customer"
require "intercom/user"
require "intercom/company"
require "intercom/note"
Expand Down
17 changes: 17 additions & 0 deletions lib/intercom/api_operations/search.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require 'intercom/search_collection_proxy'
require 'intercom/utils'

module Intercom
module ApiOperations
module Search
def search(params)
collection_name = Utils.resource_class_to_collection_name(collection_class)
search_details = {
url: "/#{collection_name}/search",
params: params
}
SearchCollectionProxy.new(collection_name, search_details: search_details, client: @client)
end
end
end
end
4 changes: 4 additions & 0 deletions lib/intercom/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ def counts
Intercom::Service::Counts.new(self)
end

def customers
Intercom::Service::Customer.new(self)
end

def events
Intercom::Service::Event.new(self)
end
Expand Down
10 changes: 10 additions & 0 deletions lib/intercom/customer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require 'intercom/traits/api_resource'

module Intercom
class Customer
include Traits::ApiResource

def identity_vars ; [:id, :email, :user_id] ; end
def flat_store_attributes ; [:custom_attributes] ; end
end
end
82 changes: 82 additions & 0 deletions lib/intercom/search_collection_proxy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
require "intercom/utils"

module Intercom
class SearchCollectionProxy

attr_reader :resource_name, :resource_class

def initialize(resource_name, search_details: {}, client:)
@resource_name = resource_name
@resource_class = Utils.constantize_resource_name(resource_name)
@search_url = search_details[:url]
@search_params = search_details[:params]
@client = client
end

def each(&block)
loop do
response_hash = @client.post(@search_url, payload)
raise Intercom::HttpError.new('Http Error - No response entity returned') unless response_hash
deserialize_response_hash(response_hash, block)
break unless has_next_link?(response_hash)
end
self
end

def [](target_index)
self.each_with_index do |item, index|
return item if index == target_index
end
nil
end

include Enumerable

private

def deserialize_response_hash(response_hash, block)
top_level_type = response_hash.delete('type')
top_level_entity_key = Utils.entity_key_from_type(top_level_type)
response_hash[top_level_entity_key].each do |object_json|
block.call Lib::TypedJsonDeserializer.new(object_json).deserialize
end
end

def has_next_link?(response_hash)
paging_info = response_hash.delete('pages')
paging_next = paging_info["next"]
if paging_next
@search_params[:starting_after] = paging_next["starting_after"]
return true
else
return false
end
end

def payload
payload = {
query: @search_params[:query]
}
if @search_params[:sort_field] || @search_params[:sort_order]
payload[:sort] = {}
if @search_params[:sort_field]
payload[:sort][:field] = @search_params[:sort_field]
end
if @search_params[:sort_order]
payload[:sort][:order] = @search_params[:sort_order]
end
end
if @search_params[:per_page] || @search_params[:starting_after]
payload[:pagination] = {}
if @search_params[:per_page]
payload[:pagination][:per_page] = @search_params[:per_page]
end
if @search_params[:starting_after]
payload[:pagination][:starting_after] = @search_params[:starting_after]
end
end
return payload
end

end
end
14 changes: 14 additions & 0 deletions lib/intercom/service/customer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'intercom/service/base_service'
require 'intercom/api_operations/search'

module Intercom
module Service
class Customer < BaseService
include ApiOperations::Search

def collection_class
Intercom::Customer
end
end
end
end
23 changes: 23 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
require 'time'
include WebMock::API

def test_customer(email="[email protected]")
customer = test_user(email)
customer["type"] = "customer"
customer["role"] = "user"
customer
end

def test_user(email="[email protected]")
{
"type" =>"user",
Expand Down Expand Up @@ -228,6 +235,22 @@ def page_of_users(include_next_link= false)
}
end

def page_of_customers(include_starting_after= false)
{
"type"=>"customer.list",
"pages"=>
{
"type"=>"pages",
"next"=> (include_starting_after ? { "page" => 2, "starting_after" => "EnCrYpTeDsTrInG" } : nil),
"page"=>1,
"per_page"=>50,
"total_pages"=>7
},
"customers"=> [test_customer("[email protected]"), test_customer("[email protected]"), test_customer("[email protected]")],
"total_count"=>314
}
end

def users_scroll(include_users= false)
{
"type"=>"user.list",
Expand Down
56 changes: 56 additions & 0 deletions spec/unit/intercom/search_collection_proxy_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
require "spec_helper"

describe Intercom::SearchCollectionProxy do
let (:client) { Intercom::Client.new(app_id: 'app_id', api_key: 'api_key') }

it "send query to the customer search endpoint" do
client.expects(:post).with("/customers/search", { query: {} }). returns(page_of_customers(false))
client.customers.search(query: {}).first
end

it "send query to the customer search endpoint with sort_field" do
client.expects(:post).with("/customers/search", { query: {}, sort: { field: "name" } }). returns(page_of_customers(false))
client.customers.search(query: {}, sort_field: "name").first
end

it "send query to the customer search endpoint with sort_field and sort_order" do
client.expects(:post).with("/customers/search", { query: {}, sort: { field: "name", order: "ascending" } }). returns(page_of_customers(false))
client.customers.search(query: {}, sort_field: "name", sort_order: "ascending").first
end

it "send query to the customer search endpoint with per_page" do
client.expects(:post).with("/customers/search", { query: {}, pagination: { per_page: 10 }}). returns(page_of_customers(false))
client.customers.search(query: {}, per_page: 10).first
end

it "send query to the customer search endpoint with starting_after" do
client.expects(:post).with("/customers/search", { query: {}, pagination: { starting_after: "EnCrYpTeDsTrInG" }}). returns(page_of_customers(false))
client.customers.search(query: {}, starting_after: "EnCrYpTeDsTrInG").first
end

it "stops iterating if no starting_after value" do
client.expects(:post).with("/customers/search", { query: {} }). returns(page_of_customers(false))
emails = []
client.customers.search(query: {}).each { |user| emails << user.email }
emails.must_equal %W([email protected] [email protected] [email protected])
end

it "keeps iterating if starting_after value" do
client.expects(:post).with("/customers/search", { query: {} }).returns(page_of_customers(true))
client.expects(:post).with("/customers/search", { query: {}, pagination: { starting_after: "EnCrYpTeDsTrInG" }}).returns(page_of_customers(false))
emails = []
client.customers.search(query: {}).each { |user| emails << user.email }
end

it "supports indexed array access" do
client.expects(:post).with("/customers/search", { query: {} }).returns(page_of_customers(false))
client.customers.search(query: {})[0].email.must_equal '[email protected]'
end

it "supports map" do
client.expects(:post).with("/customers/search", { query: {} }).returns(page_of_customers(false))
emails = client.customers.search(query: {}).map { |user| user.email }
emails.must_equal %W([email protected] [email protected] [email protected])
end

end

0 comments on commit ec6435c

Please sign in to comment.