hubspot-api-ruby 0.8.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +40 -154
- data/Rakefile +0 -2
- data/hubspot-api-ruby.gemspec +5 -7
- data/lib/hubspot/association.rb +106 -0
- data/lib/hubspot/company.rb +2 -13
- data/lib/hubspot/config.rb +14 -9
- data/lib/hubspot/connection.rb +20 -14
- data/lib/hubspot/contact.rb +5 -1
- data/lib/hubspot/contact_list.rb +0 -7
- data/lib/hubspot/custom_event.rb +25 -0
- data/lib/hubspot/deal.rb +53 -32
- data/lib/hubspot/engagement.rb +0 -1
- data/lib/hubspot/exceptions.rb +2 -0
- data/lib/hubspot/file.rb +2 -2
- data/lib/hubspot/meeting.rb +44 -0
- data/lib/hubspot/properties.rb +1 -1
- data/lib/hubspot/railtie.rb +0 -4
- data/lib/hubspot/resource.rb +4 -4
- data/lib/hubspot/utils.rb +0 -30
- data/lib/hubspot-api-ruby.rb +3 -0
- data/spec/lib/hubspot/association_spec.rb +165 -0
- data/spec/lib/hubspot/blog_spec.rb +10 -21
- data/spec/lib/hubspot/company_properties_spec.rb +8 -11
- data/spec/lib/hubspot/company_spec.rb +18 -36
- data/spec/lib/hubspot/config_spec.rb +24 -14
- data/spec/lib/hubspot/connection_spec.rb +44 -55
- data/spec/lib/hubspot/contact_list_spec.rb +82 -71
- data/spec/lib/hubspot/contact_properties_spec.rb +5 -34
- data/spec/lib/hubspot/contact_spec.rb +2 -4
- data/spec/lib/hubspot/custom_event_spec.rb +28 -0
- data/spec/lib/hubspot/deal_pipeline_spec.rb +4 -15
- data/spec/lib/hubspot/deal_properties_spec.rb +11 -58
- data/spec/lib/hubspot/deal_spec.rb +46 -47
- data/spec/lib/hubspot/engagement_spec.rb +21 -40
- data/spec/lib/hubspot/event_spec.rb +3 -2
- data/spec/lib/hubspot/file_spec.rb +16 -30
- data/spec/lib/hubspot/form_spec.rb +34 -34
- data/spec/lib/hubspot/meeting_spec.rb +81 -0
- data/spec/lib/hubspot/owner_spec.rb +2 -3
- data/spec/lib/hubspot/resource_spec.rb +41 -1
- data/spec/lib/hubspot/utils_spec.rb +6 -39
- data/spec/lib/hubspot-ruby_spec.rb +1 -1
- data/spec/spec_helper.rb +13 -4
- data/spec/support/vcr.rb +4 -6
- metadata +11 -27
- data/lib/tasks/hubspot.rake +0 -53
- data/spec/lib/hubspot/topic_spec.rb +0 -23
- data/spec/lib/tasks/hubspot_spec.rb +0 -119
- data/spec/support/capture_output.rb +0 -21
- data/spec/support/rake.rb +0 -46
- data/spec/support/tests_helper.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4aa748bc77ade29ff96016950aa3f3d8d9bbb309167b200319798d9edf8664bc
|
4
|
+
data.tar.gz: f09d8a29a3dbbcaa18071670e46d4c10c6727731409bbf4a7eee5099d6fd15e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac1c6faae15a4569c92d8061a270f7b55aad7c64eaf2f16bc057b31312204164771600a5ebb511a0d7e0977123baf65b22125f69832dc6b2af63637bb4689814
|
7
|
+
data.tar.gz: 37b874280272f531177bf348d038b5b6e935f2a4edefeef1c1c7d4e5e68c2613047a75ea782daa412053fb8f50c312d6a27d5f264509f758cd6eb82abf1afe8d
|
data/README.md
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
# HubSpot REST API wrappers for ruby
|
2
2
|
|
3
|
-
**This is the master branch and contains unreleased and potentially breaking changes. If you are looking for the most recent stable release you want the [v0-stable branch](https://github.com/adimichele/hubspot-ruby/tree/v0-stable).**
|
4
|
-
|
5
3
|
Wraps the HubSpot REST API for convenient access from ruby applications.
|
6
4
|
|
7
5
|
Documentation for the HubSpot REST API can be found here: https://developers.hubspot.com/docs/endpoints
|
8
6
|
|
7
|
+
## Disclaimer
|
8
|
+
|
9
|
+
This gem is a fork of the unofficial [hubspot-ruby](https://github.com/HubspotCommunity/hubspot-ruby) gem which is unfortunately not maintained anymore.
|
10
|
+
|
11
|
+
The API has evolved quite a bit and while this is not a drop-in replacement you should feel familiar if you come from `hubspot-ruby`.
|
12
|
+
|
13
|
+
This project and the code therein was not created by and is not supported by HubSpot, Inc or any of its affiliates.
|
14
|
+
|
9
15
|
## Setup
|
10
16
|
|
11
17
|
gem install hubspot-api-ruby
|
@@ -24,16 +30,16 @@ Authentication docs].
|
|
24
30
|
Below is a complete list of configuration options with the default values:
|
25
31
|
```ruby
|
26
32
|
Hubspot.configure({
|
27
|
-
|
33
|
+
access_token: <ACCESS_TOKEN>,
|
28
34
|
base_url: "https://api.hubapi.com",
|
29
35
|
portal_id: <PORTAL_ID>,
|
30
36
|
logger: Logger.new(nil),
|
31
|
-
access_token: <ACCESS_TOKEN>,
|
32
37
|
client_id: <CLIENT_ID>,
|
33
38
|
client_secret: <CLIENT_SECRET>,
|
34
39
|
redirect_uri: <REDIRECT_URI>,
|
35
40
|
read_timeout: nil, # or :timeout to set read_timeout and open_timeout
|
36
41
|
open_timeout: nil,
|
42
|
+
hapikey: <HAPIKEY>,
|
37
43
|
})
|
38
44
|
```
|
39
45
|
|
@@ -44,15 +50,20 @@ environment.
|
|
44
50
|
[HubSpot API Authentication Docs]: https://developers.hubspot.com/docs/methods/auth/oauth-overview
|
45
51
|
[HubSpot Developer Tools]: https://developers.hubspot.com/docs/devtools
|
46
52
|
|
47
|
-
## Authentication with
|
53
|
+
## Authentication with a private app access token
|
48
54
|
|
49
|
-
To set the HubSpot
|
55
|
+
To set the HubSpot access token, run the following:
|
50
56
|
```ruby
|
51
|
-
Hubspot.configure(
|
57
|
+
Hubspot.configure(access_token: 'YOUR_ACCESS_TOKEN')
|
52
58
|
```
|
53
59
|
|
54
|
-
|
55
|
-
|
60
|
+
Note: some APIs require the portal ID to be set.
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
Hubspot.configure(access_token: 'YOUR_ACCESS_TOKEN', portal_id: 'YOUR_PORTAL_ID')
|
64
|
+
```
|
65
|
+
|
66
|
+
[To learn how to create and manage private apps and access tokens, click here.](https://developers.hubspot.com/docs/api/private-apps)
|
56
67
|
|
57
68
|
## Authentication with OAuth 2.0
|
58
69
|
|
@@ -105,153 +116,25 @@ Hubspot::OAuth.refresh(refresh_token)
|
|
105
116
|
|
106
117
|
At this time, OAuth tokens are configured globally rather than on a per-connection basis.
|
107
118
|
|
108
|
-
##
|
109
|
-
|
110
|
-
Classes have been created that map to Hubspot resource types and attempt to abstract away as much of the API specific details as possible. These classes generally follow the [ActiveRecord](https://en.wikipedia.org/wiki/Active_record_pattern) pattern and general Ruby conventions. Anyone familiar with [Ruby On Rails](https://rubyonrails.org/) should find this API closely maps with familiar concepts.
|
111
|
-
|
112
|
-
|
113
|
-
### Creating a new resource
|
114
|
-
|
115
|
-
```ruby
|
116
|
-
irb(main):001:0> company = Hubspot::Company.new(name: "My Company LLC.")
|
117
|
-
=> #<Hubspot::Company:0x000055b9219cc068 @changes={"name"=>"My Company LLC."}, @properties={}, @id=nil, @persisted=false, @deleted=false>
|
118
|
-
|
119
|
-
irb(main):002:0> company.persisted?
|
120
|
-
=> false
|
121
|
-
|
122
|
-
irb(main):003:0> company.save
|
123
|
-
=> true
|
124
|
-
|
125
|
-
irb(main):004:0> company.persisted?
|
126
|
-
=> true
|
127
|
-
```
|
128
|
-
|
129
|
-
```ruby
|
130
|
-
irb(main):001:0> company = Hubspot::Company.create(name: "Second Financial LLC.")
|
131
|
-
=> #<Hubspot::Company:0x0000557ea7119fb0 @changes={}, @properties={"hs_lastmodifieddate"=>{"value"=>"1552234087467", "timestamp"=>1552234087467, "source"=>"CALCULATED", "sourceId"=>nil, "versions"=>[{"name"=>"hs_lastmodifieddate", "value"=>"1552234087467", "timestamp"=>1552234087467, "source"=>"CALCULATED", "sourceVid"=>[], "requestId"=>"fd45773b-30d0-4d9d-b3b8-a85e01534e46"}]}, "name"=>{"value"=>"Second Financial LLC.", "timestamp"=>1552234087467, "source"=>"API", "sourceId"=>nil, "versions"=>[{"name"=>"name", "value"=>"Second Financial LLC.", "timestamp"=>1552234087467, "source"=>"API", "sourceVid"=>[], "requestId"=>"fd45773b-30d0-4d9d-b3b8-a85e01534e46"}]}, "createdate"=>{"value"=>"1552234087467", "timestamp"=>1552234087467, "source"=>"API", "sourceId"=>nil, "versions"=>[{"name"=>"createdate", "value"=>"1552234087467", "timestamp"=>1552234087467, "source"=>"API", "sourceVid"=>[], "requestId"=>"fd45773b-30d0-4d9d-b3b8-a85e01534e46"}, {"name"=>"createdate", "value"=>"1552234087467", "timestamp"=>1552234087467, "sourceId"=>"API", "source"=>"API", "sourceVid"=>[], "requestId"=>"fd45773b-30d0-4d9d-b3b8-a85e01534e46"}]}}, @id=1726317857, @persisted=true, @deleted=false, @metadata={"portalId"=>62515, "companyId"=>1726317857, "isDeleted"=>false, "additionalDomains"=>[], "stateChanges"=>[], "mergeAudits"=>[]}>
|
132
|
-
|
133
|
-
irb(main):002:0> company.persisted?
|
134
|
-
=> true
|
135
|
-
```
|
136
|
-
|
137
|
-
|
138
|
-
### Find an existing resource
|
139
|
-
|
140
|
-
**Note:** Hubspot uses a combination of different names for the "ID" property of a resource based on what type of resource it is (eg. vid for Contact). This library attempts to abstract that away and generalizes an `id` property for all resources
|
141
|
-
|
142
|
-
```ruby
|
143
|
-
irb(main):001:0> company = Hubspot::Company.find(1726317857)
|
144
|
-
=> #<Hubspot::Company:0x0000562e4988c9a8 @changes={}, @properties={"hs_lastmodifieddate"=>{"value"=>"1552234087467", "timestamp"=>1552234087467, "source"=>"CALCULATED", "sourceId"=>nil, "versions"=>[{"name"=>"hs_lastmodifieddate", "value"=>"1552234087467", "timestamp"=>1552234087467, "source"=>"CALCULATED", "sourceVid"=>[], "requestId"=>"fd45773b-30d0-4d9d-b3b8-a85e01534e46"}]}, "name"=>{"value"=>"Second Financial LLC.", "timestamp"=>1552234087467, "source"=>"API", "sourceId"=>nil, "versions"=>[{"name"=>"name", "value"=>"Second Financial LLC.", "timestamp"=>1552234087467, "source"=>"API", "sourceVid"=>[], "requestId"=>"fd45773b-30d0-4d9d-b3b8-a85e01534e46"}]}, "createdate"=>{"value"=>"1552234087467", "timestamp"=>1552234087467, "source"=>"API", "sourceId"=>nil, "versions"=>[{"name"=>"createdate", "value"=>"1552234087467", "timestamp"=>1552234087467, "source"=>"API", "sourceVid"=>[], "requestId"=>"fd45773b-30d0-4d9d-b3b8-a85e01534e46"}]}}, @id=1726317857, @persisted=true, @deleted=false, @metadata={"portalId"=>62515, "companyId"=>1726317857, "isDeleted"=>false, "additionalDomains"=>[], "stateChanges"=>[], "mergeAudits"=>[]}>
|
145
|
-
|
146
|
-
irb(main):002:0> company = Hubspot::Company.find(1)
|
147
|
-
Traceback (most recent call last):
|
148
|
-
6: from ./hubspot-api-ruby/bin/console:20:in `<main>'
|
149
|
-
5: from (irb):2
|
150
|
-
4: from ./hubspot-api-ruby/lib/hubspot/resource.rb:17:in `find'
|
151
|
-
3: from ./hubspot-api-ruby/lib/hubspot/resource.rb:81:in `reload'
|
152
|
-
2: from ./hubspot-api-ruby/lib/hubspot/connection.rb:10:in `get_json'
|
153
|
-
1: from ./hubspot-api-ruby/lib/hubspot/connection.rb:52:in `handle_response'
|
154
|
-
Hubspot::RequestError (Response body: {"status":"error","message":"resource not found","correlationId":"7c8ba50e-16a4-4a52-a304-ff249175a8f1","requestId":"b4898274bf8992924082b4a460b90cbe"})
|
155
|
-
```
|
156
|
-
|
157
|
-
|
158
|
-
### Updating resource properties
|
159
|
-
|
160
|
-
```ruby
|
161
|
-
irb(main):001:0> company = Hubspot::Company.find(1726317857)
|
162
|
-
=> #<Hubspot::Company:0x0000563b9f3ee230 @changes={}, @properties={"hs_lastmodifieddate"=>{"value"=>"1552234087467", "timestamp"=>1552234087467, "source"=>"CALCULATED", "sourceId"=>nil, "versions"=>[{"name"=>"hs_lastmodifieddate", "value"=>"1552234087467", "timestamp"=>1552234087467, "source"=>"CALCULATED", "sourceVid"=>[], "requestId"=>"fd45773b-30d0-4d9d-b3b8-a85e01534e46"}]}, "name"=>{"value"=>"Second Financial LLC.", "timestamp"=>1552234087467, "source"=>"API", "sourceId"=>nil, "versions"=>[{"name"=>"name", "value"=>"Second Financial LLC.", "timestamp"=>1552234087467, "source"=>"API", "sourceVid"=>[], "requestId"=>"fd45773b-30d0-4d9d-b3b8-a85e01534e46"}]}, "createdate"=>{"value"=>"1552234087467", "timestamp"=>1552234087467, "source"=>"API", "sourceId"=>nil, "versions"=>[{"name"=>"createdate", "value"=>"1552234087467", "timestamp"=>1552234087467, "source"=>"API", "sourceVid"=>[], "requestId"=>"fd45773b-30d0-4d9d-b3b8-a85e01534e46"}]}}, @id=1726317857, @persisted=true, @deleted=false, @metadata={"portalId"=>62515, "companyId"=>1726317857, "isDeleted"=>false, "additionalDomains"=>[], "stateChanges"=>[], "mergeAudits"=>[]}>
|
163
|
-
|
164
|
-
irb(main):002:0> company.name
|
165
|
-
=> "Second Financial LLC."
|
166
|
-
|
167
|
-
irb(main):003:0> company.name = "Third Financial LLC."
|
168
|
-
=> "Third Financial LLC."
|
169
|
-
|
170
|
-
irb(main):004:0> company.changed?
|
171
|
-
=> true
|
172
|
-
|
173
|
-
irb(main):005:0> company.changes
|
174
|
-
=> {:name=>"Third Financial LLC."}
|
175
|
-
|
176
|
-
irb(main):006:0> company.save
|
177
|
-
=> true
|
178
|
-
|
179
|
-
irb(main):007:0> company.changed?
|
180
|
-
=> false
|
181
|
-
|
182
|
-
irb(main):008:0> company.changes
|
183
|
-
=> {}
|
184
|
-
```
|
185
|
-
|
186
|
-
**Note:** Unlike ActiveRecord in Rails, in some cases not all properties of a resource are known. If these properties are not returned by the API then they will not have a getter method defined for them until they've been set first. This may change in the future to improve the user experience and different methods are being tested.
|
187
|
-
|
188
|
-
```ruby
|
189
|
-
irb(main):001:0> company = Hubspot::Company.new
|
190
|
-
=> #<Hubspot::Company:0x0000561d0a8bdff8 @changes={}, @properties={}, @id=nil, @persisted=false, @deleted=false>
|
191
|
-
|
192
|
-
irb(main):002:0> company.name
|
193
|
-
Traceback (most recent call last):
|
194
|
-
3: from ./hubspot-api-ruby/bin/console:20:in `<main>'
|
195
|
-
2: from (irb):2
|
196
|
-
1: from ./hubspot-api-ruby/lib/hubspot/resource.rb:215:in `method_missing'
|
197
|
-
NoMethodError (undefined method `name' for #<Hubspot::Company:0x0000561d0a8bdff8>)
|
198
|
-
|
199
|
-
irb(main):003:0> company.name = "Foobar"
|
200
|
-
=> "Foobar"
|
201
|
-
|
202
|
-
irb(main):004:0> company.name
|
203
|
-
=> "Foobar"
|
204
|
-
```
|
205
|
-
|
206
|
-
### Collections
|
119
|
+
## Authentication with an API key
|
207
120
|
|
208
|
-
|
121
|
+
NOTE: API keys are deprecated. [Read more here](https://developers.hubspot.com/changelog/upcoming-api-key-sunset) and [find out how to migrate to private apps there](https://developers.hubspot.com/docs/api/migrate-an-api-key-integration-to-a-private-app).
|
209
122
|
|
123
|
+
To set the HubSpot API key, aka `hapikey`, run the following:
|
210
124
|
```ruby
|
211
|
-
|
212
|
-
=> #<Hubspot::PagedCollection:0x000055ba3c2b55d8 @limit_param="limit", @limit=25, @offset_param="offset", @offset=nil, @options={}, @fetch_proc=#<Proc:0x000055ba3c2b5538@./hubspot-api-ruby/lib/hubspot/contact.rb:18>, @resources=[...snip...], @next_offset=9242374, @has_more=true>
|
213
|
-
|
214
|
-
irb(main):002:0> contacts.more?
|
215
|
-
=> true
|
216
|
-
|
217
|
-
irb(main):003:0> contacts.next_offset
|
218
|
-
=> 9242374
|
219
|
-
|
220
|
-
irb(main):004:0> contacts.size
|
221
|
-
=> 25
|
222
|
-
|
223
|
-
irb(main):005:0> contacts.first
|
224
|
-
=> #<Hubspot::Contact:0x000055ba3c29bac0 @changes={}, @properties={"firstname"=>{"value"=>"My Street X 1551971239 => My Street X 1551971267 => My Street X 1551971279"}, "lastmodifieddate"=>{"value"=>"1551971286841"}, "company"=>{"value"=>"MadKudu"}, "lastname"=>{"value"=>"Test0830181615"}}, @id=9153674, @persisted=true, @deleted=false, @metadata={"addedAt"=>1535664601481, "vid"=>9153674, "canonical-vid"=>9153674, "merged-vids"=>[], "portal-id"=>62515, "is-contact"=>true, "profile-token"=>"AO_T-mPNHk6O7jh8u8D2IlrhZn7GO91w-weZrC93_UaJvdB0U4o6Uc_PkPJ3DOpf15sUplrxMzG9weiTTpPI05Nr04zxnaNYBVcWHOlMbVlJ2Avq1KGoCBVbIoQucOy_YmCBIfOXRtcc", "profile-url"=>"https://app.hubspot.com/contacts/62515/contact/9153674", "form-submissions"=>[], "identity-profiles"=>[{"vid"=>9153674, "saved-at-timestamp"=>1535664601272, "deleted-changed-timestamp"=>0, "identities"=>[{"type"=>"EMAIL", "value"=>"test.0830181615@test.com", "timestamp"=>1535664601151, "is-primary"=>true}, {"type"=>"LEAD_GUID", "value"=>"01a107c4-3872-44e0-ab2e-47061507ffa1", "timestamp"=>1535664601259}]}], "merge-audits"=>[]}>
|
225
|
-
|
226
|
-
irb(main):006:0> contacts.next_page
|
227
|
-
=> #<Hubspot::PagedCollection:0x000055ba3c2b55d8 @limit_param="limit", @limit=25, @offset_param="offset", @offset=9242374, @options={}, @fetch_proc=#<Proc:0x000055ba3c2b5538@./hubspot-api-ruby/lib/hubspot/contact.rb:18>, @resources=[...snip...], @next_offset=9324874, @has_more=true>
|
125
|
+
Hubspot.configure(hapikey: "YOUR_API_KEY")
|
228
126
|
```
|
229
127
|
|
230
|
-
|
231
|
-
|
232
|
-
```ruby
|
233
|
-
irb(main):001:0> companies = Hubspot::Company.all(limit: 5)
|
234
|
-
=> #<Hubspot::PagedCollection:0x000055d5314fe0c8 @limit_param="limit", @limit=5, @offset_param="offset", @offset=nil, @options={}, @fetch_proc=#<Proc:0x000055d5314fe028@./hubspot-api-ruby/lib/hubspot/company.rb:21>, @resources=[...snip...], @next_offset=116011506, @has_more=true>
|
235
|
-
|
236
|
-
irb(main):002:0> companies.size
|
237
|
-
=> 5
|
238
|
-
|
239
|
-
irb(main):003:0> companies.update_all(lifecyclestage: "opportunity")
|
240
|
-
=> true
|
128
|
+
If you have a HubSpot account, you can find your API key by logging in and
|
129
|
+
visiting: https://app.hubspot.com/keys/get
|
241
130
|
|
242
|
-
|
243
|
-
=> #<Hubspot::PagedCollection:0x000055d5314fe0c8 @limit_param="limit", @limit=5, @offset_param="offset", @offset=nil, @options={}, @fetch_proc=#<Proc:0x000055d5314fe028@./hubspot-api-ruby/lib/hubspot/company.rb:21>, @resources=[...snip...], @next_offset=116011506, @has_more=true>
|
244
|
-
```
|
131
|
+
## Usage
|
245
132
|
|
246
|
-
|
133
|
+
Classes have been created that map to Hubspot resource types and attempt to abstract away as much of the API specific details as possible. These classes generally follow the [ActiveRecord](https://en.wikipedia.org/wiki/Active_record_pattern) pattern and general Ruby conventions. Anyone familiar with [Ruby On Rails](https://rubyonrails.org/) should find this API closely maps with familiar concepts.
|
247
134
|
|
248
|
-
|
249
|
-
irb(main):001:0> contact = Hubspot::Contact.find(9324874)
|
250
|
-
=> #<Hubspot::Contact:0x000055a87c87aee0 ...snip... >
|
135
|
+
### Example
|
251
136
|
|
252
|
-
|
253
|
-
=> true
|
254
|
-
```
|
137
|
+
https://github.com/lounna-sas/hubspot-api-ruby/wiki
|
255
138
|
|
256
139
|
## Resource types
|
257
140
|
|
@@ -281,15 +164,18 @@ By default, the VCR recording mode is set to `:none`, which allows recorded
|
|
281
164
|
requests to be re-played but raises for any new request. This prevents the test
|
282
165
|
suite from issuing unexpected HTTP requests.
|
283
166
|
|
284
|
-
|
285
|
-
environment variable:
|
167
|
+
Some requests require to be done on a live hubspot portal, you can set the `HUBSPOT_ACCESS_TOKEN` and `HUBSPOT_PORTAL_ID` environement variables, for example inside a `.env.test` file :
|
286
168
|
|
287
|
-
```
|
288
|
-
|
169
|
+
```
|
170
|
+
HUBSPOT_ACCESS_TOKEN=xxxx
|
171
|
+
HUBSPOT_PORTAL_ID=yyyy
|
289
172
|
```
|
290
173
|
|
291
|
-
|
174
|
+
To add a new test or update a VCR recording, run the test with the `VCR_RECORD_MODE`
|
175
|
+
environment variable, for instance:
|
292
176
|
|
293
|
-
|
177
|
+
```sh
|
178
|
+
VCR_RECORD_MODE=new_episodes bundle exec rspec spec
|
179
|
+
```
|
294
180
|
|
295
|
-
|
181
|
+
[VCR]: https://github.com/vcr/vcr
|
data/Rakefile
CHANGED
data/hubspot-api-ruby.gemspec
CHANGED
@@ -1,19 +1,18 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "hubspot-api-ruby"
|
3
|
-
s.version = "0.
|
3
|
+
s.version = "0.10.0"
|
4
4
|
s.require_paths = ["lib"]
|
5
|
-
s.authors = ["Jonathan"
|
6
|
-
s.email = ["jonathan@hoggo.com"
|
5
|
+
s.authors = ["Jonathan"]
|
6
|
+
s.email = ["jonathan@hoggo.com"]
|
7
7
|
s.description = "hubspot-api-ruby is a wrapper for the HubSpot REST API"
|
8
8
|
s.licenses = ["MIT"]
|
9
9
|
s.files = [".rspec", "Gemfile", "Guardfile", "LICENSE.txt", "README.md", "RELEASING.md", "Rakefile", "hubspot-api-ruby.gemspec"]
|
10
10
|
s.files += Dir["lib/**/*.rb"]
|
11
|
-
s.files += Dir["lib/**/*.rake"]
|
12
11
|
s.files += Dir["spec/**/*.rb"]
|
13
|
-
s.homepage = "
|
12
|
+
s.homepage = "https://github.com/captaincontrat/hubspot-api-ruby"
|
14
13
|
s.summary = "hubspot-api-ruby is a wrapper for the HubSpot REST API"
|
15
14
|
s.metadata = {
|
16
|
-
"changelog_uri" => "https://github.com/
|
15
|
+
"changelog_uri" => "https://github.com/captaincontrat/hubspot-api-ruby/blob/master/History.md"
|
17
16
|
}
|
18
17
|
|
19
18
|
s.required_ruby_version = ">= 2.3"
|
@@ -23,7 +22,6 @@ Gem::Specification.new do |s|
|
|
23
22
|
s.add_runtime_dependency "httparty", ">= 0.10"
|
24
23
|
|
25
24
|
# Add development-only dependencies here
|
26
|
-
s.add_development_dependency("appraisal", "~> 2.2")
|
27
25
|
s.add_development_dependency("dotenv")
|
28
26
|
s.add_development_dependency("rake", "~> 11.0")
|
29
27
|
s.add_development_dependency("rspec", "~> 3.8")
|
@@ -0,0 +1,106 @@
|
|
1
|
+
class Hubspot::Association
|
2
|
+
OBJECT_TARGET_TO_CLASS = {
|
3
|
+
"Contact" => Hubspot::Contact,
|
4
|
+
"Deal" => Hubspot::Deal,
|
5
|
+
"Company" => Hubspot::Company
|
6
|
+
}.freeze
|
7
|
+
|
8
|
+
ASSOCIATION_DEFINITIONS = {
|
9
|
+
"Company" => {
|
10
|
+
"Contact" => 2,
|
11
|
+
"Deal" => 6,
|
12
|
+
"Company" => 13
|
13
|
+
},
|
14
|
+
"Deal" => {
|
15
|
+
"Company" => 5,
|
16
|
+
"Contact" => 3
|
17
|
+
},
|
18
|
+
"Contact" => {
|
19
|
+
"Deal" => 4
|
20
|
+
}
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
class << self
|
24
|
+
def create(object_type, object_id, to_object_type, to_object_id)
|
25
|
+
batch_create(object_type, to_object_type, [{from_id: object_id, to_id: to_object_id}])
|
26
|
+
end
|
27
|
+
|
28
|
+
# Make multiple associations in a single API call
|
29
|
+
# {https://developers.hubspot.com/docs/api/crm/associations}
|
30
|
+
# usage:
|
31
|
+
# Hubspot::Association.batch_create("Company", "Contact", [{from_id: 1, to_id: 2}]])
|
32
|
+
def batch_create(from_object_type, to_object_type, associations)
|
33
|
+
definition_id = ASSOCIATION_DEFINITIONS.dig(from_object_type, to_object_type)
|
34
|
+
request = { inputs: associations.map { |assocation| build_create_association_body(assocation, definition_id) } }
|
35
|
+
response = Hubspot::Connection.post_json("/crm/v4/associations/#{from_object_type}/#{to_object_type}/batch/create", params: { no_parse: true }, body: request)
|
36
|
+
return false if response.parsed_response["errors"].present?
|
37
|
+
|
38
|
+
response.success?
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(object_type, object_id, to_object_type, to_object_id)
|
42
|
+
batch_delete(object_type, to_object_type, [{from_id: object_id, to_id: to_object_id}])
|
43
|
+
end
|
44
|
+
|
45
|
+
# Remove multiple associations in a single API call
|
46
|
+
# {https://developers.hubspot.com/docs/api/crm/associations}
|
47
|
+
# usage:
|
48
|
+
# Hubspot::Association.batch_delete("Company", "Contact", [{ from_id: 1, to_id: 2}])
|
49
|
+
def batch_delete(from_object_type, to_object_type, associations)
|
50
|
+
request = { inputs: build_delete_associations_body(associations) }
|
51
|
+
Hubspot::Connection.post_json("/crm/v4/associations/#{from_object_type}/#{to_object_type}/batch/archive", params: { no_parse: true }, body: request).success?
|
52
|
+
end
|
53
|
+
|
54
|
+
# Retrieve all associated resources given a source (object_type and object_id) and a relation type (to_object_type)
|
55
|
+
# {https://developers.hubspot.com/docs/api/crm/associations}
|
56
|
+
# Warning: it will return at most 1000 objects and make up to 1001 queries
|
57
|
+
# Hubspot::Association.all("Company", 42, "Contact")
|
58
|
+
def all(object_type, object_id, to_object_type)
|
59
|
+
klass = OBJECT_TARGET_TO_CLASS[to_object_type]
|
60
|
+
raise(Hubspot::InvalidParams, 'Object type not supported') unless klass.present?
|
61
|
+
|
62
|
+
response = Hubspot::Connection.get_json("/crm/v4/objects/#{object_type}/#{object_id}/associations/#{to_object_type}", {})
|
63
|
+
response['results'].map { |result| klass.find(result["toObjectId"]) }
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def build_create_association_body(association, definition_id)
|
69
|
+
{
|
70
|
+
from: {
|
71
|
+
id: association[:from_id]
|
72
|
+
},
|
73
|
+
to: {
|
74
|
+
id: association[:to_id]
|
75
|
+
},
|
76
|
+
types: [
|
77
|
+
{
|
78
|
+
associationCategory: "HUBSPOT_DEFINED",
|
79
|
+
associationTypeId: definition_id
|
80
|
+
}
|
81
|
+
]
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_delete_associations_body(associations)
|
86
|
+
normalized_associations = associations.inject({}) do |memo, association|
|
87
|
+
memo[association[:from_id]] ||= []
|
88
|
+
memo[association[:from_id]] << association[:to_id]
|
89
|
+
memo
|
90
|
+
end
|
91
|
+
|
92
|
+
normalized_associations.map do |from_id, to_ids|
|
93
|
+
{
|
94
|
+
from: {
|
95
|
+
id: from_id
|
96
|
+
},
|
97
|
+
to: to_ids.map do |to_id|
|
98
|
+
{
|
99
|
+
id: to_id
|
100
|
+
}
|
101
|
+
end
|
102
|
+
}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/lib/hubspot/company.rb
CHANGED
@@ -2,7 +2,6 @@ class Hubspot::Company < Hubspot::Resource
|
|
2
2
|
self.id_field = "companyId"
|
3
3
|
self.property_name_field = "name"
|
4
4
|
|
5
|
-
ADD_CONTACT_PATH = '/companies/v2/companies/:id/contacts/:contact_id'
|
6
5
|
ALL_PATH = '/companies/v2/companies/paged'
|
7
6
|
BATCH_UPDATE_PATH = '/companies/v1/batch-async/update'
|
8
7
|
CONTACTS_PATH = '/companies/v2/companies/:id/contacts'
|
@@ -12,7 +11,6 @@ class Hubspot::Company < Hubspot::Resource
|
|
12
11
|
FIND_PATH = '/companies/v2/companies/:id'
|
13
12
|
RECENTLY_CREATED_PATH = '/companies/v2/companies/recent/created'
|
14
13
|
RECENTLY_MODIFIED_PATH = '/companies/v2/companies/recent/modified'
|
15
|
-
REMOVE_CONTACT_PATH = '/companies/v2/companies/:id/contacts/:contact_id'
|
16
14
|
SEARCH_DOMAIN_PATH = '/companies/v2/domains/:domain/companies'
|
17
15
|
UPDATE_PATH = '/companies/v2/companies/:id'
|
18
16
|
|
@@ -80,20 +78,11 @@ class Hubspot::Company < Hubspot::Resource
|
|
80
78
|
end
|
81
79
|
|
82
80
|
def add_contact(id, contact_id)
|
83
|
-
Hubspot::
|
84
|
-
ADD_CONTACT_PATH,
|
85
|
-
params: { id: id, contact_id: contact_id }
|
86
|
-
)
|
87
|
-
true
|
81
|
+
Hubspot::Association.create("Company", id, "Contact", contact_id)
|
88
82
|
end
|
89
83
|
|
90
84
|
def remove_contact(id, contact_id)
|
91
|
-
Hubspot::
|
92
|
-
REMOVE_CONTACT_PATH,
|
93
|
-
{ id: id, contact_id: contact_id }
|
94
|
-
)
|
95
|
-
|
96
|
-
true
|
85
|
+
Hubspot::Association.delete("Company", id, "Contact", contact_id)
|
97
86
|
end
|
98
87
|
|
99
88
|
def batch_update(companies, opts = {})
|
data/lib/hubspot/config.rb
CHANGED
@@ -5,7 +5,7 @@ module Hubspot
|
|
5
5
|
class Config
|
6
6
|
CONFIG_KEYS = [
|
7
7
|
:hapikey, :base_url, :portal_id, :logger, :access_token, :client_id,
|
8
|
-
:client_secret, :redirect_uri, :read_timeout, :open_timeout
|
8
|
+
:client_secret, :redirect_uri, :read_timeout, :open_timeout, :custom_event_prefix
|
9
9
|
]
|
10
10
|
DEFAULT_LOGGER = Logger.new(nil)
|
11
11
|
DEFAULT_BASE_URL = "https://api.hubapi.com".freeze
|
@@ -15,21 +15,26 @@ module Hubspot
|
|
15
15
|
|
16
16
|
def configure(config)
|
17
17
|
config.stringify_keys!
|
18
|
-
@hapikey = config[
|
19
|
-
@base_url = config[
|
20
|
-
@portal_id = config[
|
21
|
-
@logger = config[
|
22
|
-
@access_token = config[
|
23
|
-
@client_id = config[
|
24
|
-
@client_secret = config[
|
25
|
-
@redirect_uri = config[
|
18
|
+
@hapikey = config['hapikey']
|
19
|
+
@base_url = config['base_url'] || DEFAULT_BASE_URL
|
20
|
+
@portal_id = config['portal_id']
|
21
|
+
@logger = config['logger'] || DEFAULT_LOGGER
|
22
|
+
@access_token = config['access_token']
|
23
|
+
@client_id = config['client_id'] if config['client_id'].present?
|
24
|
+
@client_secret = config['client_secret'] if config['client_secret'].present?
|
25
|
+
@redirect_uri = config['redirect_uri'] if config['redirect_uri'].present?
|
26
26
|
@read_timeout = config['read_timeout'] || config['timeout']
|
27
27
|
@open_timeout = config['open_timeout'] || config['timeout']
|
28
|
+
@custom_event_prefix = config['custom_event_prefix']
|
28
29
|
|
29
30
|
unless authentication_uncertain?
|
30
31
|
raise Hubspot::ConfigurationError.new("You must provide either an access_token or an hapikey")
|
31
32
|
end
|
32
33
|
|
34
|
+
if hapikey.present?
|
35
|
+
Hubspot::Deprecator.build.deprecation_warning("hapikey", "please use access_token instead. See https://developers.hubspot.com/docs/api/migrate-an-api-key-integration-to-a-private-app")
|
36
|
+
end
|
37
|
+
|
33
38
|
if access_token.present?
|
34
39
|
Hubspot::Connection.headers("Authorization" => "Bearer #{access_token}")
|
35
40
|
end
|
data/lib/hubspot/connection.rb
CHANGED
@@ -7,7 +7,7 @@ module Hubspot
|
|
7
7
|
url = generate_url(path, opts)
|
8
8
|
response = get(url, format: :json, read_timeout: read_timeout(opts), open_timeout: open_timeout(opts))
|
9
9
|
log_request_and_response url, response
|
10
|
-
handle_response(response)
|
10
|
+
handle_response(response).parsed_response
|
11
11
|
end
|
12
12
|
|
13
13
|
def post_json(path, opts)
|
@@ -24,9 +24,9 @@ module Hubspot
|
|
24
24
|
)
|
25
25
|
|
26
26
|
log_request_and_response url, response, opts[:body]
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
handle_response(response).yield_self do |r|
|
28
|
+
no_parse ? r : r.parsed_response
|
29
|
+
end
|
30
30
|
end
|
31
31
|
|
32
32
|
def put_json(path, options)
|
@@ -43,17 +43,16 @@ module Hubspot
|
|
43
43
|
)
|
44
44
|
|
45
45
|
log_request_and_response(url, response, options[:body])
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
handle_response(response).yield_self do |r|
|
47
|
+
no_parse ? r : r.parsed_response
|
48
|
+
end
|
49
49
|
end
|
50
50
|
|
51
51
|
def delete_json(path, opts)
|
52
52
|
url = generate_url(path, opts)
|
53
53
|
response = delete(url, format: :json, read_timeout: read_timeout(opts), open_timeout: open_timeout(opts))
|
54
54
|
log_request_and_response url, response, opts[:body]
|
55
|
-
|
56
|
-
response
|
55
|
+
handle_response(response)
|
57
56
|
end
|
58
57
|
|
59
58
|
protected
|
@@ -67,11 +66,10 @@ module Hubspot
|
|
67
66
|
end
|
68
67
|
|
69
68
|
def handle_response(response)
|
70
|
-
if response.success?
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end
|
69
|
+
return response if response.success?
|
70
|
+
|
71
|
+
raise(Hubspot::NotFoundError.new(response)) if response.not_found?
|
72
|
+
raise(Hubspot::RequestError.new(response))
|
75
73
|
end
|
76
74
|
|
77
75
|
def log_request_and_response(uri, response, body=nil)
|
@@ -149,4 +147,12 @@ module Hubspot
|
|
149
147
|
get(url, body: opts[:body], headers: opts[:headers])
|
150
148
|
end
|
151
149
|
end
|
150
|
+
|
151
|
+
class CustomEventConnection < Connection
|
152
|
+
def self.trigger(path, opts)
|
153
|
+
url = generate_url(path, opts[:params], { hapikey: true })
|
154
|
+
headers = (opts[:headers] || {}).merge('content-type': 'application/json')
|
155
|
+
post(url, body: opts[:body].to_json, headers: headers)
|
156
|
+
end
|
157
|
+
end
|
152
158
|
end
|
data/lib/hubspot/contact.rb
CHANGED
@@ -12,7 +12,6 @@ class Hubspot::Contact < Hubspot::Resource
|
|
12
12
|
MERGE_PATH = '/contacts/v1/contact/merge-vids/:id/'
|
13
13
|
SEARCH_PATH = '/contacts/v1/search/query'
|
14
14
|
UPDATE_PATH = '/contacts/v1/contact/vid/:id/profile'
|
15
|
-
UPDATE_PATH = '/contacts/v1/contact/vid/:id/profile'
|
16
15
|
BATCH_UPDATE_PATH = '/contacts/v1/contact/batch'
|
17
16
|
|
18
17
|
class << self
|
@@ -28,6 +27,11 @@ class Hubspot::Contact < Hubspot::Resource
|
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
30
|
+
def find_by_vid(vid)
|
31
|
+
response = Hubspot::Connection.get_json(FIND_PATH, id: vid)
|
32
|
+
from_result(response)
|
33
|
+
end
|
34
|
+
|
31
35
|
def find_by_email(email)
|
32
36
|
response = Hubspot::Connection.get_json(FIND_BY_EMAIL_PATH, email: email)
|
33
37
|
from_result(response)
|
data/lib/hubspot/contact_list.rb
CHANGED
@@ -10,7 +10,6 @@ module Hubspot
|
|
10
10
|
RECENT_CONTACTS_PATH = LIST_PATH + '/contacts/recent'
|
11
11
|
ADD_CONTACT_PATH = LIST_PATH + '/add'
|
12
12
|
REMOVE_CONTACT_PATH = LIST_PATH + '/remove'
|
13
|
-
REFRESH_PATH = LIST_PATH + '/refresh'
|
14
13
|
|
15
14
|
class << self
|
16
15
|
# {http://developers.hubspot.com/docs/methods/lists/create_list}
|
@@ -92,12 +91,6 @@ module Hubspot
|
|
92
91
|
end
|
93
92
|
end
|
94
93
|
|
95
|
-
# {http://developers.hubspot.com/docs/methods/lists/refresh_list}
|
96
|
-
def refresh
|
97
|
-
response = Hubspot::Connection.post_json(REFRESH_PATH, params: { list_id: @id, no_parse: true }, body: {})
|
98
|
-
response.code == 204
|
99
|
-
end
|
100
|
-
|
101
94
|
# {http://developers.hubspot.com/docs/methods/lists/add_contact_to_list}
|
102
95
|
def add(contacts)
|
103
96
|
contact_ids = [contacts].flatten.uniq.compact.map(&:id)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'hubspot/utils'
|
2
|
+
|
3
|
+
module Hubspot
|
4
|
+
#
|
5
|
+
# HubSpot Custom Behavioral Events HTTP API
|
6
|
+
#
|
7
|
+
# {https://developers.hubspot.com/docs/api/analytics/events}
|
8
|
+
#
|
9
|
+
class CustomEvent
|
10
|
+
POST_EVENT_PATH = '/events/v3/send'
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def trigger(event_name, email, properties, options = {})
|
14
|
+
options[:params] ||= {}
|
15
|
+
options[:body] ||= {}
|
16
|
+
options[:body].merge!(
|
17
|
+
eventName: "#{Hubspot::Config.custom_event_prefix}_#{event_name}",
|
18
|
+
email: email,
|
19
|
+
properties: properties
|
20
|
+
)
|
21
|
+
Hubspot::CustomEventConnection.trigger(POST_EVENT_PATH, options).success?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|