hubspot-api-ruby 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +40 -154
  3. data/Rakefile +0 -2
  4. data/hubspot-api-ruby.gemspec +5 -7
  5. data/lib/hubspot/association.rb +106 -0
  6. data/lib/hubspot/company.rb +2 -13
  7. data/lib/hubspot/config.rb +14 -9
  8. data/lib/hubspot/connection.rb +20 -14
  9. data/lib/hubspot/contact.rb +5 -1
  10. data/lib/hubspot/contact_list.rb +0 -7
  11. data/lib/hubspot/custom_event.rb +25 -0
  12. data/lib/hubspot/deal.rb +53 -32
  13. data/lib/hubspot/engagement.rb +0 -1
  14. data/lib/hubspot/exceptions.rb +2 -0
  15. data/lib/hubspot/file.rb +2 -2
  16. data/lib/hubspot/meeting.rb +44 -0
  17. data/lib/hubspot/properties.rb +1 -1
  18. data/lib/hubspot/railtie.rb +0 -4
  19. data/lib/hubspot/resource.rb +4 -4
  20. data/lib/hubspot/utils.rb +0 -30
  21. data/lib/hubspot-api-ruby.rb +3 -0
  22. data/spec/lib/hubspot/association_spec.rb +165 -0
  23. data/spec/lib/hubspot/blog_spec.rb +10 -21
  24. data/spec/lib/hubspot/company_properties_spec.rb +8 -11
  25. data/spec/lib/hubspot/company_spec.rb +18 -36
  26. data/spec/lib/hubspot/config_spec.rb +24 -14
  27. data/spec/lib/hubspot/connection_spec.rb +44 -55
  28. data/spec/lib/hubspot/contact_list_spec.rb +82 -71
  29. data/spec/lib/hubspot/contact_properties_spec.rb +5 -34
  30. data/spec/lib/hubspot/contact_spec.rb +2 -4
  31. data/spec/lib/hubspot/custom_event_spec.rb +28 -0
  32. data/spec/lib/hubspot/deal_pipeline_spec.rb +4 -15
  33. data/spec/lib/hubspot/deal_properties_spec.rb +11 -58
  34. data/spec/lib/hubspot/deal_spec.rb +46 -47
  35. data/spec/lib/hubspot/engagement_spec.rb +21 -40
  36. data/spec/lib/hubspot/event_spec.rb +3 -2
  37. data/spec/lib/hubspot/file_spec.rb +16 -30
  38. data/spec/lib/hubspot/form_spec.rb +34 -34
  39. data/spec/lib/hubspot/meeting_spec.rb +81 -0
  40. data/spec/lib/hubspot/owner_spec.rb +2 -3
  41. data/spec/lib/hubspot/resource_spec.rb +41 -1
  42. data/spec/lib/hubspot/utils_spec.rb +6 -39
  43. data/spec/lib/hubspot-ruby_spec.rb +1 -1
  44. data/spec/spec_helper.rb +13 -4
  45. data/spec/support/vcr.rb +4 -6
  46. metadata +11 -27
  47. data/lib/tasks/hubspot.rake +0 -53
  48. data/spec/lib/hubspot/topic_spec.rb +0 -23
  49. data/spec/lib/tasks/hubspot_spec.rb +0 -119
  50. data/spec/support/capture_output.rb +0 -21
  51. data/spec/support/rake.rb +0 -46
  52. data/spec/support/tests_helper.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8c321bf08699c556e265ef8bb24a6f66ad0993ed284a66adb4dacbcc5263748
4
- data.tar.gz: 9a9fd7fccb6ac668affde2373ed824a3f5ac95fc7ed1ca6afc90e7e523dcd83e
3
+ metadata.gz: 4aa748bc77ade29ff96016950aa3f3d8d9bbb309167b200319798d9edf8664bc
4
+ data.tar.gz: f09d8a29a3dbbcaa18071670e46d4c10c6727731409bbf4a7eee5099d6fd15e2
5
5
  SHA512:
6
- metadata.gz: 872f7fc25b8acfd21ca29fa0223f36506993581ced761f90838adcb5425bad857bec190f5b804f9c466f1919988d76e80b5834004228e722ca920216b2b92406
7
- data.tar.gz: f9608077e1a5346205baf9216259fbfdee3e6d49f1abac18ac05e5226b3c05db86b61c0ed6d0e266510c2b9cd898e899da66e12335bdf8a4b2796677bac788ff
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
- hapikey: <HAPIKEY>,
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 an API key
53
+ ## Authentication with a private app access token
48
54
 
49
- To set the HubSpot API key, aka `hapikey`, run the following:
55
+ To set the HubSpot access token, run the following:
50
56
  ```ruby
51
- Hubspot.configure(hapikey: "YOUR_API_KEY")
57
+ Hubspot.configure(access_token: 'YOUR_ACCESS_TOKEN')
52
58
  ```
53
59
 
54
- If you have a HubSpot account, you can find your API key by logging in and
55
- visiting: https://app.hubspot.com/keys/get
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
- ## Usage
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
- To make working with API endpoints that return multiple resources easier, the returned instances will be wrapped in a collection instance. Just like in Rails, the collection instance provides helper methods for limiting the number of returned resources, paging through the results, and handles passing the options each time a new API call is made. The collection exposes all Ruby Array methods so you can use things like `size()`, `first()`, `last()`, and `map()`.
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
- irb(main):001:0> contacts = Hubspot::Contact.all
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
- For Hubspot resources that support batch updates for updating multiple resources, the collection provides an `update_all()` method:
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
- irb(main):004:0> companies.refresh
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
- ### Deleting a resource
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
- ```ruby
249
- irb(main):001:0> contact = Hubspot::Contact.find(9324874)
250
- => #<Hubspot::Contact:0x000055a87c87aee0 ...snip... >
135
+ ### Example
251
136
 
252
- irb(main):002:0> contact.delete
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
- To add a new test or update a VCR recording, run the test with the `VCR_RECORD`
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
- ```sh
288
- VCR_RECORD=1 bundle exec rspec spec
169
+ ```
170
+ HUBSPOT_ACCESS_TOKEN=xxxx
171
+ HUBSPOT_PORTAL_ID=yyyy
289
172
  ```
290
173
 
291
- [VCR]: https://github.com/vcr/vcr
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
- ## Disclaimer
177
+ ```sh
178
+ VCR_RECORD_MODE=new_episodes bundle exec rspec spec
179
+ ```
294
180
 
295
- This project and the code therein was not created by and is not supported by HubSpot, Inc or any of its affiliates.
181
+ [VCR]: https://github.com/vcr/vcr
data/Rakefile CHANGED
@@ -28,5 +28,3 @@ Rake::RDocTask.new do |rdoc|
28
28
  rdoc.rdoc_files.include('README*')
29
29
  rdoc.rdoc_files.include('lib/**/*.rb')
30
30
  end
31
-
32
- Dir.glob('lib/tasks/*.rake').each { |r| load r }
@@ -1,19 +1,18 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "hubspot-api-ruby"
3
- s.version = "0.8.0"
3
+ s.version = "0.10.0"
4
4
  s.require_paths = ["lib"]
5
- s.authors = ["Jonathan", "Juliette"]
6
- s.email = ["jonathan@hoggo.com", "juliette@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 = "http://github.com/lounna-sas/hubspot-api-ruby"
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/lounna-sas/hubspot-api-ruby/blob/master/History.md"
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
@@ -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::Connection.put_json(
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::Connection.delete_json(
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 = {})
@@ -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["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?
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
@@ -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
- raise(Hubspot::RequestError.new(response)) unless response.success?
28
-
29
- no_parse ? response : response.parsed_response
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
- raise(Hubspot::RequestError.new(response)) unless response.success?
47
-
48
- no_parse ? response : response.parsed_response
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
- raise(Hubspot::RequestError.new(response)) unless response.success?
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
- response.parsed_response
72
- else
73
- raise(Hubspot::RequestError.new(response))
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
@@ -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)
@@ -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