HubspotCommunity / hubspot-ruby

Ruby wrappers for the HubSpot REST API
https://developers.hubspot.com/docs/endpoints
MIT License
190 stars 257 forks source link

Refactoring resource interface into Hubspot::Resource #176

Closed cbisnett closed 5 years ago

cbisnett commented 5 years ago

This PR provides a base object Hubspot::Resource that provides the base CRUD functionality (similar to ActiveRecord) for interacting with Hubspot resources. The goal of this refactoring is to provide a standard implementation across all of the Hubspot objects supported in this library.

Some of the benefits of the Hubspot API and the drawbacks are that the returned data is very verbose. Without wrapper objects, it's annoying to traverse the several layers of nesting required to get some property. The Hubspot endpoints are also inconsistent with which HTTP methods are supported, the naming scheme of the properties, etc. Part of the goal of this refactoring and the library in general is to hide these inconsistencies from developers so they can easily and quickly interact with the Hubspot API through a familiar abstraction.

In addition to creating a basic Hubspot::Resource base class, I've added two new classes Company2 and Contact2 to demonstrate how easy it is to rewrite the existing Company and Contact classes and simplify the duplicated code. Both of these classes support creating, reading, updating, and deleting the resource as well as add some additional specialized methods for searching for a contact by email address and looking up companies with a specific domain.

[chris@chris-5520 hubspot-ruby]$ console
irb(main):001:0> contact = Hubspot::Contact2.find 10998774
=> #<Hubspot::Contact2:46971242437020 @id=10998774>
irb(main):002:0> contact.firstname
=> "Foo"
irb(main):003:0> contact.firstname = "FooBar"
=> "FooBar"
irb(main):004:0> contact.changes
=> {:firstname=>"FooBar"}
irb(main):005:0> contact.save
=> #<Hubspot::Contact2:46971242437020 @id=10998774>
irb(main):006:0> contact[:firstname]
=> {"value"=>"FooBar", "versions"=>[{"value"=>"Foo", "source-type"=>"API", "source-id"=>nil, "source-label"=>nil, "timestamp"=>1546719509765, "selected"=>false}, {"value"=>"Blah", "source-type"=>"API", "source-id"=>nil, "source-label"=>nil, "timestamp"=>1546714596252, "selected"=>false}]}
irb(main):007:0> contact.metadata
=> {"vid"=>10998774, "canonical-vid"=>10998774, "merged-vids"=>[], "portal-id"=>62515, "is-contact"=>true, "profile-token"=>"AO_T-mPbze_wmcQEl6BXZUt9MAeUc1mgxU4-pd7IJjR5gWTGIyMC_EBdl4Kg6Ou7bXmSmpX6RwgEBMPSDuDX7UtR2_ne1DbwHzFEvdaJLaTpxBOBKNfTZsbkcQE1uS9QFUWG0P4vZNSQ", "profile-url"=>"https://app.hubspot.com/contacts/62515/lists/public/contact/_AO_T-mPbze_wmcQEl6BXZUt9MAeUc1mgxU4-pd7IJjR5gWTGIyMC_EBdl4Kg6Ou7bXmSmpX6RwgEBMPSDuDX7UtR2_ne1DbwHzFEvdaJLaTpxBOBKNfTZsbkcQE1uS9QFUWG0P4vZNSQ/", "form-submissions"=>[], "list-memberships"=>[{"static-list-id"=>447447, "internal-list-id"=>448202, "timestamp"=>1546714597376, "vid"=>10998774, "is-member"=>true}, {"static-list-id"=>457793, "internal-list-id"=>458560, "timestamp"=>1546714597377, "vid"=>10998774, "is-member"=>true}, {"static-list-id"=>464229, "internal-list-id"=>465004, "timestamp"=>1546714597377, "vid"=>10998774, "is-member"=>true}, {"static-list-id"=>464230, "internal-list-id"=>465005, "timestamp"=>1546714597377, "vid"=>10998774, "is-member"=>true}, {"static-list-id"=>471986, "internal-list-id"=>472762, "timestamp"=>1546714597377, "vid"=>10998774, "is-member"=>true}, {"static-list-id"=>474346, "internal-list-id"=>475123, "timestamp"=>1546714597376, "vid"=>10998774, "is-member"=>true}, {"static-list-id"=>486613, "internal-list-id"=>487399, "timestamp"=>1546714597377, "vid"=>10998774, "is-member"=>true}], "identity-profiles"=>[{"vid"=>10998774, "saved-at-timestamp"=>1546714596265, "deleted-changed-timestamp"=>0, "identities"=>[{"type"=>"EMAIL", "value"=>"blah@example.com", "timestamp"=>1546714596256, "is-primary"=>true}, {"type"=>"LEAD_GUID", "value"=>"be30556c-843f-4008-9133-723a20c2fccc", "timestamp"=>1546714596262}]}], "merge-audits"=>[]}
irb(main):008:0> contact.delete
=> #<Hubspot::Contact2:46971242437020 @id=10998774>
irb(main):009:0> contact.deleted?
=> true

The same basic functionality works for Company2 with much less code than previously and the implementation would be the same across all resource objects.

This is a minimal working implementation and need some addition support for errors and other things but I think it's enough to show the idea and get some feedback. There is still a bunch of internal cleanup and standardization I want to tackle but getting a standardized interface like this into v1.0 means we can tackle the internal stuff without breaking changes. Please let me know what you think.

SViccari commented 5 years ago

@cbisnett Thank you for working on this! I haven't had time to look at this just yet but I will schedule the time to look at this on Friday (1/11) :)

cbisnett commented 5 years ago

Since I've migrated Contacts and Companies and all existing tests are passing, I'm going to merge this into master to continue to develop there.