intercom 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -8
  3. data/Gemfile +3 -0
  4. data/README.md +208 -52
  5. data/changes.txt +3 -0
  6. data/intercom.gemspec +2 -2
  7. data/lib/ext/hash.rb +18 -0
  8. data/lib/intercom.rb +38 -43
  9. data/lib/intercom/api_operations/count.rb +16 -0
  10. data/lib/intercom/api_operations/delete.rb +15 -0
  11. data/lib/intercom/api_operations/find.rb +22 -0
  12. data/lib/intercom/api_operations/find_all.rb +33 -0
  13. data/lib/intercom/api_operations/list.rb +17 -0
  14. data/lib/intercom/api_operations/load.rb +15 -0
  15. data/lib/intercom/api_operations/save.rb +44 -0
  16. data/lib/intercom/collection_proxy.rb +66 -0
  17. data/lib/intercom/company.rb +29 -0
  18. data/lib/intercom/conversation.rb +15 -0
  19. data/lib/intercom/count.rb +21 -0
  20. data/lib/intercom/errors.rb +52 -0
  21. data/lib/intercom/event.rb +4 -101
  22. data/lib/intercom/extended_api_operations/reply.rb +16 -0
  23. data/lib/intercom/extended_api_operations/tags.rb +14 -0
  24. data/lib/intercom/extended_api_operations/users.rb +17 -0
  25. data/lib/intercom/generic_handlers/base_handler.rb +22 -0
  26. data/lib/intercom/generic_handlers/count.rb +59 -0
  27. data/lib/intercom/generic_handlers/tag.rb +71 -0
  28. data/lib/intercom/generic_handlers/tag_find_all.rb +47 -0
  29. data/lib/intercom/lib/dynamic_accessors.rb +59 -0
  30. data/lib/intercom/lib/dynamic_accessors_on_method_missing.rb +53 -0
  31. data/lib/intercom/lib/flat_store.rb +31 -0
  32. data/lib/intercom/lib/typed_json_deserializer.rb +52 -0
  33. data/lib/intercom/message.rb +9 -0
  34. data/lib/intercom/note.rb +14 -42
  35. data/lib/intercom/request.rb +40 -4
  36. data/lib/intercom/segment.rb +14 -0
  37. data/lib/intercom/tag.rb +19 -78
  38. data/lib/intercom/traits/api_resource.rb +120 -0
  39. data/lib/intercom/traits/dirty_tracking.rb +33 -0
  40. data/lib/intercom/traits/generic_handler_binding.rb +29 -0
  41. data/lib/intercom/traits/incrementable_attributes.rb +23 -0
  42. data/lib/intercom/user.rb +25 -361
  43. data/lib/intercom/utils.rb +50 -0
  44. data/lib/intercom/version.rb +1 -1
  45. data/spec/spec_helper.rb +64 -33
  46. data/spec/unit/intercom/collection_proxy_spec.rb +34 -0
  47. data/spec/unit/intercom/event_spec.rb +25 -0
  48. data/spec/unit/intercom/{flat_store_spec.rb → lib/flat_store_spec.rb} +7 -7
  49. data/spec/unit/intercom/note_spec.rb +5 -4
  50. data/spec/unit/intercom/tag_spec.rb +3 -3
  51. data/spec/unit/intercom/traits/api_resource_spec.rb +79 -0
  52. data/spec/unit/intercom/user_spec.rb +101 -119
  53. data/spec/unit/intercom_spec.rb +7 -7
  54. metadata +50 -26
  55. data/lib/intercom/flat_store.rb +0 -27
  56. data/lib/intercom/hashable_object.rb +0 -22
  57. data/lib/intercom/impression.rb +0 -63
  58. data/lib/intercom/message_thread.rb +0 -189
  59. data/lib/intercom/requires_parameters.rb +0 -10
  60. data/lib/intercom/social_profile.rb +0 -24
  61. data/lib/intercom/unix_timestamp_unwrapper.rb +0 -12
  62. data/lib/intercom/user_collection_proxy.rb +0 -52
  63. data/lib/intercom/user_resource.rb +0 -82
  64. data/spec/integration/fixtures/v1-user.json +0 -45
  65. data/spec/integration/fixtures/v1-users-impression.json +0 -3
  66. data/spec/integration/fixtures/v1-users-message_thread.json +0 -44
  67. data/spec/integration/fixtures/v1-users-message_threads.json +0 -46
  68. data/spec/integration/fixtures/v1-users-note.json +0 -49
  69. data/spec/integration/fixtures/v1-users.json +0 -144
  70. data/spec/integration/intercom_api_integration_spec.rb +0 -134
  71. data/spec/unit/intercom/impression_spec.rb +0 -18
  72. data/spec/unit/intercom/message_thread_spec.rb +0 -74
  73. data/spec/unit/intercom/user_collection_proxy_spec.rb +0 -46
  74. data/spec/unit/intercom/user_event_spec.rb +0 -83
  75. data/spec/unit/intercom/user_resource_spec.rb +0 -13
@@ -0,0 +1,9 @@
1
+ require 'intercom/api_operations/save'
2
+ require 'intercom/traits/api_resource'
3
+
4
+ module Intercom
5
+ class Message
6
+ include ApiOperations::Save
7
+ include Traits::ApiResource
8
+ end
9
+ end
data/lib/intercom/note.rb CHANGED
@@ -1,45 +1,17 @@
1
- require 'intercom/user_resource'
1
+ require 'intercom/api_operations/save'
2
+ require 'intercom/api_operations/list'
3
+ require 'intercom/api_operations/find_all'
4
+ require 'intercom/api_operations/find'
5
+ require 'intercom/api_operations/load'
6
+ require 'intercom/traits/api_resource'
2
7
 
3
8
  module Intercom
4
-
5
- ##
6
- # Represents a note on a user
7
- #
8
- # A note contains a note (the text of the note you want to leave)
9
- #
10
- # == Examples
11
- #
12
- # note = Intercom::Note.create(:email => "person@example.com", :body => "This is the note you want to make on the user account")
13
-
14
- # You can also create a note and save it like this:
15
- # note = Intercom::Note.new
16
- # note.body = "This is the note you want to make on the user account"
17
- # note.save
18
-
19
- class Note < UserResource
20
- ##
21
- # Creates a new Note using params and saves it
22
- # @see #save
23
- def self.create(params)
24
- requires_parameters(params, %W(body))
25
- Note.new(params).save
26
- end
27
-
28
- ##
29
- # Records a note on a user of your application
30
- def save
31
- response = Intercom.post("/v1/users/notes", to_hash)
32
- self.update_from_api_response(response)
33
- end
34
-
35
- ##
36
- # Set the text of the note for the user
37
- def body=(body)
38
- @attributes["body"] = body
39
- end
40
-
41
- def user
42
- User.from_api(@attributes['user'])
43
- end
9
+ class Note
10
+ include ApiOperations::Save
11
+ include ApiOperations::List
12
+ include ApiOperations::FindAll
13
+ include ApiOperations::Find
14
+ include ApiOperations::Load
15
+ include Traits::ApiResource
44
16
  end
45
- end
17
+ end
@@ -12,7 +12,6 @@ module Intercom
12
12
 
13
13
  def set_common_headers(method, base_uri)
14
14
  method.basic_auth(CGI.unescape(base_uri.user), CGI.unescape(base_uri.password))
15
- method.add_field('Accept', 'application/json')
16
15
  method.add_field('AcceptEncoding', 'gzip, deflate')
17
16
  end
18
17
 
@@ -40,7 +39,7 @@ module Intercom
40
39
  end
41
40
 
42
41
  def self.default_headers
43
- {'Accept-Encoding' => 'gzip, deflate', 'Accept' => 'application/json'}
42
+ {'Accept-Encoding' => 'gzip, deflate', 'Accept' => 'application/vnd.intercom.3+json'}
44
43
  end
45
44
 
46
45
  def client(uri)
@@ -60,9 +59,13 @@ module Intercom
60
59
  set_common_headers(net_http_method, base_uri)
61
60
  client(base_uri).start do |http|
62
61
  response = http.request(net_http_method)
63
- raise_errors_on_failure(response)
64
62
  decoded = decode(response['content-encoding'], response.body)
65
- JSON.parse(decoded) unless decoded.strip.empty?
63
+ unless decoded.strip.empty?
64
+ parsed_body = JSON.parse(decoded)
65
+ raise_application_errors_on_failure(parsed_body, response.code.to_i) if parsed_body['type'] == 'error.list'
66
+ end
67
+ raise_errors_on_failure(response)
68
+ parsed_body
66
69
  end
67
70
  rescue Timeout::Error
68
71
  raise Intercom::ServiceUnavailableError
@@ -87,6 +90,39 @@ module Intercom
87
90
  end
88
91
  end
89
92
 
93
+ def raise_application_errors_on_failure(error_list_details, http_code)
94
+ # Currently, we don't support multiple errors
95
+ error_details = error_list_details['errors'].first
96
+ error_code = error_details['type'] || error_details['code']
97
+ parsed_http_code = (http_code > 0 ? http_code : nil)
98
+ error_context = {
99
+ :http_code => parsed_http_code,
100
+ :application_error_code => error_code
101
+ }
102
+ case error_code
103
+ when 'unauthorized'
104
+ raise Intercom::AuthenticationError.new(error_details['message'], error_context)
105
+ when "bad_request", "missing_parameter", 'parameter_invalid'
106
+ raise Intercom::BadRequestError.new(error_details['message'], error_context)
107
+ when "not_found"
108
+ raise Intercom::ResourceNotFound.new(error_details['message'], error_context)
109
+ when "rate_limit_exceeded"
110
+ raise Intercom::RateLimitExceeded.new(error_details['message'], error_context)
111
+ when nil, ''
112
+ raise Intercom::UnexpectedError.new(message_for_unexpected_error_without_type(error_details, parsed_http_code), error_context)
113
+ else
114
+ raise Intercom::UnexpectedError.new(message_for_unexpected_error_with_type(error_details, parsed_http_code), error_context)
115
+ end
116
+ end
117
+
118
+ def message_for_unexpected_error_with_type(error_details, parsed_http_code)
119
+ "The error of type '#{error_details['type']}' is not recognized. It occurred with the message: #{error_details['message']} and http_code: '#{parsed_http_code}'. Please contact Intercom with these details."
120
+ end
121
+
122
+ def message_for_unexpected_error_without_type(error_details, parsed_http_code)
123
+ "An unexpected error occured. It occurred with the message: #{error_details['message']} and http_code: '#{parsed_http_code}'. Please contact Intercom with these details."
124
+ end
125
+
90
126
  def self.append_query_string_to_url(url, params)
91
127
  return url if params.empty?
92
128
  query_string = params.map { |k, v| "#{k.to_s}=#{CGI::escape(v.to_s)}" }.join('&')
@@ -0,0 +1,14 @@
1
+ require 'intercom/api_operations/count'
2
+ require 'intercom/api_operations/find'
3
+ require 'intercom/api_operations/save'
4
+ require 'intercom/traits/api_resource'
5
+
6
+ module Intercom
7
+ class Segment
8
+ include ApiOperations::List
9
+ include ApiOperations::Find
10
+ include ApiOperations::Save
11
+ include ApiOperations::Count
12
+ include Traits::ApiResource
13
+ end
14
+ end
data/lib/intercom/tag.rb CHANGED
@@ -1,82 +1,23 @@
1
- require 'intercom/requires_parameters'
2
- require 'intercom/hashable_object'
1
+ require 'intercom/api_operations/count'
2
+ require 'intercom/api_operations/save'
3
+ require 'intercom/api_operations/list'
4
+ require 'intercom/api_operations/find'
5
+ require 'intercom/api_operations/find_all'
6
+ require 'intercom/traits/api_resource'
7
+ require 'intercom/traits/generic_handler_binding'
8
+ require 'intercom/generic_handlers/tag'
9
+ require 'intercom/generic_handlers/tag_find_all'
3
10
 
4
11
  module Intercom
5
-
6
- ##
7
- # Represents a tag
8
- #
9
- # A tag consists of a name, and (optionally) users that you would like to tag. Returns details about the tag and a count of the number of users currently tagged.
10
- #
11
- # == Examples
12
- #
13
- # tag = Intercom::Tag.create(:name => "Super Tag", :user_ids => ['abc123', 'def456'])
14
- # tag = Intercom::Tag.create(:name => "Super Tag", :emails => ['bob@example.com', 'joe@example.com'])
15
- #
16
- # You can also create a tag and save it like this:
17
- # tag = Intercom::Tag.new
18
- # tag.name = "Super Tag"
19
- # tag.user_ids = ['abc123', 'def456']
20
- # tag.tag_or_untag = "tag"
21
- # tag.save
22
- #
23
- # Or update a tag and save it like this:
24
- # tag = Intercom::Tag.find_by_name "Super Tag"
25
- # tag.user_ids = ['abc123']
26
- # tag.tag_or_untag = "untag"
27
- # tag.save
28
-
29
12
  class Tag
30
- extend RequiresParameters
31
- include HashableObject
32
-
33
- attr_accessor :name
34
- attr_reader :segment, :tagged_user_count, :id
35
- attr_writer :user_ids, :emails, :tag_or_untag
36
-
37
- def initialize(attributes={})
38
- from_hash(attributes)
39
- end
40
-
41
- ##
42
- # Finds a Tag using params
43
- def self.find(params)
44
- response = Intercom.get("/v1/tags", params)
45
- from_api(response)
46
- end
47
-
48
- def self.from_api(api_response)
49
- tag = Tag.new
50
- tag.from_hash(api_response)
51
- tag.displayable_self
52
- end
53
-
54
- ##
55
- # Finds a Tag using a name
56
- def self.find_by_name(name)
57
- find({:name => name})
58
- end
59
-
60
- ##
61
- # Creates a new Tag using params and saves it
62
- # @see #save
63
- def self.create(params)
64
- requires_parameters(params, %W(name))
65
- Tag.new(params).save
66
- end
67
-
68
- ##
69
- # Saves a Tag on your application
70
- def save
71
- response = Intercom.post("/v1/tags", to_wire)
72
- self.from_hash(response)
73
- displayable_self
74
- end
75
-
76
- ##
77
- # Create a new clean instance to return (only showing the readable attributes)
78
- def displayable_self
79
- Tag.new(self.displayable_attributes)
80
- end
13
+ include ApiOperations::Count
14
+ include ApiOperations::Save
15
+ include ApiOperations::List
16
+ include ApiOperations::Find
17
+ include ApiOperations::FindAll
18
+ include Traits::ApiResource
19
+ include Traits::GenericHandlerBinding
20
+ include GenericHandlers::Tag
21
+ include GenericHandlers::TagFindAll
81
22
  end
82
- end
23
+ end
@@ -0,0 +1,120 @@
1
+ require 'intercom/lib/flat_store'
2
+ require 'intercom/lib/dynamic_accessors'
3
+ require 'intercom/lib/dynamic_accessors_on_method_missing'
4
+ require 'intercom/traits/dirty_tracking'
5
+ require 'intercom/lib/typed_json_deserializer'
6
+
7
+ module Intercom
8
+ module Traits
9
+
10
+ module ApiResource
11
+ include DirtyTracking
12
+
13
+ attr_accessor :id
14
+
15
+ def initialize(attributes = {})
16
+ from_hash(attributes)
17
+ end
18
+
19
+ def from_response(response)
20
+ from_hash(response)
21
+ reset_changed_fields!
22
+ self
23
+ end
24
+
25
+ def from_hash(hash)
26
+ hash.each do |attribute, value|
27
+ next if type_field?(attribute)
28
+ initialize_property(attribute, value)
29
+ end
30
+ initialize_missing_flat_store_attributes if respond_to? :flat_store_attributes
31
+ self
32
+ end
33
+
34
+ def to_hash
35
+ instance_variables_excluding_dirty_tracking_field.inject({}) do |hash, variable|
36
+ hash[variable.to_s.delete("@")] = instance_variable_get(variable)
37
+ hash
38
+ end
39
+ end
40
+
41
+ def to_submittable_hash
42
+ submittable_hash = {}
43
+ to_hash.each do |attribute, value|
44
+ submittable_hash[attribute] = value if submittable_attribute?(attribute, value)
45
+ end
46
+ submittable_hash
47
+ end
48
+
49
+ def method_missing(method_sym, *arguments, &block)
50
+ Lib::DynamicAccessorsOnMethodMissing.new(method_sym, *arguments, self).
51
+ define_accessors_or_call { super }
52
+ end
53
+
54
+ def flat_store_attribute?(attribute)
55
+ (respond_to?(:flat_store_attributes)) && (flat_store_attributes.map(&:to_s).include?(attribute.to_s))
56
+ end
57
+
58
+ private
59
+
60
+ def initialize_property(attribute, value)
61
+ Lib::DynamicAccessors.define_accessors(attribute, value, self) unless accessors_already_defined?(attribute)
62
+ set_property(attribute, value)
63
+ end
64
+
65
+ def accessors_already_defined?(attribute)
66
+ respond_to?(attribute) && respond_to?("#{attribute}=")
67
+ end
68
+
69
+ def set_property(attribute, value)
70
+ if typed_value?(value)
71
+ value_to_set = Intercom::Lib::TypedJsonDeserializer.new(value).deserialize
72
+ elsif flat_store_attribute?(attribute)
73
+ value_to_set = Intercom::Lib::FlatStore.new(value)
74
+ else
75
+ value_to_set = value
76
+ end
77
+ call_setter_for_attribute(attribute, value_to_set)
78
+ end
79
+
80
+ def typed_value?(value)
81
+ value.is_a? Hash and !!value['type']
82
+ end
83
+
84
+ def call_setter_for_attribute(attribute, value)
85
+ setter_method = "#{attribute.to_s}="
86
+ self.send(setter_method, value)
87
+ end
88
+
89
+ def type_field?(attribute)
90
+ attribute == 'type'
91
+ end
92
+
93
+ def initialize_missing_flat_store_attributes
94
+ flat_store_attributes.each do |attribute|
95
+ unless instance_variables_excluding_dirty_tracking_field.map(&:to_s).include? "@#{attribute}"
96
+ initialize_property(attribute, {})
97
+ end
98
+ end
99
+ end
100
+
101
+ def submittable_attribute?(attribute, value)
102
+ # FlatStores always submitted, even if not changed, as we don't track their dirtyness
103
+ value.is_a?(Intercom::Lib::FlatStore) || field_changed?(attribute)
104
+ end
105
+
106
+ module ClassMethods
107
+ def from_api(api_response)
108
+ object = self.new
109
+ object.from_response(api_response)
110
+ object
111
+ end
112
+ end
113
+
114
+ def self.included(base)
115
+ base.extend(ClassMethods)
116
+ end
117
+
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,33 @@
1
+ require 'set'
2
+
3
+ module Intercom
4
+ module Traits
5
+ module DirtyTracking
6
+
7
+ def reset_changed_fields!
8
+ @changed_fields = Set.new
9
+ end
10
+
11
+ def mark_fields_as_changed!(field_names)
12
+ @changed_fields ||= Set.new
13
+ field_names.each do |attr|
14
+ @changed_fields.add(attr.to_s)
15
+ end
16
+ end
17
+
18
+ def mark_field_as_changed!(field_name)
19
+ @changed_fields ||= Set.new
20
+ @changed_fields.add(field_name.to_s)
21
+ end
22
+
23
+ def field_changed?(field_name)
24
+ @changed_fields ||= Set.new
25
+ @changed_fields.include?(field_name.to_s)
26
+ end
27
+
28
+ def instance_variables_excluding_dirty_tracking_field
29
+ instance_variables.reject{|var| var == :@changed_fields}
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ module Intercom
2
+ module Traits
3
+
4
+ # Allows us to have one class level method missing handler across all entities
5
+ # which can dispatch to the appropriate function based on the method name
6
+ module GenericHandlerBinding
7
+
8
+ module ClassMethods
9
+ def method_missing(method_sym, *arguments, &block)
10
+ if respond_to? :generic_tag and GenericHandlers::Tag.handles_method?(method_sym)
11
+ return generic_tag(method_sym, *arguments, block)
12
+ elsif respond_to? :generic_tag_find_all and GenericHandlers::TagFindAll.handles_method?(method_sym)
13
+ return generic_tag_find_all(method_sym, *arguments, block)
14
+ elsif respond_to? :generic_count and GenericHandlers::Count.handles_method?(method_sym)
15
+ return generic_count(method_sym, *arguments, block)
16
+ end
17
+ super
18
+ rescue Intercom::NoMethodMissingHandler
19
+ super
20
+ end
21
+ end
22
+
23
+ def self.included(base)
24
+ base.extend(ClassMethods)
25
+ end
26
+
27
+ end
28
+ end
29
+ end