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.
- checksums.yaml +4 -4
- data/.travis.yml +0 -8
- data/Gemfile +3 -0
- data/README.md +208 -52
- data/changes.txt +3 -0
- data/intercom.gemspec +2 -2
- data/lib/ext/hash.rb +18 -0
- data/lib/intercom.rb +38 -43
- 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 +22 -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 +15 -0
- data/lib/intercom/api_operations/save.rb +44 -0
- data/lib/intercom/collection_proxy.rb +66 -0
- data/lib/intercom/company.rb +29 -0
- data/lib/intercom/conversation.rb +15 -0
- data/lib/intercom/count.rb +21 -0
- data/lib/intercom/errors.rb +52 -0
- data/lib/intercom/event.rb +4 -101
- 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 +52 -0
- data/lib/intercom/message.rb +9 -0
- data/lib/intercom/note.rb +14 -42
- data/lib/intercom/request.rb +40 -4
- data/lib/intercom/segment.rb +14 -0
- data/lib/intercom/tag.rb +19 -78
- data/lib/intercom/traits/api_resource.rb +120 -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 +23 -0
- data/lib/intercom/user.rb +25 -361
- data/lib/intercom/utils.rb +50 -0
- data/lib/intercom/version.rb +1 -1
- data/spec/spec_helper.rb +64 -33
- data/spec/unit/intercom/collection_proxy_spec.rb +34 -0
- data/spec/unit/intercom/event_spec.rb +25 -0
- data/spec/unit/intercom/{flat_store_spec.rb → lib/flat_store_spec.rb} +7 -7
- data/spec/unit/intercom/note_spec.rb +5 -4
- data/spec/unit/intercom/tag_spec.rb +3 -3
- data/spec/unit/intercom/traits/api_resource_spec.rb +79 -0
- data/spec/unit/intercom/user_spec.rb +101 -119
- data/spec/unit/intercom_spec.rb +7 -7
- metadata +50 -26
- data/lib/intercom/flat_store.rb +0 -27
- data/lib/intercom/hashable_object.rb +0 -22
- data/lib/intercom/impression.rb +0 -63
- data/lib/intercom/message_thread.rb +0 -189
- data/lib/intercom/requires_parameters.rb +0 -10
- data/lib/intercom/social_profile.rb +0 -24
- data/lib/intercom/unix_timestamp_unwrapper.rb +0 -12
- data/lib/intercom/user_collection_proxy.rb +0 -52
- data/lib/intercom/user_resource.rb +0 -82
- data/spec/integration/fixtures/v1-user.json +0 -45
- data/spec/integration/fixtures/v1-users-impression.json +0 -3
- data/spec/integration/fixtures/v1-users-message_thread.json +0 -44
- data/spec/integration/fixtures/v1-users-message_threads.json +0 -46
- data/spec/integration/fixtures/v1-users-note.json +0 -49
- data/spec/integration/fixtures/v1-users.json +0 -144
- data/spec/integration/intercom_api_integration_spec.rb +0 -134
- data/spec/unit/intercom/impression_spec.rb +0 -18
- data/spec/unit/intercom/message_thread_spec.rb +0 -74
- data/spec/unit/intercom/user_collection_proxy_spec.rb +0 -46
- data/spec/unit/intercom/user_event_spec.rb +0 -83
- data/spec/unit/intercom/user_resource_spec.rb +0 -13
data/lib/intercom/note.rb
CHANGED
@@ -1,45 +1,17 @@
|
|
1
|
-
require 'intercom/
|
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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
data/lib/intercom/request.rb
CHANGED
@@ -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
|
-
|
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/
|
2
|
-
require 'intercom/
|
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
|
-
|
31
|
-
include
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|