hubspot-api-ruby 0.8.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 +7 -0
- data/.rspec +3 -0
- data/Gemfile +3 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +18 -0
- data/README.md +295 -0
- data/RELEASING.md +6 -0
- data/Rakefile +32 -0
- data/hubspot-api-ruby.gemspec +42 -0
- data/lib/hubspot-api-ruby.rb +39 -0
- data/lib/hubspot/blog.rb +98 -0
- data/lib/hubspot/collection.rb +41 -0
- data/lib/hubspot/company.rb +160 -0
- data/lib/hubspot/company_properties.rb +59 -0
- data/lib/hubspot/config.rb +63 -0
- data/lib/hubspot/connection.rb +152 -0
- data/lib/hubspot/contact.rb +110 -0
- data/lib/hubspot/contact_list.rb +129 -0
- data/lib/hubspot/contact_properties.rb +59 -0
- data/lib/hubspot/deal.rb +173 -0
- data/lib/hubspot/deal_pipeline.rb +58 -0
- data/lib/hubspot/deal_properties.rb +59 -0
- data/lib/hubspot/deprecator.rb +7 -0
- data/lib/hubspot/engagement.rb +222 -0
- data/lib/hubspot/event.rb +21 -0
- data/lib/hubspot/exceptions.rb +18 -0
- data/lib/hubspot/file.rb +38 -0
- data/lib/hubspot/form.rb +95 -0
- data/lib/hubspot/oauth.rb +50 -0
- data/lib/hubspot/owner.rb +57 -0
- data/lib/hubspot/paged_collection.rb +35 -0
- data/lib/hubspot/properties.rb +123 -0
- data/lib/hubspot/railtie.rb +10 -0
- data/lib/hubspot/resource.rb +270 -0
- data/lib/hubspot/subscription.rb +37 -0
- data/lib/hubspot/topic.rb +37 -0
- data/lib/hubspot/utils.rb +127 -0
- data/lib/tasks/hubspot.rake +53 -0
- data/spec/factories/companies.rb +9 -0
- data/spec/factories/contacts.rb +10 -0
- data/spec/lib/hubspot-ruby_spec.rb +12 -0
- data/spec/lib/hubspot/blog_spec.rb +150 -0
- data/spec/lib/hubspot/company_properties_spec.rb +410 -0
- data/spec/lib/hubspot/company_spec.rb +340 -0
- data/spec/lib/hubspot/config_spec.rb +87 -0
- data/spec/lib/hubspot/connection_spec.rb +214 -0
- data/spec/lib/hubspot/contact_list_spec.rb +301 -0
- data/spec/lib/hubspot/contact_properties_spec.rb +245 -0
- data/spec/lib/hubspot/contact_spec.rb +223 -0
- data/spec/lib/hubspot/deal_pipeline_spec.rb +85 -0
- data/spec/lib/hubspot/deal_properties_spec.rb +262 -0
- data/spec/lib/hubspot/deal_spec.rb +185 -0
- data/spec/lib/hubspot/deprecator_spec.rb +15 -0
- data/spec/lib/hubspot/engagement_spec.rb +177 -0
- data/spec/lib/hubspot/event_spec.rb +33 -0
- data/spec/lib/hubspot/file_spec.rb +38 -0
- data/spec/lib/hubspot/form_spec.rb +189 -0
- data/spec/lib/hubspot/owner_spec.rb +56 -0
- data/spec/lib/hubspot/properties_spec.rb +45 -0
- data/spec/lib/hubspot/resource_spec.rb +54 -0
- data/spec/lib/hubspot/topic_spec.rb +23 -0
- data/spec/lib/hubspot/utils_spec.rb +164 -0
- data/spec/lib/tasks/hubspot_spec.rb +119 -0
- data/spec/shared_examples/saveable_resource.rb +45 -0
- data/spec/shared_examples/updateable_resource.rb +87 -0
- data/spec/spec_helper.rb +44 -0
- data/spec/support/capture_output.rb +21 -0
- data/spec/support/cassette_helper.rb +19 -0
- data/spec/support/hubspot_api_helpers.rb +13 -0
- data/spec/support/rake.rb +46 -0
- data/spec/support/tests_helper.rb +17 -0
- data/spec/support/vcr.rb +16 -0
- metadata +369 -0
@@ -0,0 +1,37 @@
|
|
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
|
+
|
27
|
+
def unsubscribe_from_all_emails(email)
|
28
|
+
response =
|
29
|
+
Hubspot::Connection.put_json(
|
30
|
+
SUBSCRIPTION_PATH, params: { email_address: email },
|
31
|
+
body: { "unsubscribeFromAll": true }
|
32
|
+
)
|
33
|
+
new(response)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Hubspot
|
2
|
+
#
|
3
|
+
# HubSpot Topics API
|
4
|
+
#
|
5
|
+
class Topic
|
6
|
+
TOPICS_PATH = "/blogs/v3/topics"
|
7
|
+
TOPIC_PATH = "/blogs/v3/topics/:topic_id"
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# Lists the topics
|
11
|
+
# {https://developers.hubspot.com/docs/methods/blogv2/get_topics)
|
12
|
+
# @return [Hubspot::Topic] array of topics
|
13
|
+
def list
|
14
|
+
response = Hubspot::Connection.get_json(TOPICS_PATH, {})
|
15
|
+
response['objects'].map { |t| new(t) }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Finds the details for a specific topic_id
|
19
|
+
# {https://developers.hubspot.com/docs/methods/blogv2/get_topics_topic_id }
|
20
|
+
# @return Hubspot::Topic
|
21
|
+
def find_by_topic_id(id)
|
22
|
+
response = Hubspot::Connection.get_json(TOPIC_PATH, { topic_id: id })
|
23
|
+
new(response)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :properties
|
28
|
+
|
29
|
+
def initialize(response_hash)
|
30
|
+
@properties = response_hash #no need to parse anything, we have properties
|
31
|
+
end
|
32
|
+
|
33
|
+
def [](property)
|
34
|
+
@properties[property]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Hubspot
|
2
|
+
class Utils
|
3
|
+
class << self
|
4
|
+
# Parses the hubspot properties format into a key-value hash
|
5
|
+
def properties_to_hash(props)
|
6
|
+
newprops = HashWithIndifferentAccess.new
|
7
|
+
props.each { |k, v| newprops[k] = v["value"] }
|
8
|
+
newprops
|
9
|
+
end
|
10
|
+
|
11
|
+
# Converts an array of property objects into a hash with the property name
|
12
|
+
# as the key
|
13
|
+
def properties_array_to_hash(props)
|
14
|
+
props.inject({}) { |h, x| h[x["name"]] = x; h }
|
15
|
+
end
|
16
|
+
|
17
|
+
# Turns a hash into the hubspot properties format
|
18
|
+
def hash_to_properties(hash, opts = {})
|
19
|
+
key_name = opts[:key_name] || "property"
|
20
|
+
hash.map { |k, v| { key_name => k.to_s, "value" => v } }
|
21
|
+
end
|
22
|
+
|
23
|
+
def dump_properties(klass, hapikey=ENV['HUBSPOT_API_KEY'], filter={})
|
24
|
+
Hubspot::Deprecator.build.deprecation_warning("Hubspot::Utils.dump_properties")
|
25
|
+
|
26
|
+
with_hapikey(hapikey) do
|
27
|
+
{ 'groups' => klass.groups({}, filter),
|
28
|
+
'properties' => klass.all({}, filter).select { |p| !p['hubspotDefined'] }
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def restore_properties(klass, hapikey=ENV['HUPSPOT_API_KEY'], properties={}, dry_run=false)
|
34
|
+
Hubspot::Deprecator.build.deprecation_warning("Hubspot::Utils.restore_properties")
|
35
|
+
|
36
|
+
existing_properties = dump_properties(klass, hapikey)
|
37
|
+
skip, new_groups, new_props, update_props = compare_property_lists(klass, properties, existing_properties)
|
38
|
+
puts '', 'Dry Run - Changes will not be applied' if dry_run
|
39
|
+
puts '','Skipping'
|
40
|
+
skip.each { |h| puts "#{h[:reason]} - #{h[:prop]['groupName']}:#{h[:prop]['name']}" }
|
41
|
+
with_hapikey(hapikey) do
|
42
|
+
create_groups(klass, new_groups, dry_run)
|
43
|
+
create_properties(klass, new_props, dry_run)
|
44
|
+
update_properties(klass, update_props, dry_run)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_groups(klass, groups, dry_run=false)
|
49
|
+
puts '','Creating new groups'
|
50
|
+
groups.each do |g|
|
51
|
+
if dry_run || klass.create_group!(g)
|
52
|
+
puts "Created: #{g['name']}"
|
53
|
+
else
|
54
|
+
puts "Failed: #{g['name']}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_properties(klass, props, dry_run=false)
|
60
|
+
puts '','Creating new properties'
|
61
|
+
props.each do |p|
|
62
|
+
if dry_run || klass.create!(p)
|
63
|
+
puts "Created: #{p['groupName']}:#{p['name']}"
|
64
|
+
else
|
65
|
+
puts "Failed: #{p['groupName']}:#{p['name']}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def update_properties(klass, props, dry_run=false)
|
71
|
+
puts '','Updating existing properties'
|
72
|
+
props.each do |p|
|
73
|
+
if dry_run || klass.update!(p['name'], p)
|
74
|
+
puts "Updated: #{p['groupName']}:#{p['name']}"
|
75
|
+
else
|
76
|
+
puts "Failed: #{p['groupName']}:#{p['name']}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def compare_property_lists(klass, source, target)
|
82
|
+
skip = [] # Array of skipped properties and the reason
|
83
|
+
new_groups = Set.new # Array of groups to create
|
84
|
+
new_props = [] # Array of properties to add
|
85
|
+
update_props = [] # Array of properties to update
|
86
|
+
src_groups = source['groups']
|
87
|
+
dst_groups = target['groups']
|
88
|
+
src_props = source['properties']
|
89
|
+
dst_props = target['properties']
|
90
|
+
|
91
|
+
src_props.each do |src|
|
92
|
+
group = find_by_name(src['groupName'], src_groups)
|
93
|
+
if src['createdUserId'].blank? && src['updatedUserId'].blank? then
|
94
|
+
skip << { prop: src, reason: 'Not user created' }
|
95
|
+
else
|
96
|
+
dst = find_by_name(src['name'], dst_props)
|
97
|
+
if dst
|
98
|
+
if dst['readOnlyDefinition']
|
99
|
+
skip << { prop: src, reason: 'Definition is read-only' }
|
100
|
+
elsif klass.same?(src, dst)
|
101
|
+
skip << { prop: src, reason: 'No change' }
|
102
|
+
else
|
103
|
+
new_groups << group unless group.blank? || find_by_name(group['name'], dst_groups)
|
104
|
+
update_props << src
|
105
|
+
end
|
106
|
+
else
|
107
|
+
new_groups << group unless group.blank? || find_by_name(group['name'], dst_groups)
|
108
|
+
new_props << src
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
[skip, new_groups.to_a, new_props, update_props]
|
113
|
+
end
|
114
|
+
|
115
|
+
def with_hapikey(hapikey)
|
116
|
+
Hubspot.configure(hapikey: hapikey)
|
117
|
+
yield if block_given?
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def find_by_name(name, set)
|
123
|
+
set.detect { |item| item['name'] == name }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'hubspot-api-ruby'
|
2
|
+
|
3
|
+
namespace :hubspot do
|
4
|
+
desc 'Dump properties to file'
|
5
|
+
task :dump_properties, [:kind, :file, :hapikey, :include, :exclude] do |_, args|
|
6
|
+
Hubspot::Deprecator.build.deprecation_warning("hubspot:dump_properties")
|
7
|
+
|
8
|
+
hapikey = args[:hapikey] || ENV['HUBSPOT_API_KEY']
|
9
|
+
kind = args[:kind]
|
10
|
+
unless %w(contact deal).include?(kind)
|
11
|
+
raise ArgumentError, ':kind must be either "contact" or "deal"'
|
12
|
+
end
|
13
|
+
klass = kind == 'contact' ? Hubspot::ContactProperties : Hubspot::DealProperties
|
14
|
+
props = Hubspot::Utils::dump_properties(klass, hapikey, build_filter(args))
|
15
|
+
if args[:file].blank?
|
16
|
+
puts JSON.pretty_generate(props)
|
17
|
+
else
|
18
|
+
File.open(args[:file], 'w') do |f|
|
19
|
+
f.write(JSON.pretty_generate(props))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Restore properties from file'
|
25
|
+
task :restore_properties, [:kind, :file, :hapikey, :dry_run] do |_, args|
|
26
|
+
Hubspot::Deprecator.build.deprecation_warning("hubspot:restore_properties")
|
27
|
+
|
28
|
+
hapikey = args[:hapikey] || ENV['HUBSPOT_API_KEY']
|
29
|
+
if args[:file].blank?
|
30
|
+
raise ArgumentError, ':file is a required parameter'
|
31
|
+
end
|
32
|
+
kind = args[:kind]
|
33
|
+
unless %w(contact deal).include?(kind)
|
34
|
+
raise ArgumentError, ':kind must be either "contact" or "deal"'
|
35
|
+
end
|
36
|
+
klass = kind == 'contact' ? Hubspot::ContactProperties : Hubspot::DealProperties
|
37
|
+
file = File.read(args[:file])
|
38
|
+
props = JSON.parse(file)
|
39
|
+
Hubspot::Utils.restore_properties(klass, hapikey, props, args[:dry_run] != 'false')
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def build_filter(args)
|
45
|
+
{ include: val_to_array(args[:include]),
|
46
|
+
exclude: val_to_array(args[:exclude])
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def val_to_array(val)
|
51
|
+
val.blank? ? val : val.split(/\W+/)
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
FactoryBot.define do
|
3
|
+
factory :contact, class: Hubspot::Contact do
|
4
|
+
to_create { |instance| instance.save }
|
5
|
+
|
6
|
+
firstname { Faker::Name.first_name }
|
7
|
+
lastname { Faker::Name.last_name }
|
8
|
+
email { Faker::Internet.safe_email("#{Time.new.to_i.to_s[-5..-1]}#{(0..3).map { (65 + rand(26)).chr }.join}") }
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
RSpec.describe Hubspot do
|
2
|
+
describe ".configure" do
|
3
|
+
it "delegates .configure to Hubspot::Config.configure" do
|
4
|
+
options = { hapikey: "demo" }
|
5
|
+
allow(Hubspot::Config).to receive(:configure).with(options)
|
6
|
+
|
7
|
+
Hubspot.configure(options)
|
8
|
+
|
9
|
+
expect(Hubspot::Config).to have_received(:configure).with(options)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'timecop'
|
2
|
+
|
3
|
+
describe Hubspot do
|
4
|
+
before do
|
5
|
+
Hubspot.configure(hapikey: "demo")
|
6
|
+
Timecop.freeze(Time.utc(2012, 'Oct', 10))
|
7
|
+
end
|
8
|
+
|
9
|
+
after do
|
10
|
+
Timecop.return
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Hubspot::Blog do
|
14
|
+
describe ".list" do
|
15
|
+
it "returns a list of blogs" do
|
16
|
+
VCR.use_cassette("blog_list") do
|
17
|
+
result = Hubspot::Blog.list
|
18
|
+
|
19
|
+
assert_requested :get, hubspot_api_url("/content/api/v2/blogs?hapikey=demo")
|
20
|
+
expect(result).to be_kind_of(Array)
|
21
|
+
expect(result.first).to be_a(Hubspot::Blog)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe ".find_by_id" do
|
27
|
+
it "retrieves a blog by id" do
|
28
|
+
VCR.use_cassette("blog_list") do
|
29
|
+
id = 351076997
|
30
|
+
result = Hubspot::Blog.find_by_id(id)
|
31
|
+
|
32
|
+
assert_requested :get, hubspot_api_url("/content/api/v2/blogs/#{id}?hapikey=demo")
|
33
|
+
expect(result).to be_a(Hubspot::Blog)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#[]" do
|
39
|
+
it "returns the value for the given key" do
|
40
|
+
data = {
|
41
|
+
"id" => 123,
|
42
|
+
"name" => "Demo",
|
43
|
+
}
|
44
|
+
blog = Hubspot::Blog.new(data)
|
45
|
+
|
46
|
+
expect(blog["id"]).to eq(data["id"])
|
47
|
+
expect(blog["name"]).to eq(data["name"])
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when the value is unknown" do
|
51
|
+
it "returns nil" do
|
52
|
+
blog = Hubspot::Blog.new({})
|
53
|
+
|
54
|
+
expect(blog["nope"]).to be_nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#posts" do
|
60
|
+
it "returns published blog posts created in the last 2 months" do
|
61
|
+
VCR.use_cassette("blog_posts/all_blog_posts") do
|
62
|
+
blog_id = 123
|
63
|
+
created_gt = timestamp_in_milliseconds(Time.now - 2.months)
|
64
|
+
blog = Hubspot::Blog.new({ "id" => blog_id })
|
65
|
+
|
66
|
+
result = blog.posts
|
67
|
+
|
68
|
+
assert_requested :get, hubspot_api_url("/content/api/v2/blog-posts?content_group_id=#{blog_id}&created__gt=#{created_gt}&hapikey=demo&order_by=-created&state=PUBLISHED")
|
69
|
+
expect(result).to be_kind_of(Array)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it "includes given parameters in the request" do
|
74
|
+
VCR.use_cassette("blog_posts/filter_blog_posts") do
|
75
|
+
blog_id = 123
|
76
|
+
created_gt = timestamp_in_milliseconds(Time.now - 2.months)
|
77
|
+
blog = Hubspot::Blog.new({ "id" => 123 })
|
78
|
+
|
79
|
+
result = blog.posts({ state: "DRAFT" })
|
80
|
+
|
81
|
+
assert_requested :get, hubspot_api_url("/content/api/v2/blog-posts?content_group_id=#{blog_id}&created__gt=#{created_gt}&hapikey=demo&order_by=-created&state=DRAFT")
|
82
|
+
expect(result).to be_kind_of(Array)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it "raises when given an unknown state" do
|
87
|
+
blog = Hubspot::Blog.new({})
|
88
|
+
|
89
|
+
expect {
|
90
|
+
blog.posts({ state: "unknown" })
|
91
|
+
}.to raise_error(Hubspot::InvalidParams, "State parameter was invalid")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe Hubspot::BlogPost do
|
97
|
+
describe "#created_at" do
|
98
|
+
it "returns the created timestamp as a Time" do
|
99
|
+
timestamp = timestamp_in_milliseconds(Time.now)
|
100
|
+
blog_post = Hubspot::BlogPost.new({ "created" => timestamp })
|
101
|
+
|
102
|
+
expect(blog_post.created_at).to eq(Time.at(timestamp/1000))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe ".find_by_blog_post_id" do
|
107
|
+
it "retrieves a blog post by id" do
|
108
|
+
VCR.use_cassette "blog_posts" do
|
109
|
+
blog_post_id = 422192866
|
110
|
+
|
111
|
+
result = Hubspot::BlogPost.find_by_blog_post_id(blog_post_id)
|
112
|
+
|
113
|
+
assert_requested :get, hubspot_api_url("/content/api/v2/blog-posts/#{blog_post_id}?hapikey=demo")
|
114
|
+
expect(result).to be_a(Hubspot::BlogPost)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "#topics" do
|
120
|
+
it "returns the list of topics" do
|
121
|
+
VCR.use_cassette "blog_posts" do
|
122
|
+
blog_post = Hubspot::BlogPost.find_by_blog_post_id(422192866)
|
123
|
+
|
124
|
+
topics = blog_post.topics
|
125
|
+
|
126
|
+
expect(topics).to be_kind_of(Array)
|
127
|
+
expect(topics.first).to be_a(Hubspot::Topic)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "when the blog post does not have topics" do
|
132
|
+
it "returns an empty list" do
|
133
|
+
blog_post = Hubspot::BlogPost.new({ "topic_ids" => [] })
|
134
|
+
|
135
|
+
topics = blog_post.topics
|
136
|
+
|
137
|
+
expect(topics).to be_empty
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def hubspot_api_url(path)
|
144
|
+
URI.join(Hubspot::Config.base_url, path)
|
145
|
+
end
|
146
|
+
|
147
|
+
def timestamp_in_milliseconds(time)
|
148
|
+
time.to_i * 1000
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,410 @@
|
|
1
|
+
RSpec.describe Hubspot::CompanyProperties do
|
2
|
+
describe '.add_default_parameters' do
|
3
|
+
let(:opts) { {} }
|
4
|
+
subject { Hubspot::CompanyProperties.add_default_parameters(opts) }
|
5
|
+
context 'default parameters' do
|
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
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
before { Hubspot.configure(hapikey: ENV.fetch("HUBSPOT_HAPI_KEY", "demo")) }
|
18
|
+
|
19
|
+
describe ".all" do
|
20
|
+
it "should return all properties" do
|
21
|
+
VCR.use_cassette "company_properties/all_properties" do
|
22
|
+
Hubspot::CompanyProperties.create!({
|
23
|
+
"name" => "fax_number",
|
24
|
+
"label" => "Fax Number",
|
25
|
+
"description" => "The company fax number.",
|
26
|
+
"groupName" => "companyinformation",
|
27
|
+
"type" => "string",
|
28
|
+
"fieldType" => "text"
|
29
|
+
})
|
30
|
+
|
31
|
+
response = Hubspot::CompanyProperties.all
|
32
|
+
|
33
|
+
assert_hubspot_api_request(:get, "/properties/v1/companies/properties")
|
34
|
+
|
35
|
+
company_property_names = response.map { |property| property["name"] }
|
36
|
+
expect(company_property_names).to include("fax_number")
|
37
|
+
|
38
|
+
Hubspot::CompanyProperties.delete!("fax_number")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "with included groups" do
|
43
|
+
it "should return properties for the specified group[s]" do
|
44
|
+
VCR.use_cassette("company_properties/filter_by_group") do
|
45
|
+
group_a = "socialmediainformation"
|
46
|
+
group_b = "companyinformation"
|
47
|
+
|
48
|
+
Hubspot::CompanyProperties.create!({
|
49
|
+
"name" => "instagram_handle",
|
50
|
+
"label" => "Instagram Handle",
|
51
|
+
"description" => "The company's Instagram handle.",
|
52
|
+
"groupName" => group_a,
|
53
|
+
"type" => "string",
|
54
|
+
"fieldType" => "text"
|
55
|
+
})
|
56
|
+
Hubspot::CompanyProperties.create!({
|
57
|
+
"name" => "fax_number",
|
58
|
+
"label" => "Fax Number",
|
59
|
+
"description" => "The company fax number.",
|
60
|
+
"groupName" => group_b,
|
61
|
+
"type" => "string",
|
62
|
+
"fieldType" => "text"
|
63
|
+
})
|
64
|
+
|
65
|
+
response = Hubspot::CompanyProperties.all({}, { include: [group_a] })
|
66
|
+
|
67
|
+
assert_hubspot_api_request(:get, "/properties/v1/companies/properties")
|
68
|
+
|
69
|
+
group_names = response.map { |property| property["groupName"] }
|
70
|
+
expect(group_names).to include(group_a)
|
71
|
+
expect(group_names).not_to include(group_b)
|
72
|
+
|
73
|
+
Hubspot::CompanyProperties.delete!("instagram_handle")
|
74
|
+
Hubspot::CompanyProperties.delete!("fax_number")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "with excluded groups" do
|
80
|
+
it "does not return properties in the exluded groups(s)" do
|
81
|
+
VCR.use_cassette("company_properties/exclude_by_group") do
|
82
|
+
group_a = "socialmediainformation"
|
83
|
+
group_b = "companyinformation"
|
84
|
+
|
85
|
+
Hubspot::CompanyProperties.create!({
|
86
|
+
"name" => "instagram_handle",
|
87
|
+
"label" => "Instagram Handle",
|
88
|
+
"description" => "The company's Instagram handle.",
|
89
|
+
"groupName" => group_a,
|
90
|
+
"type" => "string",
|
91
|
+
"fieldType" => "text"
|
92
|
+
})
|
93
|
+
Hubspot::CompanyProperties.create!({
|
94
|
+
"name" => "fax_number",
|
95
|
+
"label" => "Fax Number",
|
96
|
+
"description" => "The company fax number.",
|
97
|
+
"groupName" => group_b,
|
98
|
+
"type" => "string",
|
99
|
+
"fieldType" => "text"
|
100
|
+
})
|
101
|
+
|
102
|
+
response = Hubspot::CompanyProperties.all({}, { exclude: [group_a] })
|
103
|
+
|
104
|
+
assert_hubspot_api_request(:get, "/properties/v1/companies/properties")
|
105
|
+
|
106
|
+
group_names = response.map { |property| property["groupName"] }
|
107
|
+
expect(group_names).not_to include(group_a)
|
108
|
+
expect(group_names).to include(group_b)
|
109
|
+
|
110
|
+
Hubspot::CompanyProperties.delete!("fax_number")
|
111
|
+
Hubspot::CompanyProperties.delete!("instagram_handle")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe ".create!" do
|
117
|
+
it "creates a company property" do
|
118
|
+
VCR.use_cassette("company_properties/create_property") do
|
119
|
+
name = "fax_number"
|
120
|
+
|
121
|
+
create_params = {
|
122
|
+
"name" => name,
|
123
|
+
"groupName" => "companyinformation",
|
124
|
+
}
|
125
|
+
|
126
|
+
response = Hubspot::CompanyProperties.create!(create_params)
|
127
|
+
|
128
|
+
assert_hubspot_api_request(
|
129
|
+
:post,
|
130
|
+
"/properties/v1/companies/properties",
|
131
|
+
body: {
|
132
|
+
name: name,
|
133
|
+
groupName: "companyinformation",
|
134
|
+
options: []
|
135
|
+
}
|
136
|
+
)
|
137
|
+
expect(response["name"]).to eq(name)
|
138
|
+
|
139
|
+
Hubspot::CompanyProperties.delete!(name)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context "with no valid parameters" do
|
144
|
+
it "returns nil" do
|
145
|
+
response = Hubspot::CompanyProperties.create!({})
|
146
|
+
|
147
|
+
expect(response).to be nil
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe ".update!" do
|
153
|
+
it "updates the company property" do
|
154
|
+
VCR.use_cassette "company_properties/update_property" do
|
155
|
+
name = "fax_number"
|
156
|
+
label = "fax number label"
|
157
|
+
new_label = "new fax number label"
|
158
|
+
|
159
|
+
Hubspot::CompanyProperties.create!({
|
160
|
+
"groupName" => "companyinformation",
|
161
|
+
"label" => label,
|
162
|
+
"name" => name,
|
163
|
+
"type" => "string",
|
164
|
+
})
|
165
|
+
|
166
|
+
update_params = {
|
167
|
+
"groupName" => "companyinformation",
|
168
|
+
"label" => new_label,
|
169
|
+
"type" => "string",
|
170
|
+
}
|
171
|
+
|
172
|
+
response = Hubspot::CompanyProperties.update!(name, update_params)
|
173
|
+
|
174
|
+
assert_hubspot_api_request(
|
175
|
+
:put,
|
176
|
+
"/properties/v1/companies/properties/named",
|
177
|
+
body: {
|
178
|
+
groupName: "companyinformation",
|
179
|
+
label: new_label,
|
180
|
+
options: [],
|
181
|
+
type: "string",
|
182
|
+
}
|
183
|
+
)
|
184
|
+
expect(response["label"]).to eq(new_label)
|
185
|
+
|
186
|
+
Hubspot::CompanyProperties.delete!(name)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context "with no valid parameters" do
|
191
|
+
it "returns nil" do
|
192
|
+
company_property_name = "fax_number"
|
193
|
+
params = {}
|
194
|
+
|
195
|
+
response = Hubspot::CompanyProperties.update!(
|
196
|
+
company_property_name,
|
197
|
+
params
|
198
|
+
)
|
199
|
+
|
200
|
+
expect(response).to be nil
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
describe ".delete!" do
|
206
|
+
it "deletes the company property" do
|
207
|
+
VCR.use_cassette("company_properties/delete_property") do
|
208
|
+
name = "fax_number"
|
209
|
+
|
210
|
+
Hubspot::CompanyProperties.create!({
|
211
|
+
"groupName" => "companyinformation",
|
212
|
+
"name" => name,
|
213
|
+
"type" => "string",
|
214
|
+
})
|
215
|
+
|
216
|
+
response = Hubspot::CompanyProperties.delete!(name)
|
217
|
+
|
218
|
+
assert_hubspot_api_request(
|
219
|
+
:delete,
|
220
|
+
"properties/v1/companies/properties/named/#{name}"
|
221
|
+
)
|
222
|
+
|
223
|
+
expect(response).to be nil
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
context "when the company property does not exist" do
|
228
|
+
it "raises an error" do
|
229
|
+
VCR.use_cassette("company_properties/delete_non_property") do
|
230
|
+
expect {
|
231
|
+
Hubspot::CompanyProperties.delete!("non-existent")
|
232
|
+
}.to raise_error(Hubspot::RequestError)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
describe "Groups" do
|
240
|
+
describe ".groups" do
|
241
|
+
it "returns all groups" do
|
242
|
+
VCR.use_cassette("company_properties/all_groups") do
|
243
|
+
group_name = "group_awesome"
|
244
|
+
Hubspot::CompanyProperties.create_group!(name: group_name)
|
245
|
+
|
246
|
+
response = Hubspot::CompanyProperties.groups
|
247
|
+
|
248
|
+
assert_hubspot_api_request(:get, "/properties/v1/companies/groups")
|
249
|
+
|
250
|
+
group_names = response.map { |group| group["name"] }
|
251
|
+
expect(group_names).to include(group_name)
|
252
|
+
|
253
|
+
Hubspot::CompanyProperties.delete_group!(group_name)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context "when included groups are provided" do
|
258
|
+
it "returns the specified groups" do
|
259
|
+
VCR.use_cassette("company_properties/groups_included") do
|
260
|
+
group_a = "socialmediainformation"
|
261
|
+
group_b = "companyinformation"
|
262
|
+
|
263
|
+
Hubspot::CompanyProperties.create!({
|
264
|
+
"name" => "instagram_handle",
|
265
|
+
"label" => "Instagram Handle",
|
266
|
+
"description" => "The company's Instagram handle.",
|
267
|
+
"groupName" => group_a,
|
268
|
+
"type" => "string",
|
269
|
+
"fieldType" => "text"
|
270
|
+
})
|
271
|
+
Hubspot::CompanyProperties.create!({
|
272
|
+
"name" => "fax_number",
|
273
|
+
"label" => "Fax Number",
|
274
|
+
"description" => "The company fax number.",
|
275
|
+
"groupName" => group_b,
|
276
|
+
"type" => "string",
|
277
|
+
"fieldType" => "text"
|
278
|
+
})
|
279
|
+
|
280
|
+
response = Hubspot::CompanyProperties.groups(
|
281
|
+
{},
|
282
|
+
{ include: group_a }
|
283
|
+
)
|
284
|
+
|
285
|
+
assert_hubspot_api_request(:get, "/properties/v1/companies/groups")
|
286
|
+
|
287
|
+
Hubspot::CompanyProperties.delete!("instagram_handle")
|
288
|
+
Hubspot::CompanyProperties.delete!("fax_number")
|
289
|
+
|
290
|
+
group_names = response.map { |group| group["name"] }
|
291
|
+
expect(group_names).to include(group_a)
|
292
|
+
expect(group_names).not_to include(group_b)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
context "when excluded groups are provided" do
|
298
|
+
it "returns groups that were not excluded" do
|
299
|
+
VCR.use_cassette("company_properties/groups_excluded") do
|
300
|
+
group_a = "socialmediainformation"
|
301
|
+
group_b = "companyinformation"
|
302
|
+
|
303
|
+
Hubspot::CompanyProperties.create!({
|
304
|
+
"name" => "instagram_handle",
|
305
|
+
"label" => "Instagram Handle",
|
306
|
+
"description" => "The company's Instagram handle.",
|
307
|
+
"groupName" => group_a,
|
308
|
+
"type" => "string",
|
309
|
+
"fieldType" => "text"
|
310
|
+
})
|
311
|
+
Hubspot::CompanyProperties.create!({
|
312
|
+
"name" => "fax_number",
|
313
|
+
"label" => "Fax Number",
|
314
|
+
"description" => "The company fax number.",
|
315
|
+
"groupName" => group_b,
|
316
|
+
"type" => "string",
|
317
|
+
"fieldType" => "text"
|
318
|
+
})
|
319
|
+
|
320
|
+
response = Hubspot::CompanyProperties.groups(
|
321
|
+
{},
|
322
|
+
{ exclude: group_a }
|
323
|
+
)
|
324
|
+
|
325
|
+
assert_hubspot_api_request(:get, "/properties/v1/companies/groups")
|
326
|
+
|
327
|
+
group_names = response.map { |group| group["name"] }
|
328
|
+
expect(group_names).not_to include(group_a)
|
329
|
+
expect(group_names).to include(group_b)
|
330
|
+
|
331
|
+
Hubspot::CompanyProperties.delete!("instagram_handle")
|
332
|
+
Hubspot::CompanyProperties.delete!("fax_number")
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
let(:params) { { 'name' => 'ff_group1', 'displayName' => 'Test Group One', 'displayOrder' => 100, 'badParam' => 99 } }
|
339
|
+
|
340
|
+
describe '.create_group!' do
|
341
|
+
context 'with no valid parameters' do
|
342
|
+
it 'should return nil' do
|
343
|
+
expect(Hubspot::CompanyProperties.create_group!({})).to be(nil)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
context 'with mixed parameters' do
|
348
|
+
cassette 'company_properties/create_group'
|
349
|
+
|
350
|
+
it 'should return the valid parameters' do
|
351
|
+
response = Hubspot::CompanyProperties.create_group!(params)
|
352
|
+
expect(Hubspot::CompanyProperties.same?(response, params)).to be true
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
context 'with some valid parameters' do
|
357
|
+
cassette 'company_properties/create_group_some_params'
|
358
|
+
|
359
|
+
let(:sub_params) { params.select { |k, _| k != 'displayName' } }
|
360
|
+
|
361
|
+
it 'should return the valid parameters' do
|
362
|
+
params['name'] = 'ff_group235'
|
363
|
+
response = Hubspot::CompanyProperties.create_group!(sub_params)
|
364
|
+
expect(Hubspot::CompanyProperties.same?(response, sub_params)).to be true
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
describe '.update_group!' do
|
370
|
+
context 'with no valid parameters' do
|
371
|
+
|
372
|
+
it 'should return nil ' do
|
373
|
+
expect(Hubspot::CompanyProperties.update_group!(params['name'], {})).to be(nil)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
context 'with mixed parameters' do
|
378
|
+
cassette 'company_properties/update_group'
|
379
|
+
|
380
|
+
it 'should return the valid parameters' do
|
381
|
+
params['displayName'] = 'Test Group OneA'
|
382
|
+
|
383
|
+
response = Hubspot::CompanyProperties.update_group!(params['name'], params)
|
384
|
+
expect(Hubspot::CompanyProperties.same?(response, params)).to be true
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
end
|
389
|
+
|
390
|
+
describe '.delete_group!' do
|
391
|
+
let(:name) { params['name'] }
|
392
|
+
|
393
|
+
context 'with existing group' do
|
394
|
+
cassette 'company_properties/delete_group'
|
395
|
+
|
396
|
+
it 'should return nil' do
|
397
|
+
expect(Hubspot::CompanyProperties.delete_group!(name)).to eq(nil)
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
context 'with non-existent group' do
|
402
|
+
cassette 'company_properties/delete_non_group'
|
403
|
+
|
404
|
+
it 'should raise an error' do
|
405
|
+
expect { Hubspot::CompanyProperties.delete_group!(name) }.to raise_error(Hubspot::RequestError)
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|