hubscreen 0.0.4 → 0.0.5
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/hubscreen.gemspec +1 -1
- data/lib/hubscreen.rb +1 -0
- data/lib/hubscreen/api_request.rb +5 -1
- data/lib/hubscreen/contact.rb +217 -0
- data/lib/hubscreen/request.rb +4 -0
- data/lib/hubscreen/response.rb +1 -1
- data/lib/hubscreen/utils.rb +125 -0
- data/lib/hubscreen/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6466da0961fcb2137144c6b5dbda8accb5a6479d
|
4
|
+
data.tar.gz: 9c812e7003d7855d52db04130d2be6f8b83f2b29
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4afbb29d4b4775f37cec9259f0a87b99d4fa954906d9e3b9c9970812bb4ff85507a1dc7419aaf5195ca080997e9bbaf248993ddeb9b6c21489100dad8002f0ff
|
7
|
+
data.tar.gz: a566bcb452938d3888480eac7e86b96dcc981ce9314bb95ac0ec7f955526dad8e68e903c61c9abe5acb35507f1b44d3da8a9eddd0d14ef62acdd22e734b016a6
|
data/hubscreen.gemspec
CHANGED
@@ -40,7 +40,7 @@ Gem::Specification.new do |spec|
|
|
40
40
|
|
41
41
|
#Gem Dependancies
|
42
42
|
spec.add_dependency(%q<activesupport>, [">= 4"])
|
43
|
-
spec.add_dependency('faraday', '>= 0.
|
43
|
+
spec.add_dependency('faraday', '>= 0.9')
|
44
44
|
spec.add_dependency('multi_json', '>= 1')
|
45
45
|
spec.add_dependency('recursive-open-struct', '>= 1.0')
|
46
46
|
|
data/lib/hubscreen.rb
CHANGED
@@ -127,16 +127,20 @@ module Hubscreen
|
|
127
127
|
|
128
128
|
def configure_request(request: nil, params: nil, headers: nil, body: nil)
|
129
129
|
if request
|
130
|
+
request.options.params_encoder = Faraday::FlatParamsEncoder
|
130
131
|
request.params.merge!(params) if params
|
131
132
|
request.headers['Content-Type'] = 'application/json'
|
132
133
|
request.headers.merge!(headers) if headers
|
133
134
|
request.body = body if body
|
134
|
-
request.options.timeout = self.timeout
|
135
|
+
request.options.timeout = self.timeout
|
135
136
|
end
|
136
137
|
end
|
137
138
|
|
139
|
+
# Note REST Client has been modified for Flat Parameter Encoding to support Hubspot's batch search functionality.
|
140
|
+
# This however limit's Faraday's ability to accept nested parameters
|
138
141
|
def rest_client
|
139
142
|
client = Faraday.new(self.api_url, proxy: self.proxy) do |faraday|
|
143
|
+
|
140
144
|
faraday.response :raise_error
|
141
145
|
faraday.adapter adapter
|
142
146
|
if @request_builder.debug
|
data/lib/hubscreen/contact.rb
CHANGED
@@ -6,6 +6,9 @@ module Hubscreen
|
|
6
6
|
# {https://developers.hubspot.com/docs/methods/contacts/contacts-overview}
|
7
7
|
#
|
8
8
|
# This is a convenience object for single Hubspot contact returned by the API
|
9
|
+
#
|
10
|
+
# This is a convenience object for handling or retrieving single Hubspot contact returned by the API
|
11
|
+
# These helpers may not be updated as fast as the API. Use at your own risk
|
9
12
|
|
10
13
|
class Contact < Hubscreen::Response
|
11
14
|
CONTACT_KEYS = [:properties,
|
@@ -15,6 +18,15 @@ module Hubscreen
|
|
15
18
|
:last_name,
|
16
19
|
:hubspot_owner_id]
|
17
20
|
|
21
|
+
CONTACT_ROOT = "v1/contact"
|
22
|
+
FIND_BY_VID_PATH = "#{CONTACT_ROOT}/vid" #Note full endpoint is GET /contacts/v1/contact/vid/:contact_id/profile
|
23
|
+
FIND_BY_EMAIL_PATH = "#{CONTACT_ROOT}/email" #Note full endpoint is GET /contacts/v1/contact/email/:contact_email/profile
|
24
|
+
CREATE_OR_UPDATE_PATH = '#{CONTACT_ROOT}/createOrUpdate/email' #Note full endpoint is POST /contacts/v1/contact/createOrUpdate/email/:contact_email
|
25
|
+
UPDATE_PATH = "#{CONTACT_ROOT}/vid" #Note full endpoint is POST /contacts/v1/contact/vid/:contact_id/profile
|
26
|
+
UPDATE_BATCH_PATH = "#{CONTACT_ROOT}/batch" # POST /contacts/v1/contact/batch/
|
27
|
+
FIND_BY_EMAIL_BATCH_PATH = "#{CONTACT_ROOT}/emails/batch" # Note full endpoint is GET /contacts/v1/contact/emails/batch/:contact_emails
|
28
|
+
|
29
|
+
|
18
30
|
attr_accessor *CONTACT_KEYS
|
19
31
|
|
20
32
|
def initialize(response)
|
@@ -37,6 +49,211 @@ module Hubscreen
|
|
37
49
|
"<Hubscreen::Contact vid:#{@vid}, email:'#{@email}', first_name:'#{@first_name}', last_name:'#{@last_name}', company:'#{@company}', hubspot_owner_id:'#{@hubspot_owner_id}', properties:<Not Shown>, raw_response:<Not Shown>, raw_hash:<Not Shown>>"
|
38
50
|
end
|
39
51
|
|
52
|
+
# Convenience Methods
|
53
|
+
class << self
|
54
|
+
|
55
|
+
# Find Contact by vid
|
56
|
+
#
|
57
|
+
# If Hubspot cannot find the contact, will return the 404 response. Always check for a 200 status code before proceeding
|
58
|
+
def find(vid)
|
59
|
+
begin
|
60
|
+
Hubscreen::Request.new.contacts(FIND_BY_VID_PATH,vid).profile.get.contact
|
61
|
+
rescue Hubscreen::RequestError => e
|
62
|
+
failure_response = Response.new(e.response)
|
63
|
+
failure_response.status_code = e.response[:status_code]
|
64
|
+
return failure_response
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Find Contact by email
|
69
|
+
#
|
70
|
+
# If Hubspot cannot find the contact, will return the 404 response. Always check for a 200 status code before proceeding
|
71
|
+
def find_by_email(email)
|
72
|
+
begin
|
73
|
+
Hubscreen::Request.new.contacts(FIND_BY_EMAIL_PATH,email).profile.get.contact
|
74
|
+
rescue Hubscreen::RequestError => e
|
75
|
+
failure_response = Response.new(e.response)
|
76
|
+
failure_response.status_code = e.response[:status_code]
|
77
|
+
return failure_response
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Create New Contact
|
82
|
+
#
|
83
|
+
# {https://developers.hubspot.com/docs/methods/contacts/create_contact}
|
84
|
+
#
|
85
|
+
# Will return a Contact object representing the new contact
|
86
|
+
def create!(email,properties={})
|
87
|
+
begin
|
88
|
+
params = properties.stringify_keys.merge('email' => email)
|
89
|
+
post_data = {properties: Hubscreen::Utils.hash_to_properties(params)}
|
90
|
+
Hubscreen::Request.new.contacts(CONTACT_ROOT).post(body: post_data).contact
|
91
|
+
rescue Hubscreen::RequestError => e
|
92
|
+
|
93
|
+
failure_response = Response.new(e.response)
|
94
|
+
failure_response.status_code = e.response[:status_code]
|
95
|
+
return failure_response
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Update New Contact
|
100
|
+
#
|
101
|
+
# {https://developers.hubspot.com/docs/methods/contacts/update_contact}
|
102
|
+
#
|
103
|
+
# Will return a Contact object representing the updated contact
|
104
|
+
def update!(vid,properties={})
|
105
|
+
begin
|
106
|
+
params = properties.stringify_keys
|
107
|
+
post_data = {properties: Hubscreen::Utils.hash_to_properties(params)}
|
108
|
+
Hubscreen::Request.new.contacts(UPDATE_PATH,vid).profile.post(body: post_data).contact
|
109
|
+
rescue Hubscreen::RequestError => e
|
110
|
+
#binding.pry
|
111
|
+
failure_response = Response.new(e.response)
|
112
|
+
failure_response.status_code = e.response[:status_code]
|
113
|
+
return failure_response
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Upsert Contact (Create or Update)
|
118
|
+
#
|
119
|
+
# {https://developers.hubspot.com/docs/methods/contacts/create_or_update}
|
120
|
+
#
|
121
|
+
# Will return a Contact object representing the new contact
|
122
|
+
def upsert(email,properties={})
|
123
|
+
begin
|
124
|
+
params = properties.stringify_keys.merge('email' => email)
|
125
|
+
post_data = {properties: Hubscreen::Utils.hash_to_properties(params)}
|
126
|
+
Hubscreen::Request.new.contacts(CREATE_OR_UPDATE_PATH,email).post(body: post_data).contact
|
127
|
+
rescue Hubscreen::RequestError => e
|
128
|
+
|
129
|
+
failure_response = Response.new(e.response)
|
130
|
+
failure_response.status_code = e.response[:status_code]
|
131
|
+
return failure_response
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Batch Methods
|
136
|
+
|
137
|
+
# Create or update a group of contacts
|
138
|
+
#
|
139
|
+
# {https://developers.hubspot.com/docs/methods/contacts/batch_create_or_update}
|
140
|
+
#
|
141
|
+
# POST /contacts/v1/contact/batch/
|
142
|
+
#
|
143
|
+
# From Hubspot:
|
144
|
+
# Create a group of contacts or update them if they already exist. Particularly useful for periodic syncs from another contacts database to HubSpot.
|
145
|
+
#
|
146
|
+
# Performance is best when calls are limited to 100 or fewer contacts.
|
147
|
+
#
|
148
|
+
# When using this endpoint, please keep in mind that any errors with a single contact in your batch will prevent the entire batch from processing. If this happens, we'll return a 400 response with additional details as to the cause.
|
149
|
+
#
|
150
|
+
# This method only supports update by vid (vid is mandatory)
|
151
|
+
# To use this method, pass in a array of hashes using the following structure:
|
152
|
+
#
|
153
|
+
# [
|
154
|
+
# {
|
155
|
+
# vid: 5464,
|
156
|
+
# firstname: "George",
|
157
|
+
# lastname: "Henry"
|
158
|
+
# },
|
159
|
+
# {
|
160
|
+
# vid: 5464,
|
161
|
+
# firstname: "Codey",
|
162
|
+
# lastname: "Lang"
|
163
|
+
# }
|
164
|
+
# ]
|
165
|
+
def upsert_batch_by_vid(contacts = [])
|
166
|
+
begin
|
167
|
+
params = []
|
168
|
+
contacts.each do |contact|
|
169
|
+
params << {'vid': contact[:vid],'properties': Hubscreen::Utils.hash_to_properties(contact.except(:vid).stringify_keys)}
|
170
|
+
end
|
171
|
+
post_data = params
|
172
|
+
Hubscreen::Request.new.contacts(UPDATE_BATCH_PATH).post(body: post_data)
|
173
|
+
rescue Hubscreen::RequestError => e
|
174
|
+
|
175
|
+
failure_response = Response.new(e.response)
|
176
|
+
failure_response.status_code = e.response[:status_code]
|
177
|
+
return failure_response
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Create or update a group of contacts
|
182
|
+
#
|
183
|
+
# {https://developers.hubspot.com/docs/methods/contacts/batch_create_or_update}
|
184
|
+
#
|
185
|
+
# POST /contacts/v1/contact/batch/
|
186
|
+
#
|
187
|
+
# From Hubspot:
|
188
|
+
# Create a group of contacts or update them if they already exist. Particularly useful for periodic syncs from another contacts database to HubSpot.
|
189
|
+
#
|
190
|
+
# Performance is best when calls are limited to 100 or fewer contacts.
|
191
|
+
#
|
192
|
+
# When using this endpoint, please keep in mind that any errors with a single contact in your batch will prevent the entire batch from processing. If this happens, we'll return a 400 response with additional details as to the cause.
|
193
|
+
#
|
194
|
+
# This method only supports update by emails (email is manatory)
|
195
|
+
# DO NOT PASS VID
|
196
|
+
#
|
197
|
+
# To use this method, pass in a array of hashes using the following structure:
|
198
|
+
#
|
199
|
+
# [
|
200
|
+
# {
|
201
|
+
# email: "first@email.com",
|
202
|
+
# firstname: "George",
|
203
|
+
# lastname: "Henry"
|
204
|
+
# },
|
205
|
+
# {
|
206
|
+
# email: "second@email.com",
|
207
|
+
# firstname: "Codey",
|
208
|
+
# lastname: "Lang"
|
209
|
+
# }
|
210
|
+
# ]
|
211
|
+
def upsert_batch_by_email(contacts = [])
|
212
|
+
begin
|
213
|
+
params = []
|
214
|
+
contacts.each do |contact|
|
215
|
+
params << {'email': contact[:email],'properties': Hubscreen::Utils.hash_to_properties(contact.except(:email).stringify_keys)}
|
216
|
+
end
|
217
|
+
post_data = params
|
218
|
+
Hubscreen::Request.new.contacts(UPDATE_BATCH_PATH).post(body: post_data)
|
219
|
+
rescue Hubscreen::RequestError => e
|
220
|
+
|
221
|
+
failure_response = Response.new(e.response)
|
222
|
+
failure_response.status_code = e.response[:status_code]
|
223
|
+
return failure_response
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Search for a batch of contacts by email address
|
228
|
+
#
|
229
|
+
# {https://developers.hubspot.com/docs/methods/contacts/get_batch_by_email}
|
230
|
+
#
|
231
|
+
# GET /contacts/v1/contact/emails/batch/:contact_emails
|
232
|
+
#
|
233
|
+
# This method will return an array of Contact objects representing the search results
|
234
|
+
#
|
235
|
+
# If there is a connection error, the status code of the first element will reflect it. Always check this.
|
236
|
+
def find_by_emails(emails = [])
|
237
|
+
|
238
|
+
begin
|
239
|
+
params = {email: emails} # Based on Faraday code >= v0.9
|
240
|
+
response = Hubscreen::Request.new.contacts(FIND_BY_EMAIL_BATCH_PATH).get(params: params)
|
241
|
+
results = []
|
242
|
+
response.raw_hash.each do |key,value|
|
243
|
+
results << new(Response.new(value))
|
244
|
+
end
|
245
|
+
return results
|
246
|
+
rescue Hubscreen::RequestError => e
|
247
|
+
failure_response = Response.new(e.response)
|
248
|
+
failure_response.status_code = e.response[:status_code]
|
249
|
+
return [failure_response]
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
253
|
+
|
254
|
+
|
255
|
+
|
256
|
+
end
|
40
257
|
|
41
258
|
end
|
42
259
|
|
data/lib/hubscreen/request.rb
CHANGED
data/lib/hubscreen/response.rb
CHANGED
@@ -7,7 +7,7 @@ module Hubscreen
|
|
7
7
|
# By default all APIRequests will return a Response object. To disable this, set Hubscreen.configure(encapsulate_response: false)
|
8
8
|
|
9
9
|
class Response
|
10
|
-
attr_accessor :raw_hash, :raw_response
|
10
|
+
attr_accessor :raw_hash, :raw_response, :status_code
|
11
11
|
|
12
12
|
def initialize(response_json_hash)
|
13
13
|
@raw_hash = response_json_hash
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# Based on [hubspot-crm gem](https://github.com/adimichele/hubspot-ruby)
|
2
|
+
|
3
|
+
module Hubscreen
|
4
|
+
class Utils
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# Parses the hubspot properties format into a key-value hash
|
8
|
+
def properties_to_hash(props)
|
9
|
+
newprops = HashWithIndifferentAccess.new
|
10
|
+
props.each { |k, v| newprops[k] = v["value"] }
|
11
|
+
newprops
|
12
|
+
end
|
13
|
+
|
14
|
+
# Turns a hash into the hubspot properties format
|
15
|
+
def hash_to_properties(hash, opts = {})
|
16
|
+
key_name = opts[:key_name] || "property"
|
17
|
+
hash.map { |k, v| { key_name => k.to_s, "value" => v } }
|
18
|
+
end
|
19
|
+
|
20
|
+
def dump_properties(klass, hapikey=ENV['HUBSPOT_API_KEY'], filter={})
|
21
|
+
with_hapikey(hapikey) do
|
22
|
+
{ 'groups' => klass.groups({}, filter),
|
23
|
+
'properties' => klass.all({}, filter).select { |p| !p['hubspotDefined'] }
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def restore_properties(klass, hapikey=ENV['HUPSPOT_API_KEY'], properties={}, dry_run=false)
|
29
|
+
existing_properties = dump_properties(klass, hapikey)
|
30
|
+
skip, new_groups, new_props, update_props = compare_property_lists(klass, properties, existing_properties)
|
31
|
+
puts '', 'Dry Run - Changes will not be applied' if dry_run
|
32
|
+
puts '','Skipping'
|
33
|
+
skip.each { |h| puts "#{h[:reason]} - #{h[:prop]['groupName']}:#{h[:prop]['name']}" }
|
34
|
+
with_hapikey(hapikey) do
|
35
|
+
create_groups(klass, new_groups, dry_run)
|
36
|
+
create_properties(klass, new_props, dry_run)
|
37
|
+
update_properties(klass, update_props, dry_run)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def create_groups(klass, groups, dry_run=false)
|
42
|
+
puts '','Creating new groups'
|
43
|
+
groups.each do |g|
|
44
|
+
if dry_run || klass.create_group!(g)
|
45
|
+
puts "Created: #{g['name']}"
|
46
|
+
else
|
47
|
+
puts "Failed: #{g['name']}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def create_properties(klass, props, dry_run=false)
|
53
|
+
puts '','Creating new properties'
|
54
|
+
props.each do |p|
|
55
|
+
if dry_run || klass.create!(p)
|
56
|
+
puts "Created: #{p['groupName']}:#{p['name']}"
|
57
|
+
else
|
58
|
+
puts "Failed: #{p['groupName']}:#{p['name']}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def update_properties(klass, props, dry_run=false)
|
64
|
+
puts '','Updating existing properties'
|
65
|
+
props.each do |p|
|
66
|
+
if dry_run || klass.update!(p['name'], p)
|
67
|
+
puts "Updated: #{p['groupName']}:#{p['name']}"
|
68
|
+
else
|
69
|
+
puts "Failed: #{p['groupName']}:#{p['name']}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def compare_property_lists(klass, source, target)
|
75
|
+
skip = [] # Array of skipped properties and the reason
|
76
|
+
new_groups = Set.new # Array of groups to create
|
77
|
+
new_props = [] # Array of properties to add
|
78
|
+
update_props = [] # Array of properties to update
|
79
|
+
src_groups = source['groups']
|
80
|
+
dst_groups = target['groups']
|
81
|
+
src_props = source['properties']
|
82
|
+
dst_props = target['properties']
|
83
|
+
|
84
|
+
src_props.each do |src|
|
85
|
+
group = find_by_name(src['groupName'], src_groups)
|
86
|
+
if src['createdUserId'].blank? && src['updatedUserId'].blank? then
|
87
|
+
skip << { prop: src, reason: 'Not user created' }
|
88
|
+
else
|
89
|
+
dst = find_by_name(src['name'], dst_props)
|
90
|
+
if dst
|
91
|
+
if dst['readOnlyDefinition']
|
92
|
+
skip << { prop: src, reason: 'Definition is read-only' }
|
93
|
+
elsif klass.same?(src, dst)
|
94
|
+
skip << { prop: src, reason: 'No change' }
|
95
|
+
else
|
96
|
+
new_groups << group unless group.blank? || find_by_name(group['name'], dst_groups)
|
97
|
+
update_props << src
|
98
|
+
end
|
99
|
+
else
|
100
|
+
new_groups << group unless group.blank? || find_by_name(group['name'], dst_groups)
|
101
|
+
new_props << src
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
[skip, new_groups.to_a, new_props, update_props]
|
106
|
+
end
|
107
|
+
|
108
|
+
def with_hapikey(hapikey)
|
109
|
+
begin
|
110
|
+
Hubspot.configure(hapikey: hapikey) unless hapikey.blank?
|
111
|
+
yield if block_given?
|
112
|
+
ensure
|
113
|
+
Hubspot.configure(hapikey: ENV['HUBSPOT_API_KEY']) unless hapikey.blank?
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def find_by_name(name, set)
|
120
|
+
set.detect { |item| item['name'] == name }
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/hubscreen/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hubscreen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bryan Vaz
|
@@ -114,14 +114,14 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - ">="
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '0.
|
117
|
+
version: '0.9'
|
118
118
|
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '0.
|
124
|
+
version: '0.9'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: multi_json
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -178,6 +178,7 @@ files:
|
|
178
178
|
- lib/hubscreen/exceptions.rb
|
179
179
|
- lib/hubscreen/request.rb
|
180
180
|
- lib/hubscreen/response.rb
|
181
|
+
- lib/hubscreen/utils.rb
|
181
182
|
- lib/hubscreen/version.rb
|
182
183
|
homepage: https://github.com/bryanvaz/hubscreen
|
183
184
|
licenses:
|