ruby_hubspot_api 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +17 -4
- data/Gemfile.lock +12 -16
- data/lib/hubspot/api_client.rb +10 -3
- data/lib/hubspot/batch.rb +76 -31
- data/lib/hubspot/config.rb +5 -5
- data/lib/hubspot/form.rb +26 -0
- data/lib/hubspot/paged_batch.rb +18 -3
- data/lib/hubspot/paged_collection.rb +8 -4
- data/lib/hubspot/resource.rb +121 -47
- data/lib/hubspot/user.rb +25 -1
- data/lib/hubspot/version.rb +1 -1
- data/lib/hubspot.rb +1 -0
- data/lib/ruby_hubspot_api.rb +6 -1
- data/lib/support/patches.rb +27 -0
- data/ruby_hubspot_api.gemspec +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52c9ee03d417a29792dbd370399982a03662cb621c0ab3e5ec0a2abd7de89ac5
|
4
|
+
data.tar.gz: aae2926993d4b6dcf78b8e728c031631225f667c882f0b6d0710601ff62c0772
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d2c9d82565c619a189bfc0780c914bb1b10cf769eb94b66e1e702fa5268fedadf7e3b9a188f6a1b0caa67c1b2d432e8f6401ffb890ca01efc768abc16ae3d3a
|
7
|
+
data.tar.gz: e66a9d5daa8373529a3a2ee07c474d288d94327fc40d83ed08e050e1dbb83c8f89217c95083035396f212ed78b97348f1a9437889d2719cd9dbf49d4624cbce3
|
data/.rubocop.yml
CHANGED
@@ -1,6 +1,19 @@
|
|
1
|
-
|
1
|
+
Documentation:
|
2
|
+
Enabled: false
|
3
|
+
|
4
|
+
Metrics/LineLength:
|
5
|
+
Max: 100
|
6
|
+
Exclude:
|
7
|
+
- spec/**/*.rb
|
8
|
+
|
9
|
+
# Metrics/MethodLength:
|
10
|
+
# Max: 20
|
11
|
+
|
12
|
+
# Metrics/ClassLength:
|
13
|
+
# Max: 200
|
2
14
|
|
3
|
-
# Ignore Metrics/BlockLength for the spec directory
|
4
15
|
Metrics/BlockLength:
|
5
|
-
|
6
|
-
|
16
|
+
# Max: 50
|
17
|
+
# Ignore Metrics/BlockLength for the spec directory
|
18
|
+
Exclude:
|
19
|
+
- spec/**/*.rb
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ruby_hubspot_api (0.2)
|
4
|
+
ruby_hubspot_api (0.2.2)
|
5
5
|
httparty (>= 0.1, < 1.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -9,35 +9,33 @@ GEM
|
|
9
9
|
specs:
|
10
10
|
addressable (2.8.7)
|
11
11
|
public_suffix (>= 2.0.2, < 7.0)
|
12
|
-
bigdecimal (3.
|
12
|
+
bigdecimal (3.0.2)
|
13
13
|
byebug (11.1.3)
|
14
|
-
codecov (0.
|
15
|
-
|
16
|
-
simplecov
|
14
|
+
codecov (0.6.0)
|
15
|
+
simplecov (>= 0.15, < 0.22)
|
17
16
|
coderay (1.1.3)
|
18
17
|
crack (1.0.0)
|
19
18
|
bigdecimal
|
20
19
|
rexml
|
21
20
|
diff-lcs (1.5.1)
|
22
|
-
docile (1.
|
21
|
+
docile (1.3.5)
|
23
22
|
dotenv (2.8.1)
|
24
23
|
hashdiff (1.1.1)
|
25
24
|
httparty (0.21.0)
|
26
25
|
mini_mime (>= 1.0.0)
|
27
26
|
multi_xml (>= 0.5.2)
|
28
|
-
json (2.7.2)
|
29
27
|
method_source (1.1.0)
|
30
28
|
mini_mime (1.1.2)
|
31
29
|
multi_xml (0.6.0)
|
32
|
-
pry (0.
|
30
|
+
pry (0.14.2)
|
33
31
|
coderay (~> 1.1)
|
34
32
|
method_source (~> 1.0)
|
35
|
-
pry-byebug (3.
|
33
|
+
pry-byebug (3.8.0)
|
36
34
|
byebug (~> 11.0)
|
37
|
-
pry (~> 0.
|
35
|
+
pry (~> 0.10)
|
38
36
|
public_suffix (4.0.7)
|
39
37
|
rake (13.2.1)
|
40
|
-
rexml (3.
|
38
|
+
rexml (3.2.5)
|
41
39
|
rspec (3.13.0)
|
42
40
|
rspec-core (~> 3.13.0)
|
43
41
|
rspec-expectations (~> 3.13.0)
|
@@ -51,15 +49,13 @@ GEM
|
|
51
49
|
diff-lcs (>= 1.2.0, < 2.0)
|
52
50
|
rspec-support (~> 3.13.0)
|
53
51
|
rspec-support (3.13.1)
|
54
|
-
simplecov (0.
|
52
|
+
simplecov (0.18.5)
|
55
53
|
docile (~> 1.1)
|
56
54
|
simplecov-html (~> 0.11)
|
57
|
-
simplecov_json_formatter (~> 0.1)
|
58
55
|
simplecov-html (0.13.1)
|
59
56
|
simplecov-lcov (0.8.0)
|
60
|
-
simplecov_json_formatter (0.1.4)
|
61
57
|
vcr (6.0.0)
|
62
|
-
webmock (3.
|
58
|
+
webmock (3.18.1)
|
63
59
|
addressable (>= 2.8.0)
|
64
60
|
crack (>= 0.3.2)
|
65
61
|
hashdiff (>= 0.4.0, < 2.0.0)
|
@@ -84,4 +80,4 @@ DEPENDENCIES
|
|
84
80
|
webmock (>= 3.0)
|
85
81
|
|
86
82
|
BUNDLED WITH
|
87
|
-
2.
|
83
|
+
2.2.34
|
data/lib/hubspot/api_client.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# HubSpot API Client
|
4
|
+
# Handles all HTTP interactions with the HubSpot API.
|
5
|
+
# It manages GET, POST, PATCH, and DELETE requests.
|
3
6
|
module Hubspot
|
4
7
|
# All interations with the Hubspot API happen here...
|
5
8
|
class ApiClient
|
@@ -57,10 +60,14 @@ module Hubspot
|
|
57
60
|
|
58
61
|
def log_request(http_method, url, response, start_time, extra = nil)
|
59
62
|
d = Time.now - start_time
|
60
|
-
|
63
|
+
|
64
|
+
Hubspot.logger.info(
|
65
|
+
"#{http_method.to_s.upcase} #{url} took #{d.round(2)}s with status #{response.code}"
|
66
|
+
)
|
67
|
+
|
61
68
|
return unless Hubspot.logger.debug?
|
62
69
|
|
63
|
-
Hubspot.logger.debug("Request body: #{extra}") if extra
|
70
|
+
Hubspot.logger.debug("Request body: #{extra[:body]}") if extra
|
64
71
|
Hubspot.logger.debug("Response body: #{response.body}")
|
65
72
|
end
|
66
73
|
|
@@ -91,7 +98,7 @@ module Hubspot
|
|
91
98
|
|
92
99
|
def retry_request(request, retries)
|
93
100
|
# Re-issues the original request using the retry logic
|
94
|
-
http_method = request.http_method::METHOD.downcase
|
101
|
+
http_method = request.http_method::METHOD.downcase
|
95
102
|
response = HTTParty.send(http_method, request.uri, request.options)
|
96
103
|
handle_response(response, retries)
|
97
104
|
end
|
data/lib/hubspot/batch.rb
CHANGED
@@ -31,8 +31,24 @@ module Hubspot
|
|
31
31
|
CONTACT_LIMIT = 10
|
32
32
|
DEFAULT_LIMIT = 100
|
33
33
|
|
34
|
+
def inspect
|
35
|
+
"#<#{self.class.name} " \
|
36
|
+
"@resource_count=#{@resources.size}, " \
|
37
|
+
"@id_property=#{@id_property.inspect}, " \
|
38
|
+
"@resource_type=#{@resources.first&.resource_name}, " \
|
39
|
+
"@responses_count=#{@responses.size}>"
|
40
|
+
end
|
41
|
+
|
34
42
|
# rubocop:disable Lint/MissingSuper
|
35
|
-
def initialize(resources = [], id_property: 'id')
|
43
|
+
def initialize(resources = [], id_property: 'id', resource_matcher: nil)
|
44
|
+
if resource_matcher
|
45
|
+
unless resource_matcher.is_a?(Proc) && resource_matcher.arity == 2
|
46
|
+
raise ArgumentError, 'resource_matcher must be a proc that accepts exactly 2 arguments'
|
47
|
+
end
|
48
|
+
|
49
|
+
@resource_matcher = resource_matcher
|
50
|
+
end
|
51
|
+
|
36
52
|
@resources = []
|
37
53
|
@id_property = id_property # Set id_property for the batch (default: 'id')
|
38
54
|
@responses = [] # Store multiple BatchResponse objects here
|
@@ -122,9 +138,12 @@ module Hubspot
|
|
122
138
|
next if resource.changes.empty?
|
123
139
|
|
124
140
|
{
|
125
|
-
|
126
|
-
|
127
|
-
|
141
|
+
# Dynamically get the ID based on the batch's id_property
|
142
|
+
id: resource.public_send(@id_property),
|
143
|
+
# Use the helper method to decide whether to include idProperty
|
144
|
+
idProperty: determine_id_property,
|
145
|
+
# Gather the changes for the resource
|
146
|
+
properties: resource.changes
|
128
147
|
}.compact # Removes nil keys
|
129
148
|
end.compact # Removes nil entries
|
130
149
|
end
|
@@ -146,7 +165,8 @@ module Hubspot
|
|
146
165
|
|
147
166
|
# Perform batch request based on the provided action (upsert, update, create, or archive)
|
148
167
|
def batch_request(type, inputs, action)
|
149
|
-
response = self.class.post("/crm/v3/objects/#{type}/batch/#{action}",
|
168
|
+
response = self.class.post("/crm/v3/objects/#{type}/batch/#{action}",
|
169
|
+
body: { inputs: inputs }.to_json)
|
150
170
|
BatchResponse.new(response.code, handle_response(response))
|
151
171
|
end
|
152
172
|
|
@@ -157,7 +177,8 @@ module Hubspot
|
|
157
177
|
# check if there are any resources without a value from the id_property
|
158
178
|
return unless @resources.any? { |resource| resource.public_send(id_property).blank? }
|
159
179
|
|
160
|
-
raise ArgumentError,
|
180
|
+
raise ArgumentError,
|
181
|
+
"All resources must have a non-blank value for #{@id_property} to perform upsert"
|
161
182
|
end
|
162
183
|
|
163
184
|
# Return the appropriate batch size limit for the resource type
|
@@ -167,6 +188,8 @@ module Hubspot
|
|
167
188
|
|
168
189
|
# Process responses from the batch API call
|
169
190
|
def process_responses
|
191
|
+
# TODO: issue a warning if the id_property is email and the action is upsert*
|
192
|
+
# people may have more than one email address abd Hubspot views that as one record
|
170
193
|
@responses.each do |response|
|
171
194
|
next unless response['results']
|
172
195
|
|
@@ -192,36 +215,56 @@ module Hubspot
|
|
192
215
|
end
|
193
216
|
|
194
217
|
def find_resource_from_result(result)
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
end
|
218
|
+
action_method = method_for_action
|
219
|
+
send(action_method, result) if action_method
|
220
|
+
end
|
221
|
+
|
222
|
+
def method_for_action
|
223
|
+
{
|
224
|
+
'create' => :find_resource_for_created_result,
|
225
|
+
'update' => :find_resource_from_updated_result,
|
226
|
+
'upsert' => :find_resource_from_upserted_result
|
227
|
+
}[@action]
|
228
|
+
end
|
229
|
+
|
230
|
+
def find_resource_for_created_result(result)
|
231
|
+
properties = result['properties']
|
232
|
+
|
233
|
+
@resources.reject(&:persisted?).find do |resource|
|
234
|
+
next unless resource.changes.any?
|
235
|
+
|
236
|
+
resource.changes.all? { |key, value| properties[key.to_s] == value }
|
215
237
|
end
|
216
238
|
end
|
217
239
|
|
240
|
+
def find_resource_from_updated_result(result)
|
241
|
+
resource_id = id_property == 'id' ? result['id'].to_i : result.dig('properties', id_property)
|
242
|
+
find_resource_from_id(resource_id)
|
243
|
+
end
|
244
|
+
|
218
245
|
def find_resource_from_id(resource_id)
|
219
|
-
|
246
|
+
return find_resource_from_id_property(resource_id) unless @id_property == 'id'
|
247
|
+
|
248
|
+
@resources.find { |resource| resource.id == resource_id }
|
220
249
|
end
|
221
250
|
|
222
|
-
|
223
|
-
|
224
|
-
|
251
|
+
def find_resource_from_id_property(resource_id)
|
252
|
+
@resources.find do |resource|
|
253
|
+
resource.respond_to?(@id_property) && resource.public_send(@id_property) == resource_id
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def find_resource_from_upserted_result(result)
|
258
|
+
# if this was inserted then match on all the fields
|
259
|
+
return find_resource_for_created_result(result['properties']) if result['new']
|
260
|
+
|
261
|
+
# call the custom resource matcher if specified
|
262
|
+
if @resource_matcher
|
263
|
+
@resources.find { |resource| @resource_matcher.call(resource, result) }
|
264
|
+
else
|
265
|
+
find_resource_from_updated_result(result)
|
266
|
+
end
|
267
|
+
end
|
225
268
|
|
226
269
|
def update_resource_properties(resource, properties)
|
227
270
|
properties.each do |key, value|
|
@@ -238,7 +281,9 @@ module Hubspot
|
|
238
281
|
|
239
282
|
class << self
|
240
283
|
def read(object_class, object_ids = [], id_property: 'id')
|
241
|
-
|
284
|
+
unless object_class < Hubspot::Resource
|
285
|
+
raise ArgumentError, 'Must be a valid Hubspot resource class'
|
286
|
+
end
|
242
287
|
|
243
288
|
# fetch all the matching resources with paging handled
|
244
289
|
resources = object_class.batch_read(object_ids, id_property: id_property).all
|
data/lib/hubspot/config.rb
CHANGED
@@ -15,6 +15,11 @@ module Hubspot
|
|
15
15
|
apply_log_level
|
16
16
|
end
|
17
17
|
|
18
|
+
# Apply the log level to the logger
|
19
|
+
def apply_log_level
|
20
|
+
@logger.level = @log_level
|
21
|
+
end
|
22
|
+
|
18
23
|
private
|
19
24
|
|
20
25
|
# Initialize the default logger
|
@@ -43,11 +48,6 @@ module Hubspot
|
|
43
48
|
end
|
44
49
|
# rubocop:enable Metrics/MethodLength
|
45
50
|
|
46
|
-
# Apply the log level to the logger
|
47
|
-
def apply_log_level
|
48
|
-
@logger.level = @log_level
|
49
|
-
end
|
50
|
-
|
51
51
|
# Set the default log level based on environment
|
52
52
|
def default_log_level
|
53
53
|
if defined?(Rails) && Rails.env.test?
|
data/lib/hubspot/form.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hubspot
|
4
|
+
class Form < Resource
|
5
|
+
METADATA_FIELDS = %w[createdAt updatedAt archived].freeze
|
6
|
+
|
7
|
+
def inspect
|
8
|
+
"#<#{self.class.name} " \
|
9
|
+
"@name=#{name}, " \
|
10
|
+
"@fieldGroups=#{respond_to?('fieldGroups') ? fieldGroups.size : '-'}>"
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def api_root
|
15
|
+
'/marketing/v3'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Extract ID from data and leave as a string
|
22
|
+
def extract_id(data)
|
23
|
+
data.delete('id')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/hubspot/paged_batch.rb
CHANGED
@@ -10,6 +10,15 @@ module Hubspot
|
|
10
10
|
|
11
11
|
MAX_LIMIT = 100 # HubSpot max items per page
|
12
12
|
|
13
|
+
# customised inspect
|
14
|
+
def inspect
|
15
|
+
"#<#{self.class.name} " \
|
16
|
+
"@url=#{@url.inspect}, " \
|
17
|
+
"@params=#{@params.inspect}, " \
|
18
|
+
"@resource_class=#{@resource_class.inspect}, " \
|
19
|
+
"@object_ids_count=#{@object_ids.size}>"
|
20
|
+
end
|
21
|
+
|
13
22
|
# rubocop:disable Lint/MissingSuper
|
14
23
|
def initialize(url:, params: {}, resource_class: nil, object_ids: [])
|
15
24
|
@url = url
|
@@ -22,8 +31,7 @@ module Hubspot
|
|
22
31
|
def each_page
|
23
32
|
@object_ids.each_slice(MAX_LIMIT) do |ids|
|
24
33
|
response = fetch_page(ids)
|
25
|
-
|
26
|
-
mapped_results = @resource_class ? results.map { |result| @resource_class.new(result) } : results
|
34
|
+
mapped_results = process_results(response)
|
27
35
|
yield mapped_results unless mapped_results.empty?
|
28
36
|
end
|
29
37
|
end
|
@@ -45,12 +53,19 @@ module Hubspot
|
|
45
53
|
private
|
46
54
|
|
47
55
|
def fetch_page(object_ids)
|
48
|
-
params_with_ids = @params.dup
|
56
|
+
params_with_ids = @params.dup || {}
|
49
57
|
params_with_ids[:inputs] = object_ids.map { |id| { id: id } }
|
50
58
|
|
51
59
|
response = self.class.post(@url, body: params_with_ids.to_json)
|
52
60
|
|
53
61
|
handle_response(response)
|
54
62
|
end
|
63
|
+
|
64
|
+
def process_results(response)
|
65
|
+
results = response['results'] || []
|
66
|
+
return results unless @resource_class
|
67
|
+
|
68
|
+
results.map { |result| @resource_class.new(result) }
|
69
|
+
end
|
55
70
|
end
|
56
71
|
end
|
@@ -10,21 +10,18 @@ module Hubspot
|
|
10
10
|
|
11
11
|
MAX_LIMIT = 100 # HubSpot max items per page
|
12
12
|
|
13
|
-
# rubocop:disable Lint/MissingSuper
|
14
13
|
def initialize(url:, params: {}, resource_class: nil, method: :get)
|
15
14
|
@url = url
|
16
15
|
@params = params
|
17
16
|
@resource_class = resource_class
|
18
17
|
@method = method.to_sym
|
19
18
|
end
|
20
|
-
# rubocop:enable Lint/MissingSuper
|
21
19
|
|
22
20
|
def each_page
|
23
21
|
offset = nil
|
24
22
|
loop do
|
25
23
|
response = fetch_page(offset)
|
26
|
-
|
27
|
-
mapped_results = @resource_class ? results.map { |result| @resource_class.new(result) } : results
|
24
|
+
mapped_results = process_results(response)
|
28
25
|
yield mapped_results unless mapped_results.empty?
|
29
26
|
offset = response.dig('paging', 'next', 'after')
|
30
27
|
break unless offset
|
@@ -83,5 +80,12 @@ module Hubspot
|
|
83
80
|
self.class.send(@method, @url, body: params.to_json)
|
84
81
|
end
|
85
82
|
end
|
83
|
+
|
84
|
+
def process_results(response)
|
85
|
+
results = response['results'] || []
|
86
|
+
return results unless @resource_class
|
87
|
+
|
88
|
+
results.map { |result| @resource_class.new(result) }
|
89
|
+
end
|
86
90
|
end
|
87
91
|
end
|
data/lib/hubspot/resource.rb
CHANGED
@@ -8,13 +8,14 @@ module Hubspot
|
|
8
8
|
# rubocop:disable Metrics/ClassLength
|
9
9
|
|
10
10
|
# HubSpot Resource Base Class
|
11
|
-
# This class provides common functionality for interacting with
|
11
|
+
# This class provides common functionality for interacting with
|
12
|
+
# HubSpot API resources such as Contacts, Companies, etc
|
12
13
|
#
|
13
|
-
# It supports common operations like finding, creating, updating,
|
14
|
+
# It supports common operations like finding, creating, updating,
|
15
|
+
# and deleting resources, as well as batch operations.
|
14
16
|
#
|
15
|
-
# This class is meant to be inherited by specific resources
|
16
|
-
#
|
17
|
-
# You can access the properties of a resource instance by calling the property name as method
|
17
|
+
# This class is meant to be inherited by specific resources
|
18
|
+
# like `Hubspot::Contact`.
|
18
19
|
#
|
19
20
|
# Example Usage:
|
20
21
|
# Hubspot::Contact.find(1)
|
@@ -49,8 +50,12 @@ module Hubspot
|
|
49
50
|
# contact = Hubspot::Contact.find(1)
|
50
51
|
#
|
51
52
|
# Returns An instance of the resource.
|
52
|
-
def find(id)
|
53
|
-
|
53
|
+
def find(id, properties = nil)
|
54
|
+
all_properties = build_property_list(properties)
|
55
|
+
if all_properties.is_a?(Array) && !all_properties.empty?
|
56
|
+
params = { query: { properties: all_properties } }
|
57
|
+
end
|
58
|
+
response = get("#{api_root}/#{resource_name}/#{id}", params || {})
|
54
59
|
instantiate_from_response(response)
|
55
60
|
end
|
56
61
|
|
@@ -67,8 +72,11 @@ module Hubspot
|
|
67
72
|
# Returns An instance of the resource.
|
68
73
|
def find_by(property, value, properties = nil)
|
69
74
|
params = { idProperty: property }
|
70
|
-
|
71
|
-
|
75
|
+
|
76
|
+
all_properties = build_property_list(properties)
|
77
|
+
params[:properties] = all_properties unless all_properties.empty?
|
78
|
+
|
79
|
+
response = get("#{api_root}/#{resource_name}/#{value}", query: params)
|
72
80
|
instantiate_from_response(response)
|
73
81
|
end
|
74
82
|
|
@@ -81,7 +89,7 @@ module Hubspot
|
|
81
89
|
#
|
82
90
|
# Returns [Resource] The newly created resource.
|
83
91
|
def create(params)
|
84
|
-
response = post("
|
92
|
+
response = post("#{api_root}/#{resource_name}", body: { properties: params }.to_json)
|
85
93
|
instantiate_from_response(response)
|
86
94
|
end
|
87
95
|
|
@@ -93,10 +101,11 @@ module Hubspot
|
|
93
101
|
# Example:
|
94
102
|
# contact.update(1, name: "Jane Doe")
|
95
103
|
#
|
96
|
-
# Returns True if the update was successful
|
104
|
+
# Returns True if the update was successful
|
97
105
|
def update(id, params)
|
98
|
-
response = patch("
|
99
|
-
|
106
|
+
response = patch("#{api_root}/#{resource_name}/#{id}",
|
107
|
+
body: { properties: params }.to_json)
|
108
|
+
handle_response(response)
|
100
109
|
|
101
110
|
true
|
102
111
|
end
|
@@ -108,10 +117,10 @@ module Hubspot
|
|
108
117
|
# Example:
|
109
118
|
# Hubspot::Contact.archive(1)
|
110
119
|
#
|
111
|
-
# Returns True if the deletion was successful
|
120
|
+
# Returns True if the deletion was successful
|
112
121
|
def archive(id)
|
113
|
-
response = delete("
|
114
|
-
|
122
|
+
response = delete("#{api_root}/#{resource_name}/#{id}")
|
123
|
+
handle_response(response)
|
115
124
|
|
116
125
|
true
|
117
126
|
end
|
@@ -125,8 +134,14 @@ module Hubspot
|
|
125
134
|
#
|
126
135
|
# Returns [PagedCollection] A collection of resources.
|
127
136
|
def list(params = {})
|
137
|
+
all_properties = build_property_list(params[:properties])
|
138
|
+
|
139
|
+
if all_properties.is_a?(Array) && !all_properties.empty?
|
140
|
+
params[:properties] = all_properties.join(',')
|
141
|
+
end
|
142
|
+
|
128
143
|
PagedCollection.new(
|
129
|
-
url:
|
144
|
+
url: list_page_uri,
|
130
145
|
params: params,
|
131
146
|
resource_class: self
|
132
147
|
)
|
@@ -142,13 +157,15 @@ module Hubspot
|
|
142
157
|
# Example:
|
143
158
|
# Hubspot::Contact.batch_read([1, 2, 3])
|
144
159
|
#
|
145
|
-
# Returns [PagedBatch] A paged batch of resources
|
146
|
-
def batch_read(object_ids = [], id_property: 'id')
|
147
|
-
params =
|
160
|
+
# Returns [PagedBatch] A paged batch of resources
|
161
|
+
def batch_read(object_ids = [], properties: [], id_property: 'id')
|
162
|
+
params = {}
|
163
|
+
params[:idProperty] = id_property unless id_property == 'id'
|
164
|
+
params[:properties] = properties unless properties.blank?
|
148
165
|
|
149
166
|
PagedBatch.new(
|
150
|
-
url: "
|
151
|
-
params: params,
|
167
|
+
url: "#{api_root}/#{resource_name}/batch/read",
|
168
|
+
params: params.empty? ? nil : params,
|
152
169
|
object_ids: object_ids,
|
153
170
|
resource_class: self
|
154
171
|
)
|
@@ -161,7 +178,7 @@ module Hubspot
|
|
161
178
|
# id_property - The property to use for identifying resources (default: 'id').
|
162
179
|
#
|
163
180
|
# Example:
|
164
|
-
# Hubspot::Contact.
|
181
|
+
# Hubspot::Contact.batch_read_all(hubspot_contact_ids)
|
165
182
|
#
|
166
183
|
# Returns [Hubspot::Batch] A batch of resources that can be operated on further
|
167
184
|
def batch_read_all(object_ids = [], id_property: 'id')
|
@@ -203,9 +220,11 @@ module Hubspot
|
|
203
220
|
#
|
204
221
|
# Example:
|
205
222
|
# property = Hubspot::Contact.property('industry_sector')
|
206
|
-
# values_for_select = property.options.each_with_object({})
|
223
|
+
# values_for_select = property.options.each_with_object({}) do |prop, hash|
|
224
|
+
# hash[prop['value']] = prop['label']
|
225
|
+
# end
|
207
226
|
#
|
208
|
-
# Returns [
|
227
|
+
# Returns [Hubspot::Property] A hubspot property
|
209
228
|
def property(property_name)
|
210
229
|
properties.detect { |prop| prop.name == property_name }
|
211
230
|
end
|
@@ -225,8 +244,10 @@ module Hubspot
|
|
225
244
|
|
226
245
|
# Search for resources using a flexible query format and optional properties.
|
227
246
|
#
|
228
|
-
# This method allows searching for resources by passing a query in the form of a string
|
229
|
-
# or a hash with special suffixes on the keys to
|
247
|
+
# This method allows searching for resources by passing a query in the form of a string
|
248
|
+
# (for full-text search) or a hash with special suffixes on the keys to
|
249
|
+
# define different comparison operators.
|
250
|
+
#
|
230
251
|
# You can also specify which properties to return and the number of results per page.
|
231
252
|
#
|
232
253
|
# Available suffixes for query keys (when using a hash):
|
@@ -244,14 +265,15 @@ module Hubspot
|
|
244
265
|
# - A String: for full-text search.
|
245
266
|
# - A Hash: where each key represents a property and may have suffixes for the comparison
|
246
267
|
# (e.g., `{ email_contains: 'example.org', age_gt: 30 }`).
|
247
|
-
# properties - An optional array of property names to return in the search results.
|
268
|
+
# properties - An optional array of property names to return in the search results.
|
248
269
|
# If not specified or empty, HubSpot will return the default set of properties.
|
249
|
-
# page_size - The number of results to return per page
|
270
|
+
# page_size - The number of results to return per page
|
271
|
+
# (default is 10 for contacts and 100 for everything else).
|
250
272
|
#
|
251
273
|
# Example Usage:
|
252
274
|
# # Full-text search for 'example.org':
|
253
|
-
#
|
254
|
-
#
|
275
|
+
# props = %w[email firstname lastname]
|
276
|
+
# contacts = Hubspot::Contact.search(query: "example.org", properties: props, page_size: 50)
|
255
277
|
#
|
256
278
|
# # Search for contacts whose email contains 'example.org' and are older than 30:
|
257
279
|
# contacts = Hubspot::Contact.search(
|
@@ -282,7 +304,7 @@ module Hubspot
|
|
282
304
|
|
283
305
|
# Perform the search and return a PagedCollection
|
284
306
|
PagedCollection.new(
|
285
|
-
url: "
|
307
|
+
url: "#{api_root}/#{resource_name}/search",
|
286
308
|
params: search_body,
|
287
309
|
resource_class: self,
|
288
310
|
method: :post
|
@@ -291,6 +313,10 @@ module Hubspot
|
|
291
313
|
|
292
314
|
# rubocop:enable Metrics/MethodLength
|
293
315
|
|
316
|
+
# The root of the api call. Mostly this will be "crm"
|
317
|
+
# but you can override this to account for a different
|
318
|
+
# object hierarchy
|
319
|
+
|
294
320
|
# Define the resource name based on the class
|
295
321
|
def resource_name
|
296
322
|
name = self.name.split('::').last.downcase
|
@@ -301,8 +327,22 @@ module Hubspot
|
|
301
327
|
end
|
302
328
|
end
|
303
329
|
|
330
|
+
# List of properties that will always be retrieved
|
331
|
+
# should be overridden in specific resource class
|
332
|
+
def required_properties
|
333
|
+
[]
|
334
|
+
end
|
335
|
+
|
304
336
|
private
|
305
337
|
|
338
|
+
def api_root
|
339
|
+
'/crm/v3/objects'
|
340
|
+
end
|
341
|
+
|
342
|
+
def list_page_uri
|
343
|
+
"#{api_root}/#{resource_name}"
|
344
|
+
end
|
345
|
+
|
306
346
|
# Instantiate a single resource object from the response
|
307
347
|
def instantiate_from_response(response)
|
308
348
|
data = handle_response(response)
|
@@ -337,9 +377,18 @@ module Hubspot
|
|
337
377
|
# Default to 'EQ' operator if no suffix is found
|
338
378
|
{ propertyName: key.to_s, operator: 'EQ' }
|
339
379
|
end
|
380
|
+
|
381
|
+
# Internal make a list of properties to request from the API
|
382
|
+
# will be merged with any required_properties defined on the class
|
383
|
+
def build_property_list(properties)
|
384
|
+
properties = [] unless properties.is_a?(Array)
|
385
|
+
raise 'Must be an array' unless required_properties.is_a?(Array)
|
386
|
+
|
387
|
+
properties.concat(required_properties).uniq
|
388
|
+
end
|
340
389
|
end
|
341
390
|
|
342
|
-
# rubocop:disable
|
391
|
+
# rubocop:disable Lint/MissingSuper
|
343
392
|
|
344
393
|
# Public: Initialize a resouce
|
345
394
|
#
|
@@ -350,13 +399,14 @@ module Hubspot
|
|
350
399
|
# of the object against your own data
|
351
400
|
#
|
352
401
|
# Example:
|
353
|
-
#
|
402
|
+
# attrs = { firstname: 'Luke', lastname: 'Skywalker', email: 'luke@jedi.org' }
|
403
|
+
# contact = Hubspot::Contact.new(attrs)
|
354
404
|
# contact.persisted? # false
|
355
405
|
# contact.save # creates the record in Hubspot
|
356
406
|
# contact.persisted? # true
|
357
407
|
# puts "Contact saved with hubspot id #{contact.id}"
|
358
408
|
#
|
359
|
-
# existing_contact = Hubspot::Contact.new(id: hubspot_id, properties: contact.
|
409
|
+
# existing_contact = Hubspot::Contact.new(id: hubspot_id, properties: contact.to_hubspot)
|
360
410
|
def initialize(data = {})
|
361
411
|
data.transform_keys!(&:to_s)
|
362
412
|
@id = extract_id(data)
|
@@ -368,8 +418,7 @@ module Hubspot
|
|
368
418
|
initialize_new_object(data)
|
369
419
|
end
|
370
420
|
end
|
371
|
-
|
372
|
-
# rubocop:enable Ling/MissingSuper
|
421
|
+
# rubocop:enable Lint/MissingSuper
|
373
422
|
|
374
423
|
# Determine the state of the object
|
375
424
|
#
|
@@ -405,25 +454,44 @@ module Hubspot
|
|
405
454
|
@id ? true : false
|
406
455
|
end
|
407
456
|
|
408
|
-
# Update the resource
|
457
|
+
# Public - Update the resource and persist to the api
|
409
458
|
#
|
410
|
-
#
|
459
|
+
# attributes - hash of properties to update in key value pairs
|
411
460
|
#
|
412
461
|
# Example:
|
413
462
|
# contact = Hubspot::Contact.find(hubspot_contact_id)
|
414
463
|
# contact.update(status: 'gold customer', last_contacted_at: Time.now.utc.iso8601)
|
415
464
|
#
|
416
465
|
# Returns Boolean
|
417
|
-
def update(
|
466
|
+
def update(attributes)
|
418
467
|
raise 'Not able to update as not persisted' unless persisted?
|
419
468
|
|
420
|
-
|
421
|
-
send("#{key}=", value) # This will trigger the @changes tracking via method_missing
|
422
|
-
end
|
469
|
+
update_attributes(attributes)
|
423
470
|
|
424
471
|
save
|
425
472
|
end
|
426
473
|
|
474
|
+
# Public - Update resource attributes
|
475
|
+
#
|
476
|
+
# Does not persist to the api but processes each attribute correctly
|
477
|
+
#
|
478
|
+
# Example:
|
479
|
+
# contact = Hubspot::Contact.find(hubspot_contact_id)
|
480
|
+
# contact.changes? # false
|
481
|
+
# contact.update_attributes(education: 'Graduate', university: 'Life')
|
482
|
+
# contact.education # Graduate
|
483
|
+
# contact.changes? # true
|
484
|
+
# contact.changes # { "education" => "Graduate", "university" => "Life" }
|
485
|
+
#
|
486
|
+
# Returns Hash of changes
|
487
|
+
def update_attributes(attributes)
|
488
|
+
raise ArgumentError, 'must be a hash' unless attributes.is_a?(Hash)
|
489
|
+
|
490
|
+
attributes.each do |key, value|
|
491
|
+
send("#{key}=", value) # This will trigger the @changes tracking via method_missing
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
427
495
|
# Archive the object in Hubspot
|
428
496
|
#
|
429
497
|
# Example:
|
@@ -488,9 +556,17 @@ module Hubspot
|
|
488
556
|
|
489
557
|
# Initialize from API response, separating metadata from properties
|
490
558
|
def initialize_from_api(data)
|
491
|
-
|
492
|
-
|
559
|
+
if data['properties']
|
560
|
+
@metadata = data.reject { |key, _v| key == 'properties' }
|
561
|
+
handle_properties(data['properties'])
|
562
|
+
else
|
563
|
+
handle_properties(data)
|
564
|
+
end
|
493
565
|
|
566
|
+
@changes = {}
|
567
|
+
end
|
568
|
+
|
569
|
+
def handle_properties(properties_data)
|
494
570
|
properties_data.each do |key, value|
|
495
571
|
if METADATA_FIELDS.include?(key)
|
496
572
|
@metadata[key] = value
|
@@ -498,8 +574,6 @@ module Hubspot
|
|
498
574
|
@properties[key] = value
|
499
575
|
end
|
500
576
|
end
|
501
|
-
|
502
|
-
@changes = {}
|
503
577
|
end
|
504
578
|
|
505
579
|
# Initialize a new object (no API response)
|
data/lib/hubspot/user.rb
CHANGED
@@ -1,8 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Hubspot
|
4
|
+
# ORM for hubspot users
|
5
|
+
#
|
6
|
+
# Hubspot users consist mostly of read_only attributes (you can add custom properties).
|
7
|
+
# As such we extend this class to ensure that we retrieve useful data back from the API
|
8
|
+
# and provide helper methods to resolve hubspot fields e.g. user.email calls user.hs_email etc
|
4
9
|
class User < Resource
|
10
|
+
class << self
|
11
|
+
def required_properties
|
12
|
+
%w[hs_email hs_given_name hs_family_name]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def first_name
|
17
|
+
hs_given_name
|
18
|
+
end
|
19
|
+
alias firstname first_name
|
20
|
+
|
21
|
+
def last_name
|
22
|
+
hs_family_name
|
23
|
+
end
|
24
|
+
alias lastname last_name
|
25
|
+
|
26
|
+
def email
|
27
|
+
hs_email
|
28
|
+
end
|
5
29
|
end
|
6
30
|
|
7
|
-
Owner = User
|
31
|
+
Owner = User
|
8
32
|
end
|
data/lib/hubspot/version.rb
CHANGED
data/lib/hubspot.rb
CHANGED
data/lib/ruby_hubspot_api.rb
CHANGED
@@ -12,13 +12,18 @@ require_relative 'hubspot/config'
|
|
12
12
|
require_relative 'hubspot/exceptions'
|
13
13
|
require_relative 'hubspot/api_client'
|
14
14
|
|
15
|
-
# load base class then
|
15
|
+
# load base class then models
|
16
16
|
require_relative 'hubspot/resource'
|
17
17
|
require_relative 'hubspot/property'
|
18
|
+
|
19
|
+
# load CRM models
|
18
20
|
require_relative 'hubspot/contact'
|
19
21
|
require_relative 'hubspot/company'
|
20
22
|
require_relative 'hubspot/user'
|
21
23
|
|
24
|
+
# load marketing models
|
25
|
+
require_relative 'hubspot/form'
|
26
|
+
|
22
27
|
# Load other components
|
23
28
|
require_relative 'hubspot/batch'
|
24
29
|
require_relative 'hubspot/paged_collection'
|
data/lib/support/patches.rb
CHANGED
@@ -9,3 +9,30 @@ class Object
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
12
|
+
|
13
|
+
# At some point this will seem like a bad idea ;)
|
14
|
+
|
15
|
+
# :nocov:
|
16
|
+
if RUBY_VERSION < '2.5.0'
|
17
|
+
class Hash
|
18
|
+
# Non-mutating version (returns a new hash with transformed keys)
|
19
|
+
def transform_keys
|
20
|
+
return enum_for(:transform_keys) unless block_given?
|
21
|
+
result = {}
|
22
|
+
each_key do |key|
|
23
|
+
result[yield(key)] = self[key]
|
24
|
+
end
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
# Mutating version (modifies the hash in place)
|
29
|
+
def transform_keys!
|
30
|
+
return enum_for(:transform_keys!) unless block_given?
|
31
|
+
keys.each do |key|
|
32
|
+
self[yield(key)] = delete(key)
|
33
|
+
end
|
34
|
+
self
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
# :nocov:end
|
data/ruby_hubspot_api.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.homepage = 'https://github.com/sensadrome/ruby_hubspot_api'
|
17
17
|
spec.license = 'MIT'
|
18
18
|
|
19
|
-
spec.required_ruby_version = '>= 2.
|
19
|
+
spec.required_ruby_version = '>= 2.4'
|
20
20
|
|
21
21
|
# Prevent pushing this gem to RubyGems.org.
|
22
22
|
# To allow pushes either set the 'allowed_push_host'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_hubspot_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon Brook
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-10-
|
11
|
+
date: 2024-10-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -223,6 +223,7 @@ files:
|
|
223
223
|
- lib/hubspot/config.rb
|
224
224
|
- lib/hubspot/contact.rb
|
225
225
|
- lib/hubspot/exceptions.rb
|
226
|
+
- lib/hubspot/form.rb
|
226
227
|
- lib/hubspot/paged_batch.rb
|
227
228
|
- lib/hubspot/paged_collection.rb
|
228
229
|
- lib/hubspot/property.rb
|
@@ -246,7 +247,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
246
247
|
requirements:
|
247
248
|
- - ">="
|
248
249
|
- !ruby/object:Gem::Version
|
249
|
-
version: '2.
|
250
|
+
version: '2.4'
|
250
251
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
251
252
|
requirements:
|
252
253
|
- - ">="
|