hubspot-ruby 0.2.0 → 0.3.0

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
  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