hubspot-api-ruby 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8c321bf08699c556e265ef8bb24a6f66ad0993ed284a66adb4dacbcc5263748
4
- data.tar.gz: 9a9fd7fccb6ac668affde2373ed824a3f5ac95fc7ed1ca6afc90e7e523dcd83e
3
+ metadata.gz: 5039b18c952923db03dbe7f0ae2f1ae676008179860804e34182298e6acbbcfb
4
+ data.tar.gz: 13a047c2ff4858e64cd00c173513bf0643d79ba9dc9b815d6017c148af6bf2fb
5
5
  SHA512:
6
- metadata.gz: 872f7fc25b8acfd21ca29fa0223f36506993581ced761f90838adcb5425bad857bec190f5b804f9c466f1919988d76e80b5834004228e722ca920216b2b92406
7
- data.tar.gz: f9608077e1a5346205baf9216259fbfdee3e6d49f1abac18ac05e5226b3c05db86b61c0ed6d0e266510c2b9cd898e899da66e12335bdf8a4b2796677bac788ff
6
+ metadata.gz: 17ac11777f8b4c3bb6e95f29a43ffb3ae6b1cba2ab1a27f37c423390fd6603bad8a9b140ebcaaca3df2a74995407dbcafadeceeb853a950fdb0fb8d5a0f289a3
7
+ data.tar.gz: ae7cfcbbdf2a34c7cec6505f34144436f055f7902cb1d8497845d33fff0fc25f90376c6bd221c2c4fb9c59b8cf4dc44efdb75479a6a2322bcd6970bb3b76ff43
data/README.md CHANGED
@@ -109,149 +109,9 @@ At this time, OAuth tokens are configured globally rather than on a per-connecti
109
109
 
110
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
111
 
112
+ ### Example
112
113
 
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
207
-
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()`.
209
-
210
- ```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>
228
- ```
229
-
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
241
-
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
- ```
245
-
246
- ### Deleting a resource
247
-
248
- ```ruby
249
- irb(main):001:0> contact = Hubspot::Contact.find(9324874)
250
- => #<Hubspot::Contact:0x000055a87c87aee0 ...snip... >
251
-
252
- irb(main):002:0> contact.delete
253
- => true
254
- ```
114
+ https://github.com/lounna-sas/hubspot-api-ruby/wiki
255
115
 
256
116
  ## Resource types
257
117
 
@@ -1,9 +1,9 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "hubspot-api-ruby"
3
- s.version = "0.8.0"
3
+ s.version = "0.8.1"
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"]
@@ -20,6 +20,7 @@ require 'hubspot/topic'
20
20
  require 'hubspot/deal'
21
21
  require 'hubspot/deal_pipeline'
22
22
  require 'hubspot/deal_properties'
23
+ require 'hubspot/association'
23
24
  require 'hubspot/deprecator'
24
25
  require 'hubspot/owner'
25
26
  require 'hubspot/engagement'
@@ -0,0 +1,80 @@
1
+ class Hubspot::Association
2
+ COMPANY_TO_CONTACT = 2
3
+ DEAL_TO_CONTACT = 3
4
+ CONTACT_TO_DEAL = 4
5
+ DEAL_TO_COMPANY = 5
6
+ COMPANY_TO_DEAL = 6
7
+ DEFINITION_TARGET_TO_CLASS = {
8
+ 2 => Hubspot::Contact,
9
+ 3 => Hubspot::Contact,
10
+ 4 => Hubspot::Deal,
11
+ 5 => Hubspot::Company,
12
+ 6 => Hubspot::Deal
13
+ }.freeze
14
+
15
+ BATCH_CREATE_PATH = '/crm-associations/v1/associations/create-batch'
16
+ BATCH_DELETE_PATH = '/crm-associations/v1/associations/delete-batch'
17
+ ASSOCIATIONS_PATH = '/crm-associations/v1/associations/:resource_id/HUBSPOT_DEFINED/:definition_id'
18
+
19
+ class << self
20
+ def create(from_id, to_id, definition_id)
21
+ batch_create([{ from_id: from_id, to_id: to_id, definition_id: definition_id }])
22
+ end
23
+
24
+ # Make multiple associations in a single API call
25
+ # {https://developers.hubspot.com/docs/methods/crm-associations/batch-associate-objects}
26
+ # usage:
27
+ # Hubspot::Association.batch_create([{ from_id: 1, to_id: 2, definition_id: Hubspot::Association::COMPANY_TO_CONTACT }])
28
+ def batch_create(associations)
29
+ request = associations.map { |assocation| build_association_body(assocation) }
30
+ Hubspot::Connection.put_json(BATCH_CREATE_PATH, params: { no_parse: true }, body: request).success?
31
+ end
32
+
33
+ def delete(from_id, to_id, definition_id)
34
+ batch_delete([{from_id: from_id, to_id: to_id, definition_id: definition_id}])
35
+ end
36
+
37
+ # Remove multiple associations in a single API call
38
+ # {https://developers.hubspot.com/docs/methods/crm-associations/batch-delete-associations}
39
+ # usage:
40
+ # Hubspot::Association.batch_delete([{ from_id: 1, to_id: 2, definition_id: Hubspot::Association::COMPANY_TO_CONTACT }])
41
+ def batch_delete(associations)
42
+ request = associations.map { |assocation| build_association_body(assocation) }
43
+ Hubspot::Connection.put_json(BATCH_DELETE_PATH, params: { no_parse: true }, body: request).success?
44
+ end
45
+
46
+ # Retrieve all associated resources given a source (resource_id) and a kind (definition_id)
47
+ # Example: if resource_id is a deal, using DEAL_TO_CONTACT will find every contact associated with the deal
48
+ # {https://developers.hubspot.com/docs/methods/crm-associations/get-associations}
49
+ # Warning: it will make N+M queries, where
50
+ # N is the number of PagedCollection requests necessary to get all ids,
51
+ # and M is the number of results, each resulting in a find
52
+ # usage:
53
+ # Hubspot::Association.all(42, Hubspot::Association::DEAL_TO_CONTACT)
54
+ def all(resource_id, definition_id)
55
+ opts = { resource_id: resource_id, definition_id: definition_id }
56
+ klass = DEFINITION_TARGET_TO_CLASS[definition_id]
57
+ raise(Hubspot::InvalidParams, 'Definition not supported') unless klass.present?
58
+
59
+ collection = Hubspot::PagedCollection.new(opts) do |options, offset, limit|
60
+ params = options.merge(offset: offset, limit: limit)
61
+ response = Hubspot::Connection.get_json(ASSOCIATIONS_PATH, params)
62
+
63
+ resources = response['results'].map { |result| klass.find(result) }
64
+ [resources, response['offset'], response['has-more']]
65
+ end
66
+ collection.resources
67
+ end
68
+
69
+ private
70
+
71
+ def build_association_body(assocation)
72
+ {
73
+ fromObjectId: assocation[:from_id],
74
+ toObjectId: assocation[:to_id],
75
+ category: 'HUBSPOT_DEFINED',
76
+ definitionId: assocation[:definition_id]
77
+ }
78
+ end
79
+ end
80
+ 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(id, contact_id, Hubspot::Association::COMPANY_TO_CONTACT)
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(id, contact_id, Hubspot::Association::COMPANY_TO_CONTACT)
97
86
  end
98
87
 
99
88
  def batch_update(companies, opts = {})
@@ -12,8 +12,6 @@ module Hubspot
12
12
  DEAL_PATH = "/deals/v1/deal/:deal_id"
13
13
  RECENT_UPDATED_PATH = "/deals/v1/deal/recent/modified"
14
14
  UPDATE_DEAL_PATH = '/deals/v1/deal/:deal_id'
15
- ASSOCIATE_DEAL_PATH = '/deals/v1/deal/:deal_id/associations/:OBJECTTYPE?id=:objectId'
16
- ASSOCIATED_DEAL_PATH = "/deals/v1/deal/associated/:objectType/:objectId"
17
15
 
18
16
  attr_reader :properties
19
17
  attr_reader :portal_id
@@ -61,25 +59,33 @@ module Hubspot
61
59
  response.success?
62
60
  end
63
61
 
64
- # Associate a deal with a contact or company
65
- # {http://developers.hubspot.com/docs/methods/deals/associate_deal}
66
- # Usage
67
- # Hubspot::Deal.associate!(45146940, [], [52])
68
- def associate!(deal_id, company_ids=[], vids=[])
69
- objecttype = company_ids.any? ? 'COMPANY' : 'CONTACT'
70
- object_ids = (company_ids.any? ? company_ids : vids).join('&id=')
71
- Hubspot::Connection.put_json(ASSOCIATE_DEAL_PATH, params: { deal_id: deal_id, OBJECTTYPE: objecttype, objectId: object_ids}, body: {})
72
- end
73
-
74
- # Didssociate a deal with a contact or company
75
- # {https://developers.hubspot.com/docs/methods/deals/delete_association}
76
- # Usage
77
- # Hubspot::Deal.dissociate!(45146940, [], [52])
78
- def dissociate!(deal_id, company_ids=[], vids=[])
79
- objecttype = company_ids.any? ? 'COMPANY' : 'CONTACT'
80
- object_ids = (company_ids.any? ? company_ids : vids).join('&id=')
81
- Hubspot::Connection.delete_json(ASSOCIATE_DEAL_PATH, { deal_id: deal_id, OBJECTTYPE: objecttype, objectId: object_ids })
82
- end
62
+ # Associate a deal with a contact or company
63
+ # {http://developers.hubspot.com/docs/methods/deals/associate_deal}
64
+ # Usage
65
+ # Hubspot::Deal.associate!(45146940, [32], [52])
66
+ def associate!(deal_id, company_ids=[], vids=[])
67
+ associations = company_ids.map do |id|
68
+ { from_id: deal_id, to_id: id, definition_id: Hubspot::Association::DEAL_TO_COMPANY }
69
+ end
70
+ associations += vids.map do |id|
71
+ { from_id: deal_id, to_id: id, definition_id: Hubspot::Association::DEAL_TO_CONTACT }
72
+ end
73
+ Hubspot::Association.batch_create(associations)
74
+ end
75
+
76
+ # Didssociate a deal with a contact or company
77
+ # {https://developers.hubspot.com/docs/methods/deals/delete_association}
78
+ # Usage
79
+ # Hubspot::Deal.dissociate!(45146940, [32], [52])
80
+ def dissociate!(deal_id, company_ids=[], vids=[])
81
+ associations = company_ids.map do |id|
82
+ { from_id: deal_id, to_id: id, definition_id: Hubspot::Association::DEAL_TO_COMPANY }
83
+ end
84
+ associations += vids.map do |id|
85
+ { from_id: deal_id, to_id: id, definition_id: Hubspot::Association::DEAL_TO_CONTACT }
86
+ end
87
+ Hubspot::Association.batch_delete(associations)
88
+ end
83
89
 
84
90
  def find(deal_id)
85
91
  response = Hubspot::Connection.get_json(DEAL_PATH, { deal_id: deal_id })
@@ -125,20 +131,15 @@ module Hubspot
125
131
  end
126
132
 
127
133
  # Find all deals associated to a contact or company
128
- # {http://developers.hubspot.com/docs/methods/deals/get-associated-deals}
129
134
  # @param object [Hubspot::Contact || Hubspot::Company] a contact or company
130
135
  # @return [Array] Array of Hubspot::Deal records
131
136
  def find_by_association(object)
132
- path = ASSOCIATED_DEAL_PATH
133
- objectType = case object
134
- when Hubspot::Company then :company
135
- when Hubspot::Contact then :contact
136
- else raise(Hubspot::InvalidParams, "Instance type not supported")
137
- end
138
-
139
- params = { objectType: objectType, objectId: object.id }
140
- response = Hubspot::Connection.get_json(path, params)
141
- response["results"].map { |deal_id| find(deal_id) }
137
+ definition = case object
138
+ when Hubspot::Company then Hubspot::Association::COMPANY_TO_DEAL
139
+ when Hubspot::Contact then Hubspot::Association::CONTACT_TO_DEAL
140
+ else raise(Hubspot::InvalidParams, 'Instance type not supported')
141
+ end
142
+ Hubspot::Association.all(object.id, definition)
142
143
  end
143
144
  end
144
145
 
@@ -98,7 +98,7 @@ module Hubspot
98
98
  end
99
99
 
100
100
  def [](name)
101
- @changes[name] || @properties[name]
101
+ @changes[name] || @properties.dig(name, 'value')
102
102
  end
103
103
 
104
104
  def reload
@@ -238,7 +238,7 @@ module Hubspot
238
238
  singleton_class.instance_eval do
239
239
  keys.each do |k|
240
240
  # Define a getter
241
- define_method(k) { @changes[k.to_sym] || @properties.dig(k, "value") }
241
+ define_method(k) { @changes[k.to_sym] || @properties.dig(k, 'value') }
242
242
 
243
243
  # Define a setter
244
244
  define_method("#{k}=") do |v|
@@ -257,7 +257,7 @@ module Hubspot
257
257
  # Call the new setter
258
258
  return send(method_name, arguments[0])
259
259
  elsif @properties.key?(method_name)
260
- return @properties[method_name]
260
+ return @properties[method_name]['value']
261
261
  else
262
262
  super
263
263
  end
@@ -267,4 +267,4 @@ module Hubspot
267
267
  (@properties && @properties.key?(method_name)) || super
268
268
  end
269
269
  end
270
- end
270
+ end
@@ -0,0 +1,160 @@
1
+ RSpec.describe Hubspot::Association do
2
+ before { Hubspot.configure(hapikey: 'demo') }
3
+
4
+ let(:portal_id) { 62515 }
5
+ let(:company) { create :company }
6
+ let(:contact) { create :contact }
7
+
8
+ describe '.create' do
9
+ context 'with a valid ID' do
10
+ cassette
11
+ subject { described_class.create(company.id, contact.id, described_class::COMPANY_TO_CONTACT) }
12
+
13
+ it 'associates the resources' do
14
+ expect(subject).to be true
15
+ expect(company.contact_ids.resources).to eq [contact.id]
16
+ end
17
+ end
18
+
19
+ context 'with an invalid ID' do
20
+ cassette
21
+ subject { described_class.create(company.id, 1234, described_class::COMPANY_TO_CONTACT) }
22
+
23
+ it 'raises an error' do
24
+ expect { subject }.to raise_error(Hubspot::RequestError, /One or more associations are invalid/)
25
+ end
26
+ end
27
+ end
28
+
29
+ describe '.batch_create' do
30
+ let(:deal) { Hubspot::Deal.create!(portal_id, [], [], {}) }
31
+
32
+ subject { described_class.batch_create(associations) }
33
+
34
+ context 'with a valid request' do
35
+ cassette
36
+ let(:associations) do
37
+ [
38
+ { from_id: deal.deal_id, to_id: contact.id, definition_id: described_class::DEAL_TO_CONTACT },
39
+ { from_id: deal.deal_id, to_id: company.id, definition_id: described_class::DEAL_TO_COMPANY }
40
+ ]
41
+ end
42
+
43
+ it 'associates the resources' do
44
+ expect(subject).to be true
45
+ find_deal = Hubspot::Deal.find(deal.deal_id)
46
+ expect(find_deal.vids).to eq [contact.id]
47
+ expect(find_deal.company_ids).to eq [company.id]
48
+ end
49
+ end
50
+
51
+ context 'with an invalid ID' do
52
+ cassette
53
+ let(:associations) do
54
+ [
55
+ { from_id: deal.deal_id, to_id: 1234, definition_id: described_class::DEAL_TO_CONTACT },
56
+ { from_id: deal.deal_id, to_id: company.id, definition_id: described_class::DEAL_TO_COMPANY }
57
+ ]
58
+ end
59
+
60
+ it 'raises an error' do
61
+ expect { subject }.to raise_error(Hubspot::RequestError, /One or more associations are invalid/)
62
+ find_deal = Hubspot::Deal.find(deal.deal_id)
63
+ expect(find_deal.vids).to eq []
64
+ expect(find_deal.company_ids).to eq []
65
+ end
66
+ end
67
+ end
68
+
69
+ describe '.delete' do
70
+ subject { described_class.delete(company.id, contact_id_to_dissociate, described_class::COMPANY_TO_CONTACT) }
71
+ before { described_class.create(company.id, contact.id, described_class::COMPANY_TO_CONTACT) }
72
+
73
+ context 'with a valid ID' do
74
+ cassette
75
+ let(:contact_id_to_dissociate) { contact.id }
76
+
77
+ it 'dissociates the resources' do
78
+ expect(subject).to be true
79
+ expect(company.contact_ids.resources).to eq []
80
+ end
81
+ end
82
+
83
+ context 'with an invalid ID' do
84
+ cassette
85
+ let(:contact_id_to_dissociate) { 1234 }
86
+
87
+ it 'does not raise an error' do
88
+ expect(subject).to be true
89
+ expect(company.contact_ids.resources).to eq [contact.id]
90
+ end
91
+ end
92
+ end
93
+
94
+ describe '.batch_delete' do
95
+ let(:deal) { Hubspot::Deal.create!(portal_id, [company.id], [contact.id], {}) }
96
+
97
+ subject { described_class.batch_delete(associations) }
98
+
99
+ context 'with a valid request' do
100
+ cassette
101
+ let(:associations) do
102
+ [
103
+ { from_id: deal.deal_id, to_id: contact.id, definition_id: described_class::DEAL_TO_CONTACT },
104
+ { from_id: deal.deal_id, to_id: company.id, definition_id: described_class::DEAL_TO_COMPANY }
105
+ ]
106
+ end
107
+
108
+ it 'dissociates the resources' do
109
+ expect(subject).to be true
110
+ find_deal = Hubspot::Deal.find(deal.deal_id)
111
+ expect(find_deal.vids).to eq []
112
+ expect(find_deal.company_ids).to eq []
113
+ end
114
+ end
115
+
116
+ context 'with an invalid ID' do
117
+ cassette
118
+ let(:associations) do
119
+ [
120
+ { from_id: deal.deal_id, to_id: 1234, definition_id: described_class::DEAL_TO_CONTACT },
121
+ { from_id: deal.deal_id, to_id: company.id, definition_id: described_class::DEAL_TO_COMPANY }
122
+ ]
123
+ end
124
+
125
+ it 'does not raise an error, removes the valid associations' do
126
+ expect(subject).to be true
127
+ find_deal = Hubspot::Deal.find(deal.deal_id)
128
+ expect(find_deal.vids).to eq [contact.id]
129
+ expect(find_deal.company_ids).to eq []
130
+ end
131
+ end
132
+ end
133
+
134
+ describe '.all' do
135
+ subject { described_class.all(resource_id, definition_id) }
136
+
137
+ context 'with valid params' do
138
+ cassette
139
+
140
+ let(:resource_id) { deal.deal_id }
141
+ let(:definition_id) { described_class::DEAL_TO_CONTACT }
142
+ let(:deal) { Hubspot::Deal.create!(portal_id, [], contact_ids, {}) }
143
+ let(:contact_ids) { [contact.id, second_contact.id] }
144
+ let(:second_contact) { create :contact }
145
+
146
+ it 'finds the resources' do
147
+ expect(subject.map(&:id)).to contain_exactly(*contact_ids)
148
+ end
149
+ end
150
+
151
+ context 'with unsupported definition' do
152
+ let(:resource_id) { 1234 }
153
+ let(:definition_id) { -1 }
154
+
155
+ it 'raises an error' do
156
+ expect { subject }.to raise_error(Hubspot::InvalidParams, 'Definition not supported')
157
+ end
158
+ end
159
+ end
160
+ end
@@ -1,16 +1,5 @@
1
1
  RSpec.describe Hubspot::Company do
2
- # let(:example_company_hash) do
3
- # VCR.use_cassette("company_example") do
4
- # HTTParty.get("https://api.hubapi.com/companies/v2/companies/21827084?hapikey=demo").parsed_response
5
- # end
6
- # end
7
- # let(:company_with_contacts_hash) do
8
- # VCR.use_cassette("company_with_contacts") do
9
- # HTTParty.get("https://api.hubapi.com/companies/v2/companies/115200636?hapikey=demo").parsed_response
10
- # end
11
- # end
12
-
13
- before{ Hubspot.configure(hapikey: "demo") }
2
+ before { Hubspot.configure(hapikey: 'demo') }
14
3
 
15
4
  it_behaves_like "a saveable resource", :company do
16
5
  def set_property(company)
@@ -279,13 +268,11 @@ RSpec.describe Hubspot::Company do
279
268
  subject { described_class.add_contact company.id, contact.id }
280
269
 
281
270
  it 'returns success' do
282
- expect(subject).to be_truthy
271
+ expect(subject).to be true
283
272
  end
284
273
 
285
274
  it 'adds the contact to the company' do
286
- expect {
287
- subject
288
- }.to change { company.contact_ids }.by([contact.id])
275
+ expect { subject }.to change { company.contact_ids }.by([contact.id])
289
276
  end
290
277
  end
291
278
 
@@ -294,12 +281,10 @@ RSpec.describe Hubspot::Company do
294
281
 
295
282
  let(:company) { create :company }
296
283
 
297
- subject { described_class.add_contact company.id, 1 }
284
+ subject { described_class.add_contact company.id, 1234 }
298
285
 
299
286
  it 'raises an error' do
300
- expect {
301
- subject
302
- }.to raise_error(Hubspot::RequestError, /Contact with the vid/)
287
+ expect { subject }.to raise_error(Hubspot::RequestError, /CONTACT=1234 is not valid/)
303
288
  end
304
289
  end
305
290
 
@@ -309,32 +294,30 @@ RSpec.describe Hubspot::Company do
309
294
  subject { described_class.add_contact 1, 1 }
310
295
 
311
296
  it 'raises an error' do
312
- expect {
313
- subject
314
- }.to raise_error(Hubspot::RequestError, /company with the ID/)
297
+ expect { subject }.to raise_error(Hubspot::RequestError, /COMPANY=1 is not valid/)
315
298
  end
316
299
  end
317
300
  end
318
301
 
319
302
  describe '.remove_contact' do
320
303
  context 'with a valid company ID and contact ID' do
321
- cassette allow_playback_repeats: true
304
+ cassette
322
305
 
323
- let!(:company) { create :company }
324
- let!(:contact) { create :contact, associatedCompanyId: company.id }
306
+ let(:company) { create :company }
307
+ let(:contact) { create :contact }
325
308
 
309
+ before { described_class.add_contact(company.id, contact.id) }
326
310
  subject { described_class.remove_contact company.id, contact.id }
327
311
 
328
312
  it 'returns success' do
329
- expect(subject).to be_truthy
313
+ expect(subject).to be true
330
314
  end
331
315
 
332
- # Testing this turns out to be hard since using associatedCompanyId doesn't immediately add
333
- # the contact to the company but triggers some background job to perform the update. Since
334
- # we're testing the gem interface and not the API (that's Hubspot's job) this should be OK to
335
- # leave out.
336
- #
337
- # it 'removes the contact from the company'
316
+ it 'removes the contact from the company' do
317
+ expect(company.contact_ids.resources).to eq [contact.id]
318
+ subject
319
+ expect(company.contact_ids.resources).to eq []
320
+ end
338
321
  end
339
322
  end
340
323
  end
@@ -11,7 +11,7 @@ describe Hubspot::Deal do
11
11
  end
12
12
  end
13
13
 
14
- before{ Hubspot.configure(hapikey: "demo") }
14
+ before { Hubspot.configure(hapikey: 'demo') }
15
15
 
16
16
  describe "#initialize" do
17
17
  subject{ Hubspot::Deal.new(example_deal_hash) }
@@ -107,6 +107,35 @@ describe Hubspot::Deal do
107
107
  end
108
108
  end
109
109
  end
110
+
111
+
112
+ describe '.associate' do
113
+ cassette
114
+ let(:deal) { Hubspot::Deal.create!(portal_id, [], [], {}) }
115
+ let(:company) { create :company }
116
+ let(:contact) { create :contact }
117
+ let(:contact_id) { contact.id }
118
+
119
+ subject { Hubspot::Deal.associate!(deal.deal_id, [company.id], [contact_id]) }
120
+
121
+ it 'associates the deal to the contact and the company' do
122
+ subject
123
+ find_deal = Hubspot::Deal.find(deal.deal_id)
124
+ find_deal.company_ids.should eql [company.id]
125
+ find_deal.vids.should eql [contact.id]
126
+ end
127
+
128
+ context 'when an id is invalid' do
129
+ let(:contact_id) { 1234 }
130
+
131
+ it 'raises an error and do not changes associations' do
132
+ expect { subject }.to raise_error(Hubspot::RequestError)
133
+ find_deal = Hubspot::Deal.find(deal.deal_id)
134
+ find_deal.company_ids.should eql []
135
+ find_deal.vids.should eql []
136
+ end
137
+ end
138
+ end
110
139
 
111
140
  describe ".find" do
112
141
  cassette "deal_find"
@@ -119,9 +148,9 @@ describe Hubspot::Deal do
119
148
  end
120
149
 
121
150
  describe '.find_by_company' do
122
- cassette 'deal_find_by_company'
123
- let(:company) { Hubspot::Company.create(name: 'Test Company') }
124
- let(:company_id) { company.id }
151
+ cassette
152
+ let(:company) { create :company }
153
+ let!(:deal) { Hubspot::Deal.create!(portal_id, [company.id], [], { amount: amount }) }
125
154
 
126
155
  it 'returns company deals' do
127
156
  deals = Hubspot::Deal.find_by_company(company)
@@ -130,6 +159,18 @@ describe Hubspot::Deal do
130
159
  end
131
160
  end
132
161
 
162
+ describe '.find_by_contact' do
163
+ cassette
164
+ let(:contact) { create :contact }
165
+ let!(:deal) { Hubspot::Deal.create!(portal_id, [], [contact.id], { amount: amount }) }
166
+
167
+ it 'returns contact deals' do
168
+ deals = Hubspot::Deal.find_by_contact(contact)
169
+ deals.first.deal_id.should eql deal.deal_id
170
+ deals.first.properties['amount'].should eql amount
171
+ end
172
+ end
173
+
133
174
  describe '.recent' do
134
175
  cassette 'find_all_recent_updated_deals'
135
176
 
@@ -51,4 +51,44 @@ RSpec.describe Hubspot::Resource do
51
51
  end
52
52
  end
53
53
  end
54
- end
54
+
55
+ describe '#[]' do
56
+ context 'using new' do
57
+ let(:resource) { described_class.new(properties) }
58
+ let(:properties) { { id: 1, firstname: 'John', lastname: 'Wayne' } }
59
+
60
+ it { expect(resource[:firstname]).to eq 'John' }
61
+ it { expect(resource['lastname']).to eq 'Wayne' }
62
+ it { expect(resource[:middlename]).to be nil }
63
+ end
64
+
65
+ context 'using from_result' do
66
+ let(:resource) { described_class.from_result({ properties: properties }) }
67
+ let(:properties) { { id: { 'value' => 1 }, firstname: { 'value' => 'John' }, lastname: { 'value' => 'Wayne' } } }
68
+
69
+ it { expect(resource[:firstname]).to eq 'John' }
70
+ it { expect(resource['lastname']).to eq 'Wayne' }
71
+ it { expect(resource[:middlename]).to be nil }
72
+ end
73
+ end
74
+
75
+ describe '#adding_accessors' do
76
+ describe 'getters' do
77
+ context 'using new' do
78
+ let(:resource) { described_class.new(properties) }
79
+ let(:properties) { { id: 1, firstname: 'John', lastname: 'Wayne' } }
80
+
81
+ it { expect(resource.firstname).to eq 'John' }
82
+ it { expect(resource.lastname).to eq 'Wayne' }
83
+ end
84
+
85
+ context 'using from_result' do
86
+ let(:resource) { described_class.from_result({ properties: properties }) }
87
+ let(:properties) { { id: { 'value' => 1 }, firstname: { 'value' => 'John' }, lastname: { 'value' => 'Wayne' } } }
88
+
89
+ it { expect(resource.firstname).to eq 'John' }
90
+ it { expect(resource.lastname).to eq 'Wayne' }
91
+ end
92
+ end
93
+ end
94
+ end
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hubspot-api-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan
8
- - Juliette
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2019-12-11 00:00:00.000000000 Z
11
+ date: 2019-12-31 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: activesupport
@@ -266,7 +265,6 @@ dependencies:
266
265
  description: hubspot-api-ruby is a wrapper for the HubSpot REST API
267
266
  email:
268
267
  - jonathan@hoggo.com
269
- - juliette@hoggo.com
270
268
  executables: []
271
269
  extensions: []
272
270
  extra_rdoc_files: []
@@ -280,6 +278,7 @@ files:
280
278
  - Rakefile
281
279
  - hubspot-api-ruby.gemspec
282
280
  - lib/hubspot-api-ruby.rb
281
+ - lib/hubspot/association.rb
283
282
  - lib/hubspot/blog.rb
284
283
  - lib/hubspot/collection.rb
285
284
  - lib/hubspot/company.rb
@@ -311,6 +310,7 @@ files:
311
310
  - spec/factories/companies.rb
312
311
  - spec/factories/contacts.rb
313
312
  - spec/lib/hubspot-ruby_spec.rb
313
+ - spec/lib/hubspot/association_spec.rb
314
314
  - spec/lib/hubspot/blog_spec.rb
315
315
  - spec/lib/hubspot/company_properties_spec.rb
316
316
  - spec/lib/hubspot/company_spec.rb