ruby_hubspot_api 0.1.2.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -1
- data/Gemfile.lock +9 -9
- data/README.md +252 -23
- data/lib/hubspot/api_client.rb +40 -8
- data/lib/hubspot/batch.rb +252 -0
- data/lib/hubspot/config.rb +2 -1
- data/lib/hubspot/paged_batch.rb +56 -0
- data/lib/hubspot/paged_collection.rb +3 -18
- data/lib/hubspot/resource.rb +28 -7
- data/lib/hubspot/version.rb +1 -1
- data/lib/hubspot.rb +14 -0
- data/lib/ruby_hubspot_api.rb +3 -0
- data/lib/support/patches.rb +11 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67b58c873d1e9277df15b213a804afe47189c888aab565963950bf9992050e07
|
4
|
+
data.tar.gz: 62028c4b6b0a2ac9a9a29e88cf4762cbbdb909942415d1bc2bf02e3838b405eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 98ec373c58ef3aa92eb963d2ab567bd41204bd1c80552e0abacf4cadc1a6860d5b227dde78f5fbfe8b6d3cdff65536390116f468492c6f639e87c1a83f4e60ff
|
7
|
+
data.tar.gz: 5bea871875782b5fd35ff62de85c11da16f7e763a88972ac3d6cbca3e6b700eca589eed7933058434d9c6574079b71fd5e2d0aabc8fcc3be288dd27d5a9ae405
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,29 @@
|
|
1
|
+
## v.0.2.0
|
1
2
|
|
2
|
-
|
3
|
+
- Get the development dependencies right!
|
4
|
+
- Bump the version again
|
5
|
+
- describe find_by method
|
6
|
+
- update lock
|
7
|
+
- batch :sparkle: upsert spec
|
8
|
+
- Borrowing Object#blank? method cos it actually really helps...
|
9
|
+
- batch implemntation
|
10
|
+
- logger.debug the post body and response body
|
11
|
+
- Adds a changes? Method on resource
|
12
|
+
- Adds instance method resource_name on resource
|
13
|
+
- Ensure keys are stringified
|
14
|
+
- Add all end points to the batch spec
|
15
|
+
- Adds create and archive methods to batches
|
16
|
+
- Tidy up resource code
|
17
|
+
- Cover the previously nocov'd code
|
18
|
+
- Add api client logging spec
|
19
|
+
- add configurable timeouts to requests
|
20
|
+
- Move rate limit handling to the client
|
21
|
+
- Simplify mocked responses in batch spec
|
22
|
+
- Adds PagedBatch as pager for batch/read request
|
23
|
+
- Update the Readme to add Batch operations
|
24
|
+
- #5 batch_updating
|
25
|
+
|
26
|
+
## v0.1.2
|
3
27
|
|
4
28
|
- initial setup
|
5
29
|
- Setup the configuration block
|
@@ -46,6 +70,17 @@
|
|
46
70
|
- adds the version numbers to the gemspec
|
47
71
|
- Fix dependencies
|
48
72
|
- bump version
|
73
|
+
- Fix the Readme
|
74
|
+
- Sure the search param is values where passing an array
|
75
|
+
- update changelog and Gemfile.lock
|
76
|
+
- bump version
|
77
|
+
|
78
|
+
## v0.1.1
|
79
|
+
|
80
|
+
- Fix the Readme
|
81
|
+
- Sure the search param is values where passing an array
|
82
|
+
- update changelog and Gemfile.lock
|
83
|
+
- bump version
|
49
84
|
|
50
85
|
## v0.1.0
|
51
86
|
|
data/Gemfile.lock
CHANGED
@@ -1,16 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ruby_hubspot_api (0.1.1)
|
5
|
-
bundler (>= 2.0)
|
6
|
-
dotenv (>= 2.0)
|
4
|
+
ruby_hubspot_api (0.1.2.1)
|
7
5
|
httparty (>= 0.1, < 1.0)
|
8
|
-
pry (>= 0.1)
|
9
|
-
pry-byebug (>= 3.0)
|
10
|
-
rspec (>= 3.0)
|
11
|
-
simplecov (>= 0.22, < 1.0)
|
12
|
-
vcr (>= 6.0)
|
13
|
-
webmock (>= 3.0)
|
14
6
|
|
15
7
|
GEM
|
16
8
|
remote: https://rubygems.org/
|
@@ -71,8 +63,16 @@ PLATFORMS
|
|
71
63
|
x86_64-darwin-18
|
72
64
|
|
73
65
|
DEPENDENCIES
|
66
|
+
bundler (>= 2.0)
|
67
|
+
dotenv (>= 2.0)
|
68
|
+
pry (>= 0.1)
|
69
|
+
pry-byebug (>= 3.0)
|
74
70
|
rake (>= 11.0, < 14.0)
|
71
|
+
rspec (>= 3.0)
|
75
72
|
ruby_hubspot_api!
|
73
|
+
simplecov (>= 0.22, < 1.0)
|
74
|
+
vcr (>= 6.0)
|
75
|
+
webmock (>= 3.0)
|
76
76
|
|
77
77
|
BUNDLED WITH
|
78
78
|
2.3.26
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
This gem was largely inspired by [hubspot-api-ruby](https://github.com/captaincontrat/hubspot-api-ruby) which, in turn, was inspired by the [hubspot-ruby](https://github.com/HubspotCommunity/hubspot-ruby) community gem. I wanted to use version 3 of the api and simplify some parts of the interface
|
4
4
|
|
5
|
-
The Ruby HubSpot API gem is a starting point for building an ORM-like interface to HubSpot's API.
|
5
|
+
The Ruby HubSpot API gem is a starting point for building an ORM-like interface to HubSpot's API.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
@@ -40,13 +40,13 @@ This configuration ensures that your API requests are authenticated using your H
|
|
40
40
|
|
41
41
|
## Working with Objects
|
42
42
|
|
43
|
-
|
43
|
+
_(N.B when referring to the resources in the api we will refer to them as Objects as per the Hubspot nomenclature)_
|
44
44
|
|
45
|
-
|
45
|
+
This gem allows you to interact with HubSpot objects such as contacts and companies. You can perform operations on individual instances (e.g., creating or updating records) as well as on collections (e.g., listing or searching).
|
46
46
|
|
47
|
-
|
47
|
+
### Creating and Saving an Object
|
48
48
|
|
49
|
-
|
49
|
+
Create and instance of the resource passing a hash of properties. Calling `save` on the instance will persist the object to the HubSpot API, as well as set the id property (which you can then store in your own database for example)
|
50
50
|
|
51
51
|
Example:
|
52
52
|
|
@@ -60,44 +60,96 @@ new_contact.save
|
|
60
60
|
puts "New contact ID: #{new_contact.id}"
|
61
61
|
```
|
62
62
|
|
63
|
-
|
63
|
+
### Retreiving an Object
|
64
|
+
|
65
|
+
If you know the id of the object you can fetch it from the api using the find method
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
contact = Hubspot::Contact.find(1)
|
69
|
+
puts "Contact: #{contact.firstname} #{contact.lastname}"
|
70
|
+
```
|
71
|
+
|
72
|
+
You can also retrieve a single object by using the `find_by` method. Simply specify the property and the value you want to search on:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
# find by email
|
76
|
+
contact = Hubspot::Contact.find_by('email', 'john.doe@example.org')
|
77
|
+
puts "Contact: #{contact.firstname} #{contact.lastname}"
|
64
78
|
|
65
|
-
|
79
|
+
#find by internal id (custom field)
|
80
|
+
contact = Hubspot::Contact.find_by('member_id', 123)
|
81
|
+
puts "Contact: #{contact.firstname} #{contact.lastname}"
|
82
|
+
```
|
83
|
+
|
84
|
+
### Updating an Existing Object
|
85
|
+
|
86
|
+
To update an existing object, you can either modify the object and call `save`, or use the `update` method specifying the properties you want to update. You can test whether or not the object will need to upload changes to the api by using the changes? method
|
66
87
|
|
67
88
|
Example using `save`:
|
68
89
|
|
69
90
|
```ruby
|
70
91
|
contact = Hubspot::Contact.find(1)
|
92
|
+
contact.changes? # false
|
93
|
+
|
71
94
|
contact.lastname = 'DoeUpdated'
|
95
|
+
contact.changes? # true
|
96
|
+
|
97
|
+
# save the updates to Hubspot
|
72
98
|
contact.save # true
|
99
|
+
contact.changes? # false
|
73
100
|
```
|
74
101
|
|
75
102
|
Example using `update`:
|
76
103
|
|
77
104
|
```ruby
|
78
105
|
contact = Hubspot::Contact.find(1)
|
106
|
+
# save the updates to Hubspot
|
79
107
|
contact.update(lastname: 'DoeUpdated') # true
|
80
108
|
```
|
81
109
|
|
82
|
-
|
110
|
+
If you are able to construct an Object with data stored locally you can save the inital `find` api call, but you will need to construct the persisted object specifying the id and a properties hash (as if it came from the api!)
|
111
|
+
|
112
|
+
Example:
|
83
113
|
|
84
|
-
|
114
|
+
```ruby
|
115
|
+
local_contact = Contact.find(contact_id)
|
85
116
|
|
86
|
-
|
117
|
+
hubspot_properties = {
|
118
|
+
firstname: local_contact.first_name,
|
119
|
+
lastname: local_contact.last_name,
|
120
|
+
email: local_contact.email,
|
121
|
+
# ... more properties...
|
122
|
+
}
|
123
|
+
|
124
|
+
hubspot_contact = Hubspot::Contact.new(id: contact.hs_object_id, properties: hubspot_properties )
|
125
|
+
hubspot_contact.changes? # false
|
126
|
+
|
127
|
+
# update a custom field "last_contacted"
|
128
|
+
# (in the hubspot_contact instance this will be stored in the changes property)
|
129
|
+
hubspot_contact.last_contacted = Time.now.utc.iso8601
|
130
|
+
hubspot_contact.changes? # true
|
131
|
+
|
132
|
+
# persist the change to hubspot
|
133
|
+
hubspot_contact.save #true
|
134
|
+
```
|
135
|
+
|
136
|
+
### Listing Objects
|
137
|
+
|
138
|
+
You can list all objects (such as contacts) using the `list` method, which returns a `PagedCollection`. This collection handles paginated results and is Enumerable, so responds to methods like `each_page`, `each`, and `all`. You can also pass the `page_size` parameter to control the number of records returned per page.
|
87
139
|
|
88
140
|
Example:
|
89
141
|
|
90
142
|
```ruby
|
91
143
|
contacts = Hubspot::Contact.list(page_size: 10)
|
92
144
|
|
93
|
-
# Using each_page to iterate over pages
|
145
|
+
# Using each_page to iterate over pages, there will be up to 10 contacts per page
|
94
146
|
contacts.each_page do |page|
|
95
147
|
page.each do |contact|
|
96
148
|
puts "Contact: #{contact.firstname} #{contact.lastname}"
|
97
149
|
end
|
98
150
|
end
|
99
151
|
|
100
|
-
# Or iterate over all contacts
|
152
|
+
# Or iterate over all contacts and the pagination will be handled transparently (page_size still applies)
|
101
153
|
contacts.each do |contact|
|
102
154
|
puts "Contact: #{contact.firstname} #{contact.lastname}"
|
103
155
|
end
|
@@ -106,23 +158,42 @@ end
|
|
106
158
|
all_contacts = contacts.all
|
107
159
|
```
|
108
160
|
|
109
|
-
#### Retrieving the first n items
|
161
|
+
#### Retrieving the first n items:
|
110
162
|
|
111
|
-
You can
|
163
|
+
As mentioned the list method returns a PagedCollection. You can call `first` on the result to retrieve the first item or a specified number of items:
|
112
164
|
|
113
165
|
```ruby
|
114
|
-
|
166
|
+
contacts_collection = Hubspot::Contact.list
|
115
167
|
|
116
168
|
# Retrieve the first contact
|
117
|
-
first_contact =
|
169
|
+
first_contact = contacts_collection.first
|
118
170
|
|
119
171
|
# Retrieve the first 5 contacts
|
120
|
-
first_five_contacts =
|
172
|
+
first_five_contacts = contacts_collection.first(5)
|
121
173
|
```
|
122
174
|
|
123
175
|
This will automatically set the limits and handle paging for the most efficient API calls while honouring the maximum page count for hubspot resources
|
124
176
|
|
125
|
-
####
|
177
|
+
#### Specifying properties
|
178
|
+
|
179
|
+
By default Hubspot will only send back the [default hubspot properties](https://knowledge.hubspot.com/properties/hubspots-default-contact-properties)
|
180
|
+
|
181
|
+
You can pass an array of properties to be return as follows:
|
182
|
+
|
183
|
+
Example:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
# Search for contacts with email containing "hubspot.com" and only return specific properties
|
187
|
+
contacts = Hubspot::Contact.list(
|
188
|
+
properties: ['firstname', 'lastname', 'email', 'mobile', 'custom_property_1']
|
189
|
+
)
|
190
|
+
|
191
|
+
contacts.each do |contact|
|
192
|
+
puts "Name: #{contact.firstname} #{contact.lastname}, Email: #{contact.email}, Mobile: #{contact.mobile} CustomerRef: #{contact.custom_property_1}"
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
196
|
+
### Searching
|
126
197
|
|
127
198
|
You can search for objects by passing query parameters to the `search` method. HubSpot supports several operators such as `eq`, `gte`, `lt`, and `IN` for filtering.
|
128
199
|
|
@@ -132,15 +203,21 @@ Example:
|
|
132
203
|
# Search for contacts with email containing "hubspot.com"
|
133
204
|
contacts = Hubspot::Contact.search(query: { email_contains: 'hubspot.com' })
|
134
205
|
|
206
|
+
puts "Searching for Hubspot staff in the contacts CRM"
|
207
|
+
puts ""
|
208
|
+
|
135
209
|
contacts.each do |contact|
|
136
|
-
puts "Found: #{contact.email}"
|
210
|
+
puts " Found: #{contact.firstname} #{contact.lastname} (#{contact.email})"
|
137
211
|
end
|
138
212
|
|
139
213
|
# Search for companies with number of employees greater than or equal to 100
|
140
214
|
companies = Hubspot::Company.search(query: { number_of_employees_gte: 100 })
|
141
215
|
|
216
|
+
puts "Searching for medium to large companies"
|
217
|
+
puts ""
|
218
|
+
|
142
219
|
companies.each do |company|
|
143
|
-
puts "Found: #{company.name}
|
220
|
+
puts " Found: #{company.name} (#{company.number_of_employees} employees)"
|
144
221
|
end
|
145
222
|
|
146
223
|
# Search for contacts with email in a specific list (IN operator)
|
@@ -152,15 +229,18 @@ end
|
|
152
229
|
```
|
153
230
|
|
154
231
|
### Available Search Operators:
|
155
|
-
- **
|
232
|
+
- **contains**: contains <string>
|
156
233
|
- **neq**: Not equal to.
|
234
|
+
- **gt**: Greater than.
|
157
235
|
- **gte**: Greater than or equal to.
|
236
|
+
- **lt**: Less than.
|
158
237
|
- **lte**: Less than or equal to.
|
159
238
|
- **IN**: Matches any of the values in an array.
|
160
239
|
|
161
240
|
#### Specifying Properties in Search
|
162
241
|
|
163
|
-
When performing a search, you can also specify which properties to return.
|
242
|
+
When performing a search, you can also specify which properties to return.
|
243
|
+
*NB* If you specify any properties, you will only get those properties back, and the default HubSpot properties will not be included automatically.
|
164
244
|
|
165
245
|
Example:
|
166
246
|
|
@@ -172,10 +252,159 @@ contacts = Hubspot::Contact.search(
|
|
172
252
|
)
|
173
253
|
|
174
254
|
contacts.each do |contact|
|
175
|
-
puts "Name: #{contact.firstname} #{contact.lastname}, Email: #{contact.email}, Mobile: #{contact.mobile}"
|
255
|
+
puts "Name: #{contact.firstname} #{contact.lastname}, Email: #{contact.email}, Mobile: #{contact.mobile} CustomerRef: #{contact.custom_property_1}"
|
256
|
+
end
|
257
|
+
```
|
258
|
+
|
259
|
+
## Working with batches
|
260
|
+
|
261
|
+
### Hubspot::Batch
|
262
|
+
|
263
|
+
The `Hubspot::Batch` class allows you to perform batch operations on HubSpot resources, such as contacts or companies. This includes batch `create`, `read`, `update`, `upsert`, and `archive` operations. Below are examples of how to use these methods.
|
264
|
+
|
265
|
+
#### Batch Create
|
266
|
+
|
267
|
+
To create new resources in bulk, you can use the `create` method.
|
268
|
+
|
269
|
+
In this example, `batch.create` triggers the creation of new contacts. After creation, the batch response will include the new IDs assigned to each object by HubSpot which will be assigned to the resources in the batch
|
270
|
+
|
271
|
+
```ruby
|
272
|
+
contacts = [
|
273
|
+
Hubspot::Contact.new(email: 'new.john@example.com', firstname: 'John', lastname: 'Doe'),
|
274
|
+
Hubspot::Contact.new(email: 'new.jane@example.com', firstname: 'Jane', lastname: 'Doe')
|
275
|
+
]
|
276
|
+
|
277
|
+
batch = Hubspot::Batch.new(contacts)
|
278
|
+
batch.create
|
279
|
+
|
280
|
+
batch.resources.each do |contact|
|
281
|
+
hubspot_id = contact.id
|
282
|
+
# store hubspot_id against a contact....
|
176
283
|
end
|
177
284
|
```
|
178
285
|
|
286
|
+
|
287
|
+
#### Batch Read
|
288
|
+
|
289
|
+
To read a batch of Objects by their internal hubspot id or by another uniq property you can use the `read` method. You will need to pass the class of the resource, an array of ids and optionally an id_property.
|
290
|
+
|
291
|
+
For simplicity you can also use the `batch_read` method of the corresponding class (e.g. Hubspot::Contacts, Hubspot::Company etc) passing an array of ids and optionally an id_property (defaults to 'id'). This method will return a Hubspot::Batch and the results will be in "resources"
|
292
|
+
|
293
|
+
Example using `read` along with the Hubspot id of several companies...
|
294
|
+
|
295
|
+
```ruby
|
296
|
+
# Grab an array of hubspot company ids to read from the api...
|
297
|
+
company_ids = my_companies.collect(&:hubspot_id).compact
|
298
|
+
|
299
|
+
# this will grab all the results from the api handling paging automagically
|
300
|
+
batch = Hubspot::Batch.read(Hubspot::Company, company_ids)
|
301
|
+
companies = batch.resources
|
302
|
+
|
303
|
+
# Or using the domain field
|
304
|
+
# Grab an array of company domains
|
305
|
+
company_domains = my_companies.collect(&:domain_name).compact
|
306
|
+
|
307
|
+
# calls /crm/v3/objects/companies/batch/read
|
308
|
+
batch = Hubspot::Batch.read(Hubspot::Company, company_domains, id_property: 'domain')
|
309
|
+
companies = batch.resources
|
310
|
+
```
|
311
|
+
|
312
|
+
Example of reading contacts by email and the helper method `batch_read`
|
313
|
+
By using this method you can page through the results as needed or collect them
|
314
|
+
|
315
|
+
```ruby
|
316
|
+
email_addresses = my_selected_contacts.collect(&:email).compact
|
317
|
+
|
318
|
+
batch = Hubspot::Contact.batch_read(email_addresses, id_property: 'email')
|
319
|
+
|
320
|
+
batch.each_page do |contacts|
|
321
|
+
contacts.each do |contact|
|
322
|
+
# persist some data locally
|
323
|
+
update_local_contact_from_hubspot(contact)
|
324
|
+
end
|
325
|
+
# stop the api calls if a condition is met
|
326
|
+
# break if <condition>
|
327
|
+
end
|
328
|
+
```
|
329
|
+
|
330
|
+
Finally there is another helper method on re
|
331
|
+
|
332
|
+
#### Batch Update
|
333
|
+
|
334
|
+
For updating existing resources in bulk, you can use the `update` method. If you want to locally create an object without calling the API you specify the id and pass any properties in the 'properties' hash (this is how the objects are returned from Hubspot)
|
335
|
+
|
336
|
+
```ruby
|
337
|
+
contacts = [
|
338
|
+
Hubspot::Contact.new(id: 1, properties: { firstname: 'John', lastname: 'Doe' }),
|
339
|
+
Hubspot::Contact.new(id: 2, properties: { firstname: 'Jane', lastname: 'Doe' })
|
340
|
+
]
|
341
|
+
|
342
|
+
# make a changes to each contact
|
343
|
+
contacts.each { |contact| contact.last_contacted = Time.now.utc.iso8601 }
|
344
|
+
|
345
|
+
batch = Hubspot::Batch.new(contacts)
|
346
|
+
batch.update
|
347
|
+
```
|
348
|
+
|
349
|
+
Example using a batch
|
350
|
+
```ruby
|
351
|
+
contact_ids = my_contacts.collect(&:hubspot_id).compact
|
352
|
+
batch = Hubspot::contacts.batch_read(contact_ids)
|
353
|
+
|
354
|
+
batch.resources.each do |hubspot_contact|
|
355
|
+
my_contact = my_contacts.find { |c| c.hubspot_id == hubspot_contact.id }
|
356
|
+
# some logic or method to set any new/changed properties on hubspot_contact
|
357
|
+
update_hubspot_contact_from_local(hubspot_contact, my_contact)
|
358
|
+
end
|
359
|
+
|
360
|
+
# now we have a batch with changed resources we can update the batch
|
361
|
+
batch.update # true
|
362
|
+
```
|
363
|
+
|
364
|
+
#### Batch Upsert
|
365
|
+
|
366
|
+
The `upsert` method allows you to insert new records or update existing ones. You’ll need to specify an `id_property` (like `email`) to uniquely identify records
|
367
|
+
|
368
|
+
```ruby
|
369
|
+
contacts = [
|
370
|
+
Hubspot::Contact.new(email: 'new.john@example.com', firstname: 'John', lastname: 'Doe'),
|
371
|
+
Hubspot::Contact.new(email: 'new.jane@example.com', firstname: 'Jane', lastname: 'Doe')
|
372
|
+
]
|
373
|
+
|
374
|
+
batch = Hubspot::Batch.new(contacts, id_property: 'email')
|
375
|
+
batch.upsert
|
376
|
+
```
|
377
|
+
|
378
|
+
In this example, if a contact with the given email already exists in HubSpot, it will be updated. If it doesn't, a new contact will be created.
|
379
|
+
|
380
|
+
#### Batch Archive
|
381
|
+
|
382
|
+
To archive objects in bulk, you can use the `archive` method. This removes the objects from HubSpot.
|
383
|
+
|
384
|
+
```ruby
|
385
|
+
contacts = Hubspot::Contact.search(query: { email_contains: 'hubspot.com' }).all
|
386
|
+
|
387
|
+
batch = Hubspot::Batch.new(contacts)
|
388
|
+
batch.archive
|
389
|
+
```
|
390
|
+
|
391
|
+
The `archive` method sends a batch request to HubSpot to archive the objects. If any of the objects fail to be archived, you can check for partial success using the `partial_success?` method.
|
392
|
+
|
393
|
+
#### Error Handling and Success Checks
|
394
|
+
|
395
|
+
You can check whether the batch operation was entirely successful, partially successful, or if any failures occurred:
|
396
|
+
|
397
|
+
```ruby
|
398
|
+
if batch.all_successful?
|
399
|
+
puts "All resources were successfully processed."
|
400
|
+
elsif batch.partial_success?
|
401
|
+
puts "Some resources were successfully processed, but others failed."
|
402
|
+
else
|
403
|
+
puts "The batch operation failed."
|
404
|
+
end
|
405
|
+
```
|
406
|
+
|
407
|
+
|
179
408
|
## Contributing
|
180
409
|
|
181
410
|
There is much to do (including writing a TODO list, or at least adding issues in github!) but this should provide a solid start
|
data/lib/hubspot/api_client.rb
CHANGED
@@ -3,6 +3,9 @@
|
|
3
3
|
module Hubspot
|
4
4
|
# All interations with the Hubspot API happen here...
|
5
5
|
class ApiClient
|
6
|
+
MAX_RETRIES = 3
|
7
|
+
RETRY_WAIT_TIME = 1 # seconds
|
8
|
+
|
6
9
|
include HTTParty
|
7
10
|
base_uri 'https://api.hubapi.com'
|
8
11
|
|
@@ -32,7 +35,7 @@ module Hubspot
|
|
32
35
|
ensure_configuration!
|
33
36
|
start_time = Time.now
|
34
37
|
response = super(url, options)
|
35
|
-
log_request(:post, url, response, start_time)
|
38
|
+
log_request(:post, url, response, start_time, options)
|
36
39
|
response
|
37
40
|
end
|
38
41
|
|
@@ -40,7 +43,7 @@ module Hubspot
|
|
40
43
|
ensure_configuration!
|
41
44
|
start_time = Time.now
|
42
45
|
response = super(url, options)
|
43
|
-
log_request(:patch, url, response, start_time)
|
46
|
+
log_request(:patch, url, response, start_time, options)
|
44
47
|
response
|
45
48
|
end
|
46
49
|
|
@@ -52,23 +55,52 @@ module Hubspot
|
|
52
55
|
response
|
53
56
|
end
|
54
57
|
|
55
|
-
def log_request(http_method, url, response, start_time)
|
58
|
+
def log_request(http_method, url, response, start_time, extra = nil)
|
56
59
|
d = Time.now - start_time
|
57
60
|
Hubspot.logger.info("#{http_method.to_s.upcase} #{url} took #{d.round(2)}s with status #{response.code}")
|
58
|
-
|
61
|
+
return unless Hubspot.logger.debug?
|
62
|
+
|
63
|
+
Hubspot.logger.debug("Request body: #{extra}") if extra
|
64
|
+
Hubspot.logger.debug("Response body: #{response.body}")
|
59
65
|
end
|
60
66
|
|
61
|
-
def handle_response(response)
|
62
|
-
|
67
|
+
def handle_response(response, retries = 0)
|
68
|
+
case response.code
|
69
|
+
when 200..299
|
63
70
|
response.parsed_response
|
71
|
+
when 429
|
72
|
+
handle_rate_limit(response, retries)
|
64
73
|
else
|
65
|
-
|
66
|
-
raise Hubspot.error_from_response(response)
|
74
|
+
log_and_raise_error(response)
|
67
75
|
end
|
68
76
|
end
|
69
77
|
|
70
78
|
private
|
71
79
|
|
80
|
+
def handle_rate_limit(response, retries)
|
81
|
+
if retries < MAX_RETRIES
|
82
|
+
retry_after = response.headers['Retry-After']&.to_i || RETRY_WAIT_TIME
|
83
|
+
Hubspot.logger.warn("Rate limit hit. Retrying in #{retry_after} seconds...")
|
84
|
+
sleep(retry_after)
|
85
|
+
retry_request(response.request, retries + 1)
|
86
|
+
else
|
87
|
+
Hubspot.logger.error('Exceeded maximum retries for rate-limited request.')
|
88
|
+
raise Hubspot.error_from_response(response)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def retry_request(request, retries)
|
93
|
+
# Re-issues the original request using the retry logic
|
94
|
+
http_method = request.http_method::METHOD.downcase # Use the METHOD constant to get the method string
|
95
|
+
response = HTTParty.send(http_method, request.uri, request.options)
|
96
|
+
handle_response(response, retries)
|
97
|
+
end
|
98
|
+
|
99
|
+
def log_and_raise_error(response)
|
100
|
+
Hubspot.logger.error("API Error: #{response.code} - #{response.body}")
|
101
|
+
raise Hubspot.error_from_response(response)
|
102
|
+
end
|
103
|
+
|
72
104
|
def ensure_configuration!
|
73
105
|
raise NotConfiguredError, 'Hubspot API not configured' unless Hubspot.configured?
|
74
106
|
end
|
@@ -0,0 +1,252 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
module Hubspot
|
6
|
+
# exactly the same as a parsed_response but with the status code preserved
|
7
|
+
class BatchResponse < SimpleDelegator
|
8
|
+
attr_reader :status_code
|
9
|
+
|
10
|
+
def initialize(status_code, parsed_response)
|
11
|
+
@status_code = status_code
|
12
|
+
super(parsed_response) # Delegate to the parsed response object
|
13
|
+
end
|
14
|
+
|
15
|
+
# Check if all responses were successful (status 200)
|
16
|
+
def all_successful?
|
17
|
+
@status_code == 200
|
18
|
+
end
|
19
|
+
|
20
|
+
# Check if some responses succeeded and some failed (status 207)
|
21
|
+
def partial_success?
|
22
|
+
@status_code == 207
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Class to handle batch updates of resources
|
27
|
+
# rubocop:disable Metrics/ClassLength
|
28
|
+
class Batch < ApiClient
|
29
|
+
attr_accessor :id_property, :resources, :responses
|
30
|
+
|
31
|
+
CONTACT_LIMIT = 10
|
32
|
+
DEFAULT_LIMIT = 100
|
33
|
+
|
34
|
+
# rubocop:disable Lint/MissingSuper
|
35
|
+
def initialize(resources = [], id_property: 'id')
|
36
|
+
@resources = []
|
37
|
+
@id_property = id_property # Set id_property for the batch (default: 'id')
|
38
|
+
@responses = [] # Store multiple BatchResponse objects here
|
39
|
+
resources.each { |resource| add_resource(resource) }
|
40
|
+
end
|
41
|
+
# rubocop:enable Lint/MissingSuper
|
42
|
+
|
43
|
+
# batch create from the resources
|
44
|
+
def create
|
45
|
+
save(action: 'create')
|
46
|
+
end
|
47
|
+
|
48
|
+
def update
|
49
|
+
# validate_update_conditions
|
50
|
+
save(action: 'update')
|
51
|
+
end
|
52
|
+
|
53
|
+
# Upsert method that calls save with upsert action
|
54
|
+
def upsert
|
55
|
+
validate_upsert_conditions
|
56
|
+
save(action: 'upsert')
|
57
|
+
end
|
58
|
+
|
59
|
+
# Archive method
|
60
|
+
def archive
|
61
|
+
save(action: 'archive')
|
62
|
+
end
|
63
|
+
|
64
|
+
# Check if all responses were successful
|
65
|
+
def all_successful?
|
66
|
+
@responses.all?(&:all_successful?)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Check if some responses were successful and others failed
|
70
|
+
def partial_success?
|
71
|
+
@responses.any?(&:partial_success?) && @responses.none?(&:all_successful?)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Check if any responses failed
|
75
|
+
def any_failed?
|
76
|
+
@responses.any? { |response| !response.all_successful? && !response.partial_success? }
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_resource(resource)
|
80
|
+
if @resources.any? && @resources.first.resource_name != resource.resource_name
|
81
|
+
raise ArgumentError, 'All resources in a batch must be of the same type'
|
82
|
+
end
|
83
|
+
|
84
|
+
@resources << resource
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# rubocop:disable Metrics/MethodLength
|
90
|
+
def save(action: 'update')
|
91
|
+
@action = action
|
92
|
+
resource_type = check_single_resource_type
|
93
|
+
inputs = gather_inputs
|
94
|
+
|
95
|
+
return false if inputs.empty? # Guard clause
|
96
|
+
|
97
|
+
# Perform the batch updates in chunks based on the resource type's limit
|
98
|
+
batch_limit = batch_size_limit(resource_type)
|
99
|
+
inputs.each_slice(batch_limit) do |batch_inputs|
|
100
|
+
response = batch_request(resource_type, batch_inputs, action)
|
101
|
+
@responses << response
|
102
|
+
end
|
103
|
+
|
104
|
+
process_responses unless @action == 'archive'
|
105
|
+
|
106
|
+
# Check if any responses failed
|
107
|
+
!any_failed?
|
108
|
+
end
|
109
|
+
# rubocop:enable Metrics/MethodLength
|
110
|
+
|
111
|
+
def check_single_resource_type
|
112
|
+
raise 'Batch is empty' if @resources.empty?
|
113
|
+
|
114
|
+
@resources.first.resource_name
|
115
|
+
end
|
116
|
+
|
117
|
+
# Gather all the changes, ensuring each resource has an id and changes
|
118
|
+
def gather_inputs
|
119
|
+
return gather_archive_inputs if @action == 'archive'
|
120
|
+
|
121
|
+
@resources.map do |resource|
|
122
|
+
next if resource.changes.empty?
|
123
|
+
|
124
|
+
{
|
125
|
+
id: resource.public_send(@id_property), # Dynamically get the ID based on the batch's id_property
|
126
|
+
idProperty: determine_id_property, # Use the helper method to decide whether to include idProperty
|
127
|
+
properties: resource.changes # Gather the changes for the resource
|
128
|
+
}.compact # Removes nil keys
|
129
|
+
end.compact # Removes nil entries
|
130
|
+
end
|
131
|
+
|
132
|
+
# Gather inputs for the archive operation
|
133
|
+
def gather_archive_inputs
|
134
|
+
@resources.map do |resource|
|
135
|
+
{
|
136
|
+
id: resource.public_send(@id_property), # Use the ID or the custom property
|
137
|
+
idProperty: determine_id_property # Include idProperty if it's not "id"
|
138
|
+
}.compact
|
139
|
+
end.compact
|
140
|
+
end
|
141
|
+
|
142
|
+
# Only include idProperty if it's not "id"
|
143
|
+
def determine_id_property
|
144
|
+
@id_property == 'id' ? nil : @id_property
|
145
|
+
end
|
146
|
+
|
147
|
+
# Perform batch request based on the provided action (upsert, update, create, or archive)
|
148
|
+
def batch_request(type, inputs, action)
|
149
|
+
response = self.class.post("/crm/v3/objects/#{type}/batch/#{action}", body: { inputs: inputs }.to_json)
|
150
|
+
BatchResponse.new(response.code, handle_response(response))
|
151
|
+
end
|
152
|
+
|
153
|
+
# Validation for upsert conditions
|
154
|
+
def validate_upsert_conditions
|
155
|
+
raise ArgumentError, "id_property cannot be 'id' for upsert" if @id_property == 'id'
|
156
|
+
|
157
|
+
# check if there are any resources without a value from the id_property
|
158
|
+
return unless @resources.any? { |resource| resource.public_send(id_property).blank? }
|
159
|
+
|
160
|
+
raise ArgumentError, "All resources must have a non-blank value for #{@id_property} to perform upsert"
|
161
|
+
end
|
162
|
+
|
163
|
+
# Return the appropriate batch size limit for the resource type
|
164
|
+
def batch_size_limit(resource_type)
|
165
|
+
resource_type == 'contacts' ? CONTACT_LIMIT : DEFAULT_LIMIT
|
166
|
+
end
|
167
|
+
|
168
|
+
# Process responses from the batch API call
|
169
|
+
def process_responses
|
170
|
+
@responses.each do |response|
|
171
|
+
next unless response['results']
|
172
|
+
|
173
|
+
process_results(response['results'])
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Process each result and update the resource accordingly
|
178
|
+
def process_results(results)
|
179
|
+
results.each do |result|
|
180
|
+
resource = find_resource_from_result(result)
|
181
|
+
next unless resource
|
182
|
+
|
183
|
+
# Set the ID on the resource directly
|
184
|
+
resource.id = result['id'].to_i if result['id']
|
185
|
+
|
186
|
+
# Update the resource properties
|
187
|
+
update_resource_properties(resource, result['properties'])
|
188
|
+
|
189
|
+
# Update metadata like updatedAt
|
190
|
+
update_metadata(resource, result['updatedAt'])
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def find_resource_from_result(result)
|
195
|
+
case @action
|
196
|
+
when 'update', 'upsert'
|
197
|
+
find_resource_from_id(result['id'].to_i)
|
198
|
+
|
199
|
+
#
|
200
|
+
# when specifying idProperty in the upsert request
|
201
|
+
# the Hubspot API returns the id value (aka the hs_object_id)
|
202
|
+
# instead of the value of the <idProperty> field, so the value of the field
|
203
|
+
# is only stored in results['properties'][@id_property] if it changed!
|
204
|
+
# so this condition is redundant but left here in case Hubspot updates the response
|
205
|
+
#
|
206
|
+
# when 'upsert'
|
207
|
+
# resource_id = result.dig('properties', @id_property) || result['id']
|
208
|
+
# find_resource_from_id_property(resource_id)
|
209
|
+
#
|
210
|
+
when 'create'
|
211
|
+
# For create, check if the resource's changes are entirely contained in the result's properties
|
212
|
+
@resources.reject(&:persisted?).find do |resource|
|
213
|
+
resource.changes.any? && resource.changes.all? { |key, value| result['properties'][key.to_s] == value }
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def find_resource_from_id(resource_id)
|
219
|
+
@resources.find { |r| r.id == resource_id }
|
220
|
+
end
|
221
|
+
|
222
|
+
# def find_resource_from_id_property(resource_id)
|
223
|
+
# @resources.find { |r| r.public_send(@id_property) == resource_id }
|
224
|
+
# end
|
225
|
+
|
226
|
+
def update_resource_properties(resource, properties)
|
227
|
+
properties.each do |key, value|
|
228
|
+
if resource.changes[key]
|
229
|
+
resource.properties[key] = value
|
230
|
+
resource.changes.delete(key)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def update_metadata(resource, updated_at)
|
236
|
+
resource.metadata['updatedAt'] = updated_at if updated_at
|
237
|
+
end
|
238
|
+
|
239
|
+
class << self
|
240
|
+
def read(object_class, object_ids = [], id_property: 'id')
|
241
|
+
raise ArgumentError, 'Must be a valid Hubspot resource class' unless object_class < Hubspot::Resource
|
242
|
+
|
243
|
+
# fetch all the matching resources with paging handled
|
244
|
+
resources = object_class.batch_read(object_ids, id_property: id_property).all
|
245
|
+
|
246
|
+
# return instance of Hubspot::Batch with the resources set
|
247
|
+
new(resources, id_property: id_property)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
# rubocop:enable Metrics/ClassLength
|
252
|
+
end
|
data/lib/hubspot/config.rb
CHANGED
@@ -3,7 +3,8 @@
|
|
3
3
|
module Hubspot
|
4
4
|
# To hold Hubspot configuration
|
5
5
|
class Config
|
6
|
-
attr_accessor :access_token, :portal_id, :client_secret, :logger, :log_level
|
6
|
+
attr_accessor :access_token, :portal_id, :client_secret, :logger, :log_level,
|
7
|
+
:timeout, :open_timeout, :read_timeout, :write_timeout
|
7
8
|
|
8
9
|
def initialize
|
9
10
|
@access_token = nil
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './api_client'
|
4
|
+
require_relative './exceptions'
|
5
|
+
|
6
|
+
module Hubspot
|
7
|
+
# Enumerable class for handling paged data from the API
|
8
|
+
class PagedBatch < ApiClient
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
MAX_LIMIT = 100 # HubSpot max items per page
|
12
|
+
|
13
|
+
# rubocop:disable Lint/MissingSuper
|
14
|
+
def initialize(url:, params: {}, resource_class: nil, object_ids: [])
|
15
|
+
@url = url
|
16
|
+
@params = params
|
17
|
+
@resource_class = resource_class
|
18
|
+
@object_ids = object_ids
|
19
|
+
end
|
20
|
+
# rubocop:enable Lint/MissingSuper
|
21
|
+
|
22
|
+
def each_page
|
23
|
+
@object_ids.each_slice(MAX_LIMIT) do |ids|
|
24
|
+
response = fetch_page(ids)
|
25
|
+
results = response['results'] || []
|
26
|
+
mapped_results = @resource_class ? results.map { |result| @resource_class.new(result) } : results
|
27
|
+
yield mapped_results unless mapped_results.empty?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def all
|
32
|
+
results = []
|
33
|
+
each_page do |page|
|
34
|
+
results.concat(page)
|
35
|
+
end
|
36
|
+
results
|
37
|
+
end
|
38
|
+
|
39
|
+
def each(&block)
|
40
|
+
each_page do |page|
|
41
|
+
page.each(&block)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def fetch_page(object_ids)
|
48
|
+
params_with_ids = @params.dup
|
49
|
+
params_with_ids[:inputs] = object_ids.map { |id| { id: id } }
|
50
|
+
|
51
|
+
response = self.class.post(@url, body: params_with_ids.to_json)
|
52
|
+
|
53
|
+
handle_response(response)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -8,9 +8,6 @@ module Hubspot
|
|
8
8
|
class PagedCollection < ApiClient
|
9
9
|
include Enumerable
|
10
10
|
|
11
|
-
RATE_LIMIT_STATUS = 429
|
12
|
-
MAX_RETRIES = 3
|
13
|
-
RETRY_WAIT_TIME = 3
|
14
11
|
MAX_LIMIT = 100 # HubSpot max items per page
|
15
12
|
|
16
13
|
# rubocop:disable Lint/MissingSuper
|
@@ -68,18 +65,14 @@ module Hubspot
|
|
68
65
|
|
69
66
|
private
|
70
67
|
|
71
|
-
def fetch_page(offset
|
72
|
-
params_with_offset =
|
68
|
+
def fetch_page(offset)
|
69
|
+
params_with_offset = @params.dup
|
73
70
|
params_with_offset.merge!(after: offset) if offset
|
74
71
|
|
75
72
|
# Handle different HTTP methods
|
76
73
|
response = fetch_response_by_method(params_with_offset)
|
77
74
|
|
78
|
-
|
79
|
-
handle_rate_limit(response, offset, attempt, params_override)
|
80
|
-
else
|
81
|
-
handle_response(response)
|
82
|
-
end
|
75
|
+
handle_response(response)
|
83
76
|
end
|
84
77
|
|
85
78
|
def fetch_response_by_method(params = {})
|
@@ -90,13 +83,5 @@ module Hubspot
|
|
90
83
|
self.class.send(@method, @url, body: params.to_json)
|
91
84
|
end
|
92
85
|
end
|
93
|
-
|
94
|
-
def handle_rate_limit(response, offset, attempt, params_override)
|
95
|
-
raise Hubspot.error_from_response(response) if attempt > MAX_RETRIES
|
96
|
-
|
97
|
-
retry_after = response.headers['Retry-After']&.to_i || RETRY_WAIT_TIME
|
98
|
-
sleep(retry_after)
|
99
|
-
fetch_page(offset, attempt + 1, params_override)
|
100
|
-
end
|
101
86
|
end
|
102
87
|
end
|
data/lib/hubspot/resource.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative './api_client'
|
4
|
+
require_relative './paged_collection'
|
5
|
+
require_relative './paged_batch'
|
4
6
|
|
5
7
|
module Hubspot
|
6
8
|
# rubocop:disable Metrics/ClassLength
|
@@ -53,6 +55,21 @@ module Hubspot
|
|
53
55
|
)
|
54
56
|
end
|
55
57
|
|
58
|
+
def batch_read(object_ids = [], id_property: 'id')
|
59
|
+
params = id_property == 'id' ? {} : { idProperty: id_property }
|
60
|
+
|
61
|
+
PagedBatch.new(
|
62
|
+
url: "/crm/v3/objects/#{resource_name}/batch/read",
|
63
|
+
params: params,
|
64
|
+
object_ids: object_ids,
|
65
|
+
resource_class: self
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
def batch_read_all(object_ids = [], id_property: 'id')
|
70
|
+
Hubspot::Batch.read(self, object_ids, id_property: id_property)
|
71
|
+
end
|
72
|
+
|
56
73
|
# Get the complete list of fields (properties) for the object
|
57
74
|
def properties
|
58
75
|
@properties ||= begin
|
@@ -111,8 +128,6 @@ module Hubspot
|
|
111
128
|
|
112
129
|
# rubocop:enable Metrics/MethodLength
|
113
130
|
|
114
|
-
private
|
115
|
-
|
116
131
|
# Define the resource name based on the class
|
117
132
|
def resource_name
|
118
133
|
name = self.name.split('::').last.downcase
|
@@ -123,6 +138,8 @@ module Hubspot
|
|
123
138
|
end
|
124
139
|
end
|
125
140
|
|
141
|
+
private
|
142
|
+
|
126
143
|
# Instantiate a single resource object from the response
|
127
144
|
def instantiate_from_response(response)
|
128
145
|
data = handle_response(response)
|
@@ -161,10 +178,10 @@ module Hubspot
|
|
161
178
|
|
162
179
|
# rubocop:disable Ling/MissingSuper
|
163
180
|
def initialize(data = {})
|
181
|
+
data.transform_keys!(&:to_s)
|
164
182
|
@id = extract_id(data)
|
165
183
|
@properties = {}
|
166
184
|
@metadata = {}
|
167
|
-
|
168
185
|
if @id
|
169
186
|
initialize_from_api(data)
|
170
187
|
else
|
@@ -173,6 +190,10 @@ module Hubspot
|
|
173
190
|
end
|
174
191
|
# rubocop:enable Ling/MissingSuper
|
175
192
|
|
193
|
+
def changes?
|
194
|
+
!@changes.empty?
|
195
|
+
end
|
196
|
+
|
176
197
|
# Instance methods for update (or save)
|
177
198
|
def save
|
178
199
|
if persisted?
|
@@ -207,6 +228,10 @@ module Hubspot
|
|
207
228
|
end
|
208
229
|
alias archive delete
|
209
230
|
|
231
|
+
def resource_name
|
232
|
+
self.class.resource_name
|
233
|
+
end
|
234
|
+
|
210
235
|
# rubocop:disable Metrics/MethodLength
|
211
236
|
# Handle dynamic getter and setter methods with method_missing
|
212
237
|
def method_missing(method, *args)
|
@@ -232,19 +257,15 @@ module Hubspot
|
|
232
257
|
end
|
233
258
|
|
234
259
|
# Fallback if the method or attribute is not found
|
235
|
-
# :nocov:
|
236
260
|
super
|
237
|
-
# :nocov:
|
238
261
|
end
|
239
262
|
# rubocop:enable Metrics/MethodLength
|
240
263
|
|
241
264
|
# Ensure respond_to_missing? is properly overridden
|
242
|
-
# :nocov:
|
243
265
|
def respond_to_missing?(method_name, include_private = false)
|
244
266
|
property_name = method_name.to_s.chomp('=')
|
245
267
|
@properties.key?(property_name) || @changes.key?(property_name) || super
|
246
268
|
end
|
247
|
-
# :nocov:
|
248
269
|
|
249
270
|
private
|
250
271
|
|
data/lib/hubspot/version.rb
CHANGED
data/lib/hubspot.rb
CHANGED
@@ -19,6 +19,7 @@ module Hubspot
|
|
19
19
|
def configure
|
20
20
|
yield(config) if block_given?
|
21
21
|
set_client_headers if config.access_token
|
22
|
+
set_request_timeouts
|
22
23
|
end
|
23
24
|
|
24
25
|
def configured?
|
@@ -31,5 +32,18 @@ module Hubspot
|
|
31
32
|
def set_client_headers
|
32
33
|
Hubspot::ApiClient.headers 'Authorization' => "Bearer #{config.access_token}"
|
33
34
|
end
|
35
|
+
|
36
|
+
def set_request_timeouts
|
37
|
+
config.timeout && Hubspot::ApiClient.default_timeout(config.timeout)
|
38
|
+
timeouts = %i[open_timeout read_timeout]
|
39
|
+
timeouts << :write_timeout if RUBY_VERSION >= '2.6'
|
40
|
+
|
41
|
+
timeouts.each do |t|
|
42
|
+
timeout = config.send(t)
|
43
|
+
next unless timeout
|
44
|
+
|
45
|
+
Hubspot::ApiClient.send(t, timeout)
|
46
|
+
end
|
47
|
+
end
|
34
48
|
end
|
35
49
|
end
|
data/lib/ruby_hubspot_api.rb
CHANGED
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.
|
4
|
+
version: '0.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-09-
|
11
|
+
date: 2024-09-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -190,16 +190,19 @@ files:
|
|
190
190
|
- bin/setup
|
191
191
|
- lib/hubspot.rb
|
192
192
|
- lib/hubspot/api_client.rb
|
193
|
+
- lib/hubspot/batch.rb
|
193
194
|
- lib/hubspot/company.rb
|
194
195
|
- lib/hubspot/config.rb
|
195
196
|
- lib/hubspot/contact.rb
|
196
197
|
- lib/hubspot/exceptions.rb
|
198
|
+
- lib/hubspot/paged_batch.rb
|
197
199
|
- lib/hubspot/paged_collection.rb
|
198
200
|
- lib/hubspot/property.rb
|
199
201
|
- lib/hubspot/resource.rb
|
200
202
|
- lib/hubspot/user.rb
|
201
203
|
- lib/hubspot/version.rb
|
202
204
|
- lib/ruby_hubspot_api.rb
|
205
|
+
- lib/support/patches.rb
|
203
206
|
- ruby_hubspot_api.gemspec
|
204
207
|
homepage: https://github.com/sensadrome/ruby_hubspot_api
|
205
208
|
licenses:
|