cogniteev-intercom 2.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +13 -0
  5. data/MIT-LICENSE +21 -0
  6. data/README.md +378 -0
  7. data/Rakefile +21 -0
  8. data/changes.txt +168 -0
  9. data/intercom.gemspec +28 -0
  10. data/lib/data/cacert.pem +3965 -0
  11. data/lib/ext/sliceable_hash.rb +16 -0
  12. data/lib/intercom.rb +176 -0
  13. data/lib/intercom/admin.rb +9 -0
  14. data/lib/intercom/api_operations/convert.rb +19 -0
  15. data/lib/intercom/api_operations/count.rb +16 -0
  16. data/lib/intercom/api_operations/delete.rb +15 -0
  17. data/lib/intercom/api_operations/find.rb +23 -0
  18. data/lib/intercom/api_operations/find_all.rb +33 -0
  19. data/lib/intercom/api_operations/list.rb +17 -0
  20. data/lib/intercom/api_operations/load.rb +16 -0
  21. data/lib/intercom/api_operations/save.rb +51 -0
  22. data/lib/intercom/collection_proxy.rb +71 -0
  23. data/lib/intercom/company.rb +29 -0
  24. data/lib/intercom/contact.rb +22 -0
  25. data/lib/intercom/conversation.rb +17 -0
  26. data/lib/intercom/count.rb +21 -0
  27. data/lib/intercom/errors.rb +61 -0
  28. data/lib/intercom/event.rb +11 -0
  29. data/lib/intercom/extended_api_operations/reply.rb +16 -0
  30. data/lib/intercom/extended_api_operations/tags.rb +14 -0
  31. data/lib/intercom/extended_api_operations/users.rb +17 -0
  32. data/lib/intercom/generic_handlers/base_handler.rb +22 -0
  33. data/lib/intercom/generic_handlers/count.rb +59 -0
  34. data/lib/intercom/generic_handlers/tag.rb +71 -0
  35. data/lib/intercom/generic_handlers/tag_find_all.rb +47 -0
  36. data/lib/intercom/lib/dynamic_accessors.rb +59 -0
  37. data/lib/intercom/lib/dynamic_accessors_on_method_missing.rb +53 -0
  38. data/lib/intercom/lib/flat_store.rb +31 -0
  39. data/lib/intercom/lib/typed_json_deserializer.rb +53 -0
  40. data/lib/intercom/message.rb +9 -0
  41. data/lib/intercom/note.rb +17 -0
  42. data/lib/intercom/notification.rb +20 -0
  43. data/lib/intercom/request.rb +166 -0
  44. data/lib/intercom/segment.rb +14 -0
  45. data/lib/intercom/subscription.rb +15 -0
  46. data/lib/intercom/tag.rb +23 -0
  47. data/lib/intercom/traits/api_resource.rb +132 -0
  48. data/lib/intercom/traits/dirty_tracking.rb +33 -0
  49. data/lib/intercom/traits/generic_handler_binding.rb +29 -0
  50. data/lib/intercom/traits/incrementable_attributes.rb +12 -0
  51. data/lib/intercom/user.rb +30 -0
  52. data/lib/intercom/utils.rb +62 -0
  53. data/lib/intercom/version.rb +3 -0
  54. data/spec/spec_helper.rb +308 -0
  55. data/spec/unit/intercom/admin_spec.rb +9 -0
  56. data/spec/unit/intercom/collection_proxy_spec.rb +34 -0
  57. data/spec/unit/intercom/company_spec.rb +23 -0
  58. data/spec/unit/intercom/contact_spec.rb +25 -0
  59. data/spec/unit/intercom/event_spec.rb +25 -0
  60. data/spec/unit/intercom/lib/flat_store_spec.rb +29 -0
  61. data/spec/unit/intercom/message_spec.rb +21 -0
  62. data/spec/unit/intercom/note_spec.rb +19 -0
  63. data/spec/unit/intercom/notification_spec.rb +68 -0
  64. data/spec/unit/intercom/request_spec.rb +16 -0
  65. data/spec/unit/intercom/subscription_spec.rb +18 -0
  66. data/spec/unit/intercom/tag_spec.rb +23 -0
  67. data/spec/unit/intercom/traits/api_resource_spec.rb +85 -0
  68. data/spec/unit/intercom/user_spec.rb +230 -0
  69. data/spec/unit/intercom_spec.rb +90 -0
  70. 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,9 @@
1
+ require 'intercom/api_operations/list'
2
+ require 'intercom/traits/api_resource'
3
+
4
+ module Intercom
5
+ class Admin
6
+ include ApiOperations::List
7
+ include Traits::ApiResource
8
+ end
9
+ 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