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.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/Gemfile +3 -0
  4. data/Guardfile +9 -0
  5. data/LICENSE.txt +18 -0
  6. data/README.md +295 -0
  7. data/RELEASING.md +6 -0
  8. data/Rakefile +32 -0
  9. data/hubspot-api-ruby.gemspec +42 -0
  10. data/lib/hubspot-api-ruby.rb +39 -0
  11. data/lib/hubspot/blog.rb +98 -0
  12. data/lib/hubspot/collection.rb +41 -0
  13. data/lib/hubspot/company.rb +160 -0
  14. data/lib/hubspot/company_properties.rb +59 -0
  15. data/lib/hubspot/config.rb +63 -0
  16. data/lib/hubspot/connection.rb +152 -0
  17. data/lib/hubspot/contact.rb +110 -0
  18. data/lib/hubspot/contact_list.rb +129 -0
  19. data/lib/hubspot/contact_properties.rb +59 -0
  20. data/lib/hubspot/deal.rb +173 -0
  21. data/lib/hubspot/deal_pipeline.rb +58 -0
  22. data/lib/hubspot/deal_properties.rb +59 -0
  23. data/lib/hubspot/deprecator.rb +7 -0
  24. data/lib/hubspot/engagement.rb +222 -0
  25. data/lib/hubspot/event.rb +21 -0
  26. data/lib/hubspot/exceptions.rb +18 -0
  27. data/lib/hubspot/file.rb +38 -0
  28. data/lib/hubspot/form.rb +95 -0
  29. data/lib/hubspot/oauth.rb +50 -0
  30. data/lib/hubspot/owner.rb +57 -0
  31. data/lib/hubspot/paged_collection.rb +35 -0
  32. data/lib/hubspot/properties.rb +123 -0
  33. data/lib/hubspot/railtie.rb +10 -0
  34. data/lib/hubspot/resource.rb +270 -0
  35. data/lib/hubspot/subscription.rb +37 -0
  36. data/lib/hubspot/topic.rb +37 -0
  37. data/lib/hubspot/utils.rb +127 -0
  38. data/lib/tasks/hubspot.rake +53 -0
  39. data/spec/factories/companies.rb +9 -0
  40. data/spec/factories/contacts.rb +10 -0
  41. data/spec/lib/hubspot-ruby_spec.rb +12 -0
  42. data/spec/lib/hubspot/blog_spec.rb +150 -0
  43. data/spec/lib/hubspot/company_properties_spec.rb +410 -0
  44. data/spec/lib/hubspot/company_spec.rb +340 -0
  45. data/spec/lib/hubspot/config_spec.rb +87 -0
  46. data/spec/lib/hubspot/connection_spec.rb +214 -0
  47. data/spec/lib/hubspot/contact_list_spec.rb +301 -0
  48. data/spec/lib/hubspot/contact_properties_spec.rb +245 -0
  49. data/spec/lib/hubspot/contact_spec.rb +223 -0
  50. data/spec/lib/hubspot/deal_pipeline_spec.rb +85 -0
  51. data/spec/lib/hubspot/deal_properties_spec.rb +262 -0
  52. data/spec/lib/hubspot/deal_spec.rb +185 -0
  53. data/spec/lib/hubspot/deprecator_spec.rb +15 -0
  54. data/spec/lib/hubspot/engagement_spec.rb +177 -0
  55. data/spec/lib/hubspot/event_spec.rb +33 -0
  56. data/spec/lib/hubspot/file_spec.rb +38 -0
  57. data/spec/lib/hubspot/form_spec.rb +189 -0
  58. data/spec/lib/hubspot/owner_spec.rb +56 -0
  59. data/spec/lib/hubspot/properties_spec.rb +45 -0
  60. data/spec/lib/hubspot/resource_spec.rb +54 -0
  61. data/spec/lib/hubspot/topic_spec.rb +23 -0
  62. data/spec/lib/hubspot/utils_spec.rb +164 -0
  63. data/spec/lib/tasks/hubspot_spec.rb +119 -0
  64. data/spec/shared_examples/saveable_resource.rb +45 -0
  65. data/spec/shared_examples/updateable_resource.rb +87 -0
  66. data/spec/spec_helper.rb +44 -0
  67. data/spec/support/capture_output.rb +21 -0
  68. data/spec/support/cassette_helper.rb +19 -0
  69. data/spec/support/hubspot_api_helpers.rb +13 -0
  70. data/spec/support/rake.rb +46 -0
  71. data/spec/support/tests_helper.rb +17 -0
  72. data/spec/support/vcr.rb +16 -0
  73. 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,9 @@
1
+
2
+ FactoryBot.define do
3
+ factory :company, class: Hubspot::Company do
4
+ to_create { |instance| instance.save }
5
+
6
+ add_attribute(:name) { Faker::Company.name }
7
+ add_attribute(:domain) { Faker::Internet.domain_name }
8
+ end
9
+ 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