intercom 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|