hubspot-ruby 0.2.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 16ee939bb4d593cc623309e093902d9a937b9fda
4
- data.tar.gz: d5c6b6765d4a3a7adda09fbb6b9a1768c91b1d86
3
+ metadata.gz: 1ab6604b85e4945f699f88491ad18191a9ceda03
4
+ data.tar.gz: 8b01545c2f8c6d5e51d610233abcd55b2d9027d0
5
5
  SHA512:
6
- metadata.gz: 6f22e580fac3d5a5f4b1ac812cf21040c6105e8fc86ad4e03dc71b0aa0276bf48f4b89cf826d806eb2a7cef5be90a820c47a81a23ca27d34833bdf77a4076dfe
7
- data.tar.gz: d322208c51497e956f63d9cb7453ce90540691af81661bcea456ca3118deb53da6fcb95a9ecd8a91f2dc433a12a47a482bb2eec1fd7043a12e7fd27ef425c32c
6
+ metadata.gz: a5b5d2e3c28f811c44b14e232e308cc2af19cf4050f6963dfa00ce7c0b59022c73fde892adf9e7fe0a5a73cb6d067b512cefdae13510b06164dd0630e4430b63
7
+ data.tar.gz: 3381873ef4e85c46cbf4a17608c66c29414fa40fdf911fb7463a58be117f7a307b06905d3db269f5a56f8d98b68f70f4ed632a6816a7c8d6ffb6801af7770281
data/README.md CHANGED
@@ -37,6 +37,12 @@ Here's what you can do for now:
37
37
  Hubspot::Contact.create!("email@address.com", {firstname: "First", lastname: "Last"})
38
38
  ```
39
39
 
40
+ #### In batches
41
+
42
+ ```ruby
43
+ Hubspot::Contact.create_or_update!([{email: 'smith@example.com', firstname: 'First', lastname: 'Last'}])
44
+ ```
45
+
40
46
  ### Find a contact
41
47
 
42
48
  These methods will return a `Hubspot::Contact` object if successful, `nil` otherwise:
@@ -54,6 +60,11 @@ Given an instance of `Hubspot::Contact`, update its attributes with:
54
60
  contact.update!({firstname: "First", lastname: "Last"})
55
61
  ```
56
62
 
63
+ #### In batches
64
+
65
+ ```ruby
66
+ Hubspot::Contact.create_or_update!([{vid: '12345', firstname: 'First', lastname: 'Last'}])
67
+ ```
57
68
  ## Contributing to hubspot-ruby
58
69
 
59
70
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
data/hubspot-ruby.gemspec CHANGED
@@ -1,11 +1,12 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "hubspot-ruby"
3
- s.version = "0.2.0"
3
+ s.version = "0.3.0"
4
4
  s.require_paths = ["lib"]
5
5
  s.authors = ["Andrew DiMichele"]
6
6
  s.description = "hubspot-ruby is a wrapper for the HubSpot REST API"
7
- s.files = [".rspec", "Gemfile", "Gemfile.lock", "Guardfile", "LICENSE.txt", "README.md", "RELEASING.md", "Rakefile", "hubspot-ruby.gemspec"]
7
+ s.files = [".rspec", "Gemfile", "Guardfile", "LICENSE.txt", "README.md", "RELEASING.md", "Rakefile", "hubspot-ruby.gemspec"]
8
8
  s.files += Dir["lib/**/*.rb"]
9
+ s.files += Dir["lib/**/*.rake"]
9
10
  s.files += Dir["spec/**/*.rb"]
10
11
  s.homepage = "http://github.com/adimichele/hubspot-ruby"
11
12
  s.summary = "hubspot-ruby is a wrapper for the HubSpot REST API"
@@ -16,7 +17,7 @@ Gem::Specification.new do |s|
16
17
 
17
18
  # Add development-only dependencies here
18
19
  s.add_development_dependency("rake")
19
- s.add_development_dependency("rspec")
20
+ s.add_development_dependency("rspec", "~> 2.0")
20
21
  s.add_development_dependency("rr")
21
22
  s.add_development_dependency("webmock", "< 1.10")
22
23
  s.add_development_dependency("vcr")
@@ -58,7 +58,7 @@ module Hubspot
58
58
 
59
59
  params.each do |k,v|
60
60
  if path.match(":#{k}")
61
- path.gsub!(":#{k}",v.to_s)
61
+ path.gsub!(":#{k}", CGI.escape(v.to_s))
62
62
  params.delete(k)
63
63
  end
64
64
  end
@@ -68,13 +68,13 @@ module Hubspot
68
68
  v.is_a?(Array) ? v.map { |value| param_string(k,value) } : param_string(k,v)
69
69
  end.join("&")
70
70
 
71
- path += "?" if query.present?
71
+ path += path.include?('?') ? '&' : "?" if query.present?
72
72
  base_url + path + query
73
73
  end
74
74
 
75
75
  # convert into milliseconds since epoch
76
76
  def converted_value(value)
77
- value.is_a?(Time) ? (value.to_i * 1000) : value
77
+ value.is_a?(Time) ? (value.to_i * 1000) : CGI.escape(value.to_s)
78
78
  end
79
79
 
80
80
  def param_string(key,value)
@@ -6,24 +6,26 @@ module Hubspot
6
6
  #
7
7
  # TODO: work on all endpoints that can specify contact properties, property mode etc... as params. cf pending specs
8
8
  class Contact
9
- CREATE_CONTACT_PATH = '/contacts/v1/contact'
10
- GET_CONTACT_BY_EMAIL_PATH = '/contacts/v1/contact/email/:contact_email/profile'
11
- GET_CONTACTS_BY_EMAIL_PATH = '/contacts/v1/contact/emails/batch'
12
- GET_CONTACT_BY_ID_PATH = '/contacts/v1/contact/vid/:contact_id/profile'
13
- CONTACT_BATCH_PATH = '/contacts/v1/contact/vids/batch'
14
- GET_CONTACT_BY_UTK_PATH = '/contacts/v1/contact/utk/:contact_utk/profile'
15
- GET_CONTACTS_BY_UTK_PATH = '/contacts/v1/contact/utks/batch'
16
- UPDATE_CONTACT_PATH = '/contacts/v1/contact/vid/:contact_id/profile'
17
- DESTROY_CONTACT_PATH = '/contacts/v1/contact/vid/:contact_id'
18
- CONTACTS_PATH = '/contacts/v1/lists/all/contacts/all'
19
- RECENT_CONTACTS_PATH = '/contacts/v1/lists/recently_updated/contacts/recent'
20
- CREATE_OR_UPDATE_PATH = '/contacts/v1/contact/createOrUpdate/email/:contact_email'
21
- QUERY_PATH = '/contacts/v1/search/query'
9
+ CREATE_CONTACT_PATH = '/contacts/v1/contact'
10
+ BATCH_CREATE_OR_UPDATE_PATH = '/contacts/v1/contact/batch/'
11
+ GET_CONTACT_BY_EMAIL_PATH = '/contacts/v1/contact/email/:contact_email/profile'
12
+ GET_CONTACTS_BY_EMAIL_PATH = '/contacts/v1/contact/emails/batch'
13
+ GET_CONTACT_BY_ID_PATH = '/contacts/v1/contact/vid/:contact_id/profile'
14
+ CONTACT_BATCH_PATH = '/contacts/v1/contact/vids/batch'
15
+ GET_CONTACT_BY_UTK_PATH = '/contacts/v1/contact/utk/:contact_utk/profile'
16
+ GET_CONTACTS_BY_UTK_PATH = '/contacts/v1/contact/utks/batch'
17
+ UPDATE_CONTACT_PATH = '/contacts/v1/contact/vid/:contact_id/profile'
18
+ DESTROY_CONTACT_PATH = '/contacts/v1/contact/vid/:contact_id'
19
+ CONTACTS_PATH = '/contacts/v1/lists/all/contacts/all'
20
+ RECENTLY_UPDATED_PATH = '/contacts/v1/lists/recently_updated/contacts/recent'
21
+ CREATE_OR_UPDATE_PATH = '/contacts/v1/contact/createOrUpdate/email/:contact_email'
22
+ QUERY_PATH = '/contacts/v1/search/query'
22
23
 
23
24
  class << self
24
25
  # {https://developers.hubspot.com/docs/methods/contacts/create_contact}
25
26
  def create!(email, params={})
26
- params_with_email = params.stringify_keys.merge('email' => email)
27
+ params_with_email = params.stringify_keys
28
+ params_with_email = params.stringify_keys.merge('email' => email) if email
27
29
  post_data = {properties: Hubspot::Utils.hash_to_properties(params_with_email)}
28
30
  response = Hubspot::Connection.post_json(CREATE_CONTACT_PATH, params: {}, body: post_data )
29
31
  new(response)
@@ -33,21 +35,22 @@ module Hubspot
33
35
  # {https://developers.hubspot.com/docs/methods/contacts/get_recently_updated_contacts}
34
36
  def all(opts={})
35
37
  recent = opts.delete(:recent) { false }
38
+ paged = opts.delete(:paged) { false }
36
39
  path, opts =
37
40
  if recent
38
- [RECENT_CONTACTS_PATH, Hubspot::ContactProperties.add_default_parameters(opts)]
41
+ [RECENTLY_UPDATED_PATH, Hubspot::ContactProperties.add_default_parameters(opts)]
39
42
  else
40
43
  [CONTACTS_PATH, opts]
41
44
  end
42
45
 
43
46
  response = Hubspot::Connection.get_json(path, opts)
44
- response['contacts'].map { |c| new(c) }
47
+ response['contacts'].map! { |c| new(c) }
48
+ paged ? response : response['contacts']
45
49
  end
46
50
 
47
51
  # TODO: create or update a contact
48
52
  # PATH /contacts/v1/contact/createOrUpdate/email/:contact_email
49
53
  # API endpoint: https://developers.hubspot.com/docs/methods/contacts/create_or_update
50
- # + batch mode: https://developers.hubspot.com/docs/methods/contacts/batch_create_or_update
51
54
  def createOrUpdate(email, params={})
52
55
  post_data = {properties: Hubspot::Utils.hash_to_properties(params.stringify_keys)}
53
56
  response = Hubspot::Connection.post_json(CREATE_OR_UPDATE_PATH, params: { contact_email: email }, body: post_data )
@@ -56,6 +59,28 @@ module Hubspot
56
59
  contact
57
60
  end
58
61
 
62
+ # NOTE: Performance is best when calls are limited to 100 or fewer contacts
63
+ # {https://developers.hubspot.com/docs/methods/contacts/batch_create_or_update}
64
+ def create_or_update!(contacts)
65
+ query = contacts.map do |ch|
66
+ contact_hash = ch.with_indifferent_access
67
+ contact_param = {
68
+ properties: Hubspot::Utils.hash_to_properties(contact_hash.except(:vid))
69
+ }
70
+ if contact_hash[:vid]
71
+ contact_param.merge!(vid: contact_hash[:vid])
72
+ elsif contact_hash[:email]
73
+ contact_param.merge!(email: contact_hash[:email])
74
+ else
75
+ raise Hubspot::InvalidParams, 'expecting vid or email for contact'
76
+ end
77
+ contact_param
78
+ end
79
+ Hubspot::Connection.post_json(BATCH_CREATE_OR_UPDATE_PATH,
80
+ params: {},
81
+ body: query)
82
+ end
83
+
59
84
  # NOTE: problem with batch api endpoint
60
85
  # {https://developers.hubspot.com/docs/methods/contacts/get_contact}
61
86
  # {https://developers.hubspot.com/docs/methods/contacts/get_batch_by_vid}
@@ -119,10 +144,12 @@ module Hubspot
119
144
  end
120
145
 
121
146
  attr_reader :properties, :vid, :is_new
147
+ attr_reader :is_contact
122
148
 
123
149
  def initialize(response_hash)
124
150
  props = response_hash['properties']
125
151
  @properties = Hubspot::Utils.properties_to_hash(props) unless props.blank?
152
+ @is_contact = response_hash["is-contact"]
126
153
  @vid = response_hash['vid']
127
154
  end
128
155
 
@@ -78,13 +78,15 @@ module Hubspot
78
78
  # NOTE: caching functionality can be dependant of the nature of the list, if dynamic or not ...
79
79
  bypass_cache = opts.delete(:bypass_cache) { false }
80
80
  recent = opts.delete(:recent) { false }
81
+ paged = opts.delete(:paged) { false }
81
82
 
82
83
  if bypass_cache || @contacts.nil?
83
84
  path = recent ? RECENT_CONTACTS_PATH : CONTACTS_PATH
84
85
  opts[:list_id] = @id
85
86
 
86
87
  response = Hubspot::Connection.get_json(path, Hubspot::ContactProperties.add_default_parameters(opts))
87
- @contacts = response['contacts'].map { |c| Hubspot::Contact.new(c) }
88
+ @contacts = response['contacts'].map! { |c| Hubspot::Contact.new(c) }
89
+ paged ? response : @contacts
88
90
  else
89
91
  @contacts
90
92
  end
data/lib/hubspot/deal.rb CHANGED
@@ -11,6 +11,8 @@ module Hubspot
11
11
  DEAL_PATH = "/deals/v1/deal/:deal_id"
12
12
  RECENT_UPDATED_PATH = "/deals/v1/deal/recent/modified"
13
13
  UPDATE_DEAL_PATH = '/deals/v1/deal/:deal_id'
14
+ ASSOCIATE_DEAL_PATH = '/deals/v1/deal/:deal_id/associations/:OBJECTTYPE?id=:objectId'
15
+ ASSOCIATED_DEAL_PATH = "/deals/v1/deal/associated/:objectType/:objectId"
14
16
 
15
17
  attr_reader :properties
16
18
  attr_reader :portal_id
@@ -36,6 +38,17 @@ module Hubspot
36
38
  new(response)
37
39
  end
38
40
 
41
+ # Associate a deal with a contact or company
42
+ # {http://developers.hubspot.com/docs/methods/deals/associate_deal}
43
+ # Usage
44
+ # Hubspot::Deal.associate!(45146940, [], [52])
45
+ def associate!(deal_id, company_ids=[], vids=[])
46
+ objecttype = company_ids.any? ? 'COMPANY' : 'CONTACT'
47
+ object_ids = (company_ids.any? ? company_ids : vids).join('&id=')
48
+ Hubspot::Connection.put_json(ASSOCIATE_DEAL_PATH, params: { deal_id: deal_id, OBJECTTYPE: objecttype, objectId: object_ids}, body: {})
49
+ end
50
+
51
+
39
52
  def find(deal_id)
40
53
  response = Hubspot::Connection.get_json(DEAL_PATH, { deal_id: deal_id })
41
54
  new(response)
@@ -49,6 +62,17 @@ module Hubspot
49
62
  response = Hubspot::Connection.get_json(RECENT_UPDATED_PATH, opts)
50
63
  response['results'].map { |d| new(d) }
51
64
  end
65
+
66
+ # Find all deals associated to a company
67
+ # {http://developers.hubspot.com/docs/methods/deals/get-associated-deals}
68
+ # @param company [Hubspot::Company] the company
69
+ # @return [Array] Array of Hubspot::Deal records
70
+ def find_by_company(company)
71
+ path = ASSOCIATED_DEAL_PATH
72
+ params = { objectType: :company, objectId: company.vid }
73
+ response = Hubspot::Connection.get_json(path, params)
74
+ response["results"].map { |deal_id| find(deal_id) }
75
+ end
52
76
 
53
77
  end
54
78
 
@@ -34,6 +34,21 @@ module Hubspot
34
34
  response = Hubspot::Connection.get_json(PIPELINES_PATH, {})
35
35
  response.map { |p| new(p) }
36
36
  end
37
+
38
+ # Creates a DealPipeline
39
+ # {https://developers.hubspot.com/docs/methods/deal-pipelines/create-deal-pipeline}
40
+ # @return [Hubspot::PipeLine] Company record
41
+ def create!(post_data={})
42
+ response = Hubspot::Connection.post_json(PIPELINES_PATH, params: {}, body: post_data)
43
+ new(response)
44
+ end
45
+ end
46
+
47
+ # Destroys deal_pipeline
48
+ # {http://developers.hubspot.com/docs/methods/companies/delete_company}
49
+ # @return [TrueClass] true
50
+ def destroy!
51
+ Hubspot::Connection.delete_json(PIPELINE_PATH, pipeline_id: @pipeline_id)
37
52
  end
38
53
 
39
54
  def [](stage)
@@ -33,7 +33,7 @@ module Hubspot
33
33
  def find(engagement_id)
34
34
  begin
35
35
  response = Hubspot::Connection.get_json(ENGAGEMENT_PATH, { engagement_id: engagement_id })
36
- new(HashWithIndifferentAccess.new(response))
36
+ response ? new(HashWithIndifferentAccess.new(response)) : nil
37
37
  rescue Hubspot::RequestError => ex
38
38
  if ex.response.code == 404
39
39
  return nil
@@ -130,4 +130,47 @@ module Hubspot
130
130
  end
131
131
  end
132
132
  end
133
+
134
+ class EngagementCall < Engagement
135
+ def body
136
+ metadata['body']
137
+ end
138
+
139
+ def contact_ids
140
+ associations['contactIds']
141
+ end
142
+
143
+ def company_ids
144
+ associations['companyIds']
145
+ end
146
+
147
+ def deal_ids
148
+ associations['dealIds']
149
+ end
150
+
151
+ class << self
152
+ def create!(contact_vid, body, duration, owner_id = nil, deal_id = nil, status = 'COMPLETED', time = nil)
153
+ data = {
154
+ engagement: {
155
+ type: 'CALL'
156
+ },
157
+ associations: {
158
+ contactIds: [contact_vid],
159
+ dealIds: [deal_id],
160
+ ownerIds: [owner_id]
161
+ },
162
+ metadata: {
163
+ body: body,
164
+ status: status,
165
+ durationMilliseconds: duration
166
+ }
167
+ }
168
+
169
+ data[:engagement][:timestamp] = (time.to_i) * 1000 if time
170
+ data[:engagement][:owner_id] = owner_id if owner_id
171
+
172
+ super(data)
173
+ end
174
+ end
175
+ end
133
176
  end
@@ -3,18 +3,22 @@ module Hubspot
3
3
 
4
4
  PROPERTY_SPECS = {
5
5
  group_field_names: %w(name displayName displayOrder properties),
6
- field_names: %w(name groupName description fieldType formField type displayOrder label options),
7
- valid_field_types: %w(textarea select text date file number radio checkbox),
6
+ field_names: %w(name groupName description fieldType formField type displayOrder label options showCurrencySymbol),
7
+ valid_field_types: %w(textarea select text date file number radio checkbox booleancheckbox),
8
8
  valid_types: %w(string number bool datetime enumeration),
9
9
  options: %w(description value label hidden displayOrder)
10
10
  }
11
+ DEFAULT_PROPERTY = 'email'
11
12
 
12
13
  class << self
13
14
  # TODO: properties can be set as configuration
14
15
  # TODO: find the way how to set a list of Properties + merge same property key if present from opts
15
16
  def add_default_parameters(opts={})
16
- properties = 'email'
17
- opts.merge(property: properties)
17
+ if opts.keys.map(&:to_s).include? 'property'
18
+ opts
19
+ else
20
+ opts.merge(property: DEFAULT_PROPERTY)
21
+ end
18
22
  end
19
23
 
20
24
  def all(path, opts={}, filter={})
@@ -0,0 +1,29 @@
1
+ module Hubspot
2
+ class Subscription
3
+ SUBSCRIPTIONS_PATH = '/email/public/v1/subscriptions'
4
+ TIMELINE_PATH = '/email/public/v1/subscriptions/timeline'
5
+ SUBSCRIPTION_PATH = '/email/public/v1/subscriptions/:email_address'
6
+
7
+ attr_reader :subscribed
8
+ attr_reader :marked_as_spam
9
+ attr_reader :bounced
10
+ attr_reader :status
11
+ attr_reader :subscription_statuses
12
+
13
+ def initialize(response_hash)
14
+ @subscribed = response_hash['subscribed']
15
+ @marked_as_spam = response_hash['markedAsSpam']
16
+ @bounced = response_hash['bounced']
17
+ @status = response_hash['status']
18
+ @subscription_statuses = response_hash['SubscriptionStatuses']
19
+ end
20
+
21
+ class << self
22
+ def status(email)
23
+ response = Hubspot::Connection.get_json(SUBSCRIPTION_PATH, {email_address: email})
24
+ new(response)
25
+ end
26
+ end
27
+
28
+ end
29
+ end
data/lib/hubspot-ruby.rb CHANGED
@@ -18,6 +18,7 @@ require 'hubspot/deal_pipeline'
18
18
  require 'hubspot/deal_properties'
19
19
  require 'hubspot/owner'
20
20
  require 'hubspot/engagement'
21
+ require 'hubspot/subscription'
21
22
 
22
23
  module Hubspot
23
24
  def self.configure(config={})
@@ -0,0 +1,49 @@
1
+ require 'hubspot-ruby'
2
+
3
+ namespace :hubspot do
4
+ desc 'Dump properties to file'
5
+ task :dump_properties, [:kind, :file, :hapikey, :include, :exclude] do |_, args|
6
+ hapikey = args[:hapikey] || ENV['HUBSPOT_API_KEY']
7
+ kind = args[:kind]
8
+ unless %w(contact deal).include?(kind)
9
+ raise ArgumentError, ':kind must be either "contact" or "deal"'
10
+ end
11
+ klass = kind == 'contact' ? Hubspot::ContactProperties : Hubspot::DealProperties
12
+ props = Hubspot::Utils::dump_properties(klass, hapikey, build_filter(args))
13
+ if args[:file].blank?
14
+ puts JSON.pretty_generate(props)
15
+ else
16
+ File.open(args[:file], 'w') do |f|
17
+ f.write(JSON.pretty_generate(props))
18
+ end
19
+ end
20
+ end
21
+
22
+ desc 'Restore properties from file'
23
+ task :restore_properties, [:kind, :file, :hapikey, :dry_run] do |_, args|
24
+ hapikey = args[:hapikey] || ENV['HUBSPOT_API_KEY']
25
+ if args[:file].blank?
26
+ raise ArgumentError, ':file is a required parameter'
27
+ end
28
+ kind = args[:kind]
29
+ unless %w(contact deal).include?(kind)
30
+ raise ArgumentError, ':kind must be either "contact" or "deal"'
31
+ end
32
+ klass = kind == 'contact' ? Hubspot::ContactProperties : Hubspot::DealProperties
33
+ file = File.read(args[:file])
34
+ props = JSON.parse(file)
35
+ Hubspot::Utils.restore_properties(klass, hapikey, props, args[:dry_run] != 'false')
36
+ end
37
+
38
+ private
39
+
40
+ def build_filter(args)
41
+ { include: val_to_array(args[:include]),
42
+ exclude: val_to_array(args[:exclude])
43
+ }
44
+ end
45
+
46
+ def val_to_array(val)
47
+ val.blank? ? val : val.split(/\W+/)
48
+ end
49
+ end
@@ -1,8 +1,16 @@
1
1
  describe Hubspot::CompanyProperties do
2
2
  describe '.add_default_parameters' do
3
- subject { Hubspot::CompanyProperties.add_default_parameters({}) }
3
+ let(:opts) { {} }
4
+ subject { Hubspot::CompanyProperties.add_default_parameters(opts) }
4
5
  context 'default parameters' do
5
- its([:property]) { should == 'email' }
6
+ context 'without property parameter' do
7
+ its([:property]) { should == 'email' }
8
+ end
9
+
10
+ context 'with property parameter' do
11
+ let(:opts) { {property: 'name' } }
12
+ its([:property]) { should == 'name'}
13
+ end
6
14
  end
7
15
  end
8
16
 
@@ -108,15 +108,15 @@ describe Hubspot::Contact do
108
108
  cassette 'find_all_recent_companies'
109
109
 
110
110
  it 'must get the companies list' do
111
- companies = Hubspot::Company.all(recent: true)
111
+ companies = Hubspot::Company.all(recently_updated: true)
112
112
  expect(companies.size).to eql 20
113
113
 
114
114
  first, last = companies.first, companies.last
115
115
  expect(first).to be_a Hubspot::Company
116
- expect(first.vid).to eql 42866817
116
+ expect(first.vid).to eql 318615742
117
117
 
118
118
  expect(last).to be_a Hubspot::Company
119
- expect(last.vid).to eql 42861017
119
+ expect(last.vid).to eql 359899290
120
120
  end
121
121
  end
122
122
  end
@@ -76,30 +76,36 @@ describe Hubspot::Connection do
76
76
 
77
77
  context "with an interpolated param" do
78
78
  let(:params){ {email: "email@address.com"} }
79
- it{ should == "https://api.hubapi.com/test/email@address.com/profile?hapikey=demo" }
79
+ it{ should == "https://api.hubapi.com/test/email%40address.com/profile?hapikey=demo" }
80
80
  end
81
81
 
82
82
  context "with multiple interpolated params" do
83
83
  let(:path){ "/test/:email/:id/profile" }
84
84
  let(:params){{email: "email@address.com", id: 1234}}
85
- it{ should == "https://api.hubapi.com/test/email@address.com/1234/profile?hapikey=demo" }
85
+ it{ should == "https://api.hubapi.com/test/email%40address.com/1234/profile?hapikey=demo" }
86
86
  end
87
87
 
88
88
  context "with query params" do
89
89
  let(:params){{email: "email@address.com", id: 1234}}
90
- it{ should == "https://api.hubapi.com/test/email@address.com/profile?id=1234&hapikey=demo" }
90
+ it{ should == "https://api.hubapi.com/test/email%40address.com/profile?id=1234&hapikey=demo" }
91
91
 
92
92
  context "containing a time" do
93
93
  let(:start_time) { Time.now }
94
94
  let(:params){{email: "email@address.com", id: 1234, start: start_time}}
95
- it{ should == "https://api.hubapi.com/test/email@address.com/profile?id=1234&start=#{start_time.to_i * 1000}&hapikey=demo" }
95
+ it{ should == "https://api.hubapi.com/test/email%40address.com/profile?id=1234&start=#{start_time.to_i * 1000}&hapikey=demo" }
96
96
  end
97
97
 
98
98
  context "containing a range" do
99
99
  let(:start_time) { Time.now }
100
100
  let(:end_time) { Time.now + 1.year }
101
101
  let(:params){{email: "email@address.com", id: 1234, created__range: start_time..end_time }}
102
- it{ should == "https://api.hubapi.com/test/email@address.com/profile?id=1234&created__range=#{start_time.to_i * 1000}&created__range=#{end_time.to_i * 1000}&hapikey=demo" }
102
+ it{ should == "https://api.hubapi.com/test/email%40address.com/profile?id=1234&created__range=#{start_time.to_i * 1000}&created__range=#{end_time.to_i * 1000}&hapikey=demo" }
103
+ end
104
+
105
+ context "containing an array of strings" do
106
+ let(:path){ "/test/emails" }
107
+ let(:params){{batch_email: %w(email1@example.com email2@example.com)}}
108
+ it{ should == "https://api.hubapi.com/test/emails?email=email1%40example.com&email=email2%40example.com&hapikey=demo" }
103
109
  end
104
110
  end
105
111
 
@@ -32,12 +32,24 @@ describe Hubspot::ContactList do
32
32
 
33
33
  let(:list) { Hubspot::ContactList.new(example_contact_list_hash) }
34
34
 
35
- it 'returns by defaut 20 contact lists' do
35
+ it 'returns by default 20 contact lists' do
36
36
  expect(list.contacts.count).to eql 20
37
37
  contact = list.contacts.first
38
38
  expect(contact).to be_a(Hubspot::Contact)
39
39
  end
40
40
 
41
+ it 'returns by default 20 contact lists with paging data' do
42
+ contact_data = list.contacts({paged: true})
43
+ contacts = contact_data['contacts']
44
+
45
+ expect(contact_data).to have_key 'vid-offset'
46
+ expect(contact_data).to have_key 'has-more'
47
+
48
+ expect(contacts.count).to eql 20
49
+ contact = contacts.first
50
+ expect(contact).to be_a(Hubspot::Contact)
51
+ end
52
+
41
53
  it 'add default properties to the contacts returned' do
42
54
  contact = list.contacts.first
43
55
  expect(contact.email).to_not be_empty
@@ -86,7 +98,7 @@ describe Hubspot::ContactList do
86
98
  context 'all list types' do
87
99
  cassette 'find_all_lists'
88
100
 
89
- it 'returns by defaut 20 contact lists' do
101
+ it 'returns by default 20 contact lists' do
90
102
  lists = Hubspot::ContactList.all
91
103
  expect(lists.count).to eql 20
92
104
 
@@ -1,8 +1,16 @@
1
1
  describe Hubspot::ContactProperties do
2
2
  describe '.add_default_parameters' do
3
- subject { Hubspot::ContactProperties.add_default_parameters({}) }
3
+ let(:opts) { {} }
4
+ subject { Hubspot::ContactProperties.add_default_parameters(opts) }
4
5
  context 'default parameters' do
5
- its([:property]) { should == 'email' }
6
+ context 'without property parameter' do
7
+ its([:property]) { should == 'email' }
8
+ end
9
+
10
+ context 'with property parameter' do
11
+ let(:opts) { {property: 'firstname' } }
12
+ its([:property]) { should == 'firstname'}
13
+ end
6
14
  end
7
15
  end
8
16
 
@@ -84,6 +84,38 @@ describe Hubspot::Contact do
84
84
  end
85
85
  end
86
86
 
87
+ describe '.create_or_update!' do
88
+ cassette 'create_or_update'
89
+ let(:existing_contact) do
90
+ Hubspot::Contact.create!("morpheus@example.com", firstname: 'Morpheus')
91
+ end
92
+ before do
93
+ Hubspot::Contact.create_or_update!(params)
94
+ end
95
+
96
+ let(:params) do
97
+ [
98
+ {
99
+ vid: existing_contact.vid,
100
+ email: existing_contact.email,
101
+ firstname: 'Neo'
102
+ },
103
+ {
104
+ email: 'smith@example.com',
105
+ firstname: 'Smith'
106
+ }
107
+ ]
108
+ end
109
+
110
+ it 'creates and updates contacts' do
111
+ contact = Hubspot::Contact.find_by_id existing_contact.vid
112
+ expect(contact.properties['firstname']).to eql 'Neo'
113
+ latest_contact_email = Hubspot::Contact.all(recent: true).first.email
114
+ new_contact = Hubspot::Contact.find_by_email(latest_contact_email)
115
+ expect(new_contact.properties['firstname']).to eql 'Smith'
116
+ end
117
+ end
118
+
87
119
  describe '.find_by_email' do
88
120
  context 'given an uniq email' do
89
121
  cassette 'contact_find_by_email'
@@ -229,6 +261,31 @@ describe Hubspot::Contact do
229
261
  expect(last['lastname']).to eql 'Morgan'
230
262
  end
231
263
 
264
+ it 'must get the contacts list with paging data' do
265
+ contact_data = Hubspot::Contact.all({paged: true})
266
+ contacts = contact_data['contacts']
267
+
268
+ expect(contacts.size).to eql 20 # default page size
269
+
270
+ first = contacts.first
271
+ last = contacts.last
272
+
273
+ expect(contact_data).to have_key 'vid-offset'
274
+ expect(contact_data).to have_key 'has-more'
275
+
276
+ expect(first).to be_a Hubspot::Contact
277
+ expect(first.vid).to eql 154835
278
+ expect(first['firstname']).to eql 'HubSpot'
279
+ expect(first['lastname']).to eql 'Test'
280
+
281
+ expect(last).to be_a Hubspot::Contact
282
+ expect(last.vid).to eql 196199
283
+ expect(last['firstname']).to eql 'Eleanor'
284
+ expect(last['lastname']).to eql 'Morgan'
285
+ expect(contact_data['has-more']).to eql true
286
+ expect(contact_data['vid-offset']).to eql 196199
287
+ end
288
+
232
289
  it 'must filter only 2 contacts' do
233
290
  contacts = Hubspot::Contact.all(count: 2)
234
291
  expect(contacts.size).to eql 2
@@ -1,8 +1,16 @@
1
1
  describe Hubspot::DealProperties do
2
2
  describe '.add_default_parameters' do
3
- subject { Hubspot::DealProperties.add_default_parameters({}) }
3
+ let(:opts) { {} }
4
+ subject { Hubspot::DealProperties.add_default_parameters(opts) }
4
5
  context 'default parameters' do
5
- its([:property]) { should == 'email' }
6
+ context 'without property parameter' do
7
+ its([:property]) { should == 'email' }
8
+ end
9
+
10
+ context 'with property parameter' do
11
+ let(:opts) { {property: 'dealname' } }
12
+ its([:property]) { should == 'dealname'}
13
+ end
6
14
  end
7
15
  end
8
16
 
@@ -19,69 +19,113 @@ describe Hubspot::Engagement do
19
19
  its (:id) { should == 51484873 }
20
20
  end
21
21
 
22
- describe ".create!" do
23
- cassette "engagement_create"
24
- body = "Test note"
25
- subject { Hubspot::EngagementNote.create!(nil, body) }
26
- its(:id) { should_not be_nil }
27
- its(:body) { should eql body }
28
- end
22
+ describe 'EngagementNote' do
23
+ describe ".create!" do
24
+ cassette "engagement_create"
25
+ body = "Test note"
26
+ subject { Hubspot::EngagementNote.create!(nil, body) }
27
+ its(:id) { should_not be_nil }
28
+ its(:body) { should eql body }
29
+ end
29
30
 
30
- describe ".find" do
31
- cassette "engagement_find"
32
- let(:engagement) {Hubspot::EngagementNote.new(example_engagement_hash)}
31
+ describe ".find" do
32
+ cassette "engagement_find"
33
+ let(:engagement) {Hubspot::EngagementNote.new(example_engagement_hash)}
33
34
 
34
- it 'must find by the engagement id' do
35
- find_engagement = Hubspot::EngagementNote.find(engagement.id)
36
- find_engagement.id.should eql engagement.id
37
- find_engagement.body.should eql engagement.body
35
+ it 'must find by the engagement id' do
36
+ find_engagement = Hubspot::EngagementNote.find(engagement.id)
37
+ find_engagement.id.should eql engagement.id
38
+ find_engagement.body.should eql engagement.body
39
+ end
38
40
  end
39
- end
40
41
 
41
- describe ".find_by_company" do
42
- cassette "engagement_find_by_country"
43
- let(:engagement) {Hubspot::EngagementNote.new(example_associated_engagement_hash)}
42
+ describe ".find_by_company" do
43
+ cassette "engagement_find_by_country"
44
+ let(:engagement) {Hubspot::EngagementNote.new(example_associated_engagement_hash)}
44
45
 
45
- it 'must find by company id' do
46
- find_engagements = Hubspot::EngagementNote.find_by_company(engagement.associations["companyIds"].first)
47
- find_engagements.should_not be_nil
48
- find_engagements.any?{|engagement| engagement.id == engagement.id and engagement.body == engagement.body}.should be_true
46
+ it 'must find by company id' do
47
+ find_engagements = Hubspot::EngagementNote.find_by_company(engagement.associations["companyIds"].first)
48
+ find_engagements.should_not be_nil
49
+ find_engagements.any?{|engagement| engagement.id == engagement.id and engagement.body == engagement.body}.should be_true
50
+ end
49
51
  end
50
- end
51
52
 
52
- describe ".find_by_contact" do
53
- cassette "engagement_find_by_contact"
54
- let(:engagement) {Hubspot::EngagementNote.new(example_associated_engagement_hash)}
53
+ describe ".find_by_contact" do
54
+ cassette "engagement_find_by_contact"
55
+ let(:engagement) {Hubspot::EngagementNote.new(example_associated_engagement_hash)}
55
56
 
56
- it 'must find by contact id' do
57
- find_engagements = Hubspot::EngagementNote.find_by_contact(engagement.associations["contactIds"].first)
58
- find_engagements.should_not be_nil
59
- find_engagements.any?{|engagement| engagement.id == engagement.id and engagement.body == engagement.body}.should be_true
57
+ it 'must find by contact id' do
58
+ find_engagements = Hubspot::EngagementNote.find_by_contact(engagement.associations["contactIds"].first)
59
+ find_engagements.should_not be_nil
60
+ find_engagements.any?{|engagement| engagement.id == engagement.id and engagement.body == engagement.body}.should be_true
61
+ end
60
62
  end
61
- end
62
63
 
63
- describe ".find_by_association" do
64
- cassette "engagement_find_by_association"
64
+ describe ".find_by_association" do
65
+ cassette "engagement_find_by_association"
66
+
67
+ it 'must raise for fake association type' do
68
+ expect {
69
+ Hubspot::EngagementNote.find_by_association(1, 'FAKE_TYPE')
70
+ }.to raise_error
71
+ end
72
+ end
73
+
74
+ describe '#destroy!' do
75
+ cassette 'engagement_destroy'
76
+
77
+ let(:engagement) {Hubspot::EngagementNote.create!(nil, 'test note') }
65
78
 
66
- it 'must raise for fake association type' do
67
- expect {
68
- Hubspot::EngagementNote.find_by_association(1, 'FAKE_TYPE')
69
- }.to raise_error
79
+ it 'should remove from hubspot' do
80
+ expect(Hubspot::Engagement.find(engagement.id)).to_not be_nil
81
+
82
+ expect(engagement.destroy!).to be_true
83
+ expect(engagement.destroyed?).to be_true
84
+
85
+ expect(Hubspot::Engagement.find(engagement.id)).to be_nil
86
+ end
70
87
  end
71
88
  end
72
89
 
73
- describe '#destroy!' do
74
- cassette 'engagement_destroy'
90
+ describe 'EngagementCall' do
91
+ let(:example_engagement_hash) do
92
+ VCR.use_cassette("engagement_call_example") do
93
+ HTTParty.get("https://api.hubapi.com/engagements/v1/engagements/4709059?hapikey=demo").parsed_response
94
+ end
95
+ end
96
+
97
+ describe ".create!" do
98
+ cassette "engagement_call_create"
99
+ body = "Test call"
100
+ subject { Hubspot::EngagementCall.create!(nil, body, 0) }
101
+ its(:id) { should_not be_nil }
102
+ its(:body) { should eql body }
103
+ end
104
+
105
+ describe ".find" do
106
+ cassette "engagement_call_find"
107
+ let(:engagement) { Hubspot::EngagementNote.new(example_engagement_hash) }
108
+
109
+ it 'must find by the engagement id' do
110
+ find_engagement = Hubspot::EngagementNote.find(engagement.id)
111
+ find_engagement.id.should eql engagement.id
112
+ find_engagement.body.should eql engagement.body
113
+ end
114
+ end
115
+
116
+ describe '#destroy!' do
117
+ cassette 'engagement_call_destroy'
75
118
 
76
- let(:engagement) {Hubspot::EngagementNote.create!(nil, 'test note') }
119
+ let(:engagement) { Hubspot::EngagementCall.create!(nil, 'test call', 0) }
77
120
 
78
- it 'should remove from hubspot' do
79
- expect(Hubspot::Engagement.find(engagement.id)).to_not be_nil
121
+ it 'should remove from hubspot' do
122
+ expect(Hubspot::Engagement.find(engagement.id)).to_not be_nil
80
123
 
81
- expect(engagement.destroy!).to be_true
82
- expect(engagement.destroyed?).to be_true
124
+ expect(engagement.destroy!).to be_true
125
+ expect(engagement.destroyed?).to be_true
83
126
 
84
- expect(Hubspot::Engagement.find(engagement.id)).to be_nil
127
+ expect(Hubspot::Engagement.find(engagement.id)).to be_nil
128
+ end
85
129
  end
86
130
  end
87
131
  end
@@ -0,0 +1,35 @@
1
+ describe 'Deals pipeline API Live test', live: true do
2
+
3
+ before do
4
+ Hubspot.configure hapikey: 'demo'
5
+ end
6
+
7
+ let(:params) do
8
+ {
9
+ 'label' => 'auto pipeline1',
10
+ 'stages' => [
11
+ {
12
+ 'label' => 'initial state',
13
+ 'displayOrder' => 0,
14
+ 'probability' => 0.5
15
+ },
16
+ {
17
+ 'label' => 'next state',
18
+ 'displayOrder' => 1,
19
+ 'probability' => 0.9
20
+ }
21
+ ]
22
+ }
23
+ end
24
+
25
+ it 'should create, find, update and destroy' do
26
+ pipeline = Hubspot::DealPipeline.create!(params)
27
+
28
+ expect(pipeline.label).to eql 'auto pipeline1'
29
+ expect(pipeline.stages.size).to eql 2
30
+ expect(pipeline.stages.first['label']).to eql 'initial state'
31
+ expect(pipeline.stages[1]['label']).to eql 'next state'
32
+
33
+ pipeline.destroy!
34
+ end
35
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hubspot-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew DiMichele
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-20 00:00:00.000000000 Z
11
+ date: 2017-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -56,16 +56,16 @@ dependencies:
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '2.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '2.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rr
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -200,7 +200,6 @@ extra_rdoc_files: []
200
200
  files:
201
201
  - ".rspec"
202
202
  - Gemfile
203
- - Gemfile.lock
204
203
  - Guardfile
205
204
  - LICENSE.txt
206
205
  - README.md
@@ -225,8 +224,10 @@ files:
225
224
  - lib/hubspot/owner.rb
226
225
  - lib/hubspot/properties.rb
227
226
  - lib/hubspot/railtie.rb
227
+ - lib/hubspot/subscription.rb
228
228
  - lib/hubspot/topic.rb
229
229
  - lib/hubspot/utils.rb
230
+ - lib/tasks/properties.rake
230
231
  - spec/lib/hubspot-ruby_spec.rb
231
232
  - spec/lib/hubspot/blog_spec.rb
232
233
  - spec/lib/hubspot/company_properties_spec.rb
@@ -249,6 +250,7 @@ files:
249
250
  - spec/live/companies_properties_integration_spec.rb
250
251
  - spec/live/contacts_integration_spec.rb
251
252
  - spec/live/contacts_properties_integration_spec.rb
253
+ - spec/live/deal_pipeline_spec.rb
252
254
  - spec/live/deal_properties_integration_spec.rb
253
255
  - spec/live/deals_integration_spec.rb
254
256
  - spec/spec_helper.rb
@@ -273,7 +275,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
273
275
  version: '0'
274
276
  requirements: []
275
277
  rubyforge_project:
276
- rubygems_version: 2.5.1
278
+ rubygems_version: 2.4.5.2
277
279
  signing_key:
278
280
  specification_version: 4
279
281
  summary: hubspot-ruby is a wrapper for the HubSpot REST API
data/Gemfile.lock DELETED
@@ -1,99 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- hubspot-ruby (0.2.0)
5
- activesupport (>= 3.0.0)
6
- httparty (>= 0.10.0)
7
-
8
- GEM
9
- remote: http://rubygems.org/
10
- specs:
11
- activesupport (3.2.12)
12
- i18n (~> 0.6)
13
- multi_json (~> 1.0)
14
- addressable (2.3.6)
15
- awesome_print (1.1.0)
16
- celluloid (0.16.0)
17
- timers (~> 4.0.0)
18
- coderay (1.1.0)
19
- crack (0.4.2)
20
- safe_yaml (~> 1.0.0)
21
- diff-lcs (1.2.1)
22
- ffi (1.9.6)
23
- formatador (0.2.5)
24
- guard (2.6.1)
25
- formatador (>= 0.2.4)
26
- listen (~> 2.7)
27
- lumberjack (~> 1.0)
28
- pry (>= 0.9.12)
29
- thor (>= 0.18.1)
30
- guard-rspec (3.1.0)
31
- guard (>= 1.8)
32
- rspec (~> 2.13)
33
- hitimes (1.2.2)
34
- httparty (0.10.2)
35
- multi_json (~> 1.0)
36
- multi_xml (>= 0.5.2)
37
- i18n (0.6.4)
38
- json (1.8.3)
39
- listen (2.7.11)
40
- celluloid (>= 0.15.2)
41
- rb-fsevent (>= 0.9.3)
42
- rb-inotify (>= 0.9)
43
- lumberjack (1.0.9)
44
- method_source (0.8.2)
45
- multi_json (1.6.1)
46
- multi_xml (0.5.3)
47
- pry (0.10.1)
48
- coderay (~> 1.1.0)
49
- method_source (~> 0.8.1)
50
- slop (~> 3.4)
51
- rake (10.4.2)
52
- rb-fsevent (0.9.4)
53
- rb-inotify (0.9.5)
54
- ffi (>= 0.5.0)
55
- rdoc (4.0.0)
56
- json (~> 1.4)
57
- rr (1.0.4)
58
- rspec (2.13.0)
59
- rspec-core (~> 2.13.0)
60
- rspec-expectations (~> 2.13.0)
61
- rspec-mocks (~> 2.13.0)
62
- rspec-core (2.13.1)
63
- rspec-expectations (2.13.0)
64
- diff-lcs (>= 1.1.3, < 2.0)
65
- rspec-mocks (2.13.0)
66
- safe_yaml (1.0.4)
67
- simplecov (0.7.1)
68
- multi_json (~> 1.0)
69
- simplecov-html (~> 0.7.1)
70
- simplecov-html (0.7.1)
71
- slop (3.6.0)
72
- thor (0.19.1)
73
- timecop (0.7.1)
74
- timers (4.0.1)
75
- hitimes
76
- vcr (2.4.0)
77
- webmock (1.9.3)
78
- addressable (>= 2.2.7)
79
- crack (>= 0.3.2)
80
-
81
- PLATFORMS
82
- ruby
83
-
84
- DEPENDENCIES
85
- awesome_print
86
- bundler
87
- guard-rspec
88
- hubspot-ruby!
89
- rake
90
- rdoc
91
- rr
92
- rspec
93
- simplecov
94
- timecop
95
- vcr
96
- webmock (< 1.10)
97
-
98
- BUNDLED WITH
99
- 1.13.2