cogniteev-intercom 2.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +6 -0
- data/Gemfile +13 -0
- data/MIT-LICENSE +21 -0
- data/README.md +378 -0
- data/Rakefile +21 -0
- data/changes.txt +168 -0
- data/intercom.gemspec +28 -0
- data/lib/data/cacert.pem +3965 -0
- data/lib/ext/sliceable_hash.rb +16 -0
- data/lib/intercom.rb +176 -0
- data/lib/intercom/admin.rb +9 -0
- data/lib/intercom/api_operations/convert.rb +19 -0
- data/lib/intercom/api_operations/count.rb +16 -0
- data/lib/intercom/api_operations/delete.rb +15 -0
- data/lib/intercom/api_operations/find.rb +23 -0
- data/lib/intercom/api_operations/find_all.rb +33 -0
- data/lib/intercom/api_operations/list.rb +17 -0
- data/lib/intercom/api_operations/load.rb +16 -0
- data/lib/intercom/api_operations/save.rb +51 -0
- data/lib/intercom/collection_proxy.rb +71 -0
- data/lib/intercom/company.rb +29 -0
- data/lib/intercom/contact.rb +22 -0
- data/lib/intercom/conversation.rb +17 -0
- data/lib/intercom/count.rb +21 -0
- data/lib/intercom/errors.rb +61 -0
- data/lib/intercom/event.rb +11 -0
- data/lib/intercom/extended_api_operations/reply.rb +16 -0
- data/lib/intercom/extended_api_operations/tags.rb +14 -0
- data/lib/intercom/extended_api_operations/users.rb +17 -0
- data/lib/intercom/generic_handlers/base_handler.rb +22 -0
- data/lib/intercom/generic_handlers/count.rb +59 -0
- data/lib/intercom/generic_handlers/tag.rb +71 -0
- data/lib/intercom/generic_handlers/tag_find_all.rb +47 -0
- data/lib/intercom/lib/dynamic_accessors.rb +59 -0
- data/lib/intercom/lib/dynamic_accessors_on_method_missing.rb +53 -0
- data/lib/intercom/lib/flat_store.rb +31 -0
- data/lib/intercom/lib/typed_json_deserializer.rb +53 -0
- data/lib/intercom/message.rb +9 -0
- data/lib/intercom/note.rb +17 -0
- data/lib/intercom/notification.rb +20 -0
- data/lib/intercom/request.rb +166 -0
- data/lib/intercom/segment.rb +14 -0
- data/lib/intercom/subscription.rb +15 -0
- data/lib/intercom/tag.rb +23 -0
- data/lib/intercom/traits/api_resource.rb +132 -0
- data/lib/intercom/traits/dirty_tracking.rb +33 -0
- data/lib/intercom/traits/generic_handler_binding.rb +29 -0
- data/lib/intercom/traits/incrementable_attributes.rb +12 -0
- data/lib/intercom/user.rb +30 -0
- data/lib/intercom/utils.rb +62 -0
- data/lib/intercom/version.rb +3 -0
- data/spec/spec_helper.rb +308 -0
- data/spec/unit/intercom/admin_spec.rb +9 -0
- data/spec/unit/intercom/collection_proxy_spec.rb +34 -0
- data/spec/unit/intercom/company_spec.rb +23 -0
- data/spec/unit/intercom/contact_spec.rb +25 -0
- data/spec/unit/intercom/event_spec.rb +25 -0
- data/spec/unit/intercom/lib/flat_store_spec.rb +29 -0
- data/spec/unit/intercom/message_spec.rb +21 -0
- data/spec/unit/intercom/note_spec.rb +19 -0
- data/spec/unit/intercom/notification_spec.rb +68 -0
- data/spec/unit/intercom/request_spec.rb +16 -0
- data/spec/unit/intercom/subscription_spec.rb +18 -0
- data/spec/unit/intercom/tag_spec.rb +23 -0
- data/spec/unit/intercom/traits/api_resource_spec.rb +85 -0
- data/spec/unit/intercom/user_spec.rb +230 -0
- data/spec/unit/intercom_spec.rb +90 -0
- metadata +214 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
class SliceableHash
|
2
|
+
|
3
|
+
def initialize(hash)
|
4
|
+
@hash = hash
|
5
|
+
end
|
6
|
+
|
7
|
+
# Return a hash that includes only the given keys.
|
8
|
+
def slice(*keys)
|
9
|
+
keys.map! { |key| @hash.convert_key(key) } if @hash.respond_to?(:convert_key, true)
|
10
|
+
keys.each_with_object(@hash.class.new) { |k, hash| hash[k] = @hash[k] if @hash.has_key?(k) && if_string_not_empty(@hash[k]) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def if_string_not_empty(val)
|
14
|
+
val.kind_of?(String) ? !val.empty? : true
|
15
|
+
end
|
16
|
+
end
|
data/lib/intercom.rb
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
require "intercom/version"
|
2
|
+
require "intercom/contact"
|
3
|
+
require "intercom/user"
|
4
|
+
require "intercom/company"
|
5
|
+
require "intercom/note"
|
6
|
+
require "intercom/tag"
|
7
|
+
require "intercom/segment"
|
8
|
+
require "intercom/event"
|
9
|
+
require "intercom/conversation"
|
10
|
+
require "intercom/message"
|
11
|
+
require "intercom/admin"
|
12
|
+
require "intercom/count"
|
13
|
+
require "intercom/request"
|
14
|
+
require "intercom/subscription"
|
15
|
+
require "intercom/notification"
|
16
|
+
require "intercom/utils"
|
17
|
+
require "intercom/errors"
|
18
|
+
require "json"
|
19
|
+
|
20
|
+
##
|
21
|
+
# Intercom is a customer relationship management and messaging tool for web app owners
|
22
|
+
#
|
23
|
+
# This library provides ruby bindings for the Intercom API (https://api.intercom.io)
|
24
|
+
#
|
25
|
+
# == Basic Usage
|
26
|
+
# === Configure Intercom with your access credentials
|
27
|
+
# Intercom.app_id = "my_app_id"
|
28
|
+
# Intercom.app_api_key = "my_api_key"
|
29
|
+
# === Make requests to the API
|
30
|
+
# Intercom::User.find(:email => "bob@example.com")
|
31
|
+
#
|
32
|
+
module Intercom
|
33
|
+
@hostname = "api.intercom.io"
|
34
|
+
@protocol = "https"
|
35
|
+
|
36
|
+
@endpoints = nil
|
37
|
+
@current_endpoint = nil
|
38
|
+
@app_id = nil
|
39
|
+
@app_api_key = nil
|
40
|
+
@rate_limit_details = {}
|
41
|
+
|
42
|
+
def self.app_id=(app_id)
|
43
|
+
@app_id = app_id
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.app_id
|
47
|
+
@app_id
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.app_api_key=(app_api_key)
|
51
|
+
@app_api_key = app_api_key
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.app_api_key
|
55
|
+
@app_api_key
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.rate_limit_details=(rate_limit_details)
|
59
|
+
@rate_limit_details = rate_limit_details
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.rate_limit_details
|
63
|
+
@rate_limit_details
|
64
|
+
end
|
65
|
+
|
66
|
+
# This method is obsolete and used to warn of backwards incompatible changes on upgrading
|
67
|
+
def self.api_key=(val)
|
68
|
+
raise ArgumentError, "#{compatibility_warning_text} #{compatibility_workaround_text} #{related_docs_text}"
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def self.target_base_url
|
74
|
+
raise ArgumentError, "#{configuration_required_text} #{related_docs_text}" if [@app_id, @app_api_key].any?(&:nil?)
|
75
|
+
basic_auth_part = "#{@app_id}:#{@app_api_key}@"
|
76
|
+
current_endpoint.gsub(/(https?:\/\/)(.*)/, "\\1#{basic_auth_part}\\2")
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.related_docs_text
|
80
|
+
"See https://github.com/intercom/intercom-ruby for usage examples."
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.compatibility_warning_text
|
84
|
+
"It looks like you are upgrading from an older version of the intercom-ruby gem. Please note that this new version (#{Intercom::VERSION}) is not backwards compatible. "
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.compatibility_workaround_text
|
88
|
+
"To get rid of this error please set Intercom.app_api_key and don't set Intercom.api_key."
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.configuration_required_text
|
92
|
+
"You must set both Intercom.app_id and Intercom.app_api_key to use this client."
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.send_request_to_path(request)
|
96
|
+
request.execute(target_base_url)
|
97
|
+
rescue Intercom::ServiceUnavailableError => e
|
98
|
+
if endpoints.length > 1
|
99
|
+
retry_on_alternative_endpoint(request)
|
100
|
+
else
|
101
|
+
raise e
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.retry_on_alternative_endpoint(request)
|
106
|
+
@current_endpoint = alternative_random_endpoint
|
107
|
+
request.execute(target_base_url)
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.current_endpoint
|
111
|
+
return @current_endpoint if @current_endpoint && @endpoint_randomized_at > (Time.now - (60 * 5))
|
112
|
+
@endpoint_randomized_at = Time.now
|
113
|
+
@current_endpoint = random_endpoint
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.random_endpoint
|
117
|
+
endpoints.shuffle.first
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.alternative_random_endpoint
|
121
|
+
(endpoints.shuffle - [@current_endpoint]).first
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.post(path, payload_hash)
|
125
|
+
send_request_to_path(Intercom::Request.post(path, payload_hash))
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.delete(path, payload_hash)
|
129
|
+
send_request_to_path(Intercom::Request.delete(path, payload_hash))
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.put(path, payload_hash)
|
133
|
+
send_request_to_path(Intercom::Request.put(path, payload_hash))
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.get(path, params)
|
137
|
+
send_request_to_path(Intercom::Request.get(path, params))
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.check_required_params(params, path=nil) #nodoc
|
141
|
+
return if path.eql?("users")
|
142
|
+
raise ArgumentError.new("Expected params Hash, got #{params.class}") unless params.is_a?(Hash)
|
143
|
+
raise ArgumentError.new("Either email or user_id must be specified") unless params.keys.any? { |key| %W(email user_id).include?(key.to_s) }
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.protocol #nodoc
|
147
|
+
@protocol
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.protocol=(override) #nodoc
|
151
|
+
@protocol = override
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.hostname #nodoc
|
155
|
+
@hostname
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.hostname=(override) #nodoc
|
159
|
+
@hostname = override
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.endpoint=(endpoint) #nodoc
|
163
|
+
self.endpoints = [endpoint]
|
164
|
+
@current_endpoint = nil
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.endpoints=(endpoints) #nodoc
|
168
|
+
@endpoints = endpoints
|
169
|
+
@current_endpoint = nil
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.endpoints
|
173
|
+
@endpoints || ["#{@protocol}://#{hostname}"]
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'intercom/traits/api_resource'
|
2
|
+
|
3
|
+
module Intercom
|
4
|
+
module ApiOperations
|
5
|
+
module Convert
|
6
|
+
def convert(user)
|
7
|
+
from_response(
|
8
|
+
Intercom.post(
|
9
|
+
"/contacts/convert",
|
10
|
+
{
|
11
|
+
contact: { user_id: user_id },
|
12
|
+
user: user.identity_hash
|
13
|
+
}
|
14
|
+
)
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Intercom
|
2
|
+
module ApiOperations
|
3
|
+
module Count
|
4
|
+
module ClassMethods
|
5
|
+
def count
|
6
|
+
singular_resource_name = Utils.resource_class_to_singular_name(self)
|
7
|
+
Intercom::Count.send("#{singular_resource_name}_count")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.included(base)
|
12
|
+
base.extend(ClassMethods)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'intercom/traits/api_resource'
|
2
|
+
|
3
|
+
module Intercom
|
4
|
+
module ApiOperations
|
5
|
+
module Delete
|
6
|
+
|
7
|
+
def delete
|
8
|
+
collection_name = Utils.resource_class_to_collection_name(self.class)
|
9
|
+
Intercom.delete("/#{collection_name}/#{id}", {})
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Intercom
|
2
|
+
module ApiOperations
|
3
|
+
module Find
|
4
|
+
module ClassMethods
|
5
|
+
def find(params)
|
6
|
+
raise BadRequestError, "#{self}#find takes a hash as its parameter but you supplied #{params.inspect}" unless params.is_a? Hash
|
7
|
+
collection_name = Utils.resource_class_to_collection_name(self)
|
8
|
+
if params[:id]
|
9
|
+
response = Intercom.get("/#{collection_name}/#{params[:id]}", {})
|
10
|
+
else
|
11
|
+
response = Intercom.get("/#{collection_name}", params)
|
12
|
+
end
|
13
|
+
raise Intercom::HttpError.new('Http Error - No response entity returned') unless response
|
14
|
+
from_api(response)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.included(base)
|
19
|
+
base.extend(ClassMethods)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'intercom/collection_proxy'
|
2
|
+
|
3
|
+
module Intercom
|
4
|
+
module ApiOperations
|
5
|
+
module FindAll
|
6
|
+
module ClassMethods
|
7
|
+
def find_all(params)
|
8
|
+
raise BadRequestError, "#{self}#find takes a hash as its parameter but you supplied #{params.inspect}" unless params.is_a? Hash
|
9
|
+
collection_name = Utils.resource_class_to_collection_name(self)
|
10
|
+
finder_details = {}
|
11
|
+
if params[:id] && !type_switched_finder?(params)
|
12
|
+
finder_details[:url] = "/#{collection_name}/#{params[:id]}"
|
13
|
+
finder_details[:params] = {}
|
14
|
+
else
|
15
|
+
finder_details[:url] = "/#{collection_name}"
|
16
|
+
finder_details[:params] = params
|
17
|
+
end
|
18
|
+
CollectionProxy.new(collection_name, finder_details)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def type_switched_finder?(params)
|
24
|
+
params.include?(:type)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.included(base)
|
29
|
+
base.extend(ClassMethods)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'intercom/collection_proxy'
|
2
|
+
|
3
|
+
module Intercom
|
4
|
+
module ApiOperations
|
5
|
+
module List # TODO: Should we rename to All
|
6
|
+
module ClassMethods
|
7
|
+
def all
|
8
|
+
CollectionProxy.new(Utils.resource_class_to_collection_name(self))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.included(base)
|
13
|
+
base.extend(ClassMethods)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Intercom
|
2
|
+
module ApiOperations
|
3
|
+
module Load
|
4
|
+
def load
|
5
|
+
collection_name = Utils.resource_class_to_collection_name(self.class)
|
6
|
+
if id
|
7
|
+
response = Intercom.get("/#{collection_name}/#{id}", {})
|
8
|
+
else
|
9
|
+
raise "Cannot load #{self.class} as it does not have a valid id."
|
10
|
+
end
|
11
|
+
raise Intercom::HttpError.new('Http Error - No response entity returned') unless response
|
12
|
+
from_response(response)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'intercom/traits/api_resource'
|
2
|
+
|
3
|
+
module Intercom
|
4
|
+
module ApiOperations
|
5
|
+
module Save
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
PARAMS_NOT_PROVIDED = Object.new
|
9
|
+
def create(params = PARAMS_NOT_PROVIDED)
|
10
|
+
if self.ancestors.include?(Intercom::Contact) && params == PARAMS_NOT_PROVIDED
|
11
|
+
params = Hash.new
|
12
|
+
elsif params == PARAMS_NOT_PROVIDED
|
13
|
+
raise ArgumentError, '.create requires 1 parameter'
|
14
|
+
end
|
15
|
+
|
16
|
+
instance = self.new(params)
|
17
|
+
instance.mark_fields_as_changed!(params.keys)
|
18
|
+
instance.save
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.included(base)
|
23
|
+
base.extend(ClassMethods)
|
24
|
+
end
|
25
|
+
|
26
|
+
def save
|
27
|
+
collection_name = Utils.resource_class_to_collection_name(self.class)
|
28
|
+
if id_present? && !posted_updates?
|
29
|
+
response = Intercom.put("/#{collection_name}/#{id}", to_submittable_hash)
|
30
|
+
else
|
31
|
+
response = Intercom.post("/#{collection_name}", to_submittable_hash.merge(identity_hash))
|
32
|
+
end
|
33
|
+
from_response(response) if response # may be nil we received back a 202
|
34
|
+
end
|
35
|
+
|
36
|
+
def identity_hash
|
37
|
+
respond_to?(:identity_vars) ? SliceableHash.new(to_hash).slice(*(identity_vars.map(&:to_s))) : {}
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def id_present?
|
43
|
+
id && id.to_s != ''
|
44
|
+
end
|
45
|
+
|
46
|
+
def posted_updates?
|
47
|
+
respond_to?(:update_verb) && update_verb == 'post'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require "intercom/utils"
|
2
|
+
require "ext/sliceable_hash"
|
3
|
+
|
4
|
+
module Intercom
|
5
|
+
class CollectionProxy
|
6
|
+
|
7
|
+
attr_reader :resource_name
|
8
|
+
|
9
|
+
def initialize(resource_name, finder_details = {})
|
10
|
+
@resource_name = resource_name
|
11
|
+
@resource_class = Utils.constantize_resource_name(resource_name)
|
12
|
+
@finder_url = (finder_details[:url] || "/#{@resource_name}")
|
13
|
+
@finder_params = (finder_details[:params] || {})
|
14
|
+
end
|
15
|
+
|
16
|
+
def each(&block)
|
17
|
+
next_page = nil
|
18
|
+
loop do
|
19
|
+
if next_page
|
20
|
+
response_hash = Intercom.get(next_page, {})
|
21
|
+
else
|
22
|
+
response_hash = Intercom.get(@finder_url, @finder_params)
|
23
|
+
end
|
24
|
+
raise Intercom::HttpError.new('Http Error - No response entity returned') unless response_hash
|
25
|
+
deserialize_response_hash(response_hash, block)
|
26
|
+
next_page = extract_next_link(response_hash)
|
27
|
+
break if next_page.nil?
|
28
|
+
end
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def [](target_index)
|
33
|
+
self.each_with_index do |item, index|
|
34
|
+
return item if index == target_index
|
35
|
+
end
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
include Enumerable
|
40
|
+
|
41
|
+
def count
|
42
|
+
raise NoMethodError, "undefined method `count' for #{self.class}. Consider using the dedicated Intercom::Count interface if suitable"
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def resource_class; @resource_class; end
|
48
|
+
|
49
|
+
def deserialize_response_hash(response_hash, block)
|
50
|
+
top_level_type = response_hash.delete('type')
|
51
|
+
if resource_name == 'subscriptions'
|
52
|
+
top_level_entity_key = 'items'
|
53
|
+
else
|
54
|
+
top_level_entity_key = Utils.entity_key_from_type(top_level_type)
|
55
|
+
end
|
56
|
+
response_hash[top_level_entity_key].each do |object_json|
|
57
|
+
block.call Lib::TypedJsonDeserializer.new(object_json).deserialize
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def paging_info_present?(response_hash)
|
62
|
+
!!(response_hash['pages'] && response_hash['pages']['type'])
|
63
|
+
end
|
64
|
+
|
65
|
+
def extract_next_link(response_hash)
|
66
|
+
return nil unless paging_info_present?(response_hash)
|
67
|
+
paging_info = response_hash.delete('pages')
|
68
|
+
paging_info["next"]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|